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