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