xref: /openbsd-src/sys/net/if_pflow.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /*	$OpenBSD: if_pflow.c,v 1.11 2009/06/17 06:35:30 gollo Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
16  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/malloc.h>
22 #include <sys/param.h>
23 #include <sys/mbuf.h>
24 #include <sys/socket.h>
25 #include <sys/ioctl.h>
26 #include <sys/kernel.h>
27 #include <sys/sysctl.h>
28 #include <dev/rndvar.h>
29 
30 #include <net/if.h>
31 #include <net/if_types.h>
32 #include <net/bpf.h>
33 #include <net/route.h>
34 #include <netinet/in.h>
35 #include <netinet/if_ether.h>
36 #include <netinet/tcp.h>
37 
38 #ifdef INET
39 #include <netinet/in.h>
40 #include <netinet/in_var.h>
41 #include <netinet/in_systm.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_var.h>
44 #include <netinet/udp.h>
45 #include <netinet/udp_var.h>
46 #include <netinet/in_pcb.h>
47 #endif /* INET */
48 
49 #include <net/pfvar.h>
50 #include <net/if_pflow.h>
51 
52 #include "bpfilter.h"
53 #include "pflow.h"
54 
55 #define PFLOW_MINMTU	\
56     (sizeof(struct pflow_header) + sizeof(struct pflow_flow))
57 
58 #ifdef PFLOWDEBUG
59 #define DPRINTF(x)	do { printf x ; } while (0)
60 #else
61 #define DPRINTF(x)
62 #endif
63 
64 SLIST_HEAD(, pflow_softc) pflowif_list;
65 struct pflowstats	 pflowstats;
66 
67 void	pflowattach(int);
68 int	pflow_clone_create(struct if_clone *, int);
69 int	pflow_clone_destroy(struct ifnet *);
70 void	pflow_setmtu(struct pflow_softc *, int);
71 int	pflowoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
72 	    struct rtentry *);
73 int	pflowioctl(struct ifnet *, u_long, caddr_t);
74 void	pflowstart(struct ifnet *);
75 
76 struct mbuf *pflow_get_mbuf(struct pflow_softc *);
77 int	pflow_sendout(struct pflow_softc *);
78 int	pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
79 void	pflow_timeout(void *);
80 void	copy_flow_data(struct pflow_flow *, struct pflow_flow *,
81 	struct pf_state *, int, int);
82 int	pflow_pack_flow(struct pf_state *, struct pflow_softc *);
83 int	pflow_get_dynport(void);
84 int	export_pflow_if(struct pf_state*, struct pflow_softc *);
85 int	copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
86 
87 struct if_clone	pflow_cloner =
88     IF_CLONE_INITIALIZER("pflow", pflow_clone_create,
89     pflow_clone_destroy);
90 
91 /* from in_pcb.c */
92 extern int ipport_hifirstauto;
93 extern int ipport_hilastauto;
94 
95 /* from kern/kern_clock.c; incremented each clock tick. */
96 extern int ticks;
97 
98 void
99 pflowattach(int npflow)
100 {
101 	SLIST_INIT(&pflowif_list);
102 	if_clone_attach(&pflow_cloner);
103 }
104 
105 int
106 pflow_clone_create(struct if_clone *ifc, int unit)
107 {
108 	struct ifnet		*ifp;
109 	struct pflow_softc	*pflowif;
110 
111 	if ((pflowif = malloc(sizeof(*pflowif),
112 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
113 		return (ENOMEM);
114 
115 	pflowif->sc_sender_ip.s_addr = INADDR_ANY;
116 	pflowif->sc_sender_port = pflow_get_dynport();
117 
118 	pflowif->sc_imo.imo_membership = malloc(
119 	    (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
120 	    M_WAITOK|M_ZERO);
121 	pflowif->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
122 	pflowif->sc_receiver_ip.s_addr = 0;
123 	pflowif->sc_receiver_port = 0;
124 	pflowif->sc_sender_ip.s_addr = INADDR_ANY;
125 	pflowif->sc_sender_port = pflow_get_dynport();
126 	ifp = &pflowif->sc_if;
127 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflow%d", unit);
128 	ifp->if_softc = pflowif;
129 	ifp->if_ioctl = pflowioctl;
130 	ifp->if_output = pflowoutput;
131 	ifp->if_start = pflowstart;
132 	ifp->if_type = IFT_PFLOW;
133 	ifp->if_snd.ifq_maxlen = ifqmaxlen;
134 	ifp->if_hdrlen = PFLOW_HDRLEN;
135 	ifp->if_flags = IFF_UP;
136 	ifp->if_flags &= ~IFF_RUNNING;	/* not running, need receiver */
137 	pflow_setmtu(pflowif, ETHERMTU);
138 	timeout_set(&pflowif->sc_tmo, pflow_timeout, pflowif);
139 	if_attach(ifp);
140 	if_alloc_sadl(ifp);
141 
142 #if NBPFILTER > 0
143 	bpfattach(&pflowif->sc_if.if_bpf, ifp, DLT_RAW, 0);
144 #endif
145 
146 	/* Insert into list of pflows */
147 	SLIST_INSERT_HEAD(&pflowif_list, pflowif, sc_next);
148 	return (0);
149 }
150 
151 int
152 pflow_clone_destroy(struct ifnet *ifp)
153 {
154 	struct pflow_softc	*sc = ifp->if_softc;
155 	int			 s;
156 
157 	s = splnet();
158 	pflow_sendout(sc);
159 #if NBPFILTER > 0
160 	bpfdetach(ifp);
161 #endif
162 	if_detach(ifp);
163 	SLIST_REMOVE(&pflowif_list, sc, pflow_softc, sc_next);
164 	free(sc->sc_imo.imo_membership, M_IPMOPTS);
165 	free(sc, M_DEVBUF);
166 	splx(s);
167 	return (0);
168 }
169 
170 /*
171  * Start output on the pflow interface.
172  */
173 void
174 pflowstart(struct ifnet *ifp)
175 {
176 	struct mbuf	*m;
177 	int		 s;
178 
179 	for (;;) {
180 		s = splnet();
181 		IF_DROP(&ifp->if_snd);
182 		IF_DEQUEUE(&ifp->if_snd, m);
183 		splx(s);
184 
185 		if (m == NULL)
186 			return;
187 		m_freem(m);
188 	}
189 }
190 
191 int
192 pflowoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
193 	struct rtentry *rt)
194 {
195 	m_freem(m);
196 	return (0);
197 }
198 
199 /* ARGSUSED */
200 int
201 pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
202 {
203 	struct proc		*p = curproc;
204 	struct pflow_softc	*sc = ifp->if_softc;
205 	struct ifreq		*ifr = (struct ifreq *)data;
206 	struct pflowreq		 pflowr;
207 	int			 s, error;
208 
209 	switch (cmd) {
210 	case SIOCSIFADDR:
211 	case SIOCAIFADDR:
212 	case SIOCSIFDSTADDR:
213 	case SIOCSIFFLAGS:
214 		if ((ifp->if_flags & IFF_UP) &&
215 		    sc->sc_receiver_ip.s_addr != 0 &&
216 		    sc->sc_receiver_port != 0) {
217 			ifp->if_flags |= IFF_RUNNING;
218 			sc->sc_gcounter=pflowstats.pflow_flows;
219 		} else
220 			ifp->if_flags &= ~IFF_RUNNING;
221 		break;
222 	case SIOCSIFMTU:
223 		if (ifr->ifr_mtu < PFLOW_MINMTU)
224 			return (EINVAL);
225 		if (ifr->ifr_mtu > MCLBYTES)
226 			ifr->ifr_mtu = MCLBYTES;
227 		s = splnet();
228 		if (ifr->ifr_mtu < ifp->if_mtu)
229 			pflow_sendout(sc);
230 		pflow_setmtu(sc, ifr->ifr_mtu);
231 		splx(s);
232 		break;
233 
234 	case SIOCGETPFLOW:
235 		bzero(&pflowr, sizeof(pflowr));
236 
237 		pflowr.sender_ip = sc->sc_sender_ip;
238 		pflowr.receiver_ip = sc->sc_receiver_ip;
239 		pflowr.receiver_port = sc->sc_receiver_port;
240 
241 		if ((error = copyout(&pflowr, ifr->ifr_data,
242 		    sizeof(pflowr))))
243 			return (error);
244 		break;
245 
246 	case SIOCSETPFLOW:
247 		if ((error = suser(p, p->p_acflag)) != 0)
248 			return (error);
249 		if ((error = copyin(ifr->ifr_data, &pflowr,
250 		    sizeof(pflowr))))
251 			return (error);
252 
253 		s = splnet();
254 		pflow_sendout(sc);
255 		splx(s);
256 
257 		if (pflowr.addrmask & PFLOW_MASK_DSTIP)
258 			sc->sc_receiver_ip = pflowr.receiver_ip;
259 		if (pflowr.addrmask & PFLOW_MASK_DSTPRT)
260 			sc->sc_receiver_port = pflowr.receiver_port;
261 		if (pflowr.addrmask & PFLOW_MASK_SRCIP)
262 			sc->sc_sender_ip.s_addr = pflowr.sender_ip.s_addr;
263 
264 		if ((ifp->if_flags & IFF_UP) &&
265 		    sc->sc_receiver_ip.s_addr != 0 &&
266 		    sc->sc_receiver_port != 0) {
267 			ifp->if_flags |= IFF_RUNNING;
268 			sc->sc_gcounter=pflowstats.pflow_flows;
269 		} else
270 			ifp->if_flags &= ~IFF_RUNNING;
271 
272 		break;
273 
274 	default:
275 		return (ENOTTY);
276 	}
277 	return (0);
278 }
279 
280 void
281 pflow_setmtu(struct pflow_softc *sc, int mtu_req)
282 {
283 	int	mtu;
284 
285 	if (sc->sc_pflow_ifp && sc->sc_pflow_ifp->if_mtu < mtu_req)
286 		mtu = sc->sc_pflow_ifp->if_mtu;
287 	else
288 		mtu = mtu_req;
289 
290 	sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
291 	    sizeof (struct udpiphdr)) / sizeof(struct pflow_flow);
292 	if (sc->sc_maxcount > PFLOW_MAXFLOWS)
293 	    sc->sc_maxcount = PFLOW_MAXFLOWS;
294 	sc->sc_if.if_mtu = sizeof(struct pflow_header) +
295 	    sizeof (struct udpiphdr) +
296 	    sc->sc_maxcount * sizeof(struct pflow_flow);
297 }
298 
299 struct mbuf *
300 pflow_get_mbuf(struct pflow_softc *sc)
301 {
302 	struct pflow_header	 h;
303 	struct mbuf		*m;
304 
305 	MGETHDR(m, M_DONTWAIT, MT_DATA);
306 	if (m == NULL) {
307 		pflowstats.pflow_onomem++;
308 		return (NULL);
309 	}
310 
311 	MCLGET(m, M_DONTWAIT);
312 	if ((m->m_flags & M_EXT) == 0) {
313 		m_free(m);
314 		pflowstats.pflow_onomem++;
315 		return (NULL);
316 	}
317 
318 	m->m_len = m->m_pkthdr.len = 0;
319 	m->m_pkthdr.rcvif = NULL;
320 
321 	/* populate pflow_header */
322 	h.reserved1 = 0;
323 	h.reserved2 = 0;
324 	h.count = 0;
325 	h.version = htons(PFLOW_VERSION);
326 	h.flow_sequence = htonl(sc->sc_gcounter);
327 	h.engine_type = PFLOW_ENGINE_TYPE;
328 	h.engine_id = PFLOW_ENGINE_ID;
329 	m_copyback(m, 0, PFLOW_HDRLEN, &h);
330 
331 	sc->sc_count = 0;
332 	timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
333 	return (m);
334 }
335 
336 void
337 copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
338     struct pf_state *st, int src, int dst)
339 {
340 	struct pf_state_key	*sk = st->key[PF_SK_WIRE];
341 
342 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
343 	flow1->src_port = flow2->dest_port = sk->port[src];
344 	flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
345 	flow1->dest_port = flow2->src_port = sk->port[dst];
346 
347 	flow1->dest_as = flow2->src_as =
348 	    flow1->src_as = flow2->dest_as = 0;
349 	flow1->if_index_out = flow2->if_index_in =
350 	    flow1->if_index_in = flow2->if_index_out = 0;
351 	flow1->dest_mask = flow2->src_mask =
352 	    flow1->src_mask = flow2->dest_mask = 0;
353 
354 	flow1->flow_packets = htonl(st->packets[0]);
355 	flow2->flow_packets = htonl(st->packets[1]);
356 	flow1->flow_octets = htonl(st->bytes[0]);
357 	flow2->flow_octets = htonl(st->bytes[1]);
358 
359 	flow1->flow_start = flow2->flow_start =
360 	    htonl((st->creation - (time_second - time_uptime)) * 1000);
361 	flow1->flow_finish = flow2->flow_finish =
362 	    htonl((time_uptime - (st->rule.ptr->timeout[st->timeout] ?
363 	    st->rule.ptr->timeout[st->timeout] :
364 	    pf_default_rule.timeout[st->timeout])) * 1000);
365 	flow1->tcp_flags = flow2->tcp_flags = 0;
366 	flow1->protocol = flow2->protocol = sk->proto;
367 	flow1->tos = flow2->tos = st->rule.ptr->tos;
368 }
369 
370 int
371 export_pflow(struct pf_state *st)
372 {
373 	struct pflow_softc	*sc = NULL;
374 	struct pf_state_key	*sk = st->key[PF_SK_WIRE];
375 
376 	if (sk->af != AF_INET)
377 		return (0);
378 
379 	SLIST_FOREACH(sc, &pflowif_list, sc_next) {
380 		export_pflow_if(st, sc);
381 	}
382 
383 	return (0);
384 }
385 
386 int
387 export_pflow_if(struct pf_state *st, struct pflow_softc *sc)
388 {
389 	struct pf_state		 pfs_copy;
390 	struct ifnet		*ifp = &sc->sc_if;
391 	u_int64_t		 bytes[2];
392 	int			 ret = 0;
393 
394 	if (!(ifp->if_flags & IFF_RUNNING))
395 		return (0);
396 
397 	if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
398 	    && (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
399 		return (pflow_pack_flow(st, sc));
400 
401 	/* flow > PFLOW_MAXBYTES need special handling */
402 	bcopy(st, &pfs_copy, sizeof(pfs_copy));
403 	bytes[0] = pfs_copy.bytes[0];
404 	bytes[1] = pfs_copy.bytes[1];
405 
406 	while (bytes[0] > PFLOW_MAXBYTES) {
407 		pfs_copy.bytes[0] = PFLOW_MAXBYTES;
408 		pfs_copy.bytes[1] = 0;
409 
410 		if ((ret = pflow_pack_flow(&pfs_copy, sc)) != 0)
411 			return (ret);
412 		if ((bytes[0] - PFLOW_MAXBYTES) > 0)
413 			bytes[0] -= PFLOW_MAXBYTES;
414 	}
415 
416 	while (bytes[1] > (u_int64_t)PFLOW_MAXBYTES) {
417 		pfs_copy.bytes[1] = PFLOW_MAXBYTES;
418 		pfs_copy.bytes[0] = 0;
419 
420 		if ((ret = pflow_pack_flow(&pfs_copy, sc)) != 0)
421 			return (ret);
422 		if ((bytes[1] - PFLOW_MAXBYTES) > 0)
423 			bytes[1] -= PFLOW_MAXBYTES;
424 	}
425 
426 	pfs_copy.bytes[0] = bytes[0];
427 	pfs_copy.bytes[1] = bytes[1];
428 
429 	return (pflow_pack_flow(&pfs_copy, sc));
430 }
431 
432 int
433 copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
434 {
435 	int		s, ret = 0;
436 
437 	s = splnet();
438 	if (sc->sc_mbuf == NULL) {
439 		if ((sc->sc_mbuf = pflow_get_mbuf(sc)) == NULL) {
440 			splx(s);
441 			return (ENOBUFS);
442 		}
443 	}
444 	m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
445 	    (sc->sc_count * sizeof (struct pflow_flow)),
446 	    sizeof (struct pflow_flow), flow);
447 
448 	if (pflowstats.pflow_flows == sc->sc_gcounter)
449 		pflowstats.pflow_flows++;
450 	sc->sc_gcounter++;
451 	sc->sc_count++;
452 
453 	if (sc->sc_count >= sc->sc_maxcount)
454 		ret = pflow_sendout(sc);
455 
456 	splx(s);
457 	return(ret);
458 }
459 
460 int
461 pflow_pack_flow(struct pf_state *st, struct pflow_softc *sc)
462 {
463 	struct pflow_flow	 flow1;
464 	struct pflow_flow	 flow2;
465 	int			 ret = 0;
466 
467 	bzero(&flow1, sizeof(flow1));
468 	bzero(&flow2, sizeof(flow2));
469 
470 	if (st->direction == PF_OUT)
471 		copy_flow_data(&flow1, &flow2, st, 1, 0);
472 	else
473 		copy_flow_data(&flow1, &flow2, st, 0, 1);
474 
475 	if (st->bytes[0] != 0) /* first flow from state */
476 		ret = copy_flow_to_m(&flow1, sc);
477 
478 	if (st->bytes[1] != 0) /* second flow from state */
479 		ret = copy_flow_to_m(&flow2, sc);
480 
481 	return (ret);
482 }
483 
484 void
485 pflow_timeout(void *v)
486 {
487 	struct pflow_softc	*sc = v;
488 	int			 s;
489 
490 	s = splnet();
491 	pflow_sendout(sc);
492 	splx(s);
493 }
494 
495 /* This must be called in splnet() */
496 int
497 pflow_sendout(struct pflow_softc *sc)
498 {
499 	struct mbuf		*m = sc->sc_mbuf;
500 	struct pflow_header	*h;
501 	struct ifnet		*ifp = &sc->sc_if;
502 
503 	timeout_del(&sc->sc_tmo);
504 
505 	if (m == NULL)
506 		return (0);
507 
508 	sc->sc_mbuf = NULL;
509 	if (!(ifp->if_flags & IFF_RUNNING)) {
510 		m_freem(m);
511 		return (0);
512 	}
513 
514 	pflowstats.pflow_packets++;
515 	h = mtod(m, struct pflow_header *);
516 	h->count = htons(sc->sc_count);
517 
518 	/* populate pflow_header */
519 	h->uptime_ms = htonl(time_uptime * 1000);
520 	h->time_sec = htonl(time_second);
521 	h->time_nanosec = htonl(ticks);
522 
523 	return (pflow_sendout_mbuf(sc, m));
524 }
525 
526 int
527 pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
528 {
529 	struct udpiphdr	*ui;
530 	u_int16_t	 len = m->m_pkthdr.len;
531 	struct ifnet	*ifp = &sc->sc_if;
532 	struct ip	*ip;
533 	int		 err;
534 
535 	/* UDP Header*/
536 	M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
537 	if (m == NULL) {
538 		pflowstats.pflow_onomem++;
539 		return (ENOBUFS);
540 	}
541 
542 	ui = mtod(m, struct udpiphdr *);
543 	ui->ui_pr = IPPROTO_UDP;
544 	ui->ui_src = sc->sc_sender_ip;
545 	ui->ui_sport = sc->sc_sender_port;
546 	ui->ui_dst = sc->sc_receiver_ip;
547 	ui->ui_dport = sc->sc_receiver_port;
548 	ui->ui_ulen = htons(sizeof (struct udphdr) + len);
549 
550 	ip = (struct ip *)ui;
551 	ip->ip_v = IPVERSION;
552 	ip->ip_hl = sizeof(struct ip) >> 2;
553 	ip->ip_id = htons(ip_randomid());
554 	ip->ip_off = htons(IP_DF);
555 	ip->ip_tos = IPTOS_LOWDELAY;
556 	ip->ip_ttl = IPDEFTTL;
557 	ip->ip_len = htons(sizeof (struct udpiphdr) + len);
558 
559 	/*
560 	 * Compute the pseudo-header checksum; defer further checksumming
561 	 * until ip_output() or hardware (if it exists).
562 	 */
563 	m->m_pkthdr.csum_flags |= M_UDPV4_CSUM_OUT;
564 	ui->ui_sum = in_cksum_phdr(ui->ui_src.s_addr,
565 	    ui->ui_dst.s_addr, htons(len + sizeof(struct udphdr) +
566 	    IPPROTO_UDP));
567 
568 #if NBPFILTER > 0
569 	if (ifp->if_bpf) {
570 		ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
571 		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
572 	}
573 #endif
574 
575 	sc->sc_if.if_opackets++;
576 	sc->sc_if.if_obytes += m->m_pkthdr.len;
577 
578 	if ((err = ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL))) {
579 		pflowstats.pflow_oerrors++;
580 		sc->sc_if.if_oerrors++;
581 	}
582 	return (err);
583 }
584 
585 int
586 pflow_get_dynport(void)
587 {
588 	u_int16_t	tmp, low, high, cut;
589 
590 	low = ipport_hifirstauto;     /* sysctl */
591 	high = ipport_hilastauto;
592 
593 	cut = arc4random_uniform(1 + high - low) + low;
594 
595 	for (tmp = cut; tmp <= high; ++(tmp)) {
596 		if (!in_baddynamic(tmp, IPPROTO_UDP))
597 			return (htons(tmp));
598 	}
599 
600 	for (tmp = cut - 1; tmp >= low; --(tmp)) {
601 		if (!in_baddynamic(tmp, IPPROTO_UDP))
602 			return (htons(tmp));
603 	}
604 
605 	return (htons(ipport_hilastauto)); /* XXX */
606 }
607 
608 int
609 pflow_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
610     void *newp, size_t newlen)
611 {
612 	if (namelen != 1)
613 		return (ENOTDIR);
614 
615 	switch (name[0]) {
616 	case NET_PFLOW_STATS:
617 		if (newp != NULL)
618 			return (EPERM);
619 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
620 		    &pflowstats, sizeof(pflowstats)));
621 	default:
622 		return (EOPNOTSUPP);
623 	}
624 	return (0);
625 }
626