Download raw body.
if_pflow IPFIX NAT support
I sent out an earlier version of this years ago, but finally
got it tested and working. This adds template fields (as per the
IANA IPFIX IE list) and generates the new templates for pflow's
IPFIX (Netflow v10) exporter to explain these fields to
collectors.
In the earlier pflow days, pflow would actually send the pre-
NAT IPs as source or destination for flows in one direction and
post-NAT for the other. When IPFIX was added, the v5/9 exporter
was corrected to show only post-NAT IPs, with the intention
to extend the templates and support NAT:
Index: if_pflow.c
===================================================================
RCS file: /cvs/src/sys/net/if_pflow.c,v
retrieving revision 1.111
diff -u -p -u -r1.111 if_pflow.c
--- if_pflow.c 7 Jul 2025 02:28:50 -0000 1.111
+++ if_pflow.c 20 Sep 2025 03:40:58 -0000
@@ -89,29 +89,34 @@ int pflowioctl(struct ifnet *, u_long, c
struct mbuf *pflow_get_mbuf(struct pflow_softc *, u_int16_t);
void pflow_flush(struct pflow_softc *);
int pflow_sendout_v5(struct pflow_softc *);
-int pflow_sendout_ipfix(struct pflow_softc *, sa_family_t);
+int pflow_sendout_ipfix(struct pflow_softc *, sa_family_t, size_t, u_int16_t);
int pflow_sendout_ipfix_tmpl(struct pflow_softc *);
int pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
void pflow_timeout(void *);
void pflow_timeout6(void *);
void pflow_timeout_tmpl(void *);
+void pflow_timeout_nat(void *);
void copy_flow_data(struct pflow_flow *, struct pflow_flow *,
struct pf_state *, struct pf_state_key *, int, int);
void copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *,
struct pflow_ipfix_flow4 *, struct pf_state *, struct pf_state_key *,
struct pflow_softc *, int, int);
+void copy_flow_ipfix_nat_4_data(struct pflow_ipfix_nat_flow4 *,
+ struct pflow_ipfix_nat_flow4 *, struct pf_state *,
+ struct pf_state_key *, struct pf_state_key *,
+ struct pflow_softc *, int, int);
void copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *,
struct pflow_ipfix_flow6 *, struct pf_state *, struct pf_state_key *,
struct pflow_softc *, int, int);
int pflow_pack_flow(struct pf_state *, struct pf_state_key *,
struct pflow_softc *);
int pflow_pack_flow_ipfix(struct pf_state *, struct pf_state_key *,
- struct pflow_softc *);
+ struct pf_state_key *, struct pflow_softc *);
int export_pflow_if(struct pf_state*, struct pf_state_key *,
- struct pflow_softc *);
+ struct pf_state_key *, struct pflow_softc *);
int copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
-int copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow,
- struct pflow_softc *sc);
+int copy_flow_ipfix_4_to_m(void *flow, size_t size,
+ struct pflow_softc *sc, u_int16_t tmpl);
int copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow,
struct pflow_softc *sc);
@@ -172,8 +177,8 @@ pflow_clone_create(struct if_clone *ifc,
/* ipfix IPv4 template */
pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.tmpl_id =
htons(PFLOW_IPFIX_TMPL_IPV4_ID);
- pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.field_count
- = htons(PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.field_count =
+ htons(PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.field_id =
htons(PFIX_IE_sourceIPv4Address);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.len = htons(4);
@@ -211,6 +216,60 @@ pflow_clone_create(struct if_clone *ifc,
htons(PFIX_IE_protocolIdentifier);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.len = htons(1);
+ /* ipfix IPv4 NAT template */
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.h.tmpl_id =
+ htons(PFLOW_IPFIX_TMPL_NAT_IPV4_ID);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.h.field_count =
+ htons(PFLOW_IPFIX_TMPL_NAT_IPV4_FIELD_COUNT);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.src_ip.field_id =
+ htons(PFIX_IE_sourceIPv4Address);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.src_ip.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.dest_ip.field_id =
+ htons(PFIX_IE_destinationIPv4Address);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.dest_ip.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.if_index_in.field_id =
+ htons(PFIX_IE_ingressInterface);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.if_index_in.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.if_index_out.field_id =
+ htons(PFIX_IE_egressInterface);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.if_index_out.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.packets.field_id =
+ htons(PFIX_IE_packetDeltaCount);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.packets.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.octets.field_id =
+ htons(PFIX_IE_octetDeltaCount);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.octets.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.start.field_id =
+ htons(PFIX_IE_flowStartMilliseconds);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.start.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.finish.field_id =
+ htons(PFIX_IE_flowEndMilliseconds);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.finish.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_src_ip.field_id =
+ htons(PFIX_IE_postNATSourceIPv4Address);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_src_ip.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_dest_ip.field_id =
+ htons(PFIX_IE_postNATDestinationIPv4Address);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_dest_ip.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_src_port.field_id =
+ htons(PFIX_IE_postNAPTSourceTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_src_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_dest_port.field_id =
+ htons(PFIX_IE_postNAPTDestinationTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.post_dest_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.src_port.field_id =
+ htons(PFIX_IE_sourceTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.src_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.dest_port.field_id =
+ htons(PFIX_IE_destinationTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.dest_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.tos.field_id =
+ htons(PFIX_IE_ipClassOfService);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.tos.len = htons(1);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.protocol.field_id =
+ htons(PFIX_IE_protocolIdentifier);
+ pflowif->sc_tmpl_ipfix.ipv4_nat_tmpl.protocol.len = htons(1);
+
/* ipfix IPv6 template */
pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.tmpl_id =
htons(PFLOW_IPFIX_TMPL_IPV6_ID);
@@ -270,6 +329,7 @@ pflow_clone_create(struct if_clone *ifc,
timeout_set_proc(&pflowif->sc_tmo, pflow_timeout, pflowif);
timeout_set_proc(&pflowif->sc_tmo6, pflow_timeout6, pflowif);
timeout_set_proc(&pflowif->sc_tmo_tmpl, pflow_timeout_tmpl, pflowif);
+ timeout_set_proc(&pflowif->sc_tmo_nat, pflow_timeout_nat, pflowif);
task_set(&pflowif->sc_outputtask, pflow_output_process, pflowif);
@@ -302,6 +362,7 @@ pflow_clone_destroy(struct ifnet *ifp)
timeout_del(&sc->sc_tmo);
timeout_del(&sc->sc_tmo6);
timeout_del(&sc->sc_tmo_tmpl);
+ timeout_del(&sc->sc_tmo_nat);
pflow_flush(sc);
taskq_del_barrier(net_tq(ifp->if_index), &sc->sc_outputtask);
@@ -618,7 +679,7 @@ int
pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz)
{
sc->sc_maxcount4 = (mtu - hdrsz -
- sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow4);
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_nat_flow4);
sc->sc_maxcount6 = (mtu - hdrsz -
sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow6);
if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
@@ -626,7 +687,7 @@ pflow_calc_mtu(struct pflow_softc *sc, i
if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
sc->sc_maxcount6 = PFLOW_MAXFLOWS;
return (hdrsz + sizeof(struct udpiphdr) +
- MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_flow4),
+ MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_nat_flow4),
sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6)));
}
@@ -787,6 +848,59 @@ copy_flow_ipfix_4_data(struct pflow_ipfi
}
void
+copy_flow_ipfix_nat_4_data(struct pflow_ipfix_nat_flow4 *flow1,
+ struct pflow_ipfix_nat_flow4 *flow2, struct pf_state *st,
+ struct pf_state_key *sk, struct pf_state_key *skw,
+ struct pflow_softc *sc, int src, int dst)
+{
+ flow1->src_ip = sk->addr[src].v4.s_addr;
+ flow1->dest_ip = sk->addr[dst].v4.s_addr;
+ flow2->src_ip = sk->addr[dst].v4.s_addr;
+ flow2->dest_ip = sk->addr[src].v4.s_addr;
+
+ flow1->post_src_ip = skw->addr[src].v4.s_addr;
+ flow1->post_dest_ip = skw->addr[dst].v4.s_addr;
+ flow1->post_src_port = skw->port[src];
+ flow1->post_dest_port = skw->port[dst];
+
+ flow2->post_src_ip = skw->addr[dst].v4.s_addr;
+ flow2->post_dest_ip = skw->addr[src].v4.s_addr;
+ flow2->post_src_port = skw->port[dst];
+ flow2->post_dest_port = skw->port[src];
+
+ flow1->if_index_in = htonl(st->if_index_in);
+ flow1->if_index_out = htonl(st->if_index_out);
+ flow2->if_index_in = htonl(st->if_index_out);
+ flow2->if_index_out = htonl(st->if_index_in);
+
+ flow1->flow_packets = htobe64(st->packets[0]);
+ flow2->flow_packets = htobe64(st->packets[1]);
+ flow1->flow_octets = htobe64(st->bytes[0]);
+ flow2->flow_octets = htobe64(st->bytes[1]);
+
+ /*
+ * Pretend the flow was created when the machine came up when creation
+ * is in the future of the last time a package was seen due to pfsync.
+ */
+ if (st->creation > st->expire)
+ flow1->flow_start = flow2->flow_start = htobe64((gettime() -
+ getuptime())*1000);
+ else
+ flow1->flow_start = flow2->flow_start = htobe64((gettime() -
+ (getuptime() - st->creation))*1000);
+ flow1->flow_finish = flow2->flow_finish = htobe64((gettime() -
+ (getuptime() - st->expire))*1000);
+
+ flow1->src_port = sk->port[src];
+ flow1->dest_port = sk->port[dst];
+ flow2->src_port = sk->port[dst];
+ flow2->dest_port = sk->port[src];
+
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr ? st->rule.ptr->tos : 0;
+}
+
+void
copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
struct pflow_ipfix_flow6 *flow2, struct pf_state *st,
struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
@@ -829,20 +943,21 @@ int
export_pflow(struct pf_state *st)
{
struct pflow_softc *sc = NULL;
- struct pf_state_key *sk;
+ struct pf_state_key *sk, *skw;
sk = st->key[st->direction == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
+ skw = st->key[st->direction == PF_OUT ? PF_SK_WIRE : PF_SK_STACK];
SMR_SLIST_FOREACH(sc, &pflowif_list, sc_next) {
mtx_enter(&sc->sc_mtx);
switch (sc->sc_version) {
case PFLOW_PROTO_5:
if (sk->af == AF_INET)
- export_pflow_if(st, sk, sc);
+ export_pflow_if(st, sk, skw, sc);
break;
case PFLOW_PROTO_10:
if (sk->af == AF_INET || sk->af == AF_INET6)
- export_pflow_if(st, sk, sc);
+ export_pflow_if(st, sk, skw, sc);
break;
default: /* NOTREACHED */
break;
@@ -855,18 +970,20 @@ export_pflow(struct pf_state *st)
int
export_pflow_if(struct pf_state *st, struct pf_state_key *sk,
- struct pflow_softc *sc)
+ struct pf_state_key *skw, struct pflow_softc *sc)
{
struct pf_state pfs_copy;
struct ifnet *ifp = &sc->sc_if;
u_int64_t bytes[2];
int ret = 0;
+ printf("pflow: exporting flow, bytes[0]=%llu, bytes[1]=%llu\n",
+ st->bytes[0], st->bytes[1]);
if (!(ifp->if_flags & IFF_RUNNING))
return (0);
if (sc->sc_version == PFLOW_PROTO_10)
- return (pflow_pack_flow_ipfix(st, sk, sc));
+ return (pflow_pack_flow_ipfix(st, sk, skw, sc));
/* PFLOW_PROTO_5 */
if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
@@ -929,31 +1046,48 @@ copy_flow_to_m(struct pflow_flow *flow,
return(ret);
}
-int
-copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc)
+int copy_flow_ipfix_4_to_m(void *flow, size_t size, struct pflow_softc *sc,
+ u_int16_t tmpl)
{
int ret = 0;
MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
- if (sc->sc_mbuf == NULL) {
- if ((sc->sc_mbuf =
- pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV4_ID)) == NULL) {
- return (ENOBUFS);
- }
- sc->sc_count4 = 0;
- timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
- }
- m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
- (sc->sc_count4 * sizeof(struct pflow_ipfix_flow4)),
- sizeof(struct pflow_ipfix_flow4), flow, M_NOWAIT);
+printf("pflow: copying flow, tmpl=%d, count=%d\n", tmpl,
+ tmpl == PFLOW_IPFIX_TMPL_NAT_IPV4_ID ? sc->sc_count4_nat : sc->sc_count4);
- pflowstat_inc(pflow_flows);
- sc->sc_gcounter++;
- sc->sc_count4++;
+ if (tmpl == PFLOW_IPFIX_TMPL_NAT_IPV4_ID) {
+ if (sc->sc_mbuf_nat == NULL) {
+ if ((sc->sc_mbuf_nat = pflow_get_mbuf(sc, tmpl)) == NULL)
+ return (ENOBUFS);
+ sc->sc_count4_nat = 0;
+ timeout_add_sec(&sc->sc_tmo_nat, PFLOW_TIMEOUT);
+ }
+ m_copyback(sc->sc_mbuf_nat, PFLOW_SET_HDRLEN +
+ (sc->sc_count4_nat * size), size, flow, M_NOWAIT);
+ pflowstat_inc(pflow_flows);
+ sc->sc_gcounter++;
+ sc->sc_count4_nat++;
+
+ if (sc->sc_count4_nat >= sc->sc_maxcount4)
+ ret = pflow_sendout_ipfix(sc, AF_INET, size, tmpl);
+ } else {
+ if (sc->sc_mbuf == NULL) {
+ if ((sc->sc_mbuf = pflow_get_mbuf(sc, tmpl)) == NULL)
+ return (ENOBUFS);
+ sc->sc_count4 = 0;
+ timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
+ }
+ m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
+ (sc->sc_count4 * size), size, flow, M_NOWAIT);
+ pflowstat_inc(pflow_flows);
+ sc->sc_gcounter++;
+ sc->sc_count4++;
- if (sc->sc_count4 >= sc->sc_maxcount4)
- ret = pflow_sendout_ipfix(sc, AF_INET);
+ if (sc->sc_count4 >= sc->sc_maxcount4)
+ ret = pflow_sendout_ipfix(sc, AF_INET, size, tmpl);
+ }
+
return(ret);
}
@@ -961,6 +1095,7 @@ int
copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
{
int ret = 0;
+ int size = sizeof(struct pflow_ipfix_flow6);
MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
@@ -973,15 +1108,15 @@ copy_flow_ipfix_6_to_m(struct pflow_ipfi
timeout_add_sec(&sc->sc_tmo6, PFLOW_TIMEOUT);
}
m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
- (sc->sc_count6 * sizeof(struct pflow_ipfix_flow6)),
- sizeof(struct pflow_ipfix_flow6), flow, M_NOWAIT);
+ (sc->sc_count6 * size), size, flow, M_NOWAIT);
pflowstat_inc(pflow_flows);
sc->sc_gcounter++;
sc->sc_count6++;
if (sc->sc_count6 >= sc->sc_maxcount6)
- ret = pflow_sendout_ipfix(sc, AF_INET6);
+ ret = pflow_sendout_ipfix(sc, AF_INET6, size,
+ PFLOW_IPFIX_TMPL_IPV6_ID);
return(ret);
}
@@ -1013,27 +1148,60 @@ pflow_pack_flow(struct pf_state *st, str
int
pflow_pack_flow_ipfix(struct pf_state *st, struct pf_state_key *sk,
- struct pflow_softc *sc)
+ struct pf_state_key *skw, struct pflow_softc *sc)
{
struct pflow_ipfix_flow4 flow4_1, flow4_2;
+ struct pflow_ipfix_nat_flow4 natflow4_1, natflow4_2;
struct pflow_ipfix_flow6 flow6_1, flow6_2;
int ret = 0;
- if (sk->af == AF_INET) {
- bzero(&flow4_1, sizeof(flow4_1));
- bzero(&flow4_2, sizeof(flow4_2));
+ int is_nat = (sk != skw);
- if (st->direction == PF_OUT)
- copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
- 1, 0);
- else
- copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
- 0, 1);
-
- if (st->bytes[0] != 0) /* first flow from state */
- ret = copy_flow_ipfix_4_to_m(&flow4_1, sc);
+printf("pflow: is_nat=%d, af=%d\n", is_nat, sk->af);
- if (st->bytes[1] != 0) /* second flow from state */
- ret = copy_flow_ipfix_4_to_m(&flow4_2, sc);
+ if (sk->af == AF_INET) {
+ if (is_nat) {
+ bzero(&natflow4_1, sizeof(natflow4_1));
+ bzero(&natflow4_2, sizeof(natflow4_2));
+
+ if (st->direction == PF_OUT)
+ copy_flow_ipfix_nat_4_data(&natflow4_1,
+ &natflow4_2, st, sk, skw, sc, 1, 0);
+ else
+ copy_flow_ipfix_nat_4_data(&natflow4_1,
+ &natflow4_2, st, sk, skw, sc, 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow_ipfix_4_to_m(
+ (void *)&natflow4_1,
+ sizeof(natflow4_1), sc,
+ PFLOW_IPFIX_TMPL_NAT_IPV4_ID);
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow_ipfix_4_to_m(
+ (void *)&natflow4_2,
+ sizeof(natflow4_2), sc,
+ PFLOW_IPFIX_TMPL_NAT_IPV4_ID);
+ } else {
+ bzero(&flow4_1, sizeof(flow4_1));
+ bzero(&flow4_2, sizeof(flow4_2));
+
+ if (st->direction == PF_OUT)
+ copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st,
+ sk, sc, 1, 0);
+ else
+ copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st,
+ sk, sc, 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow_ipfix_4_to_m(
+ (void *)&flow4_1,
+ sizeof(flow4_1), sc,
+ PFLOW_IPFIX_TMPL_IPV4_ID);
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow_ipfix_4_to_m(
+ (void *)&flow4_2,
+ sizeof(flow4_2), sc,
+ PFLOW_IPFIX_TMPL_IPV4_ID);
+ }
} else if (sk->af == AF_INET6) {
bzero(&flow6_1, sizeof(flow6_1));
bzero(&flow6_2, sizeof(flow6_2));
@@ -1054,6 +1222,26 @@ pflow_pack_flow_ipfix(struct pf_state *s
return (ret);
}
+void pflow_timeout_nat(void *v)
+{
+ struct pflow_softc *sc = v;
+
+ mtx_enter(&sc->sc_mtx);
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ pflow_sendout_v5(sc);
+ break;
+ case PFLOW_PROTO_10:
+ pflow_sendout_ipfix(sc, AF_INET,
+ sizeof(struct pflow_ipfix_nat_flow4),
+ PFLOW_IPFIX_TMPL_NAT_IPV4_ID);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+ mtx_leave(&sc->sc_mtx);
+}
+
void
pflow_timeout(void *v)
{
@@ -1065,7 +1253,9 @@ pflow_timeout(void *v)
pflow_sendout_v5(sc);
break;
case PFLOW_PROTO_10:
- pflow_sendout_ipfix(sc, AF_INET);
+ pflow_sendout_ipfix(sc, AF_INET,
+ sizeof(struct pflow_ipfix_flow4),
+ PFLOW_IPFIX_TMPL_IPV4_ID);
break;
default: /* NOTREACHED */
break;
@@ -1079,7 +1269,9 @@ pflow_timeout6(void *v)
struct pflow_softc *sc = v;
mtx_enter(&sc->sc_mtx);
- pflow_sendout_ipfix(sc, AF_INET6);
+ pflow_sendout_ipfix(sc, AF_INET6,
+ sizeof(struct pflow_ipfix_flow6),
+ PFLOW_IPFIX_TMPL_IPV6_ID);
mtx_leave(&sc->sc_mtx);
}
@@ -1102,8 +1294,15 @@ pflow_flush(struct pflow_softc *sc)
pflow_sendout_v5(sc);
break;
case PFLOW_PROTO_10:
- pflow_sendout_ipfix(sc, AF_INET);
- pflow_sendout_ipfix(sc, AF_INET6);
+ pflow_sendout_ipfix(sc, AF_INET,
+ sizeof(struct pflow_ipfix_nat_flow4),
+ PFLOW_IPFIX_TMPL_NAT_IPV4_ID);
+ pflow_sendout_ipfix(sc, AF_INET,
+ sizeof(struct pflow_ipfix_flow4),
+ PFLOW_IPFIX_TMPL_IPV4_ID);
+ pflow_sendout_ipfix(sc, AF_INET6,
+ sizeof(struct pflow_ipfix_flow6),
+ PFLOW_IPFIX_TMPL_IPV6_ID);
break;
default: /* NOTREACHED */
break;
@@ -1148,7 +1347,7 @@ pflow_sendout_v5(struct pflow_softc *sc)
}
int
-pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af)
+pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af, size_t size, u_int16_t tmpl)
{
struct mbuf *m;
struct pflow_v10_header *h10;
@@ -1161,14 +1360,23 @@ pflow_sendout_ipfix(struct pflow_softc *
switch (af) {
case AF_INET:
- m = sc->sc_mbuf;
- timeout_del(&sc->sc_tmo);
- if (m == NULL)
- return (0);
- sc->sc_mbuf = NULL;
- count = sc->sc_count4;
+ if (tmpl == PFLOW_IPFIX_TMPL_NAT_IPV4_ID) {
+ m = sc->sc_mbuf_nat;
+ timeout_del(&sc->sc_tmo_nat);
+ if (m == NULL)
+ return (0);
+ sc->sc_mbuf_nat = NULL;
+ count = sc->sc_count4_nat;
+ } else {
+ m = sc->sc_mbuf;
+ timeout_del(&sc->sc_tmo);
+ if (m == NULL)
+ return (0);
+ sc->sc_mbuf = NULL;
+ count = sc->sc_count4;
+ }
set_length = sizeof(struct pflow_set_header)
- + sc->sc_count4 * sizeof(struct pflow_ipfix_flow4);
+ + count * size;
break;
case AF_INET6:
m = sc->sc_mbuf6;
@@ -1178,7 +1386,7 @@ pflow_sendout_ipfix(struct pflow_softc *
sc->sc_mbuf6 = NULL;
count = sc->sc_count6;
set_length = sizeof(struct pflow_set_header)
- + sc->sc_count6 * sizeof(struct pflow_ipfix_flow6);
+ + sc->sc_count6 * size;
break;
default:
unhandled_af(af);
Index: if_pflow.h
===================================================================
RCS file: /cvs/src/sys/net/if_pflow.h,v
retrieving revision 1.23
diff -u -p -u -r1.23 if_pflow.h
--- if_pflow.h 16 Dec 2023 22:16:02 -0000 1.23
+++ if_pflow.h 20 Sep 2025 03:40:58 -0000
@@ -33,22 +33,26 @@
/* RFC 5102 Information Element Identifiers */
-#define PFIX_IE_octetDeltaCount 1
-#define PFIX_IE_packetDeltaCount 2
-#define PFIX_IE_protocolIdentifier 4
-#define PFIX_IE_ipClassOfService 5
-#define PFIX_IE_sourceTransportPort 7
-#define PFIX_IE_sourceIPv4Address 8
-#define PFIX_IE_ingressInterface 10
-#define PFIX_IE_destinationTransportPort 11
-#define PFIX_IE_destinationIPv4Address 12
-#define PFIX_IE_egressInterface 14
-#define PFIX_IE_flowEndSysUpTime 21
-#define PFIX_IE_flowStartSysUpTime 22
-#define PFIX_IE_sourceIPv6Address 27
-#define PFIX_IE_destinationIPv6Address 28
-#define PFIX_IE_flowStartMilliseconds 152
-#define PFIX_IE_flowEndMilliseconds 153
+#define PFIX_IE_octetDeltaCount 1
+#define PFIX_IE_packetDeltaCount 2
+#define PFIX_IE_protocolIdentifier 4
+#define PFIX_IE_ipClassOfService 5
+#define PFIX_IE_sourceTransportPort 7
+#define PFIX_IE_sourceIPv4Address 8
+#define PFIX_IE_ingressInterface 10
+#define PFIX_IE_destinationTransportPort 11
+#define PFIX_IE_destinationIPv4Address 12
+#define PFIX_IE_egressInterface 14
+#define PFIX_IE_flowEndSysUpTime 21
+#define PFIX_IE_flowStartSysUpTime 22
+#define PFIX_IE_sourceIPv6Address 27
+#define PFIX_IE_destinationIPv6Address 28
+#define PFIX_IE_flowStartMilliseconds 152
+#define PFIX_IE_flowEndMilliseconds 153
+#define PFIX_IE_postNATSourceIPv4Address 225
+#define PFIX_IE_postNATDestinationIPv4Address 226
+#define PFIX_IE_postNAPTSourceTransportPort 227
+#define PFIX_IE_postNAPTDestinationTransportPort 228
struct pflow_flow {
u_int32_t src_ip;
@@ -110,6 +114,28 @@ struct pflow_ipfix_tmpl_ipv4 {
#define PFLOW_IPFIX_TMPL_IPV4_ID 256
} __packed;
+struct pflow_ipfix_tmpl_nat_ipv4 {
+ struct pflow_tmpl_hdr h;
+ struct pflow_tmpl_fspec src_ip;
+ struct pflow_tmpl_fspec dest_ip;
+ struct pflow_tmpl_fspec if_index_in;
+ struct pflow_tmpl_fspec if_index_out;
+ struct pflow_tmpl_fspec packets;
+ struct pflow_tmpl_fspec octets;
+ struct pflow_tmpl_fspec start;
+ struct pflow_tmpl_fspec finish;
+ struct pflow_tmpl_fspec post_src_ip;
+ struct pflow_tmpl_fspec post_dest_ip;
+ struct pflow_tmpl_fspec post_src_port;
+ struct pflow_tmpl_fspec post_dest_port;
+ struct pflow_tmpl_fspec src_port;
+ struct pflow_tmpl_fspec dest_port;
+ struct pflow_tmpl_fspec tos;
+ struct pflow_tmpl_fspec protocol;
+#define PFLOW_IPFIX_TMPL_NAT_IPV4_FIELD_COUNT 16
+#define PFLOW_IPFIX_TMPL_NAT_IPV4_ID 257
+} __packed;
+
/* update pflow_clone_create() when changing pflow_ipfix_tmpl_v6 */
struct pflow_ipfix_tmpl_ipv6 {
struct pflow_tmpl_hdr h;
@@ -126,13 +152,14 @@ struct pflow_ipfix_tmpl_ipv6 {
struct pflow_tmpl_fspec tos;
struct pflow_tmpl_fspec protocol;
#define PFLOW_IPFIX_TMPL_IPV6_FIELD_COUNT 12
-#define PFLOW_IPFIX_TMPL_IPV6_ID 257
+#define PFLOW_IPFIX_TMPL_IPV6_ID 258
} __packed;
struct pflow_ipfix_tmpl {
struct pflow_set_header set_header;
- struct pflow_ipfix_tmpl_ipv4 ipv4_tmpl;
- struct pflow_ipfix_tmpl_ipv6 ipv6_tmpl;
+ struct pflow_ipfix_tmpl_ipv4 ipv4_tmpl;
+ struct pflow_ipfix_tmpl_nat_ipv4 ipv4_nat_tmpl;
+ struct pflow_ipfix_tmpl_ipv6 ipv6_tmpl;
} __packed;
struct pflow_ipfix_flow4 {
@@ -151,6 +178,26 @@ struct pflow_ipfix_flow4 {
/* XXX padding needed? */
} __packed;
+struct pflow_ipfix_nat_flow4 {
+ u_int32_t src_ip; /* sourceIPv4Address*/
+ u_int32_t dest_ip; /* destinationIPv4Address */
+ u_int32_t if_index_in; /* ingressInterface */
+ u_int32_t if_index_out; /* egressInterface */
+ u_int64_t flow_packets; /* packetDeltaCount */
+ u_int64_t flow_octets; /* octetDeltaCount */
+ int64_t flow_start; /* flowStartMilliseconds */
+ int64_t flow_finish; /* flowEndMilliseconds */
+ u_int32_t post_src_ip; /* postNATSourceIPv4Address */
+ u_int32_t post_dest_ip; /* postNATDestinationIPv4Address */
+ u_int16_t post_src_port; /* postNAPTSourceTransportPort */
+ u_int16_t post_dest_port; /* postNAPTDestinationTransportPort */
+ u_int16_t src_port; /* sourceTransportPort */
+ u_int16_t dest_port; /* destinationTransportPort */
+ u_int8_t tos; /* ipClassOfService */
+ u_int8_t protocol; /* protocolIdentifier */
+ /* XXX padding needed? */
+} __packed;
+
struct pflow_ipfix_flow6 {
struct in6_addr src_ip; /* sourceIPv6Address */
struct in6_addr dest_ip; /* destinationIPv6Address */
@@ -187,6 +234,7 @@ struct pflow_softc {
unsigned int sc_count; /* [m] */
unsigned int sc_count4; /* [m] */
+ unsigned int sc_count4_nat; /* [m] */
unsigned int sc_count6; /* [m] */
unsigned int sc_maxcount; /* [m] */
unsigned int sc_maxcount4; /* [m] */
@@ -196,6 +244,7 @@ struct pflow_softc {
struct timeout sc_tmo;
struct timeout sc_tmo6;
struct timeout sc_tmo_tmpl;
+ struct timeout sc_tmo_nat;
struct mbuf_queue sc_outputqueue;
struct task sc_outputtask;
struct socket *so; /* [p] */
@@ -207,6 +256,8 @@ struct pflow_softc {
struct mbuf *sc_mbuf; /* [m] current cumulative
mbuf */
struct mbuf *sc_mbuf6; /* [m] current cumulative
+ mbuf */
+ struct mbuf *sc_mbuf_nat; /* [m] current cumulative
mbuf */
SMR_SLIST_ENTRY(pflow_softc) sc_next;
};
if_pflow IPFIX NAT support