2 * Copyright (c) 1982, 1986, 1988, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/protosw.h>
46 #include <sys/errno.h>
48 #include <sys/queue.h>
49 #include <sys/synch.h>
51 #include <sys/sysctl.h>
54 #include <net/route.h>
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/ip.h>
60 #include <netinet/in_pcb.h>
61 #include <netinet/in_var.h>
62 #include <netinet/ip_var.h>
63 #include <netinet/ip_icmp.h>
64 #include <netinet/udp.h>
65 #include <netinet/udp_var.h>
67 #include <kern/kern_subr_protos.h>
70 * UDP protocol implementation.
71 * Per RFC 768, August, 1980.
76 int udpcksum
= 0; /* XXX */
79 struct inpcbhead udb
; /* from udp_var.h */
80 struct inpcbinfo udbinfo
;
83 #define UDBHASHSIZE 64
86 struct udpstat udpstat
; /* from udp_var.h */
88 struct sockaddr_in udp_in
= { sizeof(udp_in
), AF_INET
};
90 extern struct in_addr zeroin_addr
;
91 extern u_char inetctlerrmap
[];
94 static void udp_detach
__P((struct inpcb
*));
95 static void udp_notify
__P((struct inpcb
*, int));
96 static struct mbuf
*udp_saveopt
__P((caddr_t
, int, int));
102 udbinfo
.listhead
= &udb
;
103 udbinfo
.hashbase
= phashinit(UDBHASHSIZE
, M_PCB
, &udbinfo
.hashsize
);
106 void udp_input(void *args
, ...)
108 register struct ip
*ip
;
109 register struct udphdr
*uh
;
110 register struct inpcb
*inp
;
111 struct mbuf
*opts
= 0;
114 register struct mbuf
*m
= args
;
119 iphlen
= va_arg(va
, int);
122 udpstat
.udps_ipackets
++;
125 * Strip IP options, if any; should skip this,
126 * make available to user, and use on returned packets,
127 * but we don't yet have a way to check the checksum
128 * with options still present.
130 if (iphlen
> sizeof (struct ip
)) {
131 ip_stripoptions(m
, (struct mbuf
*)0);
132 iphlen
= sizeof(struct ip
);
136 * Get IP and UDP header together in first mbuf.
138 ip
= mtod(m
, struct ip
*);
139 if (m
->m_len
< iphlen
+ sizeof(struct udphdr
)) {
140 if ((m
= m_pullup(m
, iphlen
+ sizeof(struct udphdr
))) == 0) {
141 udpstat
.udps_hdrops
++;
144 ip
= mtod(m
, struct ip
*);
146 uh
= (struct udphdr
*)((caddr_t
)ip
+ iphlen
);
149 * Make mbuf data length reflect UDP length.
150 * If not enough data to reflect UDP length, drop.
152 len
= ntohs((u_short
)uh
->uh_ulen
);
153 if (ip
->ip_len
!= len
) {
154 if (len
> ip
->ip_len
) {
155 udpstat
.udps_badlen
++;
158 m_adj(m
, len
- ip
->ip_len
);
159 /* ip->ip_len = len; */
162 * Save a copy of the IP header in case we want restore it
163 * for sending an ICMP error message in response.
168 * Checksum extended UDP header and data.
170 if (udpcksum
&& uh
->uh_sum
) {
171 bzero(((struct ipovly
*)ip
)->ih_x1
, 9);
172 ((struct ipovly
*)ip
)->ih_len
= uh
->uh_ulen
;
173 uh
->uh_sum
= in_cksum(m
, len
+ sizeof (struct ip
));
175 udpstat
.udps_badsum
++;
181 if (IN_MULTICAST(ntohl(ip
->ip_dst
.s_addr
)) ||
182 in_broadcast(ip
->ip_dst
, m
->m_pkthdr
.rcvif
)) {
185 * Deliver a multicast or broadcast datagram to *all* sockets
186 * for which the local and remote addresses and ports match
187 * those of the incoming datagram. This allows more than
188 * one process to receive multi/broadcasts on the same port.
189 * (This really ought to be done for unicast datagrams as
190 * well, but that would cause problems with existing
191 * applications that open both address-specific sockets and
192 * a wildcard socket listening to the same port -- they would
193 * end up receiving duplicates of every unicast datagram.
194 * Those applications open the multiple sockets to overcome an
195 * inadequacy of the UDP socket interface, but for backwards
196 * compatibility we avoid the problem here rather than
197 * fixing the interface. Maybe 4.5BSD will remedy this?)
201 * Construct sockaddr format source address.
203 udp_in
.sin_port
= uh
->uh_sport
;
204 udp_in
.sin_addr
= ip
->ip_src
;
205 m
->m_len
-= sizeof (struct udpiphdr
);
206 m
->m_data
+= sizeof (struct udpiphdr
);
208 * Locate pcb(s) for datagram.
209 * (Algorithm copied from raw_intr().)
212 for (inp
= udb
.lh_first
; inp
!= NULL
; inp
= inp
->inp_list
.le_next
) {
213 if (inp
->inp_lport
!= uh
->uh_dport
)
215 if (inp
->inp_laddr
.s_addr
!= INADDR_ANY
) {
216 if (inp
->inp_laddr
.s_addr
!=
220 if (inp
->inp_faddr
.s_addr
!= INADDR_ANY
) {
221 if (inp
->inp_faddr
.s_addr
!=
223 inp
->inp_fport
!= uh
->uh_sport
)
230 if ((n
= m_copy(m
, 0, M_COPYALL
)) != NULL
) {
231 if (sbappendaddr(&last
->so_rcv
,
232 (struct sockaddr
*)&udp_in
,
233 n
, (struct mbuf
*)0) == 0) {
235 udpstat
.udps_fullsock
++;
240 last
= inp
->inp_socket
;
242 * Don't look for additional matches if this one does
243 * not have either the SO_REUSEPORT or SO_REUSEADDR
244 * socket options set. This heuristic avoids searching
245 * through all pcbs in the common case of a non-shared
246 * port. It * assumes that an application will never
247 * clear these options after setting them.
249 if ((last
->so_options
&(SO_REUSEPORT
|SO_REUSEADDR
) == 0))
255 * No matching pcb found; discard datagram.
256 * (No need to send an ICMP Port Unreachable
257 * for a broadcast or multicast datgram.)
259 udpstat
.udps_noportbcast
++;
262 if (sbappendaddr(&last
->so_rcv
, (struct sockaddr
*)&udp_in
,
263 m
, (struct mbuf
*)0) == 0) {
264 udpstat
.udps_fullsock
++;
271 * Locate pcb for datagram. First look for an exact match.
273 inp
= in_pcblookuphash(&udbinfo
, ip
->ip_src
, uh
->uh_sport
,
274 ip
->ip_dst
, uh
->uh_dport
);
276 * ...and if that fails, do a wildcard search.
279 inp
= in_pcblookup(&udb
, ip
->ip_src
, uh
->uh_sport
, ip
->ip_dst
,
280 uh
->uh_dport
, INPLOOKUP_WILDCARD
);
283 udpstat
.udps_noport
++;
284 if (m
->m_flags
& (M_BCAST
| M_MCAST
)) {
285 udpstat
.udps_noportbcast
++;
289 icmp_error(m
, ICMP_UNREACH
, ICMP_UNREACH_PORT
, 0, 0);
294 * Construct sockaddr format source address.
295 * Stuff source address and datagram in user buffer.
297 udp_in
.sin_port
= uh
->uh_sport
;
298 udp_in
.sin_addr
= ip
->ip_src
;
299 if (inp
->inp_flags
& INP_CONTROLOPTS
) {
300 struct mbuf
**mp
= &opts
;
302 if (inp
->inp_flags
& INP_RECVDSTADDR
) {
303 *mp
= udp_saveopt((caddr_t
) &ip
->ip_dst
,
304 sizeof(struct in_addr
), IP_RECVDSTADDR
);
309 /* options were tossed above */
310 if (inp
->inp_flags
& INP_RECVOPTS
) {
311 *mp
= udp_saveopt((caddr_t
) opts_deleted_above
,
312 sizeof(struct in_addr
), IP_RECVOPTS
);
316 /* ip_srcroute doesn't do what we want here, need to fix */
317 if (inp
->inp_flags
& INP_RECVRETOPTS
) {
318 *mp
= udp_saveopt((caddr_t
) ip_srcroute(),
319 sizeof(struct in_addr
), IP_RECVRETOPTS
);
325 iphlen
+= sizeof(struct udphdr
);
327 m
->m_pkthdr
.len
-= iphlen
;
329 if (sbappendaddr(&inp
->inp_socket
->so_rcv
, (struct sockaddr
*)&udp_in
,
331 udpstat
.udps_fullsock
++;
334 sorwakeup(inp
->inp_socket
);
343 * Create a "control" mbuf containing the specified data
344 * with the specified type for presentation with a datagram.
347 udp_saveopt(p
, size
, type
)
352 register struct cmsghdr
*cp
;
355 if ((m
= m_get(M_DONTWAIT
, MT_CONTROL
)) == NULL
)
356 return ((struct mbuf
*) NULL
);
357 cp
= (struct cmsghdr
*) mtod(m
, struct cmsghdr
*);
358 (void)memcpy(CMSG_DATA(cp
), p
, size
);
362 cp
->cmsg_level
= IPPROTO_IP
;
363 cp
->cmsg_type
= type
;
368 * Notify a udp user of an asynchronous error;
369 * just wake up so that he can collect error status.
372 udp_notify(inp
, _errno
)
373 register struct inpcb
*inp
;
376 inp
->inp_socket
->so_error
= _errno
;
377 sorwakeup(inp
->inp_socket
);
378 sowwakeup(inp
->inp_socket
);
382 udp_ctlinput(cmd
, sa
, arg
)
387 register struct ip
*ip
= arg
;
388 register struct udphdr
*uh
;
390 if (!PRC_IS_REDIRECT(cmd
) &&
391 ((unsigned)cmd
>= PRC_NCMDS
|| inetctlerrmap
[cmd
] == 0))
394 uh
= (struct udphdr
*)((caddr_t
)ip
+ (ip
->ip_hl
<< 2));
395 in_pcbnotify(&udb
, sa
, uh
->uh_dport
, ip
->ip_src
, uh
->uh_sport
,
398 in_pcbnotify(&udb
, sa
, 0, zeroin_addr
, 0, cmd
, udp_notify
);
402 udp_output(void *arg
, ...)
404 register struct inpcb
*inp
= arg
;
405 register struct mbuf
*m
;
406 struct mbuf
*addr
, *control
;
407 register struct udpiphdr
*ui
;
409 struct in_addr laddr
;
410 int s
= 0, error
= 0;
414 m
= va_arg(va
, struct mbuf
*);
415 addr
= va_arg(va
, struct mbuf
*);
416 control
= va_arg(va
, struct mbuf
*);
419 len
= m
->m_pkthdr
.len
;
422 m_freem(control
); /* XXX */
425 laddr
= inp
->inp_laddr
;
426 if (inp
->inp_faddr
.s_addr
!= INADDR_ANY
) {
431 * Must block input while temporarily connected.
434 error
= in_pcbconnect(inp
, addr
);
440 if (inp
->inp_faddr
.s_addr
== INADDR_ANY
) {
446 * Calculate data length and get a mbuf
447 * for UDP and IP headers.
449 M_PREPEND(m
, sizeof(struct udpiphdr
), M_DONTWAIT
);
458 * Fill in mbuf with extended UDP header
459 * and addresses and length put into network format.
461 ui
= mtod(m
, struct udpiphdr
*);
462 bzero(ui
->ui_x1
, sizeof(ui
->ui_x1
));
463 ui
->ui_pr
= IPPROTO_UDP
;
464 ui
->ui_len
= htons((u_short
)len
+ sizeof (struct udphdr
));
465 ui
->ui_src
= inp
->inp_laddr
;
466 ui
->ui_dst
= inp
->inp_faddr
;
467 ui
->ui_sport
= inp
->inp_lport
;
468 ui
->ui_dport
= inp
->inp_fport
;
469 ui
->ui_ulen
= ui
->ui_len
;
472 * Stuff checksum and output datagram.
476 if ((ui
->ui_sum
= in_cksum(m
, sizeof (struct udpiphdr
) + len
)) == 0)
479 ((struct ip
*)ui
)->ip_len
= sizeof (struct udpiphdr
) + len
;
480 ((struct ip
*)ui
)->ip_ttl
= inp
->inp_ip
.ip_ttl
; /* XXX */
481 ((struct ip
*)ui
)->ip_tos
= inp
->inp_ip
.ip_tos
; /* XXX */
482 udpstat
.udps_opackets
++;
483 error
= ip_output(m
, inp
->inp_options
, &inp
->inp_route
,
484 inp
->inp_socket
->so_options
& (SO_DONTROUTE
| SO_BROADCAST
),
488 in_pcbdisconnect(inp
);
489 inp
->inp_laddr
= laddr
;
499 u_long udp_sendspace
= 9216; /* really max datagram size */
500 u_long udp_recvspace
= 40 * (1024 + sizeof(struct sockaddr_in
));
501 /* 40 1K datagrams */
505 udp_usrreq(so
, req
, m
, addr
, control
)
508 struct mbuf
*m
, *addr
, *control
;
510 struct inpcb
*inp
= sotoinpcb(so
);
514 if (req
== PRU_CONTROL
)
515 return (in_control(so
, (long)m
, (caddr_t
)addr
,
516 (struct ifnet
*)control
));
517 if (inp
== NULL
&& req
!= PRU_ATTACH
) {
522 * Note: need to block udp_input while changing
523 * the udp pcb queue and/or pcb addresses.
533 error
= in_pcballoc(so
, &udbinfo
);
537 error
= soreserve(so
, udp_sendspace
, udp_recvspace
);
540 ((struct inpcb
*) so
->so_pcb
)->inp_ip
.ip_ttl
= ip_defttl
;
549 error
= in_pcbbind(inp
, addr
);
558 if (inp
->inp_faddr
.s_addr
!= INADDR_ANY
) {
563 error
= in_pcbconnect(inp
, addr
);
578 if (inp
->inp_faddr
.s_addr
== INADDR_ANY
) {
583 in_pcbdisconnect(inp
);
584 inp
->inp_laddr
.s_addr
= INADDR_ANY
;
586 so
->so_state
&= ~SS_ISCONNECTED
; /* XXX */
594 return (udp_output(inp
, m
, addr
, control
));
597 soisdisconnected(so
);
602 in_setsockaddr(inp
, addr
);
606 in_setpeeraddr(inp
, addr
);
611 * stat: don't bother with a blocksize.
625 return (EOPNOTSUPP
); /* do not free mbuf's */
633 printf("udp control data unexpectedly retained\n");
653 * Sysctl for udp variables.
656 udp_sysctl(name
, namelen
, oldp
, oldlenp
, newp
, newlen
)
664 /* All sysctl names at this level are terminal. */
669 case UDPCTL_CHECKSUM
:
670 return (sysctl_int(oldp
, oldlenp
, newp
, newlen
, &udpcksum
));
672 return (sysctl_rdstruct(oldp
, oldlenp
, newp
, &udpstat
,
674 case UDPCTL_MAXDGRAM
:
675 return (sysctl_int(oldp
, oldlenp
, newp
, newlen
,
676 (int *)&udp_sendspace
)); /* XXX */
677 case UDPCTL_RECVSPACE
:
678 return (sysctl_int(oldp
, oldlenp
, newp
, newlen
,
679 (int *)&udp_recvspace
)); /* XXX */
681 return (ENOPROTOOPT
);