1 /* $OpenBSD: udp_usrreq.c,v 1.305 2023/01/22 12:05:44 mvs Exp $ */
2 /* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
3
4 /*
5 * Copyright (c) 1982, 1986, 1988, 1990, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
33 *
34 * NRL grants permission for redistribution and use in source and binary
35 * forms, with or without modification, of the software and documentation
36 * created at NRL provided that the following conditions are met:
37 *
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgements:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * This product includes software developed at the Information
48 * Technology Division, US Naval Research Laboratory.
49 * 4. Neither the name of the NRL nor the names of its contributors
50 * may be used to endorse or promote products derived from this software
51 * without specific prior written permission.
52 *
53 * THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
54 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
55 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
56 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
57 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
60 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
61 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
62 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 *
65 * The views and conclusions contained in the software and documentation
66 * are those of the authors and should not be interpreted as representing
67 * official policies, either expressed or implied, of the US Naval
68 * Research Laboratory (NRL).
69 */
70
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <sys/mbuf.h>
74 #include <sys/protosw.h>
75 #include <sys/socket.h>
76 #include <sys/socketvar.h>
77 #include <sys/sysctl.h>
78 #include <sys/domain.h>
79
80 #include <net/if.h>
81 #include <net/if_var.h>
82 #include <net/if_media.h>
83 #include <net/route.h>
84
85 #include <netinet/in.h>
86 #include <netinet/in_var.h>
87 #include <netinet/ip.h>
88 #include <netinet/in_pcb.h>
89 #include <netinet/ip_var.h>
90 #include <netinet/ip_icmp.h>
91 #include <netinet/udp.h>
92 #include <netinet/udp_var.h>
93
94 #ifdef IPSEC
95 #include <netinet/ip_ipsp.h>
96 #include <netinet/ip_esp.h>
97 #endif
98
99 #ifdef INET6
100 #include <netinet6/in6_var.h>
101 #include <netinet6/ip6_var.h>
102 #include <netinet6/ip6protosw.h>
103 #endif /* INET6 */
104
105 #include "pf.h"
106 #if NPF > 0
107 #include <net/pfvar.h>
108 #endif
109
110 #ifdef PIPEX
111 #include <netinet/if_ether.h>
112 #include <net/pipex.h>
113 #endif
114
115 /*
116 * UDP protocol implementation.
117 * Per RFC 768, August, 1980.
118 */
119 int udpcksum = 1;
120
121 u_int udp_sendspace = 9216; /* really max datagram size */
122 u_int udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
123 /* 40 1K datagrams */
124
125 const struct pr_usrreqs udp_usrreqs = {
126 .pru_attach = udp_attach,
127 .pru_detach = udp_detach,
128 .pru_lock = udp_lock,
129 .pru_unlock = udp_unlock,
130 .pru_bind = udp_bind,
131 .pru_connect = udp_connect,
132 .pru_disconnect = udp_disconnect,
133 .pru_shutdown = udp_shutdown,
134 .pru_send = udp_send,
135 .pru_control = in_control,
136 .pru_sockaddr = in_sockaddr,
137 .pru_peeraddr = in_peeraddr,
138 };
139
140 #ifdef INET6
141 const struct pr_usrreqs udp6_usrreqs = {
142 .pru_attach = udp_attach,
143 .pru_detach = udp_detach,
144 .pru_lock = udp_lock,
145 .pru_unlock = udp_unlock,
146 .pru_bind = udp_bind,
147 .pru_connect = udp_connect,
148 .pru_disconnect = udp_disconnect,
149 .pru_shutdown = udp_shutdown,
150 .pru_send = udp_send,
151 .pru_control = in6_control,
152 .pru_sockaddr = in6_sockaddr,
153 .pru_peeraddr = in6_peeraddr,
154 };
155 #endif
156
157 const struct sysctl_bounded_args udpctl_vars[] = {
158 { UDPCTL_CHECKSUM, &udpcksum, 0, 1 },
159 { UDPCTL_RECVSPACE, &udp_recvspace, 0, INT_MAX },
160 { UDPCTL_SENDSPACE, &udp_sendspace, 0, INT_MAX },
161 };
162
163 struct inpcbtable udbtable;
164 struct cpumem *udpcounters;
165
166 void udp_sbappend(struct inpcb *, struct mbuf *, struct ip *,
167 struct ip6_hdr *, int, struct udphdr *, struct sockaddr *,
168 u_int32_t);
169 int udp_output(struct inpcb *, struct mbuf *, struct mbuf *, struct mbuf *);
170 void udp_notify(struct inpcb *, int);
171 int udp_sysctl_udpstat(void *, size_t *, void *);
172
173 #ifndef UDB_INITIAL_HASH_SIZE
174 #define UDB_INITIAL_HASH_SIZE 128
175 #endif
176
177 void
178 udp_init(void)
179 {
180 udpcounters = counters_alloc(udps_ncounters);
181 in_pcbinit(&udbtable, UDB_INITIAL_HASH_SIZE);
182 }
183
184 int
185 udp_input(struct mbuf **mp, int *offp, int proto, int af)
186 {
187 struct mbuf *m = *mp;
188 int iphlen = *offp;
189 struct ip *ip = NULL;
190 struct udphdr *uh;
191 struct inpcb *inp = NULL;
192 struct ip save_ip;
193 int len;
194 u_int16_t savesum;
195 union {
196 struct sockaddr sa;
197 struct sockaddr_in sin;
198 #ifdef INET6
199 struct sockaddr_in6 sin6;
200 #endif /* INET6 */
201 } srcsa, dstsa;
202 struct ip6_hdr *ip6 = NULL;
203 u_int32_t ipsecflowinfo = 0;
204
205 udpstat_inc(udps_ipackets);
206
207 IP6_EXTHDR_GET(uh, struct udphdr *, m, iphlen, sizeof(struct udphdr));
208 if (!uh) {
209 udpstat_inc(udps_hdrops);
210 return IPPROTO_DONE;
211 }
212
213 /* Check for illegal destination port 0 */
214 if (uh->uh_dport == 0) {
215 udpstat_inc(udps_noport);
216 goto bad;
217 }
218
219 /*
220 * Make mbuf data length reflect UDP length.
221 * If not enough data to reflect UDP length, drop.
222 */
223 len = ntohs((u_int16_t)uh->uh_ulen);
224 switch (af) {
225 case AF_INET:
226 if (m->m_pkthdr.len - iphlen != len) {
227 if (len > (m->m_pkthdr.len - iphlen) ||
228 len < sizeof(struct udphdr)) {
229 udpstat_inc(udps_badlen);
230 goto bad;
231 }
232 m_adj(m, len - (m->m_pkthdr.len - iphlen));
233 }
234 ip = mtod(m, struct ip *);
235 /*
236 * Save a copy of the IP header in case we want restore it
237 * for sending an ICMP error message in response.
238 */
239 save_ip = *ip;
240 break;
241 #ifdef INET6
242 case AF_INET6:
243 /* jumbograms */
244 if (len == 0 && m->m_pkthdr.len - iphlen > 0xffff)
245 len = m->m_pkthdr.len - iphlen;
246 if (len != m->m_pkthdr.len - iphlen) {
247 udpstat_inc(udps_badlen);
248 goto bad;
249 }
250 ip6 = mtod(m, struct ip6_hdr *);
251 break;
252 #endif /* INET6 */
253 default:
254 unhandled_af(af);
255 }
256
257 /*
258 * Checksum extended UDP header and data.
259 * from W.R.Stevens: check incoming udp cksums even if
260 * udpcksum is not set.
261 */
262 savesum = uh->uh_sum;
263 if (uh->uh_sum == 0) {
264 udpstat_inc(udps_nosum);
265 #ifdef INET6
266 /*
267 * In IPv6, the UDP checksum is ALWAYS used.
268 */
269 if (ip6)
270 goto bad;
271 #endif /* INET6 */
272 } else {
273 if ((m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_OK) == 0) {
274 if (m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_BAD) {
275 udpstat_inc(udps_badsum);
276 goto bad;
277 }
278 udpstat_inc(udps_inswcsum);
279
280 if (ip)
281 uh->uh_sum = in4_cksum(m, IPPROTO_UDP,
282 iphlen, len);
283 #ifdef INET6
284 else if (ip6)
285 uh->uh_sum = in6_cksum(m, IPPROTO_UDP,
286 iphlen, len);
287 #endif /* INET6 */
288 if (uh->uh_sum != 0) {
289 udpstat_inc(udps_badsum);
290 goto bad;
291 }
292 }
293 }
294
295 #ifdef IPSEC
296 if (udpencap_enable && udpencap_port && esp_enable &&
297 #if NPF > 0
298 !(m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) &&
299 #endif
300 uh->uh_dport == htons(udpencap_port)) {
301 u_int32_t spi;
302 int skip = iphlen + sizeof(struct udphdr);
303
304 if (m->m_pkthdr.len - skip < sizeof(u_int32_t)) {
305 /* packet too short */
306 m_freem(m);
307 return IPPROTO_DONE;
308 }
309 m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
310 /*
311 * decapsulate if the SPI is not zero, otherwise pass
312 * to userland
313 */
314 if (spi != 0) {
315 int protoff;
316
317 if ((m = *mp = m_pullup(m, skip)) == NULL) {
318 udpstat_inc(udps_hdrops);
319 return IPPROTO_DONE;
320 }
321
322 /* remove the UDP header */
323 bcopy(mtod(m, u_char *),
324 mtod(m, u_char *) + sizeof(struct udphdr), iphlen);
325 m_adj(m, sizeof(struct udphdr));
326 skip -= sizeof(struct udphdr);
327
328 espstat_inc(esps_udpencin);
329 protoff = af == AF_INET ? offsetof(struct ip, ip_p) :
330 offsetof(struct ip6_hdr, ip6_nxt);
331 return ipsec_common_input(mp, skip, protoff,
332 af, IPPROTO_ESP, 1);
333 }
334 }
335 #endif /* IPSEC */
336
337 switch (af) {
338 case AF_INET:
339 bzero(&srcsa, sizeof(struct sockaddr_in));
340 srcsa.sin.sin_len = sizeof(struct sockaddr_in);
341 srcsa.sin.sin_family = AF_INET;
342 srcsa.sin.sin_port = uh->uh_sport;
343 srcsa.sin.sin_addr = ip->ip_src;
344
345 bzero(&dstsa, sizeof(struct sockaddr_in));
346 dstsa.sin.sin_len = sizeof(struct sockaddr_in);
347 dstsa.sin.sin_family = AF_INET;
348 dstsa.sin.sin_port = uh->uh_dport;
349 dstsa.sin.sin_addr = ip->ip_dst;
350 break;
351 #ifdef INET6
352 case AF_INET6:
353 bzero(&srcsa, sizeof(struct sockaddr_in6));
354 srcsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
355 srcsa.sin6.sin6_family = AF_INET6;
356 srcsa.sin6.sin6_port = uh->uh_sport;
357 #if 0 /*XXX inbound flowinfo */
358 srcsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ip6->ip6_flow;
359 #endif
360 /* KAME hack: recover scopeid */
361 in6_recoverscope(&srcsa.sin6, &ip6->ip6_src);
362
363 bzero(&dstsa, sizeof(struct sockaddr_in6));
364 dstsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
365 dstsa.sin6.sin6_family = AF_INET6;
366 dstsa.sin6.sin6_port = uh->uh_dport;
367 #if 0 /*XXX inbound flowinfo */
368 dstsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ip6->ip6_flow;
369 #endif
370 /* KAME hack: recover scopeid */
371 in6_recoverscope(&dstsa.sin6, &ip6->ip6_dst);
372 break;
373 #endif /* INET6 */
374 }
375
376 if (m->m_flags & (M_BCAST|M_MCAST)) {
377 SIMPLEQ_HEAD(, inpcb) inpcblist;
378
379 /*
380 * Deliver a multicast or broadcast datagram to *all* sockets
381 * for which the local and remote addresses and ports match
382 * those of the incoming datagram. This allows more than
383 * one process to receive multi/broadcasts on the same port.
384 * (This really ought to be done for unicast datagrams as
385 * well, but that would cause problems with existing
386 * applications that open both address-specific sockets and
387 * a wildcard socket listening to the same port -- they would
388 * end up receiving duplicates of every unicast datagram.
389 * Those applications open the multiple sockets to overcome an
390 * inadequacy of the UDP socket interface, but for backwards
391 * compatibility we avoid the problem here rather than
392 * fixing the interface. Maybe 4.5BSD will remedy this?)
393 */
394
395 /*
396 * Locate pcb(s) for datagram.
397 * (Algorithm copied from raw_intr().)
398 */
399 SIMPLEQ_INIT(&inpcblist);
400 rw_enter_write(&udbtable.inpt_notify);
401 mtx_enter(&udbtable.inpt_mtx);
402 TAILQ_FOREACH(inp, &udbtable.inpt_queue, inp_queue) {
403 if (inp->inp_socket->so_rcv.sb_state & SS_CANTRCVMORE)
404 continue;
405 #ifdef INET6
406 /* don't accept it if AF does not match */
407 if (ip6 && !(inp->inp_flags & INP_IPV6))
408 continue;
409 if (!ip6 && (inp->inp_flags & INP_IPV6))
410 continue;
411 #endif
412 if (rtable_l2(inp->inp_rtableid) !=
413 rtable_l2(m->m_pkthdr.ph_rtableid))
414 continue;
415 if (inp->inp_lport != uh->uh_dport)
416 continue;
417 #ifdef INET6
418 if (ip6) {
419 if (inp->inp_ip6_minhlim &&
420 inp->inp_ip6_minhlim > ip6->ip6_hlim)
421 continue;
422 if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
423 if (!IN6_ARE_ADDR_EQUAL(
424 &inp->inp_laddr6, &ip6->ip6_dst))
425 continue;
426 } else
427 #endif /* INET6 */
428 {
429 if (inp->inp_ip_minttl &&
430 inp->inp_ip_minttl > ip->ip_ttl)
431 continue;
432
433 if (inp->inp_laddr.s_addr != INADDR_ANY) {
434 if (inp->inp_laddr.s_addr !=
435 ip->ip_dst.s_addr)
436 continue;
437 }
438 }
439 #ifdef INET6
440 if (ip6) {
441 if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
442 if (!IN6_ARE_ADDR_EQUAL(
443 &inp->inp_faddr6, &ip6->ip6_src) ||
444 inp->inp_fport != uh->uh_sport)
445 continue;
446 } else
447 #endif /* INET6 */
448 if (inp->inp_faddr.s_addr != INADDR_ANY) {
449 if (inp->inp_faddr.s_addr !=
450 ip->ip_src.s_addr ||
451 inp->inp_fport != uh->uh_sport)
452 continue;
453 }
454
455 in_pcbref(inp);
456 SIMPLEQ_INSERT_TAIL(&inpcblist, inp, inp_notify);
457
458 /*
459 * Don't look for additional matches if this one does
460 * not have either the SO_REUSEPORT or SO_REUSEADDR
461 * socket options set. This heuristic avoids searching
462 * through all pcbs in the common case of a non-shared
463 * port. It assumes that an application will never
464 * clear these options after setting them.
465 */
466 if ((inp->inp_socket->so_options & (SO_REUSEPORT |
467 SO_REUSEADDR)) == 0)
468 break;
469 }
470 mtx_leave(&udbtable.inpt_mtx);
471
472 if (SIMPLEQ_EMPTY(&inpcblist)) {
473 rw_exit_write(&udbtable.inpt_notify);
474
475 /*
476 * No matching pcb found; discard datagram.
477 * (No need to send an ICMP Port Unreachable
478 * for a broadcast or multicast datgram.)
479 */
480 udpstat_inc(udps_noportbcast);
481 goto bad;
482 }
483
484 while ((inp = SIMPLEQ_FIRST(&inpcblist)) != NULL) {
485 struct mbuf *n;
486
487 SIMPLEQ_REMOVE_HEAD(&inpcblist, inp_notify);
488 if (SIMPLEQ_EMPTY(&inpcblist))
489 n = m;
490 else
491 n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
492 if (n != NULL) {
493 udp_sbappend(inp, n, ip, ip6, iphlen, uh,
494 &srcsa.sa, 0);
495 }
496 in_pcbunref(inp);
497 }
498 rw_exit_write(&udbtable.inpt_notify);
499
500 return IPPROTO_DONE;
501 }
502 /*
503 * Locate pcb for datagram.
504 */
505 #if NPF > 0
506 inp = pf_inp_lookup(m);
507 #endif
508 if (inp == NULL) {
509 #ifdef INET6
510 if (ip6)
511 inp = in6_pcblookup(&udbtable, &ip6->ip6_src,
512 uh->uh_sport, &ip6->ip6_dst, uh->uh_dport,
513 m->m_pkthdr.ph_rtableid);
514 else
515 #endif /* INET6 */
516 inp = in_pcblookup(&udbtable, ip->ip_src, uh->uh_sport,
517 ip->ip_dst, uh->uh_dport, m->m_pkthdr.ph_rtableid);
518 }
519 if (inp == NULL) {
520 udpstat_inc(udps_pcbhashmiss);
521 #ifdef INET6
522 if (ip6) {
523 inp = in6_pcblookup_listen(&udbtable, &ip6->ip6_dst,
524 uh->uh_dport, m, m->m_pkthdr.ph_rtableid);
525 } else
526 #endif /* INET6 */
527 inp = in_pcblookup_listen(&udbtable, ip->ip_dst,
528 uh->uh_dport, m, m->m_pkthdr.ph_rtableid);
529 }
530
531 #ifdef IPSEC
532 if (ipsec_in_use) {
533 struct m_tag *mtag;
534 struct tdb_ident *tdbi;
535 struct tdb *tdb;
536 int error;
537
538 mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
539 if (mtag != NULL) {
540 tdbi = (struct tdb_ident *)(mtag + 1);
541 tdb = gettdb(tdbi->rdomain, tdbi->spi,
542 &tdbi->dst, tdbi->proto);
543 } else
544 tdb = NULL;
545 error = ipsp_spd_lookup(m, af, iphlen, IPSP_DIRECTION_IN,
546 tdb, inp, NULL, NULL);
547 if (error) {
548 udpstat_inc(udps_nosec);
549 tdb_unref(tdb);
550 goto bad;
551 }
552 /* create ipsec options, id is not modified after creation */
553 if (tdb && tdb->tdb_ids)
554 ipsecflowinfo = tdb->tdb_ids->id_flow;
555 tdb_unref(tdb);
556 }
557 #endif /*IPSEC */
558
559 if (inp == NULL) {
560 udpstat_inc(udps_noport);
561 if (m->m_flags & (M_BCAST | M_MCAST)) {
562 udpstat_inc(udps_noportbcast);
563 goto bad;
564 }
565 #ifdef INET6
566 if (ip6) {
567 uh->uh_sum = savesum;
568 icmp6_error(m, ICMP6_DST_UNREACH,
569 ICMP6_DST_UNREACH_NOPORT,0);
570 } else
571 #endif /* INET6 */
572 {
573 *ip = save_ip;
574 uh->uh_sum = savesum;
575 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT,
576 0, 0);
577 }
578 return IPPROTO_DONE;
579 }
580
581 KASSERT(sotoinpcb(inp->inp_socket) == inp);
582 soassertlocked(inp->inp_socket);
583
584 #ifdef INET6
585 if (ip6 && inp->inp_ip6_minhlim &&
586 inp->inp_ip6_minhlim > ip6->ip6_hlim) {
587 goto bad;
588 } else
589 #endif
590 if (ip && inp->inp_ip_minttl &&
591 inp->inp_ip_minttl > ip->ip_ttl) {
592 goto bad;
593 }
594
595 #if NPF > 0
596 if (inp->inp_socket->so_state & SS_ISCONNECTED)
597 pf_inp_link(m, inp);
598 #endif
599
600 #ifdef PIPEX
601 if (pipex_enable && inp->inp_pipex) {
602 struct pipex_session *session;
603 int off = iphlen + sizeof(struct udphdr);
604
605 if ((session = pipex_l2tp_lookup_session(m, off)) != NULL) {
606 m = *mp = pipex_l2tp_input(m, off, session,
607 ipsecflowinfo);
608 pipex_rele_session(session);
609 if (m == NULL) {
610 in_pcbunref(inp);
611 return IPPROTO_DONE;
612 }
613 }
614 }
615 #endif
616
617 udp_sbappend(inp, m, ip, ip6, iphlen, uh, &srcsa.sa, ipsecflowinfo);
618 in_pcbunref(inp);
619 return IPPROTO_DONE;
620 bad:
621 m_freem(m);
622 in_pcbunref(inp);
623 return IPPROTO_DONE;
624 }
625
626 void
627 udp_sbappend(struct inpcb *inp, struct mbuf *m, struct ip *ip,
628 struct ip6_hdr *ip6, int hlen, struct udphdr *uh,
629 struct sockaddr *srcaddr, u_int32_t ipsecflowinfo)
630 {
631 struct socket *so = inp->inp_socket;
632 struct mbuf *opts = NULL;
633
634 hlen += sizeof(*uh);
635
636 if (inp->inp_upcall != NULL) {
637 m = (*inp->inp_upcall)(inp->inp_upcall_arg, m,
638 ip, ip6, uh, hlen);
639 if (m == NULL)
640 return;
641 }
642
643 #ifdef INET6
644 if (ip6 && (inp->inp_flags & IN6P_CONTROLOPTS ||
645 so->so_options & SO_TIMESTAMP))
646 ip6_savecontrol(inp, m, &opts);
647 #endif /* INET6 */
648 if (ip && (inp->inp_flags & INP_CONTROLOPTS ||
649 so->so_options & SO_TIMESTAMP))
650 ip_savecontrol(inp, &opts, ip, m);
651 #ifdef INET6
652 if (ip6 && (inp->inp_flags & IN6P_RECVDSTPORT)) {
653 struct mbuf **mp = &opts;
654
655 while (*mp)
656 mp = &(*mp)->m_next;
657 *mp = sbcreatecontrol((caddr_t)&uh->uh_dport, sizeof(u_int16_t),
658 IPV6_RECVDSTPORT, IPPROTO_IPV6);
659 }
660 #endif /* INET6 */
661 if (ip && (inp->inp_flags & INP_RECVDSTPORT)) {
662 struct mbuf **mp = &opts;
663
664 while (*mp)
665 mp = &(*mp)->m_next;
666 *mp = sbcreatecontrol((caddr_t)&uh->uh_dport, sizeof(u_int16_t),
667 IP_RECVDSTPORT, IPPROTO_IP);
668 }
669 #ifdef IPSEC
670 if (ipsecflowinfo && (inp->inp_flags & INP_IPSECFLOWINFO)) {
671 struct mbuf **mp = &opts;
672
673 while (*mp)
674 mp = &(*mp)->m_next;
675 *mp = sbcreatecontrol((caddr_t)&ipsecflowinfo,
676 sizeof(u_int32_t), IP_IPSECFLOWINFO, IPPROTO_IP);
677 }
678 #endif
679 m_adj(m, hlen);
680
681 mtx_enter(&inp->inp_mtx);
682 if (sbappendaddr(so, &so->so_rcv, srcaddr, m, opts) == 0) {
683 mtx_leave(&inp->inp_mtx);
684 udpstat_inc(udps_fullsock);
685 m_freem(m);
686 m_freem(opts);
687 return;
688 }
689 mtx_leave(&inp->inp_mtx);
690
691 sorwakeup(so);
692 }
693
694 /*
695 * Notify a udp user of an asynchronous error;
696 * just wake up so that he can collect error status.
697 */
698 void
699 udp_notify(struct inpcb *inp, int errno)
700 {
701 inp->inp_socket->so_error = errno;
702 sorwakeup(inp->inp_socket);
703 sowwakeup(inp->inp_socket);
704 }
705
706 #ifdef INET6
707 void
708 udp6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *d)
709 {
710 struct udphdr uh;
711 struct sockaddr_in6 sa6;
712 struct ip6_hdr *ip6;
713 struct mbuf *m;
714 int off;
715 void *cmdarg;
716 struct ip6ctlparam *ip6cp = NULL;
717 struct udp_portonly {
718 u_int16_t uh_sport;
719 u_int16_t uh_dport;
720 } *uhp;
721 struct inpcb *inp;
722 void (*notify)(struct inpcb *, int) = udp_notify;
723
724 if (sa == NULL)
725 return;
726 if (sa->sa_family != AF_INET6 ||
727 sa->sa_len != sizeof(struct sockaddr_in6))
728 return;
729
730 if ((unsigned)cmd >= PRC_NCMDS)
731 return;
732 if (PRC_IS_REDIRECT(cmd))
733 notify = in_rtchange, d = NULL;
734 else if (cmd == PRC_HOSTDEAD)
735 d = NULL;
736 else if (cmd == PRC_MSGSIZE)
737 ; /* special code is present, see below */
738 else if (inet6ctlerrmap[cmd] == 0)
739 return;
740
741 /* if the parameter is from icmp6, decode it. */
742 if (d != NULL) {
743 ip6cp = (struct ip6ctlparam *)d;
744 m = ip6cp->ip6c_m;
745 ip6 = ip6cp->ip6c_ip6;
746 off = ip6cp->ip6c_off;
747 cmdarg = ip6cp->ip6c_cmdarg;
748 } else {
749 m = NULL;
750 ip6 = NULL;
751 cmdarg = NULL;
752 /* XXX: translate addresses into internal form */
753 sa6 = *satosin6(sa);
754 if (in6_embedscope(&sa6.sin6_addr, &sa6, NULL)) {
755 /* should be impossible */
756 return;
757 }
758 }
759
760 if (ip6cp && ip6cp->ip6c_finaldst) {
761 bzero(&sa6, sizeof(sa6));
762 sa6.sin6_family = AF_INET6;
763 sa6.sin6_len = sizeof(sa6);
764 sa6.sin6_addr = *ip6cp->ip6c_finaldst;
765 /* XXX: assuming M is valid in this case */
766 sa6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
767 ip6cp->ip6c_finaldst);
768 if (in6_embedscope(ip6cp->ip6c_finaldst, &sa6, NULL)) {
769 /* should be impossible */
770 return;
771 }
772 } else {
773 /* XXX: translate addresses into internal form */
774 sa6 = *satosin6(sa);
775 if (in6_embedscope(&sa6.sin6_addr, &sa6, NULL)) {
776 /* should be impossible */
777 return;
778 }
779 }
780
781 if (ip6) {
782 /*
783 * XXX: We assume that when IPV6 is non NULL,
784 * M and OFF are valid.
785 */
786 struct sockaddr_in6 sa6_src;
787
788 /* check if we can safely examine src and dst ports */
789 if (m->m_pkthdr.len < off + sizeof(*uhp))
790 return;
791
792 bzero(&uh, sizeof(uh));
793 m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh);
794
795 bzero(&sa6_src, sizeof(sa6_src));
796 sa6_src.sin6_family = AF_INET6;
797 sa6_src.sin6_len = sizeof(sa6_src);
798 sa6_src.sin6_addr = ip6->ip6_src;
799 sa6_src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
800 &ip6->ip6_src);
801 if (in6_embedscope(&sa6_src.sin6_addr, &sa6_src, NULL)) {
802 /* should be impossible */
803 return;
804 }
805
806 if (cmd == PRC_MSGSIZE) {
807 /*
808 * Check to see if we have a valid UDP socket
809 * corresponding to the address in the ICMPv6 message
810 * payload.
811 */
812 inp = in6_pcblookup(&udbtable, &sa6.sin6_addr,
813 uh.uh_dport, &sa6_src.sin6_addr, uh.uh_sport,
814 rdomain);
815 #if 0
816 /*
817 * As the use of sendto(2) is fairly popular,
818 * we may want to allow non-connected pcb too.
819 * But it could be too weak against attacks...
820 * We should at least check if the local address (= s)
821 * is really ours.
822 */
823 if (inp == NULL) {
824 inp = in6_pcblookup_listen(&udbtable,
825 &sa6_src.sin6_addr, uh.uh_sport, NULL,
826 rdomain))
827 }
828 #endif
829
830 /*
831 * Depending on the value of "valid" and routing table
832 * size (mtudisc_{hi,lo}wat), we will:
833 * - recalculate the new MTU and create the
834 * corresponding routing entry, or
835 * - ignore the MTU change notification.
836 */
837 icmp6_mtudisc_update((struct ip6ctlparam *)d,
838 inp != NULL);
839 in_pcbunref(inp);
840
841 /*
842 * regardless of if we called icmp6_mtudisc_update(),
843 * we need to call in6_pcbnotify(), to notify path
844 * MTU change to the userland (2292bis-02), because
845 * some unconnected sockets may share the same
846 * destination and want to know the path MTU.
847 */
848 }
849
850 in6_pcbnotify(&udbtable, &sa6, uh.uh_dport,
851 &sa6_src, uh.uh_sport, rdomain, cmd, cmdarg, notify);
852 } else {
853 in6_pcbnotify(&udbtable, &sa6, 0,
854 &sa6_any, 0, rdomain, cmd, cmdarg, notify);
855 }
856 }
857 #endif
858
859 void
860 udp_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
861 {
862 struct ip *ip = v;
863 struct udphdr *uhp;
864 struct in_addr faddr;
865 struct inpcb *inp;
866 void (*notify)(struct inpcb *, int) = udp_notify;
867 int errno;
868
869 if (sa == NULL)
870 return;
871 if (sa->sa_family != AF_INET ||
872 sa->sa_len != sizeof(struct sockaddr_in))
873 return;
874 faddr = satosin(sa)->sin_addr;
875 if (faddr.s_addr == INADDR_ANY)
876 return;
877
878 if ((unsigned)cmd >= PRC_NCMDS)
879 return;
880 errno = inetctlerrmap[cmd];
881 if (PRC_IS_REDIRECT(cmd))
882 notify = in_rtchange, ip = 0;
883 else if (cmd == PRC_HOSTDEAD)
884 ip = 0;
885 else if (errno == 0)
886 return;
887 if (ip) {
888 uhp = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
889
890 #ifdef IPSEC
891 /* PMTU discovery for udpencap */
892 if (cmd == PRC_MSGSIZE && ip_mtudisc && udpencap_enable &&
893 udpencap_port && uhp->uh_sport == htons(udpencap_port)) {
894 udpencap_ctlinput(cmd, sa, rdomain, v);
895 return;
896 }
897 #endif
898 inp = in_pcblookup(&udbtable,
899 ip->ip_dst, uhp->uh_dport, ip->ip_src, uhp->uh_sport,
900 rdomain);
901 if (inp && inp->inp_socket != NULL)
902 notify(inp, errno);
903 in_pcbunref(inp);
904 } else
905 in_pcbnotifyall(&udbtable, sa, rdomain, errno, notify);
906 }
907
908 int
909 udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,
910 struct mbuf *control)
911 {
912 struct sockaddr_in *sin = NULL;
913 struct udpiphdr *ui;
914 u_int32_t ipsecflowinfo = 0;
915 struct sockaddr_in src_sin;
916 int len = m->m_pkthdr.len;
917 struct in_addr laddr;
918 int error = 0;
919
920 #ifdef DIAGNOSTIC
921 if ((inp->inp_flags & INP_IPV6) != 0)
922 panic("IPv6 inpcb to %s", __func__);
923 #endif
924
925 /*
926 * Compute the packet length of the IP header, and
927 * punt if the length looks bogus.
928 */
929 if ((len + sizeof(struct udpiphdr)) > IP_MAXPACKET) {
930 error = EMSGSIZE;
931 goto release;
932 }
933
934 memset(&src_sin, 0, sizeof(src_sin));
935
936 if (control) {
937 u_int clen;
938 struct cmsghdr *cm;
939 caddr_t cmsgs;
940
941 /*
942 * XXX: Currently, we assume all the optional information is
943 * stored in a single mbuf.
944 */
945 if (control->m_next) {
946 error = EINVAL;
947 goto release;
948 }
949
950 clen = control->m_len;
951 cmsgs = mtod(control, caddr_t);
952 do {
953 if (clen < CMSG_LEN(0)) {
954 error = EINVAL;
955 goto release;
956 }
957 cm = (struct cmsghdr *)cmsgs;
958 if (cm->cmsg_len < CMSG_LEN(0) ||
959 CMSG_ALIGN(cm->cmsg_len) > clen) {
960 error = EINVAL;
961 goto release;
962 }
963 #ifdef IPSEC
964 if ((inp->inp_flags & INP_IPSECFLOWINFO) != 0 &&
965 cm->cmsg_len == CMSG_LEN(sizeof(ipsecflowinfo)) &&
966 cm->cmsg_level == IPPROTO_IP &&
967 cm->cmsg_type == IP_IPSECFLOWINFO) {
968 ipsecflowinfo = *(u_int32_t *)CMSG_DATA(cm);
969 } else
970 #endif
971 if (cm->cmsg_len == CMSG_LEN(sizeof(struct in_addr)) &&
972 cm->cmsg_level == IPPROTO_IP &&
973 cm->cmsg_type == IP_SENDSRCADDR) {
974 memcpy(&src_sin.sin_addr, CMSG_DATA(cm),
975 sizeof(struct in_addr));
976 src_sin.sin_family = AF_INET;
977 src_sin.sin_len = sizeof(src_sin);
978 /* no check on reuse when sin->sin_port == 0 */
979 if ((error = in_pcbaddrisavail(inp, &src_sin,
980 0, curproc)))
981 goto release;
982 }
983 clen -= CMSG_ALIGN(cm->cmsg_len);
984 cmsgs += CMSG_ALIGN(cm->cmsg_len);
985 } while (clen);
986 }
987
988 if (addr) {
989 if ((error = in_nam2sin(addr, &sin)))
990 goto release;
991 if (sin->sin_port == 0) {
992 error = EADDRNOTAVAIL;
993 goto release;
994 }
995 if (inp->inp_faddr.s_addr != INADDR_ANY) {
996 error = EISCONN;
997 goto release;
998 }
999 error = in_pcbselsrc(&laddr, sin, inp);
1000 if (error)
1001 goto release;
1002
1003 if (inp->inp_lport == 0) {
1004 error = in_pcbbind(inp, NULL, curproc);
1005 if (error)
1006 goto release;
1007 }
1008
1009 if (src_sin.sin_len > 0 &&
1010 src_sin.sin_addr.s_addr != INADDR_ANY &&
1011 src_sin.sin_addr.s_addr != inp->inp_laddr.s_addr) {
1012 src_sin.sin_port = inp->inp_lport;
1013 if (inp->inp_laddr.s_addr != INADDR_ANY &&
1014 (error =
1015 in_pcbaddrisavail(inp, &src_sin, 0, curproc)))
1016 goto release;
1017 laddr = src_sin.sin_addr;
1018 }
1019 } else {
1020 if (inp->inp_faddr.s_addr == INADDR_ANY) {
1021 error = ENOTCONN;
1022 goto release;
1023 }
1024 laddr = inp->inp_laddr;
1025 }
1026
1027 /*
1028 * Calculate data length and get a mbuf
1029 * for UDP and IP headers.
1030 */
1031 M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
1032 if (m == NULL) {
1033 error = ENOBUFS;
1034 goto bail;
1035 }
1036
1037 /*
1038 * Fill in mbuf with extended UDP header
1039 * and addresses and length put into network format.
1040 */
1041 ui = mtod(m, struct udpiphdr *);
1042 bzero(ui->ui_x1, sizeof ui->ui_x1);
1043 ui->ui_pr = IPPROTO_UDP;
1044 ui->ui_len = htons((u_int16_t)len + sizeof (struct udphdr));
1045 ui->ui_src = laddr;
1046 ui->ui_dst = sin ? sin->sin_addr : inp->inp_faddr;
1047 ui->ui_sport = inp->inp_lport;
1048 ui->ui_dport = sin ? sin->sin_port : inp->inp_fport;
1049 ui->ui_ulen = ui->ui_len;
1050 ((struct ip *)ui)->ip_len = htons(sizeof (struct udpiphdr) + len);
1051 ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl;
1052 ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos;
1053 if (udpcksum)
1054 m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
1055
1056 udpstat_inc(udps_opackets);
1057
1058 /* force routing table */
1059 m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
1060
1061 #if NPF > 0
1062 if (inp->inp_socket->so_state & SS_ISCONNECTED)
1063 pf_mbuf_link_inpcb(m, inp);
1064 #endif
1065
1066 error = ip_output(m, inp->inp_options, &inp->inp_route,
1067 (inp->inp_socket->so_options & SO_BROADCAST), inp->inp_moptions,
1068 inp, ipsecflowinfo);
1069
1070 bail:
1071 m_freem(control);
1072 return (error);
1073
1074 release:
1075 m_freem(m);
1076 goto bail;
1077 }
1078
1079 int
1080 udp_attach(struct socket *so, int proto, int wait)
1081 {
1082 int error;
1083
1084 if (so->so_pcb != NULL)
1085 return EINVAL;
1086
1087 if ((error = soreserve(so, udp_sendspace, udp_recvspace)))
1088 return error;
1089
1090 NET_ASSERT_LOCKED();
1091 if ((error = in_pcballoc(so, &udbtable, wait)))
1092 return error;
1093 #ifdef INET6
1094 if (sotoinpcb(so)->inp_flags & INP_IPV6)
1095 sotoinpcb(so)->inp_ipv6.ip6_hlim = ip6_defhlim;
1096 else
1097 #endif /* INET6 */
1098 sotoinpcb(so)->inp_ip.ip_ttl = ip_defttl;
1099 return 0;
1100 }
1101
1102 int
1103 udp_detach(struct socket *so)
1104 {
1105 struct inpcb *inp;
1106
1107 soassertlocked(so);
1108
1109 inp = sotoinpcb(so);
1110 if (inp == NULL)
1111 return (EINVAL);
1112
1113 in_pcbdetach(inp);
1114 return (0);
1115 }
1116
1117 void
1118 udp_lock(struct socket *so)
1119 {
1120 struct inpcb *inp = sotoinpcb(so);
1121
1122 NET_ASSERT_LOCKED();
1123 mtx_enter(&inp->inp_mtx);
1124 }
1125
1126 void
1127 udp_unlock(struct socket *so)
1128 {
1129 struct inpcb *inp = sotoinpcb(so);
1130
1131 NET_ASSERT_LOCKED();
1132 mtx_leave(&inp->inp_mtx);
1133 }
1134
1135 int
1136 udp_bind(struct socket *so, struct mbuf *addr, struct proc *p)
1137 {
1138 struct inpcb *inp = sotoinpcb(so);
1139
1140 soassertlocked(so);
1141 return in_pcbbind(inp, addr, p);
1142 }
1143
1144 int
1145 udp_connect(struct socket *so, struct mbuf *addr)
1146 {
1147 struct inpcb *inp = sotoinpcb(so);
1148 int error;
1149
1150 soassertlocked(so);
1151
1152 #ifdef INET6
1153 if (inp->inp_flags & INP_IPV6) {
1154 if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
1155 return (EISCONN);
1156 error = in6_pcbconnect(inp, addr);
1157 } else
1158 #endif /* INET6 */
1159 {
1160 if (inp->inp_faddr.s_addr != INADDR_ANY)
1161 return (EISCONN);
1162 error = in_pcbconnect(inp, addr);
1163 }
1164
1165 if (error)
1166 return (error);
1167
1168 soisconnected(so);
1169 return (0);
1170 }
1171
1172 int
1173 udp_disconnect(struct socket *so)
1174 {
1175 struct inpcb *inp = sotoinpcb(so);
1176
1177 soassertlocked(so);
1178
1179 #ifdef INET6
1180 if (inp->inp_flags & INP_IPV6) {
1181 if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
1182 return (ENOTCONN);
1183 } else
1184 #endif /* INET6 */
1185 {
1186 if (inp->inp_faddr.s_addr == INADDR_ANY)
1187 return (ENOTCONN);
1188 }
1189
1190 #ifdef INET6
1191 if (inp->inp_flags & INP_IPV6)
1192 inp->inp_laddr6 = in6addr_any;
1193 else
1194 #endif /* INET6 */
1195 inp->inp_laddr.s_addr = INADDR_ANY;
1196
1197 in_pcbdisconnect(inp);
1198 so->so_state &= ~SS_ISCONNECTED; /* XXX */
1199
1200 return (0);
1201 }
1202
1203 int
1204 udp_shutdown(struct socket *so)
1205 {
1206 soassertlocked(so);
1207 socantsendmore(so);
1208 return (0);
1209 }
1210
1211 int
1212 udp_send(struct socket *so, struct mbuf *m, struct mbuf *addr,
1213 struct mbuf *control)
1214 {
1215 struct inpcb *inp = sotoinpcb(so);
1216 int error;
1217
1218 soassertlocked(so);
1219
1220 #ifdef PIPEX
1221 if (inp->inp_pipex) {
1222 struct pipex_session *session;
1223
1224 if (addr != NULL)
1225 session =
1226 pipex_l2tp_userland_lookup_session(m,
1227 mtod(addr, struct sockaddr *));
1228 else
1229 #ifdef INET6
1230 if (inp->inp_flags & INP_IPV6)
1231 session =
1232 pipex_l2tp_userland_lookup_session_ipv6(
1233 m, inp->inp_faddr6);
1234 else
1235 #endif
1236 session =
1237 pipex_l2tp_userland_lookup_session_ipv4(
1238 m, inp->inp_faddr);
1239 if (session != NULL) {
1240 m = pipex_l2tp_userland_output(m, session);
1241 pipex_rele_session(session);
1242
1243 if (m == NULL) {
1244 m_freem(control);
1245 return (ENOMEM);
1246 }
1247 }
1248 }
1249 #endif
1250
1251 #ifdef INET6
1252 if (inp->inp_flags & INP_IPV6)
1253 error = udp6_output(inp, m, addr, control);
1254 else
1255 #endif
1256 error = udp_output(inp, m, addr, control);
1257
1258 return (error);
1259 }
1260
1261 /*
1262 * Sysctl for udp variables.
1263 */
1264 int
1265 udp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
1266 size_t newlen)
1267 {
1268 int error;
1269
1270 /* All sysctl names at this level are terminal. */
1271 if (namelen != 1)
1272 return (ENOTDIR);
1273
1274 switch (name[0]) {
1275 case UDPCTL_BADDYNAMIC:
1276 NET_LOCK();
1277 error = sysctl_struct(oldp, oldlenp, newp, newlen,
1278 baddynamicports.udp, sizeof(baddynamicports.udp));
1279 NET_UNLOCK();
1280 return (error);
1281
1282 case UDPCTL_ROOTONLY:
1283 if (newp && securelevel > 0)
1284 return (EPERM);
1285 NET_LOCK();
1286 error = sysctl_struct(oldp, oldlenp, newp, newlen,
1287 rootonlyports.udp, sizeof(rootonlyports.udp));
1288 NET_UNLOCK();
1289 return (error);
1290
1291 case UDPCTL_STATS:
1292 if (newp != NULL)
1293 return (EPERM);
1294
1295 return (udp_sysctl_udpstat(oldp, oldlenp, newp));
1296
1297 default:
1298 NET_LOCK();
1299 error = sysctl_bounded_arr(udpctl_vars, nitems(udpctl_vars),
1300 name, namelen, oldp, oldlenp, newp, newlen);
1301 NET_UNLOCK();
1302 return (error);
1303 }
1304 /* NOTREACHED */
1305 }
1306
1307 int
1308 udp_sysctl_udpstat(void *oldp, size_t *oldlenp, void *newp)
1309 {
1310 uint64_t counters[udps_ncounters];
1311 struct udpstat udpstat;
1312 u_long *words = (u_long *)&udpstat;
1313 int i;
1314
1315 CTASSERT(sizeof(udpstat) == (nitems(counters) * sizeof(u_long)));
1316 memset(&udpstat, 0, sizeof udpstat);
1317 counters_read(udpcounters, counters, nitems(counters));
1318
1319 for (i = 0; i < nitems(counters); i++)
1320 words[i] = (u_long)counters[i];
1321
1322 return (sysctl_rdstruct(oldp, oldlenp, newp,
1323 &udpstat, sizeof(udpstat)));
1324 }
Cache object: 6e9be2f4a32f4ee2e0ef1a5ae405f8de
|