add whiteout inode number
[unix-history] / usr / src / sys / netinet / udp_usrreq.c
CommitLineData
8ae0e4b4 1/*
e7a3707f 2 * Copyright (c) 1982, 1986, 1988, 1990, 1993
1611db1e 3 * The Regents of the University of California. All rights reserved.
8ae0e4b4 4 *
e5b84169 5 * %sccs.include.redist.c%
2b6b6284 6 *
1611db1e 7 * @(#)udp_usrreq.c 8.4 (Berkeley) %G%
8ae0e4b4 8 */
20666ad3 9
5548a02f
KB
10#include <sys/param.h>
11#include <sys/malloc.h>
12#include <sys/mbuf.h>
13#include <sys/protosw.h>
14#include <sys/socket.h>
15#include <sys/socketvar.h>
16#include <sys/errno.h>
17#include <sys/stat.h>
18
19#include <net/if.h>
20#include <net/route.h>
21
22#include <netinet/in.h>
23#include <netinet/in_systm.h>
24#include <netinet/ip.h>
25#include <netinet/in_pcb.h>
26#include <netinet/ip_var.h>
27#include <netinet/ip_icmp.h>
28#include <netinet/udp.h>
29#include <netinet/udp_var.h>
d52566dd 30
2b4b57cd
BJ
31/*
32 * UDP protocol implementation.
33 * Per RFC 768, August, 1980.
34 */
c50542f3 35#ifndef COMPAT_42
dd8707ae 36int udpcksum = 1;
c50542f3
MK
37#else
38int udpcksum = 0; /* XXX */
39#endif
40
5328bc49 41struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
c46785cb
KB
42struct inpcb *udp_last_inpcb = &udb;
43
44static void udp_detach __P((struct inpcb *));
45static void udp_notify __P((struct inpcb *, int));
46static struct mbuf *udp_saveopt __P((caddr_t, int, int));
47
48void
49udp_init()
50{
51 udb.inp_next = udb.inp_prev = &udb;
52}
eb44bfb2 53
c46785cb 54void
9d91b170
MK
55udp_input(m, iphlen)
56 register struct mbuf *m;
57 int iphlen;
1bd53abe 58{
0dfbe263
MK
59 register struct ip *ip;
60 register struct udphdr *uh;
53a5409e 61 register struct inpcb *inp;
5921352b 62 struct mbuf *opts = 0;
e199a8a5 63 int len;
0dfbe263 64 struct ip save_ip;
eb44bfb2 65
5921352b 66 udpstat.udps_ipackets++;
9f5c260d
MK
67
68 /*
69 * Strip IP options, if any; should skip this,
70 * make available to user, and use on returned packets,
71 * but we don't yet have a way to check the checksum
72 * with options still present.
73 */
74 if (iphlen > sizeof (struct ip)) {
0dfbe263 75 ip_stripoptions(m, (struct mbuf *)0);
9f5c260d
MK
76 iphlen = sizeof(struct ip);
77 }
78
2b4b57cd 79 /*
4aed14e3 80 * Get IP and UDP header together in first mbuf.
2b4b57cd 81 */
0dfbe263
MK
82 ip = mtod(m, struct ip *);
83 if (m->m_len < iphlen + sizeof(struct udphdr)) {
84 if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
7212ba67
MK
85 udpstat.udps_hdrops++;
86 return;
87 }
0dfbe263 88 ip = mtod(m, struct ip *);
7212ba67 89 }
0dfbe263 90 uh = (struct udphdr *)((caddr_t)ip + iphlen);
2b4b57cd
BJ
91
92 /*
4aed14e3
BJ
93 * Make mbuf data length reflect UDP length.
94 * If not enough data to reflect UDP length, drop.
2b4b57cd 95 */
0dfbe263
MK
96 len = ntohs((u_short)uh->uh_ulen);
97 if (ip->ip_len != len) {
98 if (len > ip->ip_len) {
2b4b57cd
BJ
99 udpstat.udps_badlen++;
100 goto bad;
101 }
0dfbe263
MK
102 m_adj(m, len - ip->ip_len);
103 /* ip->ip_len = len; */
2b4b57cd 104 }
dcea2497 105 /*
0dfbe263
MK
106 * Save a copy of the IP header in case we want restore it
107 * for sending an ICMP error message in response.
dcea2497 108 */
0dfbe263 109 save_ip = *ip;
2b4b57cd
BJ
110
111 /*
4aed14e3 112 * Checksum extended UDP header and data.
2b4b57cd 113 */
0dfbe263
MK
114 if (udpcksum && uh->uh_sum) {
115 ((struct ipovly *)ip)->ih_next = 0;
116 ((struct ipovly *)ip)->ih_prev = 0;
117 ((struct ipovly *)ip)->ih_x1 = 0;
118 ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
119 if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
2b4b57cd 120 udpstat.udps_badsum++;
eb44bfb2
BJ
121 m_freem(m);
122 return;
123 }
124 }
2b4b57cd 125
c63cb169 126 if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
09ed489e 127 in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
d6fa15c2
KS
128 struct socket *last;
129 /*
130 * Deliver a multicast or broadcast datagram to *all* sockets
131 * for which the local and remote addresses and ports match
132 * those of the incoming datagram. This allows more than
133 * one process to receive multi/broadcasts on the same port.
134 * (This really ought to be done for unicast datagrams as
135 * well, but that would cause problems with existing
136 * applications that open both address-specific sockets and
137 * a wildcard socket listening to the same port -- they would
138 * end up receiving duplicates of every unicast datagram.
139 * Those applications open the multiple sockets to overcome an
140 * inadequacy of the UDP socket interface, but for backwards
141 * compatibility we avoid the problem here rather than
69d96ae2 142 * fixing the interface. Maybe 4.5BSD will remedy this?)
d6fa15c2
KS
143 */
144
145 /*
146 * Construct sockaddr format source address.
147 */
148 udp_in.sin_port = uh->uh_sport;
149 udp_in.sin_addr = ip->ip_src;
150 m->m_len -= sizeof (struct udpiphdr);
151 m->m_data += sizeof (struct udpiphdr);
152 /*
153 * Locate pcb(s) for datagram.
154 * (Algorithm copied from raw_intr().)
155 */
156 last = NULL;
157 for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
158 if (inp->inp_lport != uh->uh_dport)
159 continue;
160 if (inp->inp_laddr.s_addr != INADDR_ANY) {
161 if (inp->inp_laddr.s_addr !=
162 ip->ip_dst.s_addr)
163 continue;
164 }
165 if (inp->inp_faddr.s_addr != INADDR_ANY) {
166 if (inp->inp_faddr.s_addr !=
167 ip->ip_src.s_addr ||
168 inp->inp_fport != uh->uh_sport)
169 continue;
170 }
171
172 if (last != NULL) {
173 struct mbuf *n;
174
175 if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
176 if (sbappendaddr(&last->so_rcv,
177 (struct sockaddr *)&udp_in,
69d96ae2 178 n, (struct mbuf *)0) == 0) {
d6fa15c2 179 m_freem(n);
69d96ae2
AC
180 udpstat.udps_fullsock++;
181 } else
d6fa15c2
KS
182 sorwakeup(last);
183 }
184 }
185 last = inp->inp_socket;
186 /*
d2cc32a6
MK
187 * Don't look for additional matches if this one does
188 * not have either the SO_REUSEPORT or SO_REUSEADDR
189 * socket options set. This heuristic avoids searching
190 * through all pcbs in the common case of a non-shared
191 * port. It * assumes that an application will never
192 * clear these options after setting them.
d6fa15c2 193 */
d2cc32a6 194 if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0))
d6fa15c2
KS
195 break;
196 }
197
198 if (last == NULL) {
199 /*
200 * No matching pcb found; discard datagram.
201 * (No need to send an ICMP Port Unreachable
202 * for a broadcast or multicast datgram.)
203 */
69d96ae2 204 udpstat.udps_noportbcast++;
d6fa15c2
KS
205 goto bad;
206 }
207 if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
69d96ae2
AC
208 m, (struct mbuf *)0) == 0) {
209 udpstat.udps_fullsock++;
d6fa15c2 210 goto bad;
69d96ae2 211 }
d6fa15c2
KS
212 sorwakeup(last);
213 return;
214 }
2b4b57cd 215 /*
e199a8a5 216 * Locate pcb for datagram.
2b4b57cd 217 */
5921352b
MK
218 inp = udp_last_inpcb;
219 if (inp->inp_lport != uh->uh_dport ||
220 inp->inp_fport != uh->uh_sport ||
221 inp->inp_faddr.s_addr != ip->ip_src.s_addr ||
222 inp->inp_laddr.s_addr != ip->ip_dst.s_addr) {
223 inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport,
224 ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD);
225 if (inp)
226 udp_last_inpcb = inp;
227 udpstat.udpps_pcbcachemiss++;
228 }
72e4f44e 229 if (inp == 0) {
0dfbe263 230 udpstat.udps_noport++;
5927fb26
KM
231 if (m->m_flags & (M_BCAST | M_MCAST)) {
232 udpstat.udps_noportbcast++;
233 goto bad;
234 }
0dfbe263 235 *ip = save_ip;
9f5c260d 236 ip->ip_len += iphlen;
d99f1c28 237 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
72e4f44e
SL
238 return;
239 }
2b4b57cd
BJ
240
241 /*
242 * Construct sockaddr format source address.
243 * Stuff source address and datagram in user buffer.
244 */
0dfbe263
MK
245 udp_in.sin_port = uh->uh_sport;
246 udp_in.sin_addr = ip->ip_src;
5921352b
MK
247 if (inp->inp_flags & INP_CONTROLOPTS) {
248 struct mbuf **mp = &opts;
5921352b
MK
249
250 if (inp->inp_flags & INP_RECVDSTADDR) {
251 *mp = udp_saveopt((caddr_t) &ip->ip_dst,
252 sizeof(struct in_addr), IP_RECVDSTADDR);
253 if (*mp)
254 mp = &(*mp)->m_next;
255 }
256#ifdef notyet
257 /* options were tossed above */
258 if (inp->inp_flags & INP_RECVOPTS) {
259 *mp = udp_saveopt((caddr_t) opts_deleted_above,
260 sizeof(struct in_addr), IP_RECVOPTS);
261 if (*mp)
262 mp = &(*mp)->m_next;
263 }
264 /* ip_srcroute doesn't do what we want here, need to fix */
265 if (inp->inp_flags & INP_RECVRETOPTS) {
266 *mp = udp_saveopt((caddr_t) ip_srcroute(),
267 sizeof(struct in_addr), IP_RECVRETOPTS);
268 if (*mp)
269 mp = &(*mp)->m_next;
270 }
271#endif
272 }
0dfbe263
MK
273 iphlen += sizeof(struct udphdr);
274 m->m_len -= iphlen;
275 m->m_pkthdr.len -= iphlen;
276 m->m_data += iphlen;
ab85b059 277 if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
5921352b
MK
278 m, opts) == 0) {
279 udpstat.udps_fullsock++;
2b4b57cd 280 goto bad;
5921352b 281 }
4e60622a 282 sorwakeup(inp->inp_socket);
53a5409e 283 return;
2b4b57cd 284bad:
53a5409e 285 m_freem(m);
5921352b
MK
286 if (opts)
287 m_freem(opts);
288}
289
290/*
291 * Create a "control" mbuf containing the specified data
292 * with the specified type for presentation with a datagram.
293 */
294struct mbuf *
295udp_saveopt(p, size, type)
296 caddr_t p;
297 register int size;
298 int type;
299{
300 register struct cmsghdr *cp;
301 struct mbuf *m;
302
303 if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
304 return ((struct mbuf *) NULL);
305 cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
0924b337 306 bcopy(p, CMSG_DATA(cp), size);
5921352b
MK
307 size += sizeof(*cp);
308 m->m_len = size;
309 cp->cmsg_len = size;
310 cp->cmsg_level = IPPROTO_IP;
311 cp->cmsg_type = type;
312 return (m);
1bd53abe
BJ
313}
314
41715710
MK
315/*
316 * Notify a udp user of an asynchronous error;
317 * just wake up so that he can collect error status.
318 */
c46785cb 319static void
5921352b 320udp_notify(inp, errno)
41715710 321 register struct inpcb *inp;
64f72726 322 int errno;
41715710 323{
5921352b 324 inp->inp_socket->so_error = errno;
41715710
MK
325 sorwakeup(inp->inp_socket);
326 sowwakeup(inp->inp_socket);
327}
328
c46785cb 329void
0dfbe263 330udp_ctlinput(cmd, sa, ip)
39674d5f 331 int cmd;
dcea2497 332 struct sockaddr *sa;
0dfbe263 333 register struct ip *ip;
39674d5f 334{
0dfbe263
MK
335 register struct udphdr *uh;
336 extern struct in_addr zeroin_addr;
39674d5f 337 extern u_char inetctlerrmap[];
39674d5f 338
69d96ae2
AC
339 if (!PRC_IS_REDIRECT(cmd) &&
340 ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
dcea2497 341 return;
0dfbe263
MK
342 if (ip) {
343 uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
344 in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
345 cmd, udp_notify);
346 } else
347 in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
39674d5f
SL
348}
349
c46785cb 350int
a942fe02 351udp_output(inp, m, addr, control)
d55475b1 352 register struct inpcb *inp;
2b4b57cd 353 register struct mbuf *m;
a942fe02 354 struct mbuf *addr, *control;
9d91b170 355{
2b4b57cd 356 register struct udpiphdr *ui;
9d91b170 357 register int len = m->m_pkthdr.len;
a942fe02
MK
358 struct in_addr laddr;
359 int s, error = 0;
2b4b57cd 360
a942fe02
MK
361 if (control)
362 m_freem(control); /* XXX */
363
364 if (addr) {
365 laddr = inp->inp_laddr;
366 if (inp->inp_faddr.s_addr != INADDR_ANY) {
367 error = EISCONN;
368 goto release;
369 }
370 /*
371 * Must block input while temporarily connected.
372 */
373 s = splnet();
374 error = in_pcbconnect(inp, addr);
375 if (error) {
376 splx(s);
377 goto release;
378 }
379 } else {
380 if (inp->inp_faddr.s_addr == INADDR_ANY) {
381 error = ENOTCONN;
382 goto release;
383 }
384 }
2b4b57cd
BJ
385 /*
386 * Calculate data length and get a mbuf
4aed14e3 387 * for UDP and IP headers.
2b4b57cd 388 */
b2a072f3
KM
389 M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
390 if (m == 0) {
391 error = ENOBUFS;
392 goto release;
393 }
2b4b57cd
BJ
394
395 /*
4aed14e3 396 * Fill in mbuf with extended UDP header
2b4b57cd
BJ
397 * and addresses and length put into network format.
398 */
2b4b57cd
BJ
399 ui = mtod(m, struct udpiphdr *);
400 ui->ui_next = ui->ui_prev = 0;
401 ui->ui_x1 = 0;
402 ui->ui_pr = IPPROTO_UDP;
ee847abd 403 ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
4e60622a
BJ
404 ui->ui_src = inp->inp_laddr;
405 ui->ui_dst = inp->inp_faddr;
406 ui->ui_sport = inp->inp_lport;
407 ui->ui_dport = inp->inp_fport;
ee847abd 408 ui->ui_ulen = ui->ui_len;
2b4b57cd
BJ
409
410 /*
411 * Stuff checksum and output datagram.
412 */
413 ui->ui_sum = 0;
dd8707ae
MK
414 if (udpcksum) {
415 if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
890024da 416 ui->ui_sum = 0xffff;
ee09448f 417 }
4e60622a 418 ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
5921352b
MK
419 ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */
420 ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
0dfbe263 421 udpstat.udps_opackets++;
a942fe02 422 error = ip_output(m, inp->inp_options, &inp->inp_route,
69d96ae2
AC
423 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
424 inp->inp_moptions);
a942fe02
MK
425
426 if (addr) {
427 in_pcbdisconnect(inp);
428 inp->inp_laddr = laddr;
429 splx(s);
430 }
431 return (error);
432
433release:
434 m_freem(m);
435 return (error);
1bd53abe
BJ
436}
437
c9209cff
KM
438u_long udp_sendspace = 9216; /* really max datagram size */
439u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
440 /* 40 1K datagrams */
1d14d351 441
a8d3bf7f 442/*ARGSUSED*/
c46785cb 443int
a942fe02 444udp_usrreq(so, req, m, addr, control)
53a5409e 445 struct socket *so;
1bd53abe 446 int req;
a942fe02 447 struct mbuf *m, *addr, *control;
1bd53abe 448{
53a5409e 449 struct inpcb *inp = sotoinpcb(so);
8a2f82db 450 int error = 0;
5921352b 451 int s;
1bd53abe 452
1d14d351 453 if (req == PRU_CONTROL)
a942fe02 454 return (in_control(so, (int)m, (caddr_t)addr,
0dfbe263 455 (struct ifnet *)control));
a3fa431a
SL
456 if (inp == NULL && req != PRU_ATTACH) {
457 error = EINVAL;
458 goto release;
459 }
5921352b 460 /*
a44aeee3
MK
461 * Note: need to block udp_input while changing
462 * the udp pcb queue and/or pcb addresses.
5921352b 463 */
1bd53abe
BJ
464 switch (req) {
465
466 case PRU_ATTACH:
a3fa431a
SL
467 if (inp != NULL) {
468 error = EINVAL;
469 break;
470 }
5921352b 471 s = splnet();
8075bb0e 472 error = in_pcballoc(so, &udb);
5921352b 473 splx(s);
8075bb0e
BJ
474 if (error)
475 break;
1d14d351 476 error = soreserve(so, udp_sendspace, udp_recvspace);
8075bb0e
BJ
477 if (error)
478 break;
0321539c 479 ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
53a5409e 480 break;
1bd53abe
BJ
481
482 case PRU_DETACH:
a44aeee3 483 udp_detach(inp);
53a5409e 484 break;
1bd53abe 485
8075bb0e 486 case PRU_BIND:
a44aeee3 487 s = splnet();
a942fe02 488 error = in_pcbbind(inp, addr);
a44aeee3 489 splx(s);
8075bb0e
BJ
490 break;
491
492 case PRU_LISTEN:
493 error = EOPNOTSUPP;
494 break;
495
1bd53abe 496 case PRU_CONNECT:
a3fa431a
SL
497 if (inp->inp_faddr.s_addr != INADDR_ANY) {
498 error = EISCONN;
499 break;
500 }
a44aeee3 501 s = splnet();
a942fe02 502 error = in_pcbconnect(inp, addr);
a44aeee3 503 splx(s);
8a2f82db
SL
504 if (error == 0)
505 soisconnected(so);
1bd53abe
BJ
506 break;
507
4945768c
SL
508 case PRU_CONNECT2:
509 error = EOPNOTSUPP;
510 break;
511
2b4b57cd 512 case PRU_ACCEPT:
a3fa431a
SL
513 error = EOPNOTSUPP;
514 break;
2b4b57cd 515
53a5409e 516 case PRU_DISCONNECT:
a3fa431a
SL
517 if (inp->inp_faddr.s_addr == INADDR_ANY) {
518 error = ENOTCONN;
519 break;
520 }
a44aeee3 521 s = splnet();
405c9168 522 in_pcbdisconnect(inp);
0dfbe263 523 inp->inp_laddr.s_addr = INADDR_ANY;
a44aeee3 524 splx(s);
f96a0781 525 so->so_state &= ~SS_ISCONNECTED; /* XXX */
1bd53abe
BJ
526 break;
527
cdad2eb1
BJ
528 case PRU_SHUTDOWN:
529 socantsendmore(so);
530 break;
531
a942fe02 532 case PRU_SEND:
5921352b 533 return (udp_output(inp, m, addr, control));
1bd53abe
BJ
534
535 case PRU_ABORT:
53a5409e 536 soisdisconnected(so);
a44aeee3 537 udp_detach(inp);
1bd53abe
BJ
538 break;
539
126472ab 540 case PRU_SOCKADDR:
a942fe02 541 in_setsockaddr(inp, addr);
126472ab
SL
542 break;
543
a7343092 544 case PRU_PEERADDR:
a942fe02 545 in_setpeeraddr(inp, addr);
a7343092
SL
546 break;
547
74040e68
MK
548 case PRU_SENSE:
549 /*
550 * stat: don't bother with a blocksize.
551 */
552 return (0);
553
11d2a668
SL
554 case PRU_SENDOOB:
555 case PRU_FASTTIMO:
556 case PRU_SLOWTIMO:
557 case PRU_PROTORCV:
558 case PRU_PROTOSEND:
559 error = EOPNOTSUPP;
560 break;
4945768c 561
5f55b8b5
MK
562 case PRU_RCVD:
563 case PRU_RCVOOB:
564 return (EOPNOTSUPP); /* do not free mbuf's */
565
4945768c
SL
566 default:
567 panic("udp_usrreq");
d52566dd 568 }
5921352b 569
a3fa431a 570release:
a942fe02
MK
571 if (control) {
572 printf("udp control data unexpectedly retained\n");
573 m_freem(control);
574 }
575 if (m)
a3fa431a 576 m_freem(m);
8a2f82db 577 return (error);
d52566dd 578}
a44aeee3 579
c46785cb 580static void
a44aeee3
MK
581udp_detach(inp)
582 struct inpcb *inp;
583{
584 int s = splnet();
585
586 if (inp == udp_last_inpcb)
587 udp_last_inpcb = &udb;
588 in_pcbdetach(inp);
589 splx(s);
590}
5082da2b
KM
591
592/*
593 * Sysctl for udp variables.
594 */
595udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
596 int *name;
597 u_int namelen;
598 void *oldp;
599 size_t *oldlenp;
600 void *newp;
601 size_t newlen;
602{
67e8af50 603 /* All sysctl names at this level are terminal. */
5082da2b
KM
604 if (namelen != 1)
605 return (ENOTDIR);
606
607 switch (name[0]) {
608 case UDPCTL_CHECKSUM:
609 return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum));
610 default:
611 return (ENOPROTOOPT);
612 }
613 /* NOTREACHED */
614}