From nobody Tue Jan 16 08:52:01 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4TDjS236Tcz579g7; Tue, 16 Jan 2024 08:52:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4TDjS16V41z4Pky; Tue, 16 Jan 2024 08:52:01 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1705395122; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=bIypJhqHgI+H+DQV8Dm1QaRaT8IwKP+gW/K/pvPk02Q=; b=hJ4NQxRnIAr5rHQAz6NtEIklKkUTrQSW32RFgA8GuC7utORw87kbhpqUcMiaiwzncvr4U2 VFOL0w2aM1ECVfIHCx6H8b59F+Uv0Vq4WCIets5isMNu3YRfsjqLXoG0AE/SdUyx4JVH58 0jUYz5Rnydgke1bjPUCj/ZDa0LVGH8ZAf02D9uJSZk4LcTZaDbLcBD1xm4KjhR0Z1Lhc4I EoIx4ydyYspsAMNyP8e5GW9E5lIUfCe9BUlJvYTP9hNVbJiTDjY+3uaC6M8TjvrrUTzGWg pyZnGMoBUzGqEUDrMvStV1lAJDCQLJ8NwM6HYOG8ROfTaEFNrOzyaaGItVguhw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1705395122; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=bIypJhqHgI+H+DQV8Dm1QaRaT8IwKP+gW/K/pvPk02Q=; b=NzkHdmVXNP4kq6P2OKcMHtCw7CvfX+NhmwpNyU2zyMCga7rNZVLowE9zNt14sG9YtiD6tG ltQvtM/ChtiqAc+ia4fRXALBdNwnxDGvrvuYdxiuoASQlspC7s+hsCp8PB5mDwsRkhuw5r LGdHUwywQMVVfGuN1nk3b4gLorIHqT8NHPwsTvK4PgAQenIiZLHG5FFINj77C0a5wVVpEK DlunqYQF0uFftFnWR2UGzR241X8SwZq231CyEinpdi/8o+6MeWHWO0nLTjWQK1RumMDnf9 TE0aPqJ1X1o29sHLh+pi8EnZD9pCeqhEiKpo/wMysU968EODaT8rGZKbZYh1Sg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1705395122; a=rsa-sha256; cv=none; b=MGLUVtXbCT51kzKIttWgjuJ9pxFwS/HQVFGz5W1IY3d0443DrKTEzRbeeSGnWqO4U7IreN Zqbgc3YLhAjEriG74BlKxR1KNWx3tgEZddyvyVHIzCD3us/JiPBlR6Q7UH+qYLqW+YchsO gChXnQNt3+IwbW7hrM6yOG/xN9cUacU7o58pfUQ6wXWmM02EXh5wvumWW+eprI7aP90a0S ryzqA5c6OJe79hJK6z6dkikuUrp8Z5mrzivO3L5KRcuX9h+2UvORjJwYwCpDO05loz2igU UBSZXjPCGucK19Tycrwh0ZXDk9h2vmMsdT6dANsJ/9vVP7VM/2dM0V+lMbQCJw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4TDjS14dSvzYsv; Tue, 16 Jan 2024 08:52:01 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 40G8q1mY077286; Tue, 16 Jan 2024 08:52:01 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 40G8q15o077283; Tue, 16 Jan 2024 08:52:01 GMT (envelope-from git) Date: Tue, 16 Jan 2024 08:52:01 GMT Message-Id: <202401160852.40G8q15o077283@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: fc6e50699615 - main - pflow: add RFC8158 NAT support List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: fc6e50699615c93f39d008709f87c754d9b6c7d3 Auto-Submitted: auto-generated The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=fc6e50699615c93f39d008709f87c754d9b6c7d3 commit fc6e50699615c93f39d008709f87c754d9b6c7d3 Author: Kristof Provost AuthorDate: 2023-12-13 15:55:28 +0000 Commit: Kristof Provost CommitDate: 2024-01-16 08:45:55 +0000 pflow: add RFC8158 NAT support Extend pflow(4) to send NAT44 Session Create and Delete events. This applies only to IPFIX (i.e. proto version 10), and requires no user configuration. Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D43114 --- sbin/pfctl/parse.y | 16 ++++ sys/net/pflow.h | 44 ++++++++++ sys/netpfil/pf/pf.c | 3 +- sys/netpfil/pf/pflow.c | 213 +++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 260 insertions(+), 16 deletions(-) diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 94b7e241cd25..9ec86f898240 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -4680,6 +4680,7 @@ natrule : nataction interface af proto fromto tag tagged rtable redirpool pool_opts { struct pfctl_rule r; + struct node_state_opt *o; if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; @@ -4855,6 +4856,21 @@ natrule : nataction interface af proto fromto tag tagged rtable r.rpool.mape = $10.mape; } + o = keep_state_defaults; + while (o) { + switch (o->type) { + case PF_STATE_OPT_PFLOW: + if (r.rule_flag & PFRULE_PFLOW) { + yyerror("state pflow option: " + "multiple definitions"); + YYERROR; + } + r.rule_flag |= PFRULE_PFLOW; + break; + } + o = o->next; + } + expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, $5.dst.port, 0, 0, 0, ""); diff --git a/sys/net/pflow.h b/sys/net/pflow.h index 4a63f7640629..84fcf1327a17 100644 --- a/sys/net/pflow.h +++ b/sys/net/pflow.h @@ -68,6 +68,14 @@ #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 +#define PFIX_IE_natEvent 230 +#define PFIX_NAT_EVENT_SESSION_CREATE 4 +#define PFIX_NAT_EVENT_SESSION_DELETE 5 +#define PFIX_IE_timeStamp 323 struct pflow_flow { u_int32_t src_ip; @@ -148,10 +156,28 @@ struct pflow_ipfix_tmpl_ipv6 { #define PFLOW_IPFIX_TMPL_IPV6_ID 257 } __packed; +struct pflow_ipfix_tmpl_nat44 { + struct pflow_tmpl_hdr h; + struct pflow_tmpl_fspec timestamp; + struct pflow_tmpl_fspec nat_event; + struct pflow_tmpl_fspec protocol; + struct pflow_tmpl_fspec src_ip; + struct pflow_tmpl_fspec src_port; + struct pflow_tmpl_fspec postnat_src_ip; + struct pflow_tmpl_fspec postnat_src_port; + struct pflow_tmpl_fspec dst_ip; + struct pflow_tmpl_fspec dst_port; + struct pflow_tmpl_fspec postnat_dst_ip; + struct pflow_tmpl_fspec postnat_dst_port; +#define PFLOW_IPFIX_TMPL_NAT44_FIELD_COUNT 11 +#define PFLOW_IPFIX_TMPL_NAT44_ID 258 +}; + 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_nat44 nat44_tmpl; } __packed; struct pflow_ipfix_flow4 { @@ -186,6 +212,20 @@ struct pflow_ipfix_flow6 { /* XXX padding needed? */ } __packed; +struct pflow_ipfix_nat4 { + u_int64_t timestamp; /* timeStamp */ + u_int8_t nat_event; /* natEvent */ + u_int8_t protocol; /* protocolIdentifier */ + u_int32_t src_ip; /* sourceIPv4Address */ + u_int16_t src_port; /* sourceTransportPort */ + u_int32_t postnat_src_ip; /* postNATSourceIPv4Address */ + u_int16_t postnat_src_port;/* postNAPTSourceTransportPort */ + u_int32_t dest_ip; /* destinationIPv4Address */ + u_int16_t dest_port; /* destinationTransportPort */ + u_int32_t postnat_dest_ip;/* postNATDestinationIPv4Address */ + u_int16_t postnat_dest_port;/* postNAPTDestinationTransportPort */ +} __packed; + #ifdef _KERNEL struct pflow_softc { @@ -199,13 +239,16 @@ struct pflow_softc { unsigned int sc_count; unsigned int sc_count4; unsigned int sc_count6; + unsigned int sc_count_nat4; unsigned int sc_maxcount; unsigned int sc_maxcount4; unsigned int sc_maxcount6; + unsigned int sc_maxcount_nat4; u_int64_t sc_gcounter; u_int32_t sc_sequence; struct callout sc_tmo; struct callout sc_tmo6; + struct callout sc_tmo_nat4; struct callout sc_tmo_tmpl; struct intr_event *sc_swi_ie; void *sc_swi_cookie; @@ -219,6 +262,7 @@ struct pflow_softc { u_int32_t sc_observation_dom; struct mbuf *sc_mbuf; /* current cumulative mbuf */ struct mbuf *sc_mbuf6; /* current cumulative mbuf */ + struct mbuf *sc_mbuf_nat4; CK_LIST_ENTRY(pflow_softc) sc_next; struct epoch_context sc_epoch_ctx; }; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 89f6e000f6cf..9bd9828a99d9 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -4875,7 +4875,8 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a, s->state_flags |= PFSTATE_SLOPPY; if (pd->flags & PFDESC_TCP_NORM) /* Set by old-style scrub rules */ s->state_flags |= PFSTATE_SCRUB_TCP; - if (r->rule_flag & PFRULE_PFLOW) + if ((r->rule_flag & PFRULE_PFLOW) || + (nr != NULL && nr->rule_flag & PFRULE_PFLOW)) s->state_flags |= PFSTATE_PFLOW; s->act.log = pd->act.log & PF_LOG_ALL; diff --git a/sys/netpfil/pf/pflow.c b/sys/netpfil/pf/pflow.c index 398851bf17d0..ce5e8ec6547c 100644 --- a/sys/netpfil/pf/pflow.c +++ b/sys/netpfil/pf/pflow.c @@ -70,6 +70,12 @@ #define DPRINTF(x) #endif +enum pflow_family_t { + PFLOW_INET, + PFLOW_INET6, + PFLOW_NAT4, +}; + static void pflow_output_process(void *); static int pflow_create(int); static int pflow_destroy(int, bool); @@ -80,12 +86,13 @@ static int pflowvalidsockaddr(const struct sockaddr *, int); static struct mbuf *pflow_get_mbuf(struct pflow_softc *, u_int16_t); static void pflow_flush(struct pflow_softc *); static int pflow_sendout_v5(struct pflow_softc *); -static int pflow_sendout_ipfix(struct pflow_softc *, sa_family_t); +static int pflow_sendout_ipfix(struct pflow_softc *, enum pflow_family_t); static int pflow_sendout_ipfix_tmpl(struct pflow_softc *); static int pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *); static void pflow_timeout(void *); static void pflow_timeout6(void *); static void pflow_timeout_tmpl(void *); +static void pflow_timeout_nat4(void *); static void copy_flow_data(struct pflow_flow *, struct pflow_flow *, const struct pf_kstate *, struct pf_state_key *, int, int); static void copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *, @@ -106,6 +113,9 @@ static int copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc); static int copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc); +static int copy_nat_ipfix_4_to_m(struct pflow_ipfix_nat4 *, + const struct pf_kstate *, struct pflow_softc *, + uint8_t, uint64_t); static const char pflowname[] = "pflow"; @@ -303,6 +313,53 @@ pflow_create(int unit) htons(PFIX_IE_protocolIdentifier); pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.len = htons(1); + /* NAT44 create template */ + pflowif->sc_tmpl_ipfix.nat44_tmpl.h.tmpl_id = + htons(PFLOW_IPFIX_TMPL_NAT44_ID); + pflowif->sc_tmpl_ipfix.nat44_tmpl.h.field_count = + htons(PFLOW_IPFIX_TMPL_NAT44_FIELD_COUNT); + pflowif->sc_tmpl_ipfix.nat44_tmpl.timestamp.field_id = + htons(PFIX_IE_timeStamp); + pflowif->sc_tmpl_ipfix.nat44_tmpl.timestamp.len = + htons(8); + pflowif->sc_tmpl_ipfix.nat44_tmpl.nat_event.field_id = + htons(PFIX_IE_natEvent); + pflowif->sc_tmpl_ipfix.nat44_tmpl.nat_event.len = + htons(1); + pflowif->sc_tmpl_ipfix.nat44_tmpl.protocol.field_id = + htons(PFIX_IE_protocolIdentifier); + pflowif->sc_tmpl_ipfix.nat44_tmpl.protocol.len = htons(1); + pflowif->sc_tmpl_ipfix.nat44_tmpl.src_ip.field_id = + htons(PFIX_IE_sourceIPv4Address); + pflowif->sc_tmpl_ipfix.nat44_tmpl.src_ip.len = + htons(4); + pflowif->sc_tmpl_ipfix.nat44_tmpl.src_port.field_id = + htons(PFIX_IE_sourceTransportPort); + pflowif->sc_tmpl_ipfix.nat44_tmpl.src_port.len = htons(2); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_src_ip.field_id = + htons(PFIX_IE_postNATSourceIPv4Address); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_src_ip.len = + htons(4); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_src_port.field_id = + htons(PFIX_IE_postNAPTSourceTransportPort); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_src_port.len = + htons(2); + pflowif->sc_tmpl_ipfix.nat44_tmpl.dst_ip.field_id = + htons(PFIX_IE_destinationIPv4Address); + pflowif->sc_tmpl_ipfix.nat44_tmpl.dst_ip.len = + htons(4); + pflowif->sc_tmpl_ipfix.nat44_tmpl.dst_port.field_id = + htons(PFIX_IE_destinationTransportPort); + pflowif->sc_tmpl_ipfix.nat44_tmpl.dst_port.len = htons(2); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_dst_ip.field_id = + htons(PFIX_IE_postNATDestinationIPv4Address); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_dst_ip.len = + htons(4); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_dst_port.field_id = + htons(PFIX_IE_postNAPTDestinationTransportPort); + pflowif->sc_tmpl_ipfix.nat44_tmpl.postnat_dst_port.len = + htons(2); + pflowif->sc_id = unit; pflowif->sc_vnet = curvnet; @@ -311,6 +368,7 @@ pflow_create(int unit) callout_init_mtx(&pflowif->sc_tmo, &pflowif->sc_lock, 0); callout_init_mtx(&pflowif->sc_tmo6, &pflowif->sc_lock, 0); + callout_init_mtx(&pflowif->sc_tmo_nat4, &pflowif->sc_lock, 0); callout_init_mtx(&pflowif->sc_tmo_tmpl, &pflowif->sc_lock, 0); error = swi_add(&pflowif->sc_swi_ie, pflowname, pflow_output_process, @@ -374,10 +432,12 @@ pflow_destroy(int unit, bool drain) callout_drain(&sc->sc_tmo); callout_drain(&sc->sc_tmo6); + callout_drain(&sc->sc_tmo_nat4); callout_drain(&sc->sc_tmo_tmpl); m_freem(sc->sc_mbuf); m_freem(sc->sc_mbuf6); + m_freem(sc->sc_mbuf_nat4); PFLOW_LOCK(sc); mbufq_drain(&sc->sc_outputqueue); @@ -425,18 +485,26 @@ pflowvalidsockaddr(const struct sockaddr *sa, int ignore_port) int pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz) { + size_t min; sc->sc_maxcount4 = (mtu - hdrsz - sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow4); sc->sc_maxcount6 = (mtu - hdrsz - sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow6); + sc->sc_maxcount_nat4 = (mtu - hdrsz - + sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_nat4); if (sc->sc_maxcount4 > PFLOW_MAXFLOWS) sc->sc_maxcount4 = PFLOW_MAXFLOWS; 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), - sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6))); + if (sc->sc_maxcount_nat4 > PFLOW_MAXFLOWS) + sc->sc_maxcount_nat4 = PFLOW_MAXFLOWS; + + min = MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_flow4), + sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6)); + min = MIN(min, sc->sc_maxcount_nat4 * sizeof(struct pflow_ipfix_nat4)); + + return (hdrsz + sizeof(struct udpiphdr) + min); } static void @@ -628,6 +696,28 @@ copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1, flow1->tos = flow2->tos = st->rule.ptr->tos; } +static void +copy_nat_ipfix_4_data(struct pflow_ipfix_nat4 *nat1, + struct pflow_ipfix_nat4 *nat2, const struct pf_kstate *st, + struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst) +{ + nat1->src_ip = nat2->dest_ip = st->key[PF_SK_STACK]->addr[src].v4.s_addr; + nat1->src_port = nat2->dest_port = st->key[PF_SK_STACK]->port[src]; + nat1->dest_ip = nat2->src_ip = st->key[PF_SK_STACK]->addr[dst].v4.s_addr; + nat1->dest_port = nat2->src_port = st->key[PF_SK_STACK]->port[dst]; + nat1->postnat_src_ip = nat2->postnat_dest_ip = st->key[PF_SK_WIRE]->addr[src].v4.s_addr; + nat1->postnat_src_port = nat2->postnat_dest_port = st->key[PF_SK_WIRE]->port[src]; + nat1->postnat_dest_ip = nat2->postnat_src_ip = st->key[PF_SK_WIRE]->addr[dst].v4.s_addr; + nat1->postnat_dest_port = nat2->postnat_src_port = st->key[PF_SK_WIRE]->port[dst]; + nat1->protocol = nat2->protocol = sk->proto; + + /* + * Because we have to generate a create and delete event we'll fill out the + * timestamp and nat_event fields when we transmit. As opposed to doing this + * work a second time. + */ +} + static void export_pflow(const struct pf_kstate *st) { @@ -755,7 +845,7 @@ copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc) sc->sc_count4++; if (sc->sc_count4 >= sc->sc_maxcount4) - ret = pflow_sendout_ipfix(sc, AF_INET); + ret = pflow_sendout_ipfix(sc, PFLOW_INET); return(ret); } @@ -785,11 +875,46 @@ copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc) sc->sc_count6++; if (sc->sc_count6 >= sc->sc_maxcount6) - ret = pflow_sendout_ipfix(sc, AF_INET6); + ret = pflow_sendout_ipfix(sc, PFLOW_INET6); return(ret); } +int +copy_nat_ipfix_4_to_m(struct pflow_ipfix_nat4 *nat, const struct pf_kstate *st, + struct pflow_softc *sc, uint8_t event, uint64_t timestamp) +{ + int ret = 0; + + PFLOW_ASSERT(sc); + + if (sc->sc_mbuf_nat4 == NULL) { + if ((sc->sc_mbuf_nat4 = + pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_NAT44_ID)) == NULL) { + return (ENOBUFS); + } + sc->sc_count_nat4 = 0; + callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz, + pflow_timeout_nat4, sc); + } + + nat->nat_event = event; + nat->timestamp = htobe64(pf_get_time() - (pf_get_uptime() - timestamp)); + m_copyback(sc->sc_mbuf_nat4, PFLOW_SET_HDRLEN + + (sc->sc_count_nat4 * sizeof(struct pflow_ipfix_nat4)), + sizeof(struct pflow_ipfix_nat4), (caddr_t)nat); + sc->sc_count_nat4++; + + if (V_pflowstats.pflow_flows == sc->sc_gcounter) + V_pflowstats.pflow_flows++; + + sc->sc_gcounter++; + if (sc->sc_count_nat4 >= sc->sc_maxcount_nat4) + ret = pflow_sendout_ipfix(sc, PFLOW_NAT4); + + return (ret); +} + static int pflow_pack_flow(const struct pf_kstate *st, struct pf_state_key *sk, struct pflow_softc *sc) @@ -815,17 +940,30 @@ pflow_pack_flow(const struct pf_kstate *st, struct pf_state_key *sk, return (ret); } +static bool +pflow_is_natd(const struct pf_kstate *st) +{ + /* If ports or addresses are different we've been NAT-ed. */ + return (memcmp(st->key[PF_SK_WIRE], st->key[PF_SK_STACK], + sizeof(struct pf_addr) * 2 + sizeof(uint16_t) * 2) != 0); +} + static int pflow_pack_flow_ipfix(const struct pf_kstate *st, struct pf_state_key *sk, struct pflow_softc *sc) { struct pflow_ipfix_flow4 flow4_1, flow4_2; + struct pflow_ipfix_nat4 nat4_1, nat4_2; struct pflow_ipfix_flow6 flow6_1, flow6_2; int ret = 0; + bool nat = false; + if (sk->af == AF_INET) { bzero(&flow4_1, sizeof(flow4_1)); bzero(&flow4_2, sizeof(flow4_2)); + nat = pflow_is_natd(st); + if (st->direction == PF_OUT) copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc, 1, 0); @@ -833,11 +971,30 @@ pflow_pack_flow_ipfix(const struct pf_kstate *st, struct pf_state_key *sk, copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc, 0, 1); - if (st->bytes[0] != 0) /* first flow from state */ + if (nat) + copy_nat_ipfix_4_data(&nat4_1, &nat4_2, st, sk, sc, 1, 0); + + if (st->bytes[0] != 0) /* first flow from state */ { ret = copy_flow_ipfix_4_to_m(&flow4_1, sc); - if (st->bytes[1] != 0) /* second flow from state */ + if (ret == 0 && nat) { + ret = copy_nat_ipfix_4_to_m(&nat4_1, st, sc, + PFIX_NAT_EVENT_SESSION_CREATE, st->creation); + ret |= copy_nat_ipfix_4_to_m(&nat4_1, st, sc, + PFIX_NAT_EVENT_SESSION_DELETE, st->expire); + } + } + + if (st->bytes[1] != 0) /* second flow from state */ { ret = copy_flow_ipfix_4_to_m(&flow4_2, sc); + + if (ret == 0 && nat) { + ret = copy_nat_ipfix_4_to_m(&nat4_2, st, sc, + PFIX_NAT_EVENT_SESSION_CREATE, st->creation); + ret |= copy_nat_ipfix_4_to_m(&nat4_2, st, sc, + PFIX_NAT_EVENT_SESSION_DELETE, st->expire); + } + } } else if (sk->af == AF_INET6) { bzero(&flow6_1, sizeof(flow6_1)); bzero(&flow6_2, sizeof(flow6_2)); @@ -871,7 +1028,7 @@ pflow_timeout(void *v) pflow_sendout_v5(sc); break; case PFLOW_PROTO_10: - pflow_sendout_ipfix(sc, AF_INET); + pflow_sendout_ipfix(sc, PFLOW_INET); break; default: /* NOTREACHED */ panic("Unsupported version %d", sc->sc_version); @@ -892,7 +1049,7 @@ pflow_timeout6(void *v) return; CURVNET_SET(sc->sc_vnet); - pflow_sendout_ipfix(sc, AF_INET6); + pflow_sendout_ipfix(sc, PFLOW_INET6); CURVNET_RESTORE(); } @@ -911,6 +1068,21 @@ pflow_timeout_tmpl(void *v) CURVNET_RESTORE(); } +static void +pflow_timeout_nat4(void *v) +{ + struct pflow_softc *sc = v; + + PFLOW_ASSERT(sc); + + if (sc->sc_version != PFLOW_PROTO_10) + return; + + CURVNET_SET(sc->sc_vnet); + pflow_sendout_ipfix(sc, PFLOW_NAT4); + CURVNET_RESTORE(); +} + static void pflow_flush(struct pflow_softc *sc) { @@ -921,8 +1093,9 @@ 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, PFLOW_INET); + pflow_sendout_ipfix(sc, PFLOW_INET6); + pflow_sendout_ipfix(sc, PFLOW_NAT4); break; default: /* NOTREACHED */ break; @@ -960,7 +1133,7 @@ pflow_sendout_v5(struct pflow_softc *sc) } static int -pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af) +pflow_sendout_ipfix(struct pflow_softc *sc, enum pflow_family_t af) { struct mbuf *m; struct pflow_v10_header *h10; @@ -971,7 +1144,7 @@ pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af) PFLOW_ASSERT(sc); switch (af) { - case AF_INET: + case PFLOW_INET: m = sc->sc_mbuf; callout_stop(&sc->sc_tmo); if (m == NULL) @@ -981,7 +1154,7 @@ pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af) set_length = sizeof(struct pflow_set_header) + sc->sc_count4 * sizeof(struct pflow_ipfix_flow4); break; - case AF_INET6: + case PFLOW_INET6: m = sc->sc_mbuf6; callout_stop(&sc->sc_tmo6); if (m == NULL) @@ -991,6 +1164,16 @@ pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af) set_length = sizeof(struct pflow_set_header) + sc->sc_count6 * sizeof(struct pflow_ipfix_flow6); break; + case PFLOW_NAT4: + m = sc->sc_mbuf_nat4; + callout_stop(&sc->sc_tmo_nat4); + if (m == NULL) + return (0); + sc->sc_mbuf_nat4 = NULL; + count = sc->sc_count_nat4; + set_length = sizeof(struct pflow_set_header) + + sc->sc_count_nat4 * sizeof(struct pflow_ipfix_nat4); + break; default: panic("Unsupported AF %d", af); }