summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbenno <benno@openbsd.org>2012-02-02 12:34:37 +0000
committerbenno <benno@openbsd.org>2012-02-02 12:34:37 +0000
commitfa1d79b8ea04f1f82326007daf65a12a35f89abf (patch)
treecee62c92f4a6d5a0f09dcdc7bddfb01828730183 /sys
parentbound chaeck table expansion; problem seen by Michael Niedermayer; (diff)
downloadwireguard-openbsd-fa1d79b8ea04f1f82326007daf65a12a35f89abf.tar.xz
wireguard-openbsd-fa1d79b8ea04f1f82326007daf65a12a35f89abf.zip
add netflow v9/ipfix support to pflow(4).
large parts written by Florian Obser (florian -at- narrans -dot- de). feedback from sperreault@ gollo@ sthen@ ok from gollo@ dlg@ henning@
Diffstat (limited to 'sys')
-rw-r--r--sys/net/if_pflow.c655
-rw-r--r--sys/net/if_pflow.h161
2 files changed, 770 insertions, 46 deletions
diff --git a/sys/net/if_pflow.c b/sys/net/if_pflow.c
index 9524609c18d..45b24b9037d 100644
--- a/sys/net/if_pflow.c
+++ b/sys/net/if_pflow.c
@@ -1,6 +1,8 @@
-/* $OpenBSD: if_pflow.c,v 1.18 2011/11/25 12:52:10 dlg Exp $ */
+/* $OpenBSD: if_pflow.c,v 1.19 2012/02/02 12:34:37 benno Exp $ */
/*
+ * Copyright (c) 2011 Florian Obser <florian@narrans.de>
+ * Copyright (c) 2011 Sebastian Benoit <benoit-lists@fb12.de>
* Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
*
@@ -69,22 +71,36 @@ struct pflowstats pflowstats;
void pflowattach(int);
int pflow_clone_create(struct if_clone *, int);
int pflow_clone_destroy(struct ifnet *);
+void pflow_init_timeouts(struct pflow_softc *);
+int pflow_calc_mtu(struct pflow_softc *, int, int);
void pflow_setmtu(struct pflow_softc *, int);
int pflowoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int pflowioctl(struct ifnet *, u_long, caddr_t);
void pflowstart(struct ifnet *);
-struct mbuf *pflow_get_mbuf(struct pflow_softc *);
-int pflow_sendout(struct pflow_softc *);
+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_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 copy_flow_data(struct pflow_flow *, struct pflow_flow *,
struct pf_state *, int, int);
+void copy_flow4_data(struct pflow_flow4 *, struct pflow_flow4 *,
+ struct pf_state *, int, int);
+void copy_flow6_data(struct pflow_flow6 *, struct pflow_flow6 *,
+ struct pf_state *, int, int);
int pflow_pack_flow(struct pf_state *, struct pflow_softc *);
+int pflow_pack_flow_ipfix(struct pf_state *, struct pflow_softc *);
int pflow_get_dynport(void);
int export_pflow_if(struct pf_state*, struct pflow_softc *);
int copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
+int copy_flow4_to_m(struct pflow_flow4 *flow, struct pflow_softc *sc);
+int copy_flow6_to_m(struct pflow_flow6 *flow, struct pflow_softc *sc);
struct if_clone pflow_cloner =
IF_CLONE_INITIALIZER("pflow", pflow_clone_create,
@@ -117,9 +133,6 @@ pflow_clone_create(struct if_clone *ifc, int unit)
M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
return (ENOMEM);
- pflowif->sc_sender_ip.s_addr = INADDR_ANY;
- pflowif->sc_sender_port = pflow_get_dynport();
-
pflowif->sc_imo.imo_membership = malloc(
(sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
M_WAITOK|M_ZERO);
@@ -128,6 +141,84 @@ pflow_clone_create(struct if_clone *ifc, int unit)
pflowif->sc_receiver_port = 0;
pflowif->sc_sender_ip.s_addr = INADDR_ANY;
pflowif->sc_sender_port = pflow_get_dynport();
+ pflowif->sc_version = PFLOW_PROTO_DEFAULT;
+ bzero(&pflowif->sc_tmpl,sizeof(pflowif->sc_tmpl));
+ pflowif->sc_tmpl.set_header.set_id =
+ htons(pflowif->sc_version == PFLOW_PROTO_9?
+ PFLOW_V9_TMPL_SET_ID:PFLOW_V10_TMPL_SET_ID);
+ pflowif->sc_tmpl.set_header.set_length =
+ htons(sizeof(struct pflow_tmpl));
+
+ /* v9/v10 IPv4 template */
+ pflowif->sc_tmpl.ipv4_tmpl.h.tmpl_id = htons(PFLOW_TMPL_IPV4_ID);
+ pflowif->sc_tmpl.ipv4_tmpl.h.field_count
+ = htons(PFLOW_TMPL_IPV4_FIELD_COUNT);
+ pflowif->sc_tmpl.ipv4_tmpl.src_ip.field_id =
+ htons(PFIX_IE_sourceIPv4Address);
+ pflowif->sc_tmpl.ipv4_tmpl.src_ip.len = htons(4);
+ pflowif->sc_tmpl.ipv4_tmpl.dest_ip.field_id =
+ htons(PFIX_IE_destinationIPv4Address);
+ pflowif->sc_tmpl.ipv4_tmpl.dest_ip.len = htons(4);
+ pflowif->sc_tmpl.ipv4_tmpl.packets.field_id =
+ htons(PFIX_IE_packetDeltaCount);
+ pflowif->sc_tmpl.ipv4_tmpl.packets.len = htons(8);
+ pflowif->sc_tmpl.ipv4_tmpl.octets.field_id =
+ htons(PFIX_IE_octetDeltaCount);
+ pflowif->sc_tmpl.ipv4_tmpl.octets.len = htons(8);
+ pflowif->sc_tmpl.ipv4_tmpl.start.field_id =
+ htons(PFIX_IE_flowStartSysUpTime);
+ pflowif->sc_tmpl.ipv4_tmpl.start.len = htons(4);
+ pflowif->sc_tmpl.ipv4_tmpl.finish.field_id =
+ htons(PFIX_IE_flowEndSysUpTime);
+ pflowif->sc_tmpl.ipv4_tmpl.finish.len = htons(4);
+ pflowif->sc_tmpl.ipv4_tmpl.src_port.field_id =
+ htons(PFIX_IE_sourceTransportPort);
+ pflowif->sc_tmpl.ipv4_tmpl.src_port.len = htons(2);
+ pflowif->sc_tmpl.ipv4_tmpl.dest_port.field_id =
+ htons(PFIX_IE_destinationTransportPort);
+ pflowif->sc_tmpl.ipv4_tmpl.dest_port.len = htons(2);
+ pflowif->sc_tmpl.ipv4_tmpl.tos.field_id =
+ htons(PFIX_IE_ipClassOfService);
+ pflowif->sc_tmpl.ipv4_tmpl.tos.len = htons(1);
+ pflowif->sc_tmpl.ipv4_tmpl.protocol.field_id =
+ htons(PFIX_IE_protocolIdentifier);
+ pflowif->sc_tmpl.ipv4_tmpl.protocol.len = htons(1);
+
+ /* v9/v10 IPv6 template */
+ pflowif->sc_tmpl.ipv6_tmpl.h.tmpl_id = htons(PFLOW_TMPL_IPV6_ID);
+ pflowif->sc_tmpl.ipv6_tmpl.h.field_count =
+ htons(PFLOW_TMPL_IPV6_FIELD_COUNT);
+ pflowif->sc_tmpl.ipv6_tmpl.src_ip.field_id =
+ htons(PFIX_IE_sourceIPv6Address);
+ pflowif->sc_tmpl.ipv6_tmpl.src_ip.len = htons(16);
+ pflowif->sc_tmpl.ipv6_tmpl.dest_ip.field_id =
+ htons(PFIX_IE_destinationIPv6Address);
+ pflowif->sc_tmpl.ipv6_tmpl.dest_ip.len = htons(16);
+ pflowif->sc_tmpl.ipv6_tmpl.packets.field_id =
+ htons(PFIX_IE_packetDeltaCount);
+ pflowif->sc_tmpl.ipv6_tmpl.packets.len = htons(8);
+ pflowif->sc_tmpl.ipv6_tmpl.octets.field_id =
+ htons(PFIX_IE_octetDeltaCount);
+ pflowif->sc_tmpl.ipv6_tmpl.octets.len = htons(8);
+ pflowif->sc_tmpl.ipv6_tmpl.start.field_id =
+ htons(PFIX_IE_flowStartSysUpTime);
+ pflowif->sc_tmpl.ipv6_tmpl.start.len = htons(4);
+ pflowif->sc_tmpl.ipv6_tmpl.finish.field_id =
+ htons(PFIX_IE_flowEndSysUpTime);
+ pflowif->sc_tmpl.ipv6_tmpl.finish.len = htons(4);
+ pflowif->sc_tmpl.ipv6_tmpl.src_port.field_id =
+ htons(PFIX_IE_sourceTransportPort);
+ pflowif->sc_tmpl.ipv6_tmpl.src_port.len = htons(2);
+ pflowif->sc_tmpl.ipv6_tmpl.dest_port.field_id =
+ htons(PFIX_IE_destinationTransportPort);
+ pflowif->sc_tmpl.ipv6_tmpl.dest_port.len = htons(2);
+ pflowif->sc_tmpl.ipv6_tmpl.tos.field_id =
+ htons(PFIX_IE_ipClassOfService);
+ pflowif->sc_tmpl.ipv6_tmpl.tos.len = htons(1);
+ pflowif->sc_tmpl.ipv6_tmpl.protocol.field_id =
+ htons(PFIX_IE_protocolIdentifier);
+ pflowif->sc_tmpl.ipv6_tmpl.protocol.len = htons(1);
+
ifp = &pflowif->sc_if;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflow%d", unit);
ifp->if_softc = pflowif;
@@ -140,7 +231,7 @@ pflow_clone_create(struct if_clone *ifc, int unit)
ifp->if_flags = IFF_UP;
ifp->if_flags &= ~IFF_RUNNING; /* not running, need receiver */
pflow_setmtu(pflowif, ETHERMTU);
- timeout_set(&pflowif->sc_tmo, pflow_timeout, pflowif);
+ pflow_init_timeouts(pflowif);
if_attach(ifp);
if_alloc_sadl(ifp);
@@ -160,7 +251,7 @@ pflow_clone_destroy(struct ifnet *ifp)
int s;
s = splnet();
- pflow_sendout(sc);
+ pflow_flush(sc);
if_detach(ifp);
SLIST_REMOVE(&pflowif_list, sc, pflow_softc, sc_next);
free(sc->sc_imo.imo_membership, M_IPMOPTS);
@@ -218,6 +309,13 @@ pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
sc->sc_receiver_port != 0) {
ifp->if_flags |= IFF_RUNNING;
sc->sc_gcounter=pflowstats.pflow_flows;
+ /* send templates on startup */
+ if (sc->sc_version == PFLOW_PROTO_9
+ || sc->sc_version == PFLOW_PROTO_10) {
+ s = splnet();
+ pflow_sendout_ipfix_tmpl(sc);
+ splx(s);
+ }
} else
ifp->if_flags &= ~IFF_RUNNING;
break;
@@ -228,7 +326,7 @@ pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
ifr->ifr_mtu = MCLBYTES;
s = splnet();
if (ifr->ifr_mtu < ifp->if_mtu)
- pflow_sendout(sc);
+ pflow_flush(sc);
pflow_setmtu(sc, ifr->ifr_mtu);
splx(s);
break;
@@ -239,6 +337,7 @@ pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
pflowr.sender_ip = sc->sc_sender_ip;
pflowr.receiver_ip = sc->sc_receiver_ip;
pflowr.receiver_port = sc->sc_receiver_port;
+ pflowr.version = sc->sc_version;
if ((error = copyout(&pflowr, ifr->ifr_data,
sizeof(pflowr))))
@@ -251,10 +350,19 @@ pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if ((error = copyin(ifr->ifr_data, &pflowr,
sizeof(pflowr))))
return (error);
-
+ if (pflowr.addrmask & PFLOW_MASK_VERSION) {
+ switch(pflowr.version) {
+ case PFLOW_PROTO_5:
+ case PFLOW_PROTO_9:
+ case PFLOW_PROTO_10:
+ break;
+ default:
+ return(EINVAL);
+ }
+ }
s = splnet();
- pflow_sendout(sc);
- splx(s);
+
+ pflow_flush(sc);
if (pflowr.addrmask & PFLOW_MASK_DSTIP)
sc->sc_receiver_ip = pflowr.receiver_ip;
@@ -262,6 +370,22 @@ pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
sc->sc_receiver_port = pflowr.receiver_port;
if (pflowr.addrmask & PFLOW_MASK_SRCIP)
sc->sc_sender_ip.s_addr = pflowr.sender_ip.s_addr;
+ /* error check is above */
+ if (pflowr.addrmask & PFLOW_MASK_VERSION)
+ sc->sc_version = pflowr.version;
+
+ pflow_setmtu(sc, ETHERMTU);
+ pflow_init_timeouts(sc);
+
+ if (sc->sc_version == PFLOW_PROTO_9
+ || sc->sc_version == PFLOW_PROTO_10) {
+ sc->sc_tmpl.set_header.set_id =
+ htons(sc->sc_version == PFLOW_PROTO_9?
+ PFLOW_V9_TMPL_SET_ID:PFLOW_V10_TMPL_SET_ID);
+ pflow_sendout_ipfix_tmpl(sc);
+ }
+
+ splx(s);
if ((ifp->if_flags & IFF_UP) &&
sc->sc_receiver_ip.s_addr != 0 &&
@@ -280,6 +404,51 @@ pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
}
void
+pflow_init_timeouts(struct pflow_softc *sc)
+{
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ if (timeout_initialized(&sc->sc_tmo6))
+ timeout_del(&sc->sc_tmo6);
+ if (timeout_initialized(&sc->sc_tmo_tmpl))
+ timeout_del(&sc->sc_tmo_tmpl);
+ if (!timeout_initialized(&sc->sc_tmo))
+ timeout_set(&sc->sc_tmo, pflow_timeout, sc);
+ break;
+ case PFLOW_PROTO_9:
+ case PFLOW_PROTO_10:
+ if (!timeout_initialized(&sc->sc_tmo_tmpl))
+ timeout_set(&sc->sc_tmo_tmpl, pflow_timeout_tmpl, sc);
+ if (!timeout_initialized(&sc->sc_tmo))
+ timeout_set(&sc->sc_tmo, pflow_timeout, sc);
+ if (!timeout_initialized(&sc->sc_tmo6))
+ timeout_set(&sc->sc_tmo6, pflow_timeout6, sc);
+
+ timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+}
+
+int
+pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz)
+{
+ sc->sc_maxcount4 = (mtu - hdrsz -
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_flow4);
+ if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
+ sc->sc_maxcount4 = PFLOW_MAXFLOWS;
+ sc->sc_maxcount6 = (mtu - hdrsz -
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_flow6);
+ if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
+ sc->sc_maxcount6 = PFLOW_MAXFLOWS;
+
+ return (hdrsz + sizeof(struct udpiphdr) +
+ MIN(sc->sc_maxcount4 * sizeof(struct pflow_flow4),
+ sc->sc_maxcount6 * sizeof(struct pflow_flow6)));
+}
+
+void
pflow_setmtu(struct pflow_softc *sc, int mtu_req)
{
int mtu;
@@ -289,18 +458,33 @@ pflow_setmtu(struct pflow_softc *sc, int mtu_req)
else
mtu = mtu_req;
- sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
- sizeof (struct udpiphdr)) / sizeof(struct pflow_flow);
- if (sc->sc_maxcount > PFLOW_MAXFLOWS)
- sc->sc_maxcount = PFLOW_MAXFLOWS;
- sc->sc_if.if_mtu = sizeof(struct pflow_header) +
- sizeof (struct udpiphdr) +
- sc->sc_maxcount * sizeof(struct pflow_flow);
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_flow);
+ if (sc->sc_maxcount > PFLOW_MAXFLOWS)
+ sc->sc_maxcount = PFLOW_MAXFLOWS;
+ sc->sc_if.if_mtu = sizeof(struct pflow_header) +
+ sizeof(struct udpiphdr) +
+ sc->sc_maxcount * sizeof(struct pflow_flow);
+ break;
+ case PFLOW_PROTO_9:
+ sc->sc_if.if_mtu =
+ pflow_calc_mtu(sc, mtu, sizeof(struct pflow_v9_header));
+ break;
+ case PFLOW_PROTO_10:
+ sc->sc_if.if_mtu =
+ pflow_calc_mtu(sc, mtu, sizeof(struct pflow_v10_header));
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
}
struct mbuf *
-pflow_get_mbuf(struct pflow_softc *sc)
+pflow_get_mbuf(struct pflow_softc *sc, u_int16_t set_id)
{
+ struct pflow_set_header set_hdr;
struct pflow_header h;
struct mbuf *m;
@@ -320,18 +504,29 @@ pflow_get_mbuf(struct pflow_softc *sc)
m->m_len = m->m_pkthdr.len = 0;
m->m_pkthdr.rcvif = NULL;
- /* populate pflow_header */
- h.reserved1 = 0;
- h.reserved2 = 0;
- h.count = 0;
- h.version = htons(PFLOW_VERSION);
- h.flow_sequence = htonl(sc->sc_gcounter);
- h.engine_type = PFLOW_ENGINE_TYPE;
- h.engine_id = PFLOW_ENGINE_ID;
- m_copyback(m, 0, PFLOW_HDRLEN, &h, M_NOWAIT);
-
- sc->sc_count = 0;
- timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
+ if (sc == NULL) /* get only a new empty mbuf */
+ return (m);
+
+ if (sc->sc_version == PFLOW_PROTO_5) {
+ /* populate pflow_header */
+ h.reserved1 = 0;
+ h.reserved2 = 0;
+ h.count = 0;
+ h.version = htons(PFLOW_PROTO_5);
+ h.flow_sequence = htonl(sc->sc_gcounter);
+ h.engine_type = PFLOW_ENGINE_TYPE;
+ h.engine_id = PFLOW_ENGINE_ID;
+ m_copyback(m, 0, PFLOW_HDRLEN, &h, M_NOWAIT);
+
+ sc->sc_count = 0;
+ timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
+ } else {
+ /* populate pflow_set_header */
+ set_hdr.set_length = 0;
+ set_hdr.set_id = htons(set_id);
+ m_copyback(m, 0, PFLOW_SET_HDRLEN, &set_hdr, M_NOWAIT);
+ }
+
return (m);
}
@@ -369,17 +564,82 @@ copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
flow1->tos = flow2->tos = st->rule.ptr->tos;
}
+void
+copy_flow4_data(struct pflow_flow4 *flow1, struct pflow_flow4 *flow2,
+ struct pf_state *st, int src, int dst)
+{
+ struct pf_state_key *sk = st->key[PF_SK_WIRE];
+
+ flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
+ flow1->src_port = flow2->dest_port = sk->port[src];
+ flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
+ flow1->dest_port = flow2->src_port = sk->port[dst];
+
+ 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]);
+
+ flow1->flow_start = flow2->flow_start =
+ htonl(st->creation * 1000);
+ flow1->flow_finish = flow2->flow_finish =
+ htonl((time_uptime - (st->rule.ptr->timeout[st->timeout] ?
+ st->rule.ptr->timeout[st->timeout] :
+ pf_default_rule.timeout[st->timeout])) * 1000);
+
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr->tos;
+}
+
+void
+copy_flow6_data(struct pflow_flow6 *flow1, struct pflow_flow6 *flow2,
+ struct pf_state *st, int src, int dst)
+{
+ struct pf_state_key *sk = st->key[PF_SK_WIRE];
+ bcopy(&sk->addr[src].v6, &flow1->src_ip, sizeof(flow1->src_ip));
+ bcopy(&sk->addr[src].v6, &flow2->dest_ip, sizeof(flow2->dest_ip));
+ flow1->src_port = flow2->dest_port = sk->port[src];
+ bcopy(&sk->addr[dst].v6, &flow1->dest_ip, sizeof(flow1->dest_ip));
+ bcopy(&sk->addr[dst].v6, &flow2->src_ip, sizeof(flow2->src_ip));
+ flow1->dest_port = flow2->src_port = sk->port[dst];
+
+ 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]);
+
+ flow1->flow_start = flow2->flow_start =
+ htonl(st->creation * 1000);
+ flow1->flow_finish = flow2->flow_finish =
+ htonl((time_uptime - (st->rule.ptr->timeout[st->timeout] ?
+ st->rule.ptr->timeout[st->timeout] :
+ pf_default_rule.timeout[st->timeout])) * 1000);
+
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr->tos;
+}
+
int
export_pflow(struct pf_state *st)
{
struct pflow_softc *sc = NULL;
struct pf_state_key *sk = st->key[PF_SK_WIRE];
- if (sk->af != AF_INET)
- return (0);
-
SLIST_FOREACH(sc, &pflowif_list, sc_next) {
- export_pflow_if(st, sc);
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ if( sk->af == AF_INET )
+ export_pflow_if(st, sc);
+ break;
+ case PFLOW_PROTO_9:
+ /* ... fall through ... */
+ case PFLOW_PROTO_10:
+ if( sk->af == AF_INET || sk->af == AF_INET6 )
+ export_pflow_if(st, sc);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
}
return (0);
@@ -396,6 +656,10 @@ export_pflow_if(struct pf_state *st, struct pflow_softc *sc)
if (!(ifp->if_flags & IFF_RUNNING))
return (0);
+ if (sc->sc_version == PFLOW_PROTO_9 || sc->sc_version == PFLOW_PROTO_10)
+ return (pflow_pack_flow_ipfix(st, sc));
+
+ /* PFLOW_PROTO_5 */
if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
&& (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
return (pflow_pack_flow(st, sc));
@@ -438,14 +702,14 @@ copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
s = splnet();
if (sc->sc_mbuf == NULL) {
- if ((sc->sc_mbuf = pflow_get_mbuf(sc)) == NULL) {
+ if ((sc->sc_mbuf = pflow_get_mbuf(sc, 0)) == NULL) {
splx(s);
return (ENOBUFS);
}
}
m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
- (sc->sc_count * sizeof (struct pflow_flow)),
- sizeof (struct pflow_flow), flow, M_NOWAIT);
+ (sc->sc_count * sizeof(struct pflow_flow)),
+ sizeof(struct pflow_flow), flow, M_NOWAIT);
if (pflowstats.pflow_flows == sc->sc_gcounter)
pflowstats.pflow_flows++;
@@ -453,7 +717,68 @@ copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
sc->sc_count++;
if (sc->sc_count >= sc->sc_maxcount)
- ret = pflow_sendout(sc);
+ ret = pflow_sendout_v5(sc);
+
+ splx(s);
+ return(ret);
+}
+
+int
+copy_flow4_to_m(struct pflow_flow4 *flow, struct pflow_softc *sc)
+{
+ int s, ret = 0;
+
+ s = splnet();
+ if (sc->sc_mbuf == NULL) {
+ if ((sc->sc_mbuf =
+ pflow_get_mbuf(sc, PFLOW_TMPL_IPV4_ID)) == NULL) {
+ splx(s);
+ 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_flow4)),
+ sizeof(struct pflow_flow4), flow, M_NOWAIT);
+
+ if (pflowstats.pflow_flows == sc->sc_gcounter)
+ pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count4++;
+
+ if (sc->sc_count4 >= sc->sc_maxcount4)
+ ret = pflow_sendout_ipfix(sc, AF_INET);
+ splx(s);
+ return(ret);
+}
+
+int
+copy_flow6_to_m(struct pflow_flow6 *flow, struct pflow_softc *sc)
+{
+ int s, ret = 0;
+
+ s = splnet();
+ if (sc->sc_mbuf6 == NULL) {
+ if ((sc->sc_mbuf6 =
+ pflow_get_mbuf(sc, PFLOW_TMPL_IPV6_ID)) == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+ sc->sc_count6 = 0;
+ timeout_add_sec(&sc->sc_tmo6, PFLOW_TIMEOUT);
+ }
+ m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
+ (sc->sc_count6 * sizeof(struct pflow_flow6)),
+ sizeof(struct pflow_flow6), flow, M_NOWAIT);
+
+ if (pflowstats.pflow_flows == sc->sc_gcounter)
+ pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count6++;
+
+ if (sc->sc_count6 >= sc->sc_maxcount6)
+ ret = pflow_sendout_ipfix(sc, AF_INET6);
splx(s);
return(ret);
@@ -483,6 +808,45 @@ pflow_pack_flow(struct pf_state *st, struct pflow_softc *sc)
return (ret);
}
+int
+pflow_pack_flow_ipfix(struct pf_state *st, struct pflow_softc *sc)
+{
+ struct pf_state_key *sk = st->key[PF_SK_WIRE];
+ struct pflow_flow4 flow4_1, flow4_2;
+ struct pflow_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));
+
+ if (st->direction == PF_OUT)
+ copy_flow4_data(&flow4_1, &flow4_2, st, 1, 0);
+ else
+ copy_flow4_data(&flow4_1, &flow4_2, st, 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow4_to_m(&flow4_1, sc);
+
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow4_to_m(&flow4_2, sc);
+ } else if (sk->af == AF_INET6) {
+ bzero(&flow6_1, sizeof(flow6_1));
+ bzero(&flow6_2, sizeof(flow6_2));
+
+ if (st->direction == PF_OUT)
+ copy_flow6_data(&flow6_1, &flow6_2, st, 1, 0);
+ else
+ copy_flow6_data(&flow6_1, &flow6_2, st, 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow6_to_m(&flow6_1, sc);
+
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow6_to_m(&flow6_2, sc);
+ }
+ return (ret);
+}
+
void
pflow_timeout(void *v)
{
@@ -490,13 +854,64 @@ pflow_timeout(void *v)
int s;
s = splnet();
- pflow_sendout(sc);
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ pflow_sendout_v5(sc);
+ break;
+ case PFLOW_PROTO_9:
+ /* ... fall through ... */
+ case PFLOW_PROTO_10:
+ pflow_sendout_ipfix(sc, AF_INET);
+ default: /* NOTREACHED */
+ break;
+ }
splx(s);
}
+void
+pflow_timeout6(void *v)
+{
+ struct pflow_softc *sc = v;
+ int s;
+
+ s = splnet();
+ pflow_sendout_ipfix(sc, AF_INET6);
+ splx(s);
+}
+
+void
+pflow_timeout_tmpl(void *v)
+{
+ struct pflow_softc *sc = v;
+ int s;
+
+ s = splnet();
+ pflow_sendout_ipfix_tmpl(sc);
+ splx(s);
+}
+
+/* This must be called in splnet() */
+void
+pflow_flush(struct pflow_softc *sc)
+{
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ pflow_sendout_v5(sc);
+ break;
+ case PFLOW_PROTO_9:
+ case PFLOW_PROTO_10:
+ pflow_sendout_ipfix(sc, AF_INET);
+ pflow_sendout_ipfix(sc, AF_INET6);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+}
+
+
/* This must be called in splnet() */
int
-pflow_sendout(struct pflow_softc *sc)
+pflow_sendout_v5(struct pflow_softc *sc)
{
struct mbuf *m = sc->sc_mbuf;
struct pflow_header *h;
@@ -525,6 +940,158 @@ pflow_sendout(struct pflow_softc *sc)
return (pflow_sendout_mbuf(sc, m));
}
+/* This must be called in splnet() */
+int
+pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af)
+{
+ struct mbuf *m;
+ struct pflow_v9_header *h9;
+ struct pflow_v10_header *h10;
+ struct pflow_set_header *set_hdr;
+ struct ifnet *ifp = &sc->sc_if;
+ int set_length;
+
+ switch (af) {
+ case AF_INET:
+ m = sc->sc_mbuf;
+ timeout_del(&sc->sc_tmo);
+ if (m == NULL)
+ return (0);
+ sc->sc_mbuf = NULL;
+ break;
+ case AF_INET6:
+ m = sc->sc_mbuf6;
+ timeout_del(&sc->sc_tmo6);
+ if (m == NULL)
+ return (0);
+ sc->sc_mbuf6 = NULL;
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ m_freem(m);
+ return (0);
+ }
+
+ pflowstats.pflow_packets++;
+ set_hdr = mtod(m, struct pflow_set_header *);
+ switch (af) {
+ case AF_INET:
+ set_length = sizeof(struct pflow_set_header)
+ + sc->sc_count4 * sizeof(struct pflow_flow4);
+ break;
+ case AF_INET6:
+ set_length = sizeof(struct pflow_set_header)
+ + sc->sc_count6 * sizeof(struct pflow_flow6);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+ set_hdr->set_length = htons(set_length);
+
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_9:
+ /* populate pflow_header */
+ M_PREPEND(m, sizeof(struct pflow_v9_header), M_DONTWAIT);
+ if (m == NULL) {
+ pflowstats.pflow_onomem++;
+ return (ENOBUFS);
+ }
+ h9 = mtod(m, struct pflow_v9_header *);
+ h9->version = htons(PFLOW_PROTO_9);
+ h9->count = htons(1);
+ h9->uptime_ms = htonl(time_uptime * 1000);
+ h9->time_sec = htonl(time_second);
+ /* XXX correct mod 2^32 semantics? */
+ h9->flow_sequence = htonl(sc->sc_gcounter);
+ h9->observation_dom = htonl(PFLOW_ENGINE_TYPE);
+ break;
+ case PFLOW_PROTO_10:
+ /* populate pflow_header */
+ M_PREPEND(m, sizeof(struct pflow_v10_header), M_DONTWAIT);
+ if (m == NULL) {
+ pflowstats.pflow_onomem++;
+ return (ENOBUFS);
+ }
+ h10 = mtod(m, struct pflow_v10_header *);
+ h10->version = htons(PFLOW_PROTO_10);
+ h10->length = htons(PFLOW_V10_HDRLEN + set_length);
+ h10->time_sec = htonl(time_second);
+ /* XXX correct mod 2^32 semantics? */
+ h10->flow_sequence = htonl(sc->sc_gcounter);
+ h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+ return (pflow_sendout_mbuf(sc, m));
+}
+
+/* This must be called in splnet() */
+int
+pflow_sendout_ipfix_tmpl(struct pflow_softc *sc)
+{
+ struct mbuf *m;
+ struct pflow_v9_header *h9;
+ struct pflow_v10_header *h10;
+ struct ifnet *ifp = &sc->sc_if;
+
+ timeout_del(&sc->sc_tmo_tmpl);
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ return (0);
+ }
+ m = pflow_get_mbuf(NULL, 0);
+ if (m == NULL)
+ return (0);
+ if (m_copyback(m, 0, sizeof(struct pflow_tmpl),
+ &sc->sc_tmpl, M_NOWAIT)) {
+ m_freem(m);
+ return (0);
+ }
+ pflowstats.pflow_packets++;
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_9:
+ /* populate pflow_header */
+ M_PREPEND(m, sizeof(struct pflow_v9_header), M_DONTWAIT);
+ if (m == NULL) {
+ pflowstats.pflow_onomem++;
+ return (ENOBUFS);
+ }
+ h9 = mtod(m, struct pflow_v9_header *);
+ h9->version = htons(PFLOW_PROTO_9);
+ h9->count = htons(1);
+ h9->uptime_ms = htonl(time_uptime * 1000);
+ h9->time_sec = htonl(time_second);
+ /* XXX correct mod 2^32 semantics? */
+ h9->flow_sequence = htonl(sc->sc_gcounter);
+ h9->observation_dom = htonl(PFLOW_ENGINE_TYPE);
+ break;
+ case PFLOW_PROTO_10:
+ /* populate pflow_header */
+ M_PREPEND(m, sizeof(struct pflow_v10_header), M_DONTWAIT);
+ if (m == NULL) {
+ pflowstats.pflow_onomem++;
+ return (ENOBUFS);
+ }
+ h10 = mtod(m, struct pflow_v10_header *);
+ h10->version = htons(PFLOW_PROTO_10);
+ h10->length = htons(PFLOW_V10_HDRLEN
+ + sizeof(struct pflow_tmpl));
+ h10->time_sec = htonl(time_second);
+ /* XXX correct mod 2^32 semantics? */
+ h10->flow_sequence = htonl(sc->sc_gcounter);
+ h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+ timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
+ return (pflow_sendout_mbuf(sc, m));
+}
+
int
pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
{
@@ -547,7 +1114,7 @@ pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
ui->ui_sport = sc->sc_sender_port;
ui->ui_dst = sc->sc_receiver_ip;
ui->ui_dport = sc->sc_receiver_port;
- ui->ui_ulen = htons(sizeof (struct udphdr) + len);
+ ui->ui_ulen = htons(sizeof(struct udphdr) + len);
ip = (struct ip *)ui;
ip->ip_v = IPVERSION;
@@ -556,7 +1123,7 @@ pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
ip->ip_off = htons(IP_DF);
ip->ip_tos = IPTOS_LOWDELAY;
ip->ip_ttl = IPDEFTTL;
- ip->ip_len = htons(sizeof (struct udpiphdr) + len);
+ ip->ip_len = htons(sizeof(struct udpiphdr) + len);
/*
* Compute the pseudo-header checksum; defer further checksumming
diff --git a/sys/net/if_pflow.h b/sys/net/if_pflow.h
index 51dc017ae5a..b39e491cecf 100644
--- a/sys/net/if_pflow.h
+++ b/sys/net/if_pflow.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_pflow.h,v 1.5 2009/02/27 11:09:36 gollo Exp $ */
+/* $OpenBSD: if_pflow.h,v 1.6 2012/02/02 12:34:37 benno Exp $ */
/*
* Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
@@ -23,11 +23,29 @@
#define PFLOW_ID_LEN sizeof(u_int64_t)
#define PFLOW_MAXFLOWS 30
-#define PFLOW_VERSION 5
#define PFLOW_ENGINE_TYPE 42
#define PFLOW_ENGINE_ID 42
#define PFLOW_MAXBYTES 0xffffffff
#define PFLOW_TIMEOUT 30
+#define PFLOW_TMPL_TIMEOUT 30 /* rfc 5101 10.3.6 (p.40) recommends 600 */
+
+#define PFLOW_V9_TMPL_SET_ID 0
+#define PFLOW_V10_TMPL_SET_ID 2
+
+/* 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_destinationTransportPort 11
+#define PFIX_IE_destinationIPv4Address 12
+#define PFIX_IE_flowEndSysUpTime 21
+#define PFIX_IE_flowStartSysUpTime 22
+#define PFIX_IE_sourceIPv6Address 27
+#define PFIX_IE_destinationIPv6Address 28
struct pflow_flow {
u_int32_t src_ip;
@@ -52,6 +70,93 @@ struct pflow_flow {
u_int16_t pad2;
} __packed;
+struct pflow_set_header {
+ u_int16_t set_id;
+ u_int16_t set_length; /* total length of the set,
+ in octets, including the set header */
+} __packed;
+
+#define PFLOW_SET_HDRLEN sizeof(struct pflow_set_header)
+
+struct pflow_tmpl_hdr {
+ u_int16_t tmpl_id;
+ u_int16_t field_count;
+} __packed;
+
+/* field specifier rfc5101 sec 3.2, v9 uses the same format*/
+struct pflow_tmpl_fspec {
+ u_int16_t field_id;
+ u_int16_t len;
+} __packed;
+
+/* update pflow_clone_create() when changing pflow_v10_tmpl_v4 */
+struct pflow_tmpl_ipv4 {
+ struct pflow_tmpl_hdr h;
+ struct pflow_tmpl_fspec src_ip;
+ struct pflow_tmpl_fspec dest_ip;
+ struct pflow_tmpl_fspec packets;
+ struct pflow_tmpl_fspec octets;
+ struct pflow_tmpl_fspec start;
+ struct pflow_tmpl_fspec finish;
+ struct pflow_tmpl_fspec src_port;
+ struct pflow_tmpl_fspec dest_port;
+ struct pflow_tmpl_fspec tos;
+ struct pflow_tmpl_fspec protocol;
+#define PFLOW_TMPL_IPV4_FIELD_COUNT 10
+#define PFLOW_TMPL_IPV4_ID 256
+} __packed;
+
+/* update pflow_clone_create() when changing pflow_v10_tmpl_v6 */
+struct pflow_tmpl_ipv6 {
+ struct pflow_tmpl_hdr h;
+ struct pflow_tmpl_fspec src_ip;
+ struct pflow_tmpl_fspec dest_ip;
+ struct pflow_tmpl_fspec packets;
+ struct pflow_tmpl_fspec octets;
+ struct pflow_tmpl_fspec start;
+ struct pflow_tmpl_fspec finish;
+ struct pflow_tmpl_fspec src_port;
+ struct pflow_tmpl_fspec dest_port;
+ struct pflow_tmpl_fspec tos;
+ struct pflow_tmpl_fspec protocol;
+#define PFLOW_TMPL_IPV6_FIELD_COUNT 10
+#define PFLOW_TMPL_IPV6_ID 257
+} __packed;
+
+struct pflow_tmpl {
+ struct pflow_set_header set_header;
+ struct pflow_tmpl_ipv4 ipv4_tmpl;
+ struct pflow_tmpl_ipv6 ipv6_tmpl;
+} __packed;
+
+struct pflow_flow4 {
+ u_int32_t src_ip; /* sourceIPv4Address*/
+ u_int32_t dest_ip; /* destinationIPv4Address */
+ u_int64_t flow_packets; /* packetDeltaCount */
+ u_int64_t flow_octets; /* octetDeltaCount */
+ u_int32_t flow_start; /* flowStartSysUpTime */
+ u_int32_t flow_finish; /* flowEndSysUpTime */
+ 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_flow6 {
+ struct in6_addr src_ip; /* sourceIPv6Address */
+ struct in6_addr dest_ip; /* destinationIPv6Address */
+ u_int64_t flow_packets; /* packetDeltaCount */
+ u_int64_t flow_octets; /* octetDeltaCount */
+ u_int32_t flow_start; /* flowStartSysUpTime */
+ u_int32_t flow_finish; /* flowEndSysUpTime */
+ 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;
+
#ifdef _KERNEL
extern int pflow_ok;
@@ -61,15 +166,25 @@ struct pflow_softc {
struct ifnet *sc_pflow_ifp;
unsigned int sc_count;
+ unsigned int sc_count4;
+ unsigned int sc_count6;
unsigned int sc_maxcount;
+ unsigned int sc_maxcount4;
+ unsigned int sc_maxcount6;
u_int64_t sc_gcounter;
struct ip_moptions sc_imo;
struct timeout sc_tmo;
+ struct timeout sc_tmo6;
+ struct timeout sc_tmo_tmpl;
struct in_addr sc_sender_ip;
u_int16_t sc_sender_port;
struct in_addr sc_receiver_ip;
u_int16_t sc_receiver_port;
+ u_char sc_send_templates;
+ struct pflow_tmpl sc_tmpl;
+ u_int8_t sc_version;
struct mbuf *sc_mbuf; /* current cumulative mbuf */
+ struct mbuf *sc_mbuf6; /* current cumulative mbuf */
SLIST_ENTRY(pflow_softc) sc_next;
};
@@ -92,6 +207,27 @@ struct pflow_header {
#define PFLOW_HDRLEN sizeof(struct pflow_header)
+struct pflow_v10_header {
+ u_int16_t version;
+ u_int16_t length;
+ u_int32_t time_sec;
+ u_int32_t flow_sequence;
+ u_int32_t observation_dom;
+} __packed;
+
+#define PFLOW_V10_HDRLEN sizeof(struct pflow_v10_header)
+
+struct pflow_v9_header {
+ u_int16_t version;
+ u_int16_t count;
+ u_int32_t uptime_ms;
+ u_int32_t time_sec;
+ u_int32_t flow_sequence;
+ u_int32_t observation_dom;
+} __packed;
+
+#define PFLOW_V9_HDRLEN sizeof(struct pflow_v9_header)
+
struct pflowstats {
u_int64_t pflow_flows;
u_int64_t pflow_packets;
@@ -99,6 +235,25 @@ struct pflowstats {
u_int64_t pflow_oerrors;
};
+/* Supported flow protocols */
+#define PFLOW_PROTO_5 5 /* original pflow */
+#define PFLOW_PROTO_9 9 /* version 9 */
+#define PFLOW_PROTO_10 10 /* ipfix */
+#define PFLOW_PROTO_MAX 11
+
+#define PFLOW_PROTO_DEFAULT PFLOW_PROTO_5
+
+struct pflow_protos {
+ const char *ppr_name;
+ u_int8_t ppr_proto;
+};
+
+#define PFLOW_PROTOS { \
+ { "5", PFLOW_PROTO_5 }, \
+ { "9", PFLOW_PROTO_9 }, \
+ { "10", PFLOW_PROTO_10 }, \
+}
+
/*
* Configuration structure for SIOCSETPFLOW SIOCGETPFLOW
*/
@@ -107,9 +262,11 @@ struct pflowreq {
struct in_addr receiver_ip;
u_int16_t receiver_port;
u_int16_t addrmask;
+ u_int8_t version;
#define PFLOW_MASK_SRCIP 0x01
#define PFLOW_MASK_DSTIP 0x02
#define PFLOW_MASK_DSTPRT 0x04
+#define PFLOW_MASK_VERSION 0x08
};
#ifdef _KERNEL