BSD 4_3_Reno release
[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 *
1c15e888
C
5 * Redistribution is only permitted until one year after the first shipment
6 * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and
7 * binary forms are permitted provided that: (1) source distributions retain
8 * this entire copyright notice and comment, and (2) distributions including
9 * binaries display the following acknowledgement: This product includes
10 * software developed by the University of California, Berkeley and its
11 * contributors'' in the documentation or other materials provided with the
12 * distribution and in all advertising materials mentioning features or use
13 * of this software. Neither the name of the University nor the names of
14 * its contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2b6b6284 19 *
1c15e888 20 * @(#)udp_usrreq.c 7.18 (Berkeley) 7/25/90
8ae0e4b4 21 */
20666ad3
JB
22
23#include "param.h"
20666ad3 24#include "user.h"
9d91b170 25#include "malloc.h"
20666ad3
JB
26#include "mbuf.h"
27#include "protosw.h"
28#include "socket.h"
29#include "socketvar.h"
30#include "errno.h"
31#include "stat.h"
f4d55810 32
72e4f44e 33#include "../net/if.h"
c124e997 34#include "../net/route.h"
f4d55810 35
20666ad3 36#include "in.h"
20666ad3
JB
37#include "in_systm.h"
38#include "ip.h"
0dfbe263 39#include "in_pcb.h"
20666ad3
JB
40#include "ip_var.h"
41#include "ip_icmp.h"
42#include "udp.h"
43#include "udp_var.h"
d52566dd 44
5921352b
MK
45struct inpcb *udp_last_inpcb = &udb;
46
2b4b57cd
BJ
47/*
48 * UDP protocol implementation.
49 * Per RFC 768, August, 1980.
50 */
d52566dd
BJ
51udp_init()
52{
53
eb44bfb2 54 udb.inp_next = udb.inp_prev = &udb;
d52566dd 55}
1bd53abe 56
c50542f3 57#ifndef COMPAT_42
dd8707ae 58int udpcksum = 1;
c50542f3
MK
59#else
60int udpcksum = 0; /* XXX */
61#endif
0eda4f11 62int udp_ttl = UDP_TTL;
c50542f3 63
5328bc49 64struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
eb44bfb2 65
9d91b170
MK
66udp_input(m, iphlen)
67 register struct mbuf *m;
68 int iphlen;
1bd53abe 69{
0dfbe263
MK
70 register struct ip *ip;
71 register struct udphdr *uh;
53a5409e 72 register struct inpcb *inp;
5921352b 73 struct mbuf *opts = 0;
e199a8a5 74 int len;
0dfbe263 75 struct ip save_ip;
eb44bfb2 76
5921352b 77 udpstat.udps_ipackets++;
9f5c260d
MK
78
79 /*
80 * Strip IP options, if any; should skip this,
81 * make available to user, and use on returned packets,
82 * but we don't yet have a way to check the checksum
83 * with options still present.
84 */
85 if (iphlen > sizeof (struct ip)) {
0dfbe263 86 ip_stripoptions(m, (struct mbuf *)0);
9f5c260d
MK
87 iphlen = sizeof(struct ip);
88 }
89
2b4b57cd 90 /*
4aed14e3 91 * Get IP and UDP header together in first mbuf.
2b4b57cd 92 */
0dfbe263
MK
93 ip = mtod(m, struct ip *);
94 if (m->m_len < iphlen + sizeof(struct udphdr)) {
95 if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
7212ba67
MK
96 udpstat.udps_hdrops++;
97 return;
98 }
0dfbe263 99 ip = mtod(m, struct ip *);
7212ba67 100 }
0dfbe263 101 uh = (struct udphdr *)((caddr_t)ip + iphlen);
2b4b57cd
BJ
102
103 /*
4aed14e3
BJ
104 * Make mbuf data length reflect UDP length.
105 * If not enough data to reflect UDP length, drop.
2b4b57cd 106 */
0dfbe263
MK
107 len = ntohs((u_short)uh->uh_ulen);
108 if (ip->ip_len != len) {
109 if (len > ip->ip_len) {
2b4b57cd
BJ
110 udpstat.udps_badlen++;
111 goto bad;
112 }
0dfbe263
MK
113 m_adj(m, len - ip->ip_len);
114 /* ip->ip_len = len; */
2b4b57cd 115 }
dcea2497 116 /*
0dfbe263
MK
117 * Save a copy of the IP header in case we want restore it
118 * for sending an ICMP error message in response.
dcea2497 119 */
0dfbe263 120 save_ip = *ip;
2b4b57cd
BJ
121
122 /*
4aed14e3 123 * Checksum extended UDP header and data.
2b4b57cd 124 */
0dfbe263
MK
125 if (udpcksum && uh->uh_sum) {
126 ((struct ipovly *)ip)->ih_next = 0;
127 ((struct ipovly *)ip)->ih_prev = 0;
128 ((struct ipovly *)ip)->ih_x1 = 0;
129 ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
130 if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
2b4b57cd 131 udpstat.udps_badsum++;
eb44bfb2
BJ
132 m_freem(m);
133 return;
134 }
135 }
2b4b57cd
BJ
136
137 /*
e199a8a5 138 * Locate pcb for datagram.
2b4b57cd 139 */
5921352b
MK
140 inp = udp_last_inpcb;
141 if (inp->inp_lport != uh->uh_dport ||
142 inp->inp_fport != uh->uh_sport ||
143 inp->inp_faddr.s_addr != ip->ip_src.s_addr ||
144 inp->inp_laddr.s_addr != ip->ip_dst.s_addr) {
145 inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport,
146 ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD);
147 if (inp)
148 udp_last_inpcb = inp;
149 udpstat.udpps_pcbcachemiss++;
150 }
72e4f44e 151 if (inp == 0) {
5cdc4d65 152 /* don't send ICMP response for broadcast packet */
0dfbe263 153 udpstat.udps_noport++;
5921352b
MK
154 if (m->m_flags & M_BCAST) {
155 udpstat.udps_noportbcast++;
72e4f44e 156 goto bad;
5921352b 157 }
0dfbe263 158 *ip = save_ip;
9f5c260d 159 ip->ip_len += iphlen;
9d91b170 160 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
72e4f44e
SL
161 return;
162 }
2b4b57cd
BJ
163
164 /*
165 * Construct sockaddr format source address.
166 * Stuff source address and datagram in user buffer.
167 */
0dfbe263
MK
168 udp_in.sin_port = uh->uh_sport;
169 udp_in.sin_addr = ip->ip_src;
5921352b
MK
170 if (inp->inp_flags & INP_CONTROLOPTS) {
171 struct mbuf **mp = &opts;
172 struct mbuf *udp_saveopt();
173
174 if (inp->inp_flags & INP_RECVDSTADDR) {
175 *mp = udp_saveopt((caddr_t) &ip->ip_dst,
176 sizeof(struct in_addr), IP_RECVDSTADDR);
177 if (*mp)
178 mp = &(*mp)->m_next;
179 }
180#ifdef notyet
181 /* options were tossed above */
182 if (inp->inp_flags & INP_RECVOPTS) {
183 *mp = udp_saveopt((caddr_t) opts_deleted_above,
184 sizeof(struct in_addr), IP_RECVOPTS);
185 if (*mp)
186 mp = &(*mp)->m_next;
187 }
188 /* ip_srcroute doesn't do what we want here, need to fix */
189 if (inp->inp_flags & INP_RECVRETOPTS) {
190 *mp = udp_saveopt((caddr_t) ip_srcroute(),
191 sizeof(struct in_addr), IP_RECVRETOPTS);
192 if (*mp)
193 mp = &(*mp)->m_next;
194 }
195#endif
196 }
0dfbe263
MK
197 iphlen += sizeof(struct udphdr);
198 m->m_len -= iphlen;
199 m->m_pkthdr.len -= iphlen;
200 m->m_data += iphlen;
ab85b059 201 if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
5921352b
MK
202 m, opts) == 0) {
203 udpstat.udps_fullsock++;
2b4b57cd 204 goto bad;
5921352b 205 }
4e60622a 206 sorwakeup(inp->inp_socket);
53a5409e 207 return;
2b4b57cd 208bad:
53a5409e 209 m_freem(m);
5921352b
MK
210 if (opts)
211 m_freem(opts);
212}
213
214/*
215 * Create a "control" mbuf containing the specified data
216 * with the specified type for presentation with a datagram.
217 */
218struct mbuf *
219udp_saveopt(p, size, type)
220 caddr_t p;
221 register int size;
222 int type;
223{
224 register struct cmsghdr *cp;
225 struct mbuf *m;
226
227 if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
228 return ((struct mbuf *) NULL);
229 cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
1c15e888 230 bcopy(p, (caddr_t)cp + size, size);
5921352b
MK
231 size += sizeof(*cp);
232 m->m_len = size;
233 cp->cmsg_len = size;
234 cp->cmsg_level = IPPROTO_IP;
235 cp->cmsg_type = type;
236 return (m);
1bd53abe
BJ
237}
238
41715710
MK
239/*
240 * Notify a udp user of an asynchronous error;
241 * just wake up so that he can collect error status.
242 */
5921352b 243udp_notify(inp, errno)
41715710
MK
244 register struct inpcb *inp;
245{
246
5921352b 247 inp->inp_socket->so_error = errno;
41715710
MK
248 sorwakeup(inp->inp_socket);
249 sowwakeup(inp->inp_socket);
250}
251
0dfbe263 252udp_ctlinput(cmd, sa, ip)
39674d5f 253 int cmd;
dcea2497 254 struct sockaddr *sa;
0dfbe263 255 register struct ip *ip;
39674d5f 256{
0dfbe263
MK
257 register struct udphdr *uh;
258 extern struct in_addr zeroin_addr;
39674d5f 259 extern u_char inetctlerrmap[];
39674d5f 260
0dfbe263 261 if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)
dcea2497 262 return;
0dfbe263
MK
263 if (ip) {
264 uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
265 in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
266 cmd, udp_notify);
267 } else
268 in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
39674d5f
SL
269}
270
a942fe02 271udp_output(inp, m, addr, control)
d55475b1 272 register struct inpcb *inp;
2b4b57cd 273 register struct mbuf *m;
a942fe02 274 struct mbuf *addr, *control;
9d91b170 275{
2b4b57cd 276 register struct udpiphdr *ui;
9d91b170 277 register int len = m->m_pkthdr.len;
a942fe02
MK
278 struct in_addr laddr;
279 int s, error = 0;
2b4b57cd 280
a942fe02
MK
281 if (control)
282 m_freem(control); /* XXX */
283
284 if (addr) {
285 laddr = inp->inp_laddr;
286 if (inp->inp_faddr.s_addr != INADDR_ANY) {
287 error = EISCONN;
288 goto release;
289 }
290 /*
291 * Must block input while temporarily connected.
292 */
293 s = splnet();
294 error = in_pcbconnect(inp, addr);
295 if (error) {
296 splx(s);
297 goto release;
298 }
299 } else {
300 if (inp->inp_faddr.s_addr == INADDR_ANY) {
301 error = ENOTCONN;
302 goto release;
303 }
304 }
2b4b57cd
BJ
305 /*
306 * Calculate data length and get a mbuf
4aed14e3 307 * for UDP and IP headers.
2b4b57cd 308 */
9d91b170 309 M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT);
2b4b57cd
BJ
310
311 /*
4aed14e3 312 * Fill in mbuf with extended UDP header
2b4b57cd
BJ
313 * and addresses and length put into network format.
314 */
2b4b57cd
BJ
315 ui = mtod(m, struct udpiphdr *);
316 ui->ui_next = ui->ui_prev = 0;
317 ui->ui_x1 = 0;
318 ui->ui_pr = IPPROTO_UDP;
ee847abd 319 ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
4e60622a
BJ
320 ui->ui_src = inp->inp_laddr;
321 ui->ui_dst = inp->inp_faddr;
322 ui->ui_sport = inp->inp_lport;
323 ui->ui_dport = inp->inp_fport;
ee847abd 324 ui->ui_ulen = ui->ui_len;
2b4b57cd
BJ
325
326 /*
327 * Stuff checksum and output datagram.
328 */
329 ui->ui_sum = 0;
dd8707ae
MK
330 if (udpcksum) {
331 if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
890024da 332 ui->ui_sum = 0xffff;
ee09448f 333 }
4e60622a 334 ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
5921352b
MK
335 ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */
336 ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
0dfbe263 337 udpstat.udps_opackets++;
a942fe02
MK
338 error = ip_output(m, inp->inp_options, &inp->inp_route,
339 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST));
340
341 if (addr) {
342 in_pcbdisconnect(inp);
343 inp->inp_laddr = laddr;
344 splx(s);
345 }
346 return (error);
347
348release:
349 m_freem(m);
350 return (error);
1bd53abe
BJ
351}
352
c9209cff
KM
353u_long udp_sendspace = 9216; /* really max datagram size */
354u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
355 /* 40 1K datagrams */
1d14d351 356
a8d3bf7f 357/*ARGSUSED*/
a942fe02 358udp_usrreq(so, req, m, addr, control)
53a5409e 359 struct socket *so;
1bd53abe 360 int req;
a942fe02 361 struct mbuf *m, *addr, *control;
1bd53abe 362{
53a5409e 363 struct inpcb *inp = sotoinpcb(so);
8a2f82db 364 int error = 0;
5921352b 365 int s;
1bd53abe 366
1d14d351 367 if (req == PRU_CONTROL)
a942fe02 368 return (in_control(so, (int)m, (caddr_t)addr,
0dfbe263 369 (struct ifnet *)control));
a3fa431a
SL
370 if (inp == NULL && req != PRU_ATTACH) {
371 error = EINVAL;
372 goto release;
373 }
5921352b 374 /*
a44aeee3
MK
375 * Note: need to block udp_input while changing
376 * the udp pcb queue and/or pcb addresses.
5921352b 377 */
1bd53abe
BJ
378 switch (req) {
379
380 case PRU_ATTACH:
a3fa431a
SL
381 if (inp != NULL) {
382 error = EINVAL;
383 break;
384 }
5921352b 385 s = splnet();
8075bb0e 386 error = in_pcballoc(so, &udb);
5921352b 387 splx(s);
8075bb0e
BJ
388 if (error)
389 break;
1d14d351 390 error = soreserve(so, udp_sendspace, udp_recvspace);
8075bb0e
BJ
391 if (error)
392 break;
5921352b 393 ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl;
53a5409e 394 break;
1bd53abe
BJ
395
396 case PRU_DETACH:
a44aeee3 397 udp_detach(inp);
53a5409e 398 break;
1bd53abe 399
8075bb0e 400 case PRU_BIND:
a44aeee3 401 s = splnet();
a942fe02 402 error = in_pcbbind(inp, addr);
a44aeee3 403 splx(s);
8075bb0e
BJ
404 break;
405
406 case PRU_LISTEN:
407 error = EOPNOTSUPP;
408 break;
409
1bd53abe 410 case PRU_CONNECT:
a3fa431a
SL
411 if (inp->inp_faddr.s_addr != INADDR_ANY) {
412 error = EISCONN;
413 break;
414 }
a44aeee3 415 s = splnet();
a942fe02 416 error = in_pcbconnect(inp, addr);
a44aeee3 417 splx(s);
8a2f82db
SL
418 if (error == 0)
419 soisconnected(so);
1bd53abe
BJ
420 break;
421
4945768c
SL
422 case PRU_CONNECT2:
423 error = EOPNOTSUPP;
424 break;
425
2b4b57cd 426 case PRU_ACCEPT:
a3fa431a
SL
427 error = EOPNOTSUPP;
428 break;
2b4b57cd 429
53a5409e 430 case PRU_DISCONNECT:
a3fa431a
SL
431 if (inp->inp_faddr.s_addr == INADDR_ANY) {
432 error = ENOTCONN;
433 break;
434 }
a44aeee3 435 s = splnet();
405c9168 436 in_pcbdisconnect(inp);
0dfbe263 437 inp->inp_laddr.s_addr = INADDR_ANY;
a44aeee3 438 splx(s);
f96a0781 439 so->so_state &= ~SS_ISCONNECTED; /* XXX */
1bd53abe
BJ
440 break;
441
cdad2eb1
BJ
442 case PRU_SHUTDOWN:
443 socantsendmore(so);
444 break;
445
a942fe02 446 case PRU_SEND:
5921352b 447 return (udp_output(inp, m, addr, control));
1bd53abe
BJ
448
449 case PRU_ABORT:
53a5409e 450 soisdisconnected(so);
a44aeee3 451 udp_detach(inp);
1bd53abe
BJ
452 break;
453
126472ab 454 case PRU_SOCKADDR:
a942fe02 455 in_setsockaddr(inp, addr);
126472ab
SL
456 break;
457
a7343092 458 case PRU_PEERADDR:
a942fe02 459 in_setpeeraddr(inp, addr);
a7343092
SL
460 break;
461
74040e68
MK
462 case PRU_SENSE:
463 /*
464 * stat: don't bother with a blocksize.
465 */
466 return (0);
467
11d2a668
SL
468 case PRU_SENDOOB:
469 case PRU_FASTTIMO:
470 case PRU_SLOWTIMO:
471 case PRU_PROTORCV:
472 case PRU_PROTOSEND:
473 error = EOPNOTSUPP;
474 break;
4945768c 475
5f55b8b5
MK
476 case PRU_RCVD:
477 case PRU_RCVOOB:
478 return (EOPNOTSUPP); /* do not free mbuf's */
479
4945768c
SL
480 default:
481 panic("udp_usrreq");
d52566dd 482 }
5921352b 483
a3fa431a 484release:
a942fe02
MK
485 if (control) {
486 printf("udp control data unexpectedly retained\n");
487 m_freem(control);
488 }
489 if (m)
a3fa431a 490 m_freem(m);
8a2f82db 491 return (error);
d52566dd 492}
a44aeee3
MK
493
494udp_detach(inp)
495 struct inpcb *inp;
496{
497 int s = splnet();
498
499 if (inp == udp_last_inpcb)
500 udp_last_inpcb = &udb;
501 in_pcbdetach(inp);
502 splx(s);
503}