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