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