xref: /original-bsd/sys/netinet/udp_usrreq.c (revision 9a35f7df)
1 /*
2  * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)udp_usrreq.c	8.6 (Berkeley) 05/23/95
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 (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 		if (m->m_flags & (M_BCAST | M_MCAST)) {
232 			udpstat.udps_noportbcast++;
233 			goto bad;
234 		}
235 		*ip = save_ip;
236 		ip->ip_len += iphlen;
237 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
238 		return;
239 	}
240 
241 	/*
242 	 * Construct sockaddr format source address.
243 	 * Stuff source address and datagram in user buffer.
244 	 */
245 	udp_in.sin_port = uh->uh_sport;
246 	udp_in.sin_addr = ip->ip_src;
247 	if (inp->inp_flags & INP_CONTROLOPTS) {
248 		struct mbuf **mp = &opts;
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, CMSG_DATA(cp), 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 static void
320 udp_notify(inp, errno)
321 	register struct inpcb *inp;
322 	int errno;
323 {
324 	inp->inp_socket->so_error = errno;
325 	sorwakeup(inp->inp_socket);
326 	sowwakeup(inp->inp_socket);
327 }
328 
329 void
330 udp_ctlinput(cmd, sa, ip)
331 	int cmd;
332 	struct sockaddr *sa;
333 	register struct ip *ip;
334 {
335 	register struct udphdr *uh;
336 	extern struct in_addr zeroin_addr;
337 	extern u_char inetctlerrmap[];
338 
339 	if (!PRC_IS_REDIRECT(cmd) &&
340 	    ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
341 		return;
342 	if (ip) {
343 		uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
344 		in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
345 			cmd, udp_notify);
346 	} else
347 		in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
348 }
349 
350 int
351 udp_output(inp, m, addr, control)
352 	register struct inpcb *inp;
353 	register struct mbuf *m;
354 	struct mbuf *addr, *control;
355 {
356 	register struct udpiphdr *ui;
357 	register int len = m->m_pkthdr.len;
358 	struct in_addr laddr;
359 	int s, error = 0;
360 
361 	if (control)
362 		m_freem(control);		/* XXX */
363 
364 	if (addr) {
365 		laddr = inp->inp_laddr;
366 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
367 			error = EISCONN;
368 			goto release;
369 		}
370 		/*
371 		 * Must block input while temporarily connected.
372 		 */
373 		s = splnet();
374 		error = in_pcbconnect(inp, addr);
375 		if (error) {
376 			splx(s);
377 			goto release;
378 		}
379 	} else {
380 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
381 			error = ENOTCONN;
382 			goto release;
383 		}
384 	}
385 	/*
386 	 * Calculate data length and get a mbuf
387 	 * for UDP and IP headers.
388 	 */
389 	M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
390 	if (m == 0) {
391 		error = ENOBUFS;
392 		goto release;
393 	}
394 
395 	/*
396 	 * Fill in mbuf with extended UDP header
397 	 * and addresses and length put into network format.
398 	 */
399 	ui = mtod(m, struct udpiphdr *);
400 	ui->ui_next = ui->ui_prev = 0;
401 	ui->ui_x1 = 0;
402 	ui->ui_pr = IPPROTO_UDP;
403 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
404 	ui->ui_src = inp->inp_laddr;
405 	ui->ui_dst = inp->inp_faddr;
406 	ui->ui_sport = inp->inp_lport;
407 	ui->ui_dport = inp->inp_fport;
408 	ui->ui_ulen = ui->ui_len;
409 
410 	/*
411 	 * Stuff checksum and output datagram.
412 	 */
413 	ui->ui_sum = 0;
414 	if (udpcksum) {
415 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
416 		ui->ui_sum = 0xffff;
417 	}
418 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
419 	((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl;	/* XXX */
420 	((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos;	/* XXX */
421 	udpstat.udps_opackets++;
422 	error = ip_output(m, inp->inp_options, &inp->inp_route,
423 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
424 	    inp->inp_moptions);
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 int
444 udp_usrreq(so, req, m, addr, control)
445 	struct socket *so;
446 	int req;
447 	struct mbuf *m, *addr, *control;
448 {
449 	struct inpcb *inp = sotoinpcb(so);
450 	int error = 0;
451 	int s;
452 
453 	if (req == PRU_CONTROL)
454 		return (in_control(so, (u_long)m, (caddr_t)addr,
455 			(struct ifnet *)control));
456 	if (inp == NULL && req != PRU_ATTACH) {
457 		error = EINVAL;
458 		goto release;
459 	}
460 	/*
461 	 * Note: need to block udp_input while changing
462 	 * the udp pcb queue and/or pcb addresses.
463 	 */
464 	switch (req) {
465 
466 	case PRU_ATTACH:
467 		if (inp != NULL) {
468 			error = EINVAL;
469 			break;
470 		}
471 		s = splnet();
472 		error = in_pcballoc(so, &udb);
473 		splx(s);
474 		if (error)
475 			break;
476 		error = soreserve(so, udp_sendspace, udp_recvspace);
477 		if (error)
478 			break;
479 		((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
480 		break;
481 
482 	case PRU_DETACH:
483 		udp_detach(inp);
484 		break;
485 
486 	case PRU_BIND:
487 		s = splnet();
488 		error = in_pcbbind(inp, addr);
489 		splx(s);
490 		break;
491 
492 	case PRU_LISTEN:
493 		error = EOPNOTSUPP;
494 		break;
495 
496 	case PRU_CONNECT:
497 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
498 			error = EISCONN;
499 			break;
500 		}
501 		s = splnet();
502 		error = in_pcbconnect(inp, addr);
503 		splx(s);
504 		if (error == 0)
505 			soisconnected(so);
506 		break;
507 
508 	case PRU_CONNECT2:
509 		error = EOPNOTSUPP;
510 		break;
511 
512 	case PRU_ACCEPT:
513 		error = EOPNOTSUPP;
514 		break;
515 
516 	case PRU_DISCONNECT:
517 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
518 			error = ENOTCONN;
519 			break;
520 		}
521 		s = splnet();
522 		in_pcbdisconnect(inp);
523 		inp->inp_laddr.s_addr = INADDR_ANY;
524 		splx(s);
525 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
526 		break;
527 
528 	case PRU_SHUTDOWN:
529 		socantsendmore(so);
530 		break;
531 
532 	case PRU_SEND:
533 		return (udp_output(inp, m, addr, control));
534 
535 	case PRU_ABORT:
536 		soisdisconnected(so);
537 		udp_detach(inp);
538 		break;
539 
540 	case PRU_SOCKADDR:
541 		in_setsockaddr(inp, addr);
542 		break;
543 
544 	case PRU_PEERADDR:
545 		in_setpeeraddr(inp, addr);
546 		break;
547 
548 	case PRU_SENSE:
549 		/*
550 		 * stat: don't bother with a blocksize.
551 		 */
552 		return (0);
553 
554 	case PRU_SENDOOB:
555 	case PRU_FASTTIMO:
556 	case PRU_SLOWTIMO:
557 	case PRU_PROTORCV:
558 	case PRU_PROTOSEND:
559 		error =  EOPNOTSUPP;
560 		break;
561 
562 	case PRU_RCVD:
563 	case PRU_RCVOOB:
564 		return (EOPNOTSUPP);	/* do not free mbuf's */
565 
566 	default:
567 		panic("udp_usrreq");
568 	}
569 
570 release:
571 	if (control) {
572 		printf("udp control data unexpectedly retained\n");
573 		m_freem(control);
574 	}
575 	if (m)
576 		m_freem(m);
577 	return (error);
578 }
579 
580 static void
581 udp_detach(inp)
582 	struct inpcb *inp;
583 {
584 	int s = splnet();
585 
586 	if (inp == udp_last_inpcb)
587 		udp_last_inpcb = &udb;
588 	in_pcbdetach(inp);
589 	splx(s);
590 }
591 
592 /*
593  * Sysctl for udp variables.
594  */
595 udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
596 	int *name;
597 	u_int namelen;
598 	void *oldp;
599 	size_t *oldlenp;
600 	void *newp;
601 	size_t newlen;
602 {
603 	/* All sysctl names at this level are terminal. */
604 	if (namelen != 1)
605 		return (ENOTDIR);
606 
607 	switch (name[0]) {
608 	case UDPCTL_CHECKSUM:
609 		return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum));
610 	default:
611 		return (ENOPROTOOPT);
612 	}
613 	/* NOTREACHED */
614 }
615