xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 23199)
1*23199Smckusick /*
2*23199Smckusick  * Copyright (c) 1982 Regents of the University of California.
3*23199Smckusick  * All rights reserved.  The Berkeley software License Agreement
4*23199Smckusick  * specifies the terms and conditions for redistribution.
5*23199Smckusick  *
6*23199Smckusick  *	@(#)udp_usrreq.c	6.14 (Berkeley) 06/08/85
7*23199Smckusick  */
84784Swnj 
917065Sbloom #include "param.h"
1017065Sbloom #include "dir.h"
1117065Sbloom #include "user.h"
1217065Sbloom #include "mbuf.h"
1317065Sbloom #include "protosw.h"
1417065Sbloom #include "socket.h"
1517065Sbloom #include "socketvar.h"
1617065Sbloom #include "errno.h"
1717065Sbloom #include "stat.h"
1810897Ssam 
196584Ssam #include "../net/if.h"
206354Ssam #include "../net/route.h"
2110897Ssam 
2217065Sbloom #include "in.h"
2317065Sbloom #include "in_pcb.h"
2417065Sbloom #include "in_systm.h"
2517065Sbloom #include "ip.h"
2617065Sbloom #include "ip_var.h"
2717065Sbloom #include "ip_icmp.h"
2817065Sbloom #include "udp.h"
2917065Sbloom #include "udp_var.h"
304784Swnj 
314926Swnj /*
324926Swnj  * UDP protocol implementation.
334926Swnj  * Per RFC 768, August, 1980.
344926Swnj  */
354805Swnj udp_init()
364805Swnj {
374805Swnj 
384901Swnj 	udb.inp_next = udb.inp_prev = &udb;
394805Swnj }
404805Swnj 
4119519Skarels int	udpcksum = 1;
424926Swnj struct	sockaddr_in udp_in = { AF_INET };
434901Swnj 
444926Swnj udp_input(m0)
454926Swnj 	struct mbuf *m0;
464784Swnj {
474901Swnj 	register struct udpiphdr *ui;
484887Swnj 	register struct inpcb *inp;
494926Swnj 	register struct mbuf *m;
507844Sroot 	int len;
514784Swnj 
524926Swnj 	/*
535246Sroot 	 * Get IP and UDP header together in first mbuf.
544926Swnj 	 */
554926Swnj 	m = m0;
565308Sroot 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
575308Sroot 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
584926Swnj 		udpstat.udps_hdrops++;
595308Sroot 		return;
604926Swnj 	}
615050Swnj 	ui = mtod(m, struct udpiphdr *);
625246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
635220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
644926Swnj 
654926Swnj 	/*
665246Sroot 	 * Make mbuf data length reflect UDP length.
675246Sroot 	 * If not enough data to reflect UDP length, drop.
684926Swnj 	 */
697844Sroot 	len = ntohs((u_short)ui->ui_ulen);
704926Swnj 	if (((struct ip *)ui)->ip_len != len) {
714926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
724926Swnj 			udpstat.udps_badlen++;
734926Swnj 			goto bad;
744926Swnj 		}
7515539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
764926Swnj 		/* (struct ip *)ui->ip_len = len; */
774926Swnj 	}
784926Swnj 
794926Swnj 	/*
805246Sroot 	 * Checksum extended UDP header and data.
814926Swnj 	 */
8215539Skarels 	if (udpcksum && ui->ui_sum) {
834926Swnj 		ui->ui_next = ui->ui_prev = 0;
844926Swnj 		ui->ui_x1 = 0;
8521112Skarels 		ui->ui_len = ui->ui_ulen;
867844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
874926Swnj 			udpstat.udps_badsum++;
884901Swnj 			m_freem(m);
894901Swnj 			return;
904901Swnj 		}
914901Swnj 	}
924926Swnj 
934926Swnj 	/*
947844Sroot 	 * Locate pcb for datagram.
954926Swnj 	 */
964926Swnj 	inp = in_pcblookup(&udb,
976029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
986029Sroot 		INPLOOKUP_WILDCARD);
996584Ssam 	if (inp == 0) {
10010144Ssam 		/* don't send ICMP response for broadcast packet */
10121112Skarels 		if (in_broadcast(ui->ui_dst))
1026584Ssam 			goto bad;
1036584Ssam 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
1046584Ssam 		return;
1056584Ssam 	}
1066584Ssam 
1074926Swnj 	/*
1084926Swnj 	 * Construct sockaddr format source address.
1094926Swnj 	 * Stuff source address and datagram in user buffer.
1104926Swnj 	 */
1114926Swnj 	udp_in.sin_port = ui->ui_sport;
1124926Swnj 	udp_in.sin_addr = ui->ui_src;
1135050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1145050Swnj 	m->m_off += sizeof (struct udpiphdr);
11512767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
11612767Ssam 	    m, (struct mbuf *)0) == 0)
1174926Swnj 		goto bad;
1185050Swnj 	sorwakeup(inp->inp_socket);
1194887Swnj 	return;
1204926Swnj bad:
1214887Swnj 	m_freem(m);
1224784Swnj }
1234784Swnj 
1246584Ssam udp_abort(inp)
1256584Ssam 	struct inpcb *inp;
1266584Ssam {
1276584Ssam 	struct socket *so = inp->inp_socket;
1286584Ssam 
1296584Ssam 	in_pcbdisconnect(inp);
1306584Ssam 	soisdisconnected(so);
1316584Ssam }
1326584Ssam 
1336591Ssam udp_ctlinput(cmd, arg)
1346591Ssam 	int cmd;
1356591Ssam 	caddr_t arg;
1366591Ssam {
13721121Skarels 	struct in_addr *in;
1386591Ssam 	extern u_char inetctlerrmap[];
13921121Skarels 	int in_rtchange();
1406591Ssam 
1416591Ssam 	if (cmd < 0 || cmd > PRC_NCMDS)
1426591Ssam 		return;
1436591Ssam 	switch (cmd) {
1446591Ssam 
1456591Ssam 	case PRC_ROUTEDEAD:
1466591Ssam 		break;
1476591Ssam 
14821121Skarels 	case PRC_REDIRECT_NET:
14921121Skarels 	case PRC_REDIRECT_HOST:
15021121Skarels 		in = &((struct icmp *)arg)->icmp_ip.ip_dst;
15121121Skarels 		in_pcbnotify(&udb, in, 0, in_rtchange);
1526591Ssam 		break;
1536591Ssam 
1546591Ssam 	case PRC_IFDOWN:
15521121Skarels 		in = &((struct sockaddr_in *)arg)->sin_addr;
15621121Skarels 		goto notify;
15721121Skarels 
1586591Ssam 	case PRC_HOSTDEAD:
1596591Ssam 	case PRC_HOSTUNREACH:
16021121Skarels 		in = (struct in_addr *)arg;
16121121Skarels 		goto notify;
1626591Ssam 
1636591Ssam 	default:
16421121Skarels 		if (inetctlerrmap[cmd] == 0)
16521121Skarels 			return;		/* XXX */
16621121Skarels 		in = &((struct icmp *)arg)->icmp_ip.ip_dst;
16721121Skarels notify:
16821121Skarels 		in_pcbnotify(&udb, in, (int)inetctlerrmap[cmd], udp_abort);
1696591Ssam 	}
1706591Ssam }
1716591Ssam 
1724955Swnj udp_output(inp, m0)
1734926Swnj 	struct inpcb *inp;
1744926Swnj 	struct mbuf *m0;
1754784Swnj {
1764926Swnj 	register struct mbuf *m;
1774926Swnj 	register struct udpiphdr *ui;
17817165Skarels 	register struct socket *so;
17917165Skarels 	register int len = 0;
18016601Ssam 	register struct route *ro;
1814784Swnj 
1824926Swnj 	/*
1834926Swnj 	 * Calculate data length and get a mbuf
1845246Sroot 	 * for UDP and IP headers.
1854926Swnj 	 */
1864926Swnj 	for (m = m0; m; m = m->m_next)
1874926Swnj 		len += m->m_len;
18821112Skarels 	MGET(m, M_DONTWAIT, MT_HEADER);
1896507Ssam 	if (m == 0) {
1906507Ssam 		m_freem(m0);
1916507Ssam 		return (ENOBUFS);
1926507Ssam 	}
1934784Swnj 
1944926Swnj 	/*
1955246Sroot 	 * Fill in mbuf with extended UDP header
1964926Swnj 	 * and addresses and length put into network format.
1974926Swnj 	 */
1984926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
1994926Swnj 	m->m_len = sizeof (struct udpiphdr);
2004926Swnj 	m->m_next = m0;
2014926Swnj 	ui = mtod(m, struct udpiphdr *);
2024926Swnj 	ui->ui_next = ui->ui_prev = 0;
2034926Swnj 	ui->ui_x1 = 0;
2044926Swnj 	ui->ui_pr = IPPROTO_UDP;
20515226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
2065050Swnj 	ui->ui_src = inp->inp_laddr;
2075050Swnj 	ui->ui_dst = inp->inp_faddr;
2085050Swnj 	ui->ui_sport = inp->inp_lport;
2095050Swnj 	ui->ui_dport = inp->inp_fport;
21015226Ssam 	ui->ui_ulen = ui->ui_len;
2114784Swnj 
2124926Swnj 	/*
2134926Swnj 	 * Stuff checksum and output datagram.
2144926Swnj 	 */
2154926Swnj 	ui->ui_sum = 0;
21619519Skarels 	if (udpcksum) {
21719519Skarels 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
21815539Skarels 		ui->ui_sum = -1;
21921112Skarels 	}
2205050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
2215050Swnj 	((struct ip *)ui)->ip_ttl = MAXTTL;
22217165Skarels 	so = inp->inp_socket;
22317165Skarels 	if (so->so_options & SO_DONTROUTE)
22417165Skarels 		return (ip_output(m, (struct mbuf *)0, (struct route *)0,
22517165Skarels 		    (so->so_options & SO_BROADCAST) | IP_ROUTETOIF));
22617165Skarels 	/*
22717165Skarels 	 * Use cached route for previous datagram if
22817165Skarels 	 * this is also to the same destination.
22917165Skarels 	 *
23017165Skarels 	 * NB: We don't handle broadcasts because that
23117165Skarels 	 *     would require 3 subroutine calls.
23217165Skarels 	 */
23317165Skarels 	ro = &inp->inp_route;
23416601Ssam #define	satosin(sa)	((struct sockaddr_in *)(sa))
23517165Skarels 	if (ro->ro_rt &&
23617165Skarels 	    satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) {
23717165Skarels 		RTFREE(ro->ro_rt);
23817165Skarels 		ro->ro_rt = (struct rtentry *)0;
23917165Skarels 	}
24017165Skarels 	return (ip_output(m, (struct mbuf *)0, ro,
24117165Skarels 	    so->so_options & SO_BROADCAST));
2424784Swnj }
2434784Swnj 
24418368Skarels int	udp_sendspace = 2048;		/* really max datagram size */
24518368Skarels int	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
24618368Skarels 
2478602Sroot /*ARGSUSED*/
24812767Ssam udp_usrreq(so, req, m, nam, rights)
2494887Swnj 	struct socket *so;
2504784Swnj 	int req;
25112767Ssam 	struct mbuf *m, *nam, *rights;
2524784Swnj {
2534887Swnj 	struct inpcb *inp = sotoinpcb(so);
2546507Ssam 	int error = 0;
2554784Swnj 
25618368Skarels 	if (req == PRU_CONTROL)
25718368Skarels 		return (in_control(so, (int)m, (caddr_t)nam,
25818368Skarels 			(struct ifnet *)rights));
25912767Ssam 	if (rights && rights->m_len) {
26012767Ssam 		error = EINVAL;
26112767Ssam 		goto release;
26212767Ssam 	}
26311080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
26411080Ssam 		error = EINVAL;
26511080Ssam 		goto release;
26611080Ssam 	}
2674784Swnj 	switch (req) {
2684784Swnj 
2694784Swnj 	case PRU_ATTACH:
27011080Ssam 		if (inp != NULL) {
27111080Ssam 			error = EINVAL;
27211080Ssam 			break;
27311080Ssam 		}
2748273Sroot 		error = in_pcballoc(so, &udb);
2758273Sroot 		if (error)
2768273Sroot 			break;
27718368Skarels 		error = soreserve(so, udp_sendspace, udp_recvspace);
2788273Sroot 		if (error)
2798273Sroot 			break;
2804887Swnj 		break;
2814784Swnj 
2824784Swnj 	case PRU_DETACH:
28311080Ssam 		if (inp == NULL) {
28411080Ssam 			error = ENOTCONN;
28511080Ssam 			break;
28611080Ssam 		}
2875166Swnj 		in_pcbdetach(inp);
2884887Swnj 		break;
2894784Swnj 
2908273Sroot 	case PRU_BIND:
2918273Sroot 		error = in_pcbbind(inp, nam);
2928273Sroot 		break;
2938273Sroot 
2948273Sroot 	case PRU_LISTEN:
2958273Sroot 		error = EOPNOTSUPP;
2968273Sroot 		break;
2978273Sroot 
2984784Swnj 	case PRU_CONNECT:
29911080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
30011080Ssam 			error = EISCONN;
30111080Ssam 			break;
30211080Ssam 		}
3038273Sroot 		error = in_pcbconnect(inp, nam);
3046507Ssam 		if (error == 0)
3056507Ssam 			soisconnected(so);
3064887Swnj 		break;
3074784Swnj 
30813118Ssam 	case PRU_CONNECT2:
30913118Ssam 		error = EOPNOTSUPP;
31013118Ssam 		break;
31113118Ssam 
3124926Swnj 	case PRU_ACCEPT:
31311080Ssam 		error = EOPNOTSUPP;
31411080Ssam 		break;
3154926Swnj 
3164784Swnj 	case PRU_DISCONNECT:
31711080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
31811080Ssam 			error = ENOTCONN;
31911080Ssam 			break;
32011080Ssam 		}
3215166Swnj 		in_pcbdisconnect(inp);
3224887Swnj 		soisdisconnected(so);
3234784Swnj 		break;
3244784Swnj 
3254912Swnj 	case PRU_SHUTDOWN:
3264912Swnj 		socantsendmore(so);
3274912Swnj 		break;
3284912Swnj 
3295996Swnj 	case PRU_SEND: {
3305996Swnj 		struct in_addr laddr;
33116799Skarels 		int s;
3325996Swnj 
3338273Sroot 		if (nam) {
3345996Swnj 			laddr = inp->inp_laddr;
33511080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
33611080Ssam 				error = EISCONN;
33711080Ssam 				break;
33811080Ssam 			}
33916799Skarels 			/*
34016799Skarels 			 * Must block input while temporarily connected.
34116799Skarels 			 */
34216799Skarels 			s = splnet();
3438273Sroot 			error = in_pcbconnect(inp, nam);
34416799Skarels 			if (error) {
34516799Skarels 				splx(s);
3466507Ssam 				break;
34716799Skarels 			}
3484955Swnj 		} else {
34911080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
35011080Ssam 				error = ENOTCONN;
35111080Ssam 				break;
35211080Ssam 			}
3534955Swnj 		}
3546507Ssam 		error = udp_output(inp, m);
35511080Ssam 		m = NULL;
3568273Sroot 		if (nam) {
3575166Swnj 			in_pcbdisconnect(inp);
35818368Skarels 			inp->inp_laddr = laddr;
35916799Skarels 			splx(s);
3605996Swnj 		}
3615996Swnj 		}
3624784Swnj 		break;
3634784Swnj 
3644784Swnj 	case PRU_ABORT:
3655166Swnj 		in_pcbdetach(inp);
3664887Swnj 		sofree(so);
3674887Swnj 		soisdisconnected(so);
3684784Swnj 		break;
3694784Swnj 
3706511Ssam 	case PRU_SOCKADDR:
3718273Sroot 		in_setsockaddr(inp, nam);
3726511Ssam 		break;
3736511Ssam 
37414124Ssam 	case PRU_PEERADDR:
37514124Ssam 		in_setpeeraddr(inp, nam);
37614124Ssam 		break;
37714124Ssam 
37816988Skarels 	case PRU_SENSE:
37916988Skarels 		/*
38016988Skarels 		 * stat: don't bother with a blocksize.
38116988Skarels 		 */
38216988Skarels 		return (0);
38316988Skarels 
38412205Ssam 	case PRU_SENDOOB:
38512205Ssam 	case PRU_FASTTIMO:
38612205Ssam 	case PRU_SLOWTIMO:
38712205Ssam 	case PRU_PROTORCV:
38812205Ssam 	case PRU_PROTOSEND:
38912205Ssam 		error =  EOPNOTSUPP;
39012205Ssam 		break;
39113118Ssam 
39216799Skarels 	case PRU_RCVD:
39316799Skarels 	case PRU_RCVOOB:
39416799Skarels 		return (EOPNOTSUPP);	/* do not free mbuf's */
39516799Skarels 
39613118Ssam 	default:
39713118Ssam 		panic("udp_usrreq");
3984805Swnj 	}
39911080Ssam release:
40011080Ssam 	if (m != NULL)
40111080Ssam 		m_freem(m);
4026507Ssam 	return (error);
4034784Swnj }
404