xref: /freebsd/sys/netinet/udp_usrreq.c (revision 8e6b01171e30297084bb0b4457c4183c2746aacc)
1 /*
2  * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *	@(#)udp_usrreq.c	8.6 (Berkeley) 5/23/95
34  *	$Id: udp_usrreq.c,v 1.13 1995/08/17 22:09:14 olah Exp $
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/protosw.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/errno.h>
45 #include <sys/stat.h>
46 #include <sys/queue.h>
47 #include <vm/vm.h>
48 #include <sys/sysctl.h>
49 
50 #include <net/if.h>
51 #include <net/route.h>
52 
53 #include <netinet/in.h>
54 #include <netinet/in_systm.h>
55 #include <netinet/ip.h>
56 #include <netinet/in_pcb.h>
57 #include <netinet/in_var.h>
58 #include <netinet/ip_var.h>
59 #include <netinet/ip_icmp.h>
60 #include <netinet/udp.h>
61 #include <netinet/udp_var.h>
62 
63 /*
64  * UDP protocol implementation.
65  * Per RFC 768, August, 1980.
66  */
67 #ifndef	COMPAT_42
68 int	udpcksum = 1;
69 #else
70 int	udpcksum = 0;		/* XXX */
71 #endif
72 
73 struct	inpcbhead udb;		/* from udp_var.h */
74 struct	inpcbinfo udbinfo;
75 
76 #ifndef UDBHASHSIZE
77 #define UDBHASHSIZE 64
78 #endif
79 
80 struct	udpstat udpstat;	/* from udp_var.h */
81 
82 struct	sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
83 
84 static	void udp_detach __P((struct inpcb *));
85 static	void udp_notify __P((struct inpcb *, int));
86 static	struct mbuf *udp_saveopt __P((caddr_t, int, int));
87 
88 void
89 udp_init()
90 {
91 	LIST_INIT(&udb);
92 	udbinfo.listhead = &udb;
93 	udbinfo.hashbase = phashinit(UDBHASHSIZE, M_PCB, &udbinfo.hashsize);
94 }
95 
96 void
97 udp_input(m, iphlen)
98 	register struct mbuf *m;
99 	int iphlen;
100 {
101 	register struct ip *ip;
102 	register struct udphdr *uh;
103 	register struct inpcb *inp;
104 	struct mbuf *opts = 0;
105 	int len;
106 	struct ip save_ip;
107 
108 	udpstat.udps_ipackets++;
109 
110 	/*
111 	 * Strip IP options, if any; should skip this,
112 	 * make available to user, and use on returned packets,
113 	 * but we don't yet have a way to check the checksum
114 	 * with options still present.
115 	 */
116 	if (iphlen > sizeof (struct ip)) {
117 		ip_stripoptions(m, (struct mbuf *)0);
118 		iphlen = sizeof(struct ip);
119 	}
120 
121 	/*
122 	 * Get IP and UDP header together in first mbuf.
123 	 */
124 	ip = mtod(m, struct ip *);
125 	if (m->m_len < iphlen + sizeof(struct udphdr)) {
126 		if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
127 			udpstat.udps_hdrops++;
128 			return;
129 		}
130 		ip = mtod(m, struct ip *);
131 	}
132 	uh = (struct udphdr *)((caddr_t)ip + iphlen);
133 
134 	/*
135 	 * Make mbuf data length reflect UDP length.
136 	 * If not enough data to reflect UDP length, drop.
137 	 */
138 	len = ntohs((u_short)uh->uh_ulen);
139 	if (ip->ip_len != len) {
140 		if (len > ip->ip_len || len < sizeof(struct udphdr)) {
141 			udpstat.udps_badlen++;
142 			goto bad;
143 		}
144 		m_adj(m, len - ip->ip_len);
145 		/* ip->ip_len = len; */
146 	}
147 	/*
148 	 * Save a copy of the IP header in case we want restore it
149 	 * for sending an ICMP error message in response.
150 	 */
151 	save_ip = *ip;
152 
153 	/*
154 	 * Checksum extended UDP header and data.
155 	 */
156 	if (uh->uh_sum) {
157 		((struct ipovly *)ip)->ih_next = 0;
158 		((struct ipovly *)ip)->ih_prev = 0;
159 		((struct ipovly *)ip)->ih_x1 = 0;
160 		((struct ipovly *)ip)->ih_len = uh->uh_ulen;
161 		uh->uh_sum = in_cksum(m, len + sizeof (struct ip));
162 		if (uh->uh_sum) {
163 			udpstat.udps_badsum++;
164 			m_freem(m);
165 			return;
166 		}
167 	}
168 
169 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
170 	    in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
171 		struct socket *last;
172 		/*
173 		 * Deliver a multicast or broadcast datagram to *all* sockets
174 		 * for which the local and remote addresses and ports match
175 		 * those of the incoming datagram.  This allows more than
176 		 * one process to receive multi/broadcasts on the same port.
177 		 * (This really ought to be done for unicast datagrams as
178 		 * well, but that would cause problems with existing
179 		 * applications that open both address-specific sockets and
180 		 * a wildcard socket listening to the same port -- they would
181 		 * end up receiving duplicates of every unicast datagram.
182 		 * Those applications open the multiple sockets to overcome an
183 		 * inadequacy of the UDP socket interface, but for backwards
184 		 * compatibility we avoid the problem here rather than
185 		 * fixing the interface.  Maybe 4.5BSD will remedy this?)
186 		 */
187 
188 		/*
189 		 * Construct sockaddr format source address.
190 		 */
191 		udp_in.sin_port = uh->uh_sport;
192 		udp_in.sin_addr = ip->ip_src;
193 		m->m_len -= sizeof (struct udpiphdr);
194 		m->m_data += sizeof (struct udpiphdr);
195 		/*
196 		 * Locate pcb(s) for datagram.
197 		 * (Algorithm copied from raw_intr().)
198 		 */
199 		last = NULL;
200 		for (inp = udb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
201 			if (inp->inp_lport != uh->uh_dport)
202 				continue;
203 			if (inp->inp_laddr.s_addr != INADDR_ANY) {
204 				if (inp->inp_laddr.s_addr !=
205 				    ip->ip_dst.s_addr)
206 					continue;
207 			}
208 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
209 				if (inp->inp_faddr.s_addr !=
210 				    ip->ip_src.s_addr ||
211 				    inp->inp_fport != uh->uh_sport)
212 					continue;
213 			}
214 
215 			if (last != NULL) {
216 				struct mbuf *n;
217 
218 				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
219 					if (sbappendaddr(&last->so_rcv,
220 						(struct sockaddr *)&udp_in,
221 						n, (struct mbuf *)0) == 0) {
222 						m_freem(n);
223 						udpstat.udps_fullsock++;
224 					} else
225 						sorwakeup(last);
226 				}
227 			}
228 			last = inp->inp_socket;
229 			/*
230 			 * Don't look for additional matches if this one does
231 			 * not have either the SO_REUSEPORT or SO_REUSEADDR
232 			 * socket options set.  This heuristic avoids searching
233 			 * through all pcbs in the common case of a non-shared
234 			 * port.  It * assumes that an application will never
235 			 * clear these options after setting them.
236 			 */
237 			if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0))
238 				break;
239 		}
240 
241 		if (last == NULL) {
242 			/*
243 			 * No matching pcb found; discard datagram.
244 			 * (No need to send an ICMP Port Unreachable
245 			 * for a broadcast or multicast datgram.)
246 			 */
247 			udpstat.udps_noportbcast++;
248 			goto bad;
249 		}
250 		if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
251 		     m, (struct mbuf *)0) == 0) {
252 			udpstat.udps_fullsock++;
253 			goto bad;
254 		}
255 		sorwakeup(last);
256 		return;
257 	}
258 	/*
259 	 * Locate pcb for datagram. First look for an exact match.
260 	 */
261 	inp = in_pcblookuphash(&udbinfo, ip->ip_src, uh->uh_sport,
262 	    ip->ip_dst, uh->uh_dport);
263 	/*
264 	 * ...and if that fails, do a wildcard search.
265 	 */
266 	if (inp == NULL) {
267 		inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, ip->ip_dst,
268 		    uh->uh_dport, INPLOOKUP_WILDCARD);
269 	}
270 	if (inp == NULL) {
271 		udpstat.udps_noport++;
272 		if (m->m_flags & (M_BCAST | M_MCAST)) {
273 			udpstat.udps_noportbcast++;
274 			goto bad;
275 		}
276 		*ip = save_ip;
277 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
278 		return;
279 	}
280 
281 	/*
282 	 * Construct sockaddr format source address.
283 	 * Stuff source address and datagram in user buffer.
284 	 */
285 	udp_in.sin_port = uh->uh_sport;
286 	udp_in.sin_addr = ip->ip_src;
287 	if (inp->inp_flags & INP_CONTROLOPTS) {
288 		struct mbuf **mp = &opts;
289 
290 		if (inp->inp_flags & INP_RECVDSTADDR) {
291 			*mp = udp_saveopt((caddr_t) &ip->ip_dst,
292 			    sizeof(struct in_addr), IP_RECVDSTADDR);
293 			if (*mp)
294 				mp = &(*mp)->m_next;
295 		}
296 #ifdef notyet
297 		/* options were tossed above */
298 		if (inp->inp_flags & INP_RECVOPTS) {
299 			*mp = udp_saveopt((caddr_t) opts_deleted_above,
300 			    sizeof(struct in_addr), IP_RECVOPTS);
301 			if (*mp)
302 				mp = &(*mp)->m_next;
303 		}
304 		/* ip_srcroute doesn't do what we want here, need to fix */
305 		if (inp->inp_flags & INP_RECVRETOPTS) {
306 			*mp = udp_saveopt((caddr_t) ip_srcroute(),
307 			    sizeof(struct in_addr), IP_RECVRETOPTS);
308 			if (*mp)
309 				mp = &(*mp)->m_next;
310 		}
311 #endif
312 	}
313 	iphlen += sizeof(struct udphdr);
314 	m->m_len -= iphlen;
315 	m->m_pkthdr.len -= iphlen;
316 	m->m_data += iphlen;
317 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
318 	    m, opts) == 0) {
319 		udpstat.udps_fullsock++;
320 		goto bad;
321 	}
322 	sorwakeup(inp->inp_socket);
323 	return;
324 bad:
325 	m_freem(m);
326 	if (opts)
327 		m_freem(opts);
328 }
329 
330 /*
331  * Create a "control" mbuf containing the specified data
332  * with the specified type for presentation with a datagram.
333  */
334 struct mbuf *
335 udp_saveopt(p, size, type)
336 	caddr_t p;
337 	register int size;
338 	int type;
339 {
340 	register struct cmsghdr *cp;
341 	struct mbuf *m;
342 
343 	if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
344 		return ((struct mbuf *) NULL);
345 	cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
346 	(void)memcpy(CMSG_DATA(cp), p, size);
347 	size += sizeof(*cp);
348 	m->m_len = size;
349 	cp->cmsg_len = size;
350 	cp->cmsg_level = IPPROTO_IP;
351 	cp->cmsg_type = type;
352 	return (m);
353 }
354 
355 /*
356  * Notify a udp user of an asynchronous error;
357  * just wake up so that he can collect error status.
358  */
359 static void
360 udp_notify(inp, errno)
361 	register struct inpcb *inp;
362 	int errno;
363 {
364 	inp->inp_socket->so_error = errno;
365 	sorwakeup(inp->inp_socket);
366 	sowwakeup(inp->inp_socket);
367 }
368 
369 void
370 udp_ctlinput(cmd, sa, ip)
371 	int cmd;
372 	struct sockaddr *sa;
373 	register struct ip *ip;
374 {
375 	register struct udphdr *uh;
376 
377 	if (!PRC_IS_REDIRECT(cmd) &&
378 	    ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
379 		return;
380 	if (ip) {
381 		uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
382 		in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
383 			cmd, udp_notify);
384 	} else
385 		in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
386 }
387 
388 int
389 udp_output(inp, m, addr, control)
390 	register struct inpcb *inp;
391 	register struct mbuf *m;
392 	struct mbuf *addr, *control;
393 {
394 	register struct udpiphdr *ui;
395 	register int len = m->m_pkthdr.len;
396 	struct in_addr laddr;
397 	int s = 0, error = 0;
398 
399 	if (control)
400 		m_freem(control);		/* XXX */
401 
402 	if (addr) {
403 		laddr = inp->inp_laddr;
404 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
405 			error = EISCONN;
406 			goto release;
407 		}
408 		/*
409 		 * Must block input while temporarily connected.
410 		 */
411 		s = splnet();
412 		error = in_pcbconnect(inp, addr);
413 		if (error) {
414 			splx(s);
415 			goto release;
416 		}
417 	} else {
418 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
419 			error = ENOTCONN;
420 			goto release;
421 		}
422 	}
423 	/*
424 	 * Calculate data length and get a mbuf
425 	 * for UDP and IP headers.
426 	 */
427 	M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
428 	if (m == 0) {
429 		error = ENOBUFS;
430 		if (addr)
431 			splx(s);
432 		goto release;
433 	}
434 
435 	/*
436 	 * Fill in mbuf with extended UDP header
437 	 * and addresses and length put into network format.
438 	 */
439 	ui = mtod(m, struct udpiphdr *);
440 	ui->ui_next = ui->ui_prev = 0;
441 	ui->ui_x1 = 0;
442 	ui->ui_pr = IPPROTO_UDP;
443 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
444 	ui->ui_src = inp->inp_laddr;
445 	ui->ui_dst = inp->inp_faddr;
446 	ui->ui_sport = inp->inp_lport;
447 	ui->ui_dport = inp->inp_fport;
448 	ui->ui_ulen = ui->ui_len;
449 
450 	/*
451 	 * Stuff checksum and output datagram.
452 	 */
453 	ui->ui_sum = 0;
454 	if (udpcksum) {
455 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
456 		ui->ui_sum = 0xffff;
457 	}
458 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
459 	((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl;	/* XXX */
460 	((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos;	/* XXX */
461 	udpstat.udps_opackets++;
462 	error = ip_output(m, inp->inp_options, &inp->inp_route,
463 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
464 	    inp->inp_moptions);
465 
466 	if (addr) {
467 		in_pcbdisconnect(inp);
468 		inp->inp_laddr = laddr;
469 		splx(s);
470 	}
471 	return (error);
472 
473 release:
474 	m_freem(m);
475 	return (error);
476 }
477 
478 u_long	udp_sendspace = 9216;		/* really max datagram size */
479 u_long	udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
480 					/* 40 1K datagrams */
481 
482 /*ARGSUSED*/
483 int
484 udp_usrreq(so, req, m, addr, control)
485 	struct socket *so;
486 	int req;
487 	struct mbuf *m, *addr, *control;
488 {
489 	struct inpcb *inp = sotoinpcb(so);
490 	int error = 0;
491 	int s;
492 
493 	if (req == PRU_CONTROL)
494 		return (in_control(so, (u_long)m, (caddr_t)addr,
495 			(struct ifnet *)control));
496 	if (inp == NULL && req != PRU_ATTACH) {
497 		error = EINVAL;
498 		goto release;
499 	}
500 	/*
501 	 * Note: need to block udp_input while changing
502 	 * the udp pcb queue and/or pcb addresses.
503 	 */
504 	switch (req) {
505 
506 	case PRU_ATTACH:
507 		if (inp != NULL) {
508 			error = EINVAL;
509 			break;
510 		}
511 		s = splnet();
512 		error = in_pcballoc(so, &udbinfo);
513 		splx(s);
514 		if (error)
515 			break;
516 		error = soreserve(so, udp_sendspace, udp_recvspace);
517 		if (error)
518 			break;
519 		((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
520 		break;
521 
522 	case PRU_DETACH:
523 		udp_detach(inp);
524 		break;
525 
526 	case PRU_BIND:
527 		s = splnet();
528 		error = in_pcbbind(inp, addr);
529 		splx(s);
530 		break;
531 
532 	case PRU_LISTEN:
533 		error = EOPNOTSUPP;
534 		break;
535 
536 	case PRU_CONNECT:
537 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
538 			error = EISCONN;
539 			break;
540 		}
541 		s = splnet();
542 		error = in_pcbconnect(inp, addr);
543 		splx(s);
544 		if (error == 0)
545 			soisconnected(so);
546 		break;
547 
548 	case PRU_CONNECT2:
549 		error = EOPNOTSUPP;
550 		break;
551 
552 	case PRU_ACCEPT:
553 		error = EOPNOTSUPP;
554 		break;
555 
556 	case PRU_DISCONNECT:
557 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
558 			error = ENOTCONN;
559 			break;
560 		}
561 		s = splnet();
562 		in_pcbdisconnect(inp);
563 		inp->inp_laddr.s_addr = INADDR_ANY;
564 		splx(s);
565 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
566 		break;
567 
568 	case PRU_SHUTDOWN:
569 		socantsendmore(so);
570 		break;
571 
572 	case PRU_SEND:
573 		return (udp_output(inp, m, addr, control));
574 
575 	case PRU_ABORT:
576 		soisdisconnected(so);
577 		udp_detach(inp);
578 		break;
579 
580 	case PRU_SOCKADDR:
581 		in_setsockaddr(inp, addr);
582 		break;
583 
584 	case PRU_PEERADDR:
585 		in_setpeeraddr(inp, addr);
586 		break;
587 
588 	case PRU_SENSE:
589 		/*
590 		 * stat: don't bother with a blocksize.
591 		 */
592 		return (0);
593 
594 	case PRU_SENDOOB:
595 	case PRU_FASTTIMO:
596 	case PRU_SLOWTIMO:
597 	case PRU_PROTORCV:
598 	case PRU_PROTOSEND:
599 		error =  EOPNOTSUPP;
600 		break;
601 
602 	case PRU_RCVD:
603 	case PRU_RCVOOB:
604 		return (EOPNOTSUPP);	/* do not free mbuf's */
605 
606 	default:
607 		panic("udp_usrreq");
608 	}
609 
610 release:
611 	if (control) {
612 		printf("udp control data unexpectedly retained\n");
613 		m_freem(control);
614 	}
615 	if (m)
616 		m_freem(m);
617 	return (error);
618 }
619 
620 static void
621 udp_detach(inp)
622 	struct inpcb *inp;
623 {
624 	int s = splnet();
625 
626 	in_pcbdetach(inp);
627 	splx(s);
628 }
629 
630 /*
631  * Sysctl for udp variables.
632  */
633 int
634 udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
635 	int *name;
636 	u_int namelen;
637 	void *oldp;
638 	size_t *oldlenp;
639 	void *newp;
640 	size_t newlen;
641 {
642 	/* All sysctl names at this level are terminal. */
643 	if (namelen != 1)
644 		return (ENOTDIR);
645 
646 	switch (name[0]) {
647 	case UDPCTL_CHECKSUM:
648 		return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum));
649 	case UDPCTL_STATS:
650 		return (sysctl_rdstruct(oldp, oldlenp, newp, &udpstat,
651 					sizeof udpstat));
652 	case UDPCTL_MAXDGRAM:
653 		return (sysctl_int(oldp, oldlenp, newp, newlen,
654 				   (int *)&udp_sendspace)); /* XXX */
655 	case UDPCTL_RECVSPACE:
656 		return (sysctl_int(oldp, oldlenp, newp, newlen,
657 				   (int *)&udp_recvspace)); /* XXX */
658 	default:
659 		return (ENOPROTOOPT);
660 	}
661 	/* NOTREACHED */
662 }
663