Hi,

this patch adds support for netflow v9 and ipfix to pflow(4).
It is needed for accounting of ipv6 traffic.

It has been lightly tested on both SP and MP kernels.

It adds a new parameter "pflowproto [5|9|10]" to ifconfig, with 5
being the default. 

If you want to test it net/flowd in ports can receive v9 flows.

Any comments?

Best regards,
Florian & Benno

Index: sbin/ifconfig/ifconfig.8
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.225
diff -u -p -u -r1.225 ifconfig.8
--- sbin/ifconfig/ifconfig.8    4 Dec 2011 06:26:10 -0000       1.225
+++ sbin/ifconfig/ifconfig.8    21 Dec 2011 11:19:16 -0000
@@ -1186,10 +1186,15 @@ packets entering the MPLS domain.
 .El
 .\" PFLOW
 .Sh PFLOW
+.nr nS 1
+.Bk -words
 .Nm ifconfig
 .Ar pflow-interface
 .Op Oo Fl Oc Ns Cm flowdst Ar addr : Ns Ar port
 .Op Oo Fl Oc Ns Cm flowsrc Ar addr
+.Op Cm pflowproto Ar n
+.Ek
+.nr nS 0
 .Pp
 The following options are available for a
 .Xr pflow 4
@@ -1214,6 +1219,9 @@ is the IP address used as sender of the 
 identify the source of the data on the pflow collector.
 .It Fl flowsrc
 Unset the source address.
+.It Cm pflowproto Ar n
+Set the protocol version.
+The default is version 5.
 .El
 .\" PFSYNC
 .Sh PFSYNC
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.253
diff -u -p -u -r1.253 ifconfig.c
--- sbin/ifconfig/ifconfig.c    4 Dec 2011 06:26:10 -0000       1.253
+++ sbin/ifconfig/ifconfig.c    21 Dec 2011 11:19:16 -0000
@@ -248,6 +248,7 @@ void        setpflow_sender(const char *, int);
 void   unsetpflow_sender(const char *, int);
 void   setpflow_receiver(const char *, int);
 void   unsetpflow_receiver(const char *, int);
+void   setpflowproto(const char *, int);
 void   list_cloners(void);
 void   setifipdst(const char *, int);
 void   setifdesc(const char *, int);
@@ -405,6 +406,7 @@ const struct        cmd {
        { "-flowsrc",   1,              0,              unsetpflow_sender },
        { "flowdst",    NEXTARG,        0,              setpflow_receiver },
        { "-flowdst", 1,                0,              unsetpflow_receiver },
+       { "pflowproto", NEXTARG,        0,              setpflowproto },
        { "-inet6",     IFXF_NOINET6,   0,              setifxflags } ,
        { "keepalive",  NEXTARG2,       0,              NULL, setkeepalive },
        { "-keepalive", 1,              0,              unsetkeepalive },
@@ -3811,8 +3813,9 @@ pflow_status(void)
                 return;
 
        printf("\tpflow: sender: %s ", inet_ntoa(preq.sender_ip));
-       printf("receiver: %s:%u\n", inet_ntoa(preq.receiver_ip),
+       printf("receiver: %s:%u ", inet_ntoa(preq.receiver_ip),
            ntohs(preq.receiver_port));
+       printf("version: %d\n", preq.version);
 }
 
 /* ARGSUSED */
@@ -3909,6 +3912,34 @@ unsetpflow_receiver(const char *val, int
        bzero(&preq, sizeof(struct pflowreq));
        ifr.ifr_data = (caddr_t)&preq;
        preq.addrmask |= PFLOW_MASK_DSTIP | PFLOW_MASK_DSTPRT;
+       if (ioctl(s, SIOCSETPFLOW, (caddr_t)&ifr) == -1)
+               err(1, "SIOCSETPFLOW");
+}
+
+/* PFLOWPROTO XXX */
+void
+setpflowproto(const char *val, int d)
+{
+       struct pflow_protos ppr[] = PFLOW_PROTOS;
+       struct pflowreq preq;
+       int i;
+
+       bzero(&preq, sizeof(preq));
+       preq.version = PFLOW_PROTO_MAX;
+
+       for (i = 0; i < (sizeof(ppr) / sizeof(ppr[0])); i++) {
+               if (strcmp(val, ppr[i].ppr_name) == 0) {
+                       preq.version = ppr[i].ppr_proto;
+                       break;
+               }
+       }
+       if (preq.version == PFLOW_PROTO_MAX)
+               errx(1, "Invalid pflow protocol: %s", val);
+
+       preq.addrmask |= PFLOW_MASK_VERSION;
+
+       ifr.ifr_data = (caddr_t)&preq;
+
        if (ioctl(s, SIOCSETPFLOW, (caddr_t)&ifr) == -1)
                err(1, "SIOCSETPFLOW");
 }
Index: share/man/man4/pflow.4
===================================================================
RCS file: /opt/OpenBSD-CVS/src/share/man/man4/pflow.4,v
retrieving revision 1.10
diff -u -p -u -r1.10 pflow.4
--- share/man/man4/pflow.4      27 Nov 2009 17:56:48 -0000      1.10
+++ share/man/man4/pflow.4      21 Dec 2011 11:19:16 -0000
@@ -32,7 +32,7 @@ accounting data from the kernel using
 .Xr udp 4
 packets.
 .Nm
-is compatible with netflow v5.
+is compatible with netflow version 5, 9, and IPFIX (10).
 The data is extracted from the
 .Xr pf 4
 state table.
@@ -99,6 +99,11 @@ and 10.0.0.2:1234 as destination:
 .Bd -literal -offset indent
 # ifconfig pflow0 flowsrc 10.0.0.1 flowdst 10.0.0.2:1234
 .Ed
+.Pp
+The protocol is set to IPFIX with the following command:
+.Bd -literal -offset indent
+# ifconfig pflow0 pflowproto 10
+.Ed
 .Sh SEE ALSO
 .Xr netintro 4 ,
 .Xr pf 4 ,
@@ -106,8 +111,22 @@ and 10.0.0.2:1234 as destination:
 .Xr pf.conf 5 ,
 .Xr ifconfig 8 ,
 .Xr tcpdump 8
+.Rs
+.%R RFC 3954
+.%T "Cisco Systems NetFlow Services Export Version 9"
+.%D October 2004
+.Re
+.Rs
+.%R RFC 5101
+.%T "Specification of the IP Flow Information Export (IPFIX) Protocol for the 
Exchange of IP Traffic Flow Information"
+.%D January 2008
+.Re
 .Sh HISTORY
 The
 .Nm
 device first appeared in
 .Ox 4.5 .
+.Sh BUGS
+The IPFIX implementation is incomplete:
+The required transport protocol SCTP is not supported.
+Transport over TCP and DTLS protected flow export is also not supported.
Index: sys/net/if_pflow.c
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/net/if_pflow.c,v
retrieving revision 1.18
diff -u -p -u -r1.18 if_pflow.c
--- sys/net/if_pflow.c  25 Nov 2011 12:52:10 -0000      1.18
+++ sys/net/if_pflow.c  21 Dec 2011 11:19:16 -0000
@@ -1,6 +1,8 @@
 /*     $OpenBSD: if_pflow.c,v 1.18 2011/11/25 12:52:10 dlg Exp $       */
 
 /*
+ * Copyright (c) 2011 Florian Obser <[email protected]>
+ * Copyright (c) 2011 Sebastian Benoit <[email protected]>
  * Copyright (c) 2008 Henning Brauer <[email protected]>
  * Copyright (c) 2008 Joerg Goltermann <[email protected]>
  *
@@ -69,22 +71,38 @@ 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 *);
 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( void );
+struct mbuf *pflow_get_mbuf_v5(struct pflow_softc *);
+struct mbuf *pflow_get_mbuf_ipfix(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    export_pflow_if_ipfix(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,
@@ -128,6 +146,84 @@ pflow_clone_create(struct if_clone *ifc,
        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 +236,7 @@ pflow_clone_create(struct if_clone *ifc,
        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 +256,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 +314,13 @@ pflowioctl(struct ifnet *ifp, u_long cmd
                    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 +331,7 @@ pflowioctl(struct ifnet *ifp, u_long cmd
                        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 +342,7 @@ pflowioctl(struct ifnet *ifp, u_long cmd
                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 +355,19 @@ pflowioctl(struct ifnet *ifp, u_long cmd
                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 +375,22 @@ pflowioctl(struct ifnet *ifp, u_long cmd
                        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 +409,34 @@ pflowioctl(struct ifnet *ifp, u_long cmd
 }
 
 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: /* not reached */
+               break;
+       }
+}
+
+void
 pflow_setmtu(struct pflow_softc *sc, int mtu_req)
 {
        int     mtu;
@@ -289,19 +446,65 @@ pflow_setmtu(struct pflow_softc *sc, int
        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_maxcount4 = (mtu - sizeof(struct pflow_v9_header) -
+                   sizeof (struct udpiphdr)) / sizeof(struct pflow_flow4);
+               if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
+                   sc->sc_maxcount4 = PFLOW_MAXFLOWS;
+               sc->sc_maxcount6 = (mtu - sizeof(struct pflow_v9_header) -
+                   sizeof (struct udpiphdr)) / sizeof(struct pflow_flow6);
+               if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
+                   sc->sc_maxcount6 = PFLOW_MAXFLOWS;
+               /* XXX this is known at compile time */
+               if ( sc->sc_maxcount4 * sizeof(struct pflow_flow4) <
+                   sc->sc_maxcount6 * sizeof(struct pflow_flow6) )
+                       sc->sc_if.if_mtu = sizeof(struct pflow_v9_header) +
+                           sizeof (struct udpiphdr) + 
+                           sc->sc_maxcount4 * sizeof(struct pflow_flow4);
+               else
+                       sc->sc_if.if_mtu = sizeof(struct pflow_v9_header) +
+                           sizeof (struct udpiphdr) + 
+                           sc->sc_maxcount6 * sizeof(struct pflow_flow6);
+
+               break;
+       case PFLOW_PROTO_10:
+               sc->sc_maxcount4 = (mtu - sizeof(struct pflow_v10_header) -
+                   sizeof (struct udpiphdr)) / sizeof(struct pflow_flow4);
+               if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
+                   sc->sc_maxcount4 = PFLOW_MAXFLOWS;
+               sc->sc_maxcount6 = (mtu - sizeof(struct pflow_v10_header) -
+                   sizeof (struct udpiphdr)) / sizeof(struct pflow_flow6);
+               if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
+                   sc->sc_maxcount6 = PFLOW_MAXFLOWS;
+               /* XXX this is known at compile time */
+               if ( sc->sc_maxcount4 * sizeof(struct pflow_flow4) <
+                   sc->sc_maxcount6 * sizeof(struct pflow_flow6) )
+                       sc->sc_if.if_mtu = sizeof(struct pflow_v10_header) +
+                           sizeof (struct udpiphdr) + 
+                           sc->sc_maxcount4 * sizeof(struct pflow_flow4);
+               else
+                       sc->sc_if.if_mtu = sizeof(struct pflow_v10_header) +
+                           sizeof (struct udpiphdr) + 
+                           sc->sc_maxcount6 * sizeof(struct pflow_flow6);
+               break;
+       default: /* not reached */
+               break;
+       }
 }
 
 struct mbuf *
-pflow_get_mbuf(struct pflow_softc *sc)
+pflow_get_mbuf( void )
 {
-       struct pflow_header      h;
        struct mbuf             *m;
 
        MGETHDR(m, M_DONTWAIT, MT_DATA);
@@ -320,11 +523,26 @@ pflow_get_mbuf(struct pflow_softc *sc)
        m->m_len = m->m_pkthdr.len = 0;
        m->m_pkthdr.rcvif = NULL;
 
+       return (m);
+}
+
+struct mbuf *
+pflow_get_mbuf_v5(struct pflow_softc *sc)
+{
+       struct pflow_header      h;
+       struct mbuf             *m;
+
+       m = pflow_get_mbuf();
+
+       if (m == NULL) {
+               return (NULL);
+       }
+
        /* populate pflow_header */
        h.reserved1 = 0;
        h.reserved2 = 0;
        h.count = 0;
-       h.version = htons(PFLOW_VERSION);
+       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;
@@ -335,6 +553,27 @@ pflow_get_mbuf(struct pflow_softc *sc)
        return (m);
 }
 
+struct mbuf *
+pflow_get_mbuf_ipfix(struct pflow_softc *sc, u_int16_t set_id)
+{
+       struct pflow_set_header  set_hdr;
+       struct mbuf                     *m;
+
+       m = pflow_get_mbuf();
+
+       if (m == NULL) {
+               return (NULL);
+       }
+
+       /* 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);
+}
+
 void
 copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
     struct pf_state *st, int src, int dst)
@@ -369,17 +608,82 @@ copy_flow_data(struct pflow_flow *flow1,
        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_ipfix(st, sc);
+                       break;
+               default: /* not reached */
+                       break;
+               }
        }
 
        return (0);
@@ -432,13 +736,22 @@ export_pflow_if(struct pf_state *st, str
 }
 
 int
+export_pflow_if_ipfix(struct pf_state *st, struct pflow_softc *sc)
+{
+       if (!(sc->sc_if.if_flags & IFF_RUNNING))
+               return (0);
+
+       return (pflow_pack_flow_ipfix(st, sc));
+}
+
+int
 copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
 {
        int             s, ret = 0;
 
        s = splnet();
        if (sc->sc_mbuf == NULL) {
-               if ((sc->sc_mbuf = pflow_get_mbuf(sc)) == NULL) {
+               if ((sc->sc_mbuf = pflow_get_mbuf_v5(sc)) == NULL) {
                        splx(s);
                        return (ENOBUFS);
                }
@@ -453,7 +766,68 @@ copy_flow_to_m(struct pflow_flow *flow, 
        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_ipfix(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_ipfix(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 +857,45 @@ pflow_pack_flow(struct pf_state *st, str
        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 +903,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: /* not reached */
+               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: /* not reached */
+               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;
@@ -522,6 +986,158 @@ pflow_sendout(struct pflow_softc *sc)
        h->time_sec = htonl(time_second);
        h->time_nanosec = htonl(ticks);
 
+       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: /* not reached */
+               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: /* not reached */
+               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: /* not reached */
+               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();
+       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: /* not reached */
+               break;
+       }
+       timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
        return (pflow_sendout_mbuf(sc, m));
 }
 
Index: sys/net/if_pflow.h
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/net/if_pflow.h,v
retrieving revision 1.5
diff -u -p -u -r1.5 if_pflow.h
--- sys/net/if_pflow.h  27 Feb 2009 11:09:36 -0000      1.5
+++ sys/net/if_pflow.h  21 Dec 2011 11:19:16 -0000
@@ -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,27 @@ struct pflowstats {
        u_int64_t       pflow_oerrors;
 };
 
+/* Supported flow protocols */
+enum pflow_proto {
+       PFLOW_PROTO_5   = 5,  /* original pflow */
+       PFLOW_PROTO_9   = 9,  /* version 9 */
+       PFLOW_PROTO_10  = 10, /* ipfix */
+       PFLOW_PROTO_MAX = 11
+};
+
+#define PFLOW_PROTO_DEFAULT PFLOW_PROTO_5
+
+struct pflow_protos {
+       const char              *ppr_name;
+       enum pflow_proto         ppr_proto;
+};
+
+#define PFLOW_PROTOS {                                 \
+               { "5",  PFLOW_PROTO_5 },               \
+               { "9",  PFLOW_PROTO_9 },               \
+               { "10", PFLOW_PROTO_10 },              \
+}
+
 /*
  * Configuration structure for SIOCSETPFLOW SIOCGETPFLOW
  */
@@ -107,9 +264,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

Reply via email to