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