xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 35288)
123199Smckusick /*
229158Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
332789Sbostic  * All rights reserved.
423199Smckusick  *
532789Sbostic  * Redistribution and use in source and binary forms are permitted
634855Sbostic  * provided that the above copyright notice and this paragraph are
734855Sbostic  * duplicated in all such forms and that any documentation,
834855Sbostic  * advertising materials, and other materials related to such
934855Sbostic  * distribution and use acknowledge that the software was developed
1034855Sbostic  * by the University of California, Berkeley.  The name of the
1134855Sbostic  * University may not be used to endorse or promote products derived
1234855Sbostic  * from this software without specific prior written permission.
1334855Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434855Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534855Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1632789Sbostic  *
17*35288Skarels  *	@(#)udp_usrreq.c	7.8 (Berkeley) 07/28/88
1823199Smckusick  */
194784Swnj 
2017065Sbloom #include "param.h"
2117065Sbloom #include "dir.h"
2217065Sbloom #include "user.h"
2317065Sbloom #include "mbuf.h"
2417065Sbloom #include "protosw.h"
2517065Sbloom #include "socket.h"
2617065Sbloom #include "socketvar.h"
2717065Sbloom #include "errno.h"
2817065Sbloom #include "stat.h"
2910897Ssam 
306584Ssam #include "../net/if.h"
316354Ssam #include "../net/route.h"
3210897Ssam 
3317065Sbloom #include "in.h"
3417065Sbloom #include "in_pcb.h"
3517065Sbloom #include "in_systm.h"
3617065Sbloom #include "ip.h"
3717065Sbloom #include "ip_var.h"
3817065Sbloom #include "ip_icmp.h"
3917065Sbloom #include "udp.h"
4017065Sbloom #include "udp_var.h"
414784Swnj 
424926Swnj /*
434926Swnj  * UDP protocol implementation.
444926Swnj  * Per RFC 768, August, 1980.
454926Swnj  */
464805Swnj udp_init()
474805Swnj {
484805Swnj 
494901Swnj 	udb.inp_next = udb.inp_prev = &udb;
504805Swnj }
514805Swnj 
5226118Skarels #ifndef	COMPAT_42
5319519Skarels int	udpcksum = 1;
5426118Skarels #else
5526118Skarels int	udpcksum = 0;		/* XXX */
5626118Skarels #endif
5731398Skarels int	udp_ttl = UDP_TTL;
5826118Skarels 
594926Swnj struct	sockaddr_in udp_in = { AF_INET };
604901Swnj 
6126032Skarels udp_input(m0, ifp)
624926Swnj 	struct mbuf *m0;
6326032Skarels 	struct ifnet *ifp;
644784Swnj {
654901Swnj 	register struct udpiphdr *ui;
664887Swnj 	register struct inpcb *inp;
674926Swnj 	register struct mbuf *m;
687844Sroot 	int len;
6924823Skarels 	struct ip ip;
704784Swnj 
714926Swnj 	/*
725246Sroot 	 * Get IP and UDP header together in first mbuf.
73*35288Skarels 	 * Note: IP leaves IP header in first mbuf.
744926Swnj 	 */
754926Swnj 	m = m0;
765050Swnj 	ui = mtod(m, struct udpiphdr *);
775246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
785220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
79*35288Skarels 	if (m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) {
80*35288Skarels 		if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
81*35288Skarels 			udpstat.udps_hdrops++;
82*35288Skarels 			return;
83*35288Skarels 		}
84*35288Skarels 		ui = mtod(m, struct udpiphdr *);
85*35288Skarels 	}
864926Swnj 
874926Swnj 	/*
885246Sroot 	 * Make mbuf data length reflect UDP length.
895246Sroot 	 * If not enough data to reflect UDP length, drop.
904926Swnj 	 */
917844Sroot 	len = ntohs((u_short)ui->ui_ulen);
924926Swnj 	if (((struct ip *)ui)->ip_len != len) {
934926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
944926Swnj 			udpstat.udps_badlen++;
954926Swnj 			goto bad;
964926Swnj 		}
9715539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
9824823Skarels 		/* ((struct ip *)ui)->ip_len = len; */
994926Swnj 	}
10024823Skarels 	/*
10124823Skarels 	 * Save a copy of the IP header in case we want restore it for ICMP.
10224823Skarels 	 */
10324823Skarels 	ip = *(struct ip*)ui;
1044926Swnj 
1054926Swnj 	/*
1065246Sroot 	 * Checksum extended UDP header and data.
1074926Swnj 	 */
10815539Skarels 	if (udpcksum && ui->ui_sum) {
1094926Swnj 		ui->ui_next = ui->ui_prev = 0;
1104926Swnj 		ui->ui_x1 = 0;
11121112Skarels 		ui->ui_len = ui->ui_ulen;
1127844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
1134926Swnj 			udpstat.udps_badsum++;
1144901Swnj 			m_freem(m);
1154901Swnj 			return;
1164901Swnj 		}
1174901Swnj 	}
1184926Swnj 
1194926Swnj 	/*
1207844Sroot 	 * Locate pcb for datagram.
1214926Swnj 	 */
1224926Swnj 	inp = in_pcblookup(&udb,
1236029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
1246029Sroot 		INPLOOKUP_WILDCARD);
1256584Ssam 	if (inp == 0) {
12610144Ssam 		/* don't send ICMP response for broadcast packet */
12721112Skarels 		if (in_broadcast(ui->ui_dst))
1286584Ssam 			goto bad;
12924823Skarels 		*(struct ip *)ui = ip;
13026032Skarels 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
13126032Skarels 		    ifp);
1326584Ssam 		return;
1336584Ssam 	}
1346584Ssam 
1354926Swnj 	/*
1364926Swnj 	 * Construct sockaddr format source address.
1374926Swnj 	 * Stuff source address and datagram in user buffer.
1384926Swnj 	 */
1394926Swnj 	udp_in.sin_port = ui->ui_sport;
1404926Swnj 	udp_in.sin_addr = ui->ui_src;
1415050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1425050Swnj 	m->m_off += sizeof (struct udpiphdr);
14312767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
14412767Ssam 	    m, (struct mbuf *)0) == 0)
1454926Swnj 		goto bad;
1465050Swnj 	sorwakeup(inp->inp_socket);
1474887Swnj 	return;
1484926Swnj bad:
1494887Swnj 	m_freem(m);
1504784Swnj }
1514784Swnj 
15226426Skarels /*
15326426Skarels  * Notify a udp user of an asynchronous error;
15426426Skarels  * just wake up so that he can collect error status.
15526426Skarels  */
15626426Skarels udp_notify(inp)
15726426Skarels 	register struct inpcb *inp;
15826426Skarels {
15926426Skarels 
16026426Skarels 	sorwakeup(inp->inp_socket);
16126426Skarels 	sowwakeup(inp->inp_socket);
16226426Skarels }
16326426Skarels 
16424823Skarels udp_ctlinput(cmd, sa)
1656591Ssam 	int cmd;
16624823Skarels 	struct sockaddr *sa;
1676591Ssam {
1686591Ssam 	extern u_char inetctlerrmap[];
16924823Skarels 	struct sockaddr_in *sin;
17021121Skarels 	int in_rtchange();
1716591Ssam 
17224823Skarels 	if ((unsigned)cmd > PRC_NCMDS)
1736591Ssam 		return;
17424823Skarels 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
17524823Skarels 		return;
17624823Skarels 	sin = (struct sockaddr_in *)sa;
17724823Skarels 	if (sin->sin_addr.s_addr == INADDR_ANY)
17824823Skarels 		return;
17924823Skarels 
1806591Ssam 	switch (cmd) {
1816591Ssam 
18224823Skarels 	case PRC_QUENCH:
1836591Ssam 		break;
1846591Ssam 
18524823Skarels 	case PRC_ROUTEDEAD:
18621121Skarels 	case PRC_REDIRECT_NET:
18721121Skarels 	case PRC_REDIRECT_HOST:
18824823Skarels 	case PRC_REDIRECT_TOSNET:
18924823Skarels 	case PRC_REDIRECT_TOSHOST:
19024823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
1916591Ssam 		break;
1926591Ssam 
1936591Ssam 	default:
19421121Skarels 		if (inetctlerrmap[cmd] == 0)
19521121Skarels 			return;		/* XXX */
19624823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
19726426Skarels 			udp_notify);
1986591Ssam 	}
1996591Ssam }
2006591Ssam 
2014955Swnj udp_output(inp, m0)
20226060Skarels 	register struct inpcb *inp;
2034926Swnj 	struct mbuf *m0;
2044784Swnj {
2054926Swnj 	register struct mbuf *m;
2064926Swnj 	register struct udpiphdr *ui;
20717165Skarels 	register int len = 0;
2084784Swnj 
2094926Swnj 	/*
2104926Swnj 	 * Calculate data length and get a mbuf
2115246Sroot 	 * for UDP and IP headers.
2124926Swnj 	 */
2134926Swnj 	for (m = m0; m; m = m->m_next)
2144926Swnj 		len += m->m_len;
21521112Skarels 	MGET(m, M_DONTWAIT, MT_HEADER);
2166507Ssam 	if (m == 0) {
2176507Ssam 		m_freem(m0);
2186507Ssam 		return (ENOBUFS);
2196507Ssam 	}
2204784Swnj 
2214926Swnj 	/*
2225246Sroot 	 * Fill in mbuf with extended UDP header
2234926Swnj 	 * and addresses and length put into network format.
2244926Swnj 	 */
2254926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
2264926Swnj 	m->m_len = sizeof (struct udpiphdr);
2274926Swnj 	m->m_next = m0;
2284926Swnj 	ui = mtod(m, struct udpiphdr *);
2294926Swnj 	ui->ui_next = ui->ui_prev = 0;
2304926Swnj 	ui->ui_x1 = 0;
2314926Swnj 	ui->ui_pr = IPPROTO_UDP;
23215226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
2335050Swnj 	ui->ui_src = inp->inp_laddr;
2345050Swnj 	ui->ui_dst = inp->inp_faddr;
2355050Swnj 	ui->ui_sport = inp->inp_lport;
2365050Swnj 	ui->ui_dport = inp->inp_fport;
23715226Ssam 	ui->ui_ulen = ui->ui_len;
2384784Swnj 
2394926Swnj 	/*
2404926Swnj 	 * Stuff checksum and output datagram.
2414926Swnj 	 */
2424926Swnj 	ui->ui_sum = 0;
24319519Skarels 	if (udpcksum) {
24419519Skarels 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
24533715Skarels 		ui->ui_sum = 0xffff;
24621112Skarels 	}
2475050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
24831398Skarels 	((struct ip *)ui)->ip_ttl = udp_ttl;
24926060Skarels 	return (ip_output(m, inp->inp_options, &inp->inp_route,
25026060Skarels 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
2514784Swnj }
2524784Swnj 
25334500Skarels u_long	udp_sendspace = 2048;		/* really max datagram size */
25434500Skarels u_long	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
25518368Skarels 
2568602Sroot /*ARGSUSED*/
25712767Ssam udp_usrreq(so, req, m, nam, rights)
2584887Swnj 	struct socket *so;
2594784Swnj 	int req;
26012767Ssam 	struct mbuf *m, *nam, *rights;
2614784Swnj {
2624887Swnj 	struct inpcb *inp = sotoinpcb(so);
2636507Ssam 	int error = 0;
2644784Swnj 
26518368Skarels 	if (req == PRU_CONTROL)
26618368Skarels 		return (in_control(so, (int)m, (caddr_t)nam,
26718368Skarels 			(struct ifnet *)rights));
26812767Ssam 	if (rights && rights->m_len) {
26912767Ssam 		error = EINVAL;
27012767Ssam 		goto release;
27112767Ssam 	}
27211080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
27311080Ssam 		error = EINVAL;
27411080Ssam 		goto release;
27511080Ssam 	}
2764784Swnj 	switch (req) {
2774784Swnj 
2784784Swnj 	case PRU_ATTACH:
27911080Ssam 		if (inp != NULL) {
28011080Ssam 			error = EINVAL;
28111080Ssam 			break;
28211080Ssam 		}
2838273Sroot 		error = in_pcballoc(so, &udb);
2848273Sroot 		if (error)
2858273Sroot 			break;
28618368Skarels 		error = soreserve(so, udp_sendspace, udp_recvspace);
2878273Sroot 		if (error)
2888273Sroot 			break;
2894887Swnj 		break;
2904784Swnj 
2914784Swnj 	case PRU_DETACH:
2925166Swnj 		in_pcbdetach(inp);
2934887Swnj 		break;
2944784Swnj 
2958273Sroot 	case PRU_BIND:
2968273Sroot 		error = in_pcbbind(inp, nam);
2978273Sroot 		break;
2988273Sroot 
2998273Sroot 	case PRU_LISTEN:
3008273Sroot 		error = EOPNOTSUPP;
3018273Sroot 		break;
3028273Sroot 
3034784Swnj 	case PRU_CONNECT:
30411080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
30511080Ssam 			error = EISCONN;
30611080Ssam 			break;
30711080Ssam 		}
3088273Sroot 		error = in_pcbconnect(inp, nam);
3096507Ssam 		if (error == 0)
3106507Ssam 			soisconnected(so);
3114887Swnj 		break;
3124784Swnj 
31313118Ssam 	case PRU_CONNECT2:
31413118Ssam 		error = EOPNOTSUPP;
31513118Ssam 		break;
31613118Ssam 
3174926Swnj 	case PRU_ACCEPT:
31811080Ssam 		error = EOPNOTSUPP;
31911080Ssam 		break;
3204926Swnj 
3214784Swnj 	case PRU_DISCONNECT:
32211080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
32311080Ssam 			error = ENOTCONN;
32411080Ssam 			break;
32511080Ssam 		}
3265166Swnj 		in_pcbdisconnect(inp);
32726404Skarels 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
3284784Swnj 		break;
3294784Swnj 
3304912Swnj 	case PRU_SHUTDOWN:
3314912Swnj 		socantsendmore(so);
3324912Swnj 		break;
3334912Swnj 
3345996Swnj 	case PRU_SEND: {
3355996Swnj 		struct in_addr laddr;
33616799Skarels 		int s;
3375996Swnj 
3388273Sroot 		if (nam) {
3395996Swnj 			laddr = inp->inp_laddr;
34011080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
34111080Ssam 				error = EISCONN;
34211080Ssam 				break;
34311080Ssam 			}
34416799Skarels 			/*
34516799Skarels 			 * Must block input while temporarily connected.
34616799Skarels 			 */
34716799Skarels 			s = splnet();
3488273Sroot 			error = in_pcbconnect(inp, nam);
34916799Skarels 			if (error) {
35016799Skarels 				splx(s);
3516507Ssam 				break;
35216799Skarels 			}
3534955Swnj 		} else {
35411080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
35511080Ssam 				error = ENOTCONN;
35611080Ssam 				break;
35711080Ssam 			}
3584955Swnj 		}
3596507Ssam 		error = udp_output(inp, m);
36011080Ssam 		m = NULL;
3618273Sroot 		if (nam) {
3625166Swnj 			in_pcbdisconnect(inp);
36318368Skarels 			inp->inp_laddr = laddr;
36416799Skarels 			splx(s);
3655996Swnj 		}
3665996Swnj 		}
3674784Swnj 		break;
3684784Swnj 
3694784Swnj 	case PRU_ABORT:
37031750Skarels 		soisdisconnected(so);
3715166Swnj 		in_pcbdetach(inp);
3724784Swnj 		break;
3734784Swnj 
3746511Ssam 	case PRU_SOCKADDR:
3758273Sroot 		in_setsockaddr(inp, nam);
3766511Ssam 		break;
3776511Ssam 
37814124Ssam 	case PRU_PEERADDR:
37914124Ssam 		in_setpeeraddr(inp, nam);
38014124Ssam 		break;
38114124Ssam 
38216988Skarels 	case PRU_SENSE:
38316988Skarels 		/*
38416988Skarels 		 * stat: don't bother with a blocksize.
38516988Skarels 		 */
38616988Skarels 		return (0);
38716988Skarels 
38812205Ssam 	case PRU_SENDOOB:
38912205Ssam 	case PRU_FASTTIMO:
39012205Ssam 	case PRU_SLOWTIMO:
39112205Ssam 	case PRU_PROTORCV:
39212205Ssam 	case PRU_PROTOSEND:
39312205Ssam 		error =  EOPNOTSUPP;
39412205Ssam 		break;
39513118Ssam 
39616799Skarels 	case PRU_RCVD:
39716799Skarels 	case PRU_RCVOOB:
39816799Skarels 		return (EOPNOTSUPP);	/* do not free mbuf's */
39916799Skarels 
40013118Ssam 	default:
40113118Ssam 		panic("udp_usrreq");
4024805Swnj 	}
40311080Ssam release:
40411080Ssam 	if (m != NULL)
40511080Ssam 		m_freem(m);
4066507Ssam 	return (error);
4074784Swnj }
408