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