xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 21112)
1*21112Skarels /*	udp_usrreq.c	6.12	85/05/27	*/
24784Swnj 
317065Sbloom #include "param.h"
417065Sbloom #include "dir.h"
517065Sbloom #include "user.h"
617065Sbloom #include "mbuf.h"
717065Sbloom #include "protosw.h"
817065Sbloom #include "socket.h"
917065Sbloom #include "socketvar.h"
1017065Sbloom #include "errno.h"
1117065Sbloom #include "stat.h"
1210897Ssam 
136584Ssam #include "../net/if.h"
146354Ssam #include "../net/route.h"
1510897Ssam 
1617065Sbloom #include "in.h"
1717065Sbloom #include "in_pcb.h"
1817065Sbloom #include "in_systm.h"
1917065Sbloom #include "ip.h"
2017065Sbloom #include "ip_var.h"
2117065Sbloom #include "ip_icmp.h"
2217065Sbloom #include "udp.h"
2317065Sbloom #include "udp_var.h"
244784Swnj 
254926Swnj /*
264926Swnj  * UDP protocol implementation.
274926Swnj  * Per RFC 768, August, 1980.
284926Swnj  */
294805Swnj udp_init()
304805Swnj {
314805Swnj 
324901Swnj 	udb.inp_next = udb.inp_prev = &udb;
334805Swnj }
344805Swnj 
3519519Skarels int	udpcksum = 1;
364926Swnj struct	sockaddr_in udp_in = { AF_INET };
374901Swnj 
384926Swnj udp_input(m0)
394926Swnj 	struct mbuf *m0;
404784Swnj {
414901Swnj 	register struct udpiphdr *ui;
424887Swnj 	register struct inpcb *inp;
434926Swnj 	register struct mbuf *m;
447844Sroot 	int len;
454784Swnj 
464926Swnj 	/*
475246Sroot 	 * Get IP and UDP header together in first mbuf.
484926Swnj 	 */
494926Swnj 	m = m0;
505308Sroot 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
515308Sroot 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
524926Swnj 		udpstat.udps_hdrops++;
535308Sroot 		return;
544926Swnj 	}
555050Swnj 	ui = mtod(m, struct udpiphdr *);
565246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
575220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
584926Swnj 
594926Swnj 	/*
605246Sroot 	 * Make mbuf data length reflect UDP length.
615246Sroot 	 * If not enough data to reflect UDP length, drop.
624926Swnj 	 */
637844Sroot 	len = ntohs((u_short)ui->ui_ulen);
644926Swnj 	if (((struct ip *)ui)->ip_len != len) {
654926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
664926Swnj 			udpstat.udps_badlen++;
674926Swnj 			goto bad;
684926Swnj 		}
6915539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
704926Swnj 		/* (struct ip *)ui->ip_len = len; */
714926Swnj 	}
724926Swnj 
734926Swnj 	/*
745246Sroot 	 * Checksum extended UDP header and data.
754926Swnj 	 */
7615539Skarels 	if (udpcksum && ui->ui_sum) {
774926Swnj 		ui->ui_next = ui->ui_prev = 0;
784926Swnj 		ui->ui_x1 = 0;
79*21112Skarels 		ui->ui_len = ui->ui_ulen;
807844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
814926Swnj 			udpstat.udps_badsum++;
824901Swnj 			m_freem(m);
834901Swnj 			return;
844901Swnj 		}
854901Swnj 	}
864926Swnj 
874926Swnj 	/*
887844Sroot 	 * Locate pcb for datagram.
894926Swnj 	 */
904926Swnj 	inp = in_pcblookup(&udb,
916029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
926029Sroot 		INPLOOKUP_WILDCARD);
936584Ssam 	if (inp == 0) {
9410144Ssam 		/* don't send ICMP response for broadcast packet */
95*21112Skarels 		if (in_broadcast(ui->ui_dst))
966584Ssam 			goto bad;
976584Ssam 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
986584Ssam 		return;
996584Ssam 	}
1006584Ssam 
1014926Swnj 	/*
1024926Swnj 	 * Construct sockaddr format source address.
1034926Swnj 	 * Stuff source address and datagram in user buffer.
1044926Swnj 	 */
1054926Swnj 	udp_in.sin_port = ui->ui_sport;
1064926Swnj 	udp_in.sin_addr = ui->ui_src;
1075050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1085050Swnj 	m->m_off += sizeof (struct udpiphdr);
10912767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
11012767Ssam 	    m, (struct mbuf *)0) == 0)
1114926Swnj 		goto bad;
1125050Swnj 	sorwakeup(inp->inp_socket);
1134887Swnj 	return;
1144926Swnj bad:
1154887Swnj 	m_freem(m);
1164784Swnj }
1174784Swnj 
1186584Ssam udp_abort(inp)
1196584Ssam 	struct inpcb *inp;
1206584Ssam {
1216584Ssam 	struct socket *so = inp->inp_socket;
1226584Ssam 
1236584Ssam 	in_pcbdisconnect(inp);
1246584Ssam 	soisdisconnected(so);
1256584Ssam }
1266584Ssam 
1276591Ssam udp_ctlinput(cmd, arg)
1286591Ssam 	int cmd;
1296591Ssam 	caddr_t arg;
1306591Ssam {
1316591Ssam 	struct in_addr *sin;
1326591Ssam 	extern u_char inetctlerrmap[];
1336591Ssam 
1346591Ssam 	if (cmd < 0 || cmd > PRC_NCMDS)
1356591Ssam 		return;
1366591Ssam 	switch (cmd) {
1376591Ssam 
1386591Ssam 	case PRC_ROUTEDEAD:
1396591Ssam 		break;
1406591Ssam 
1416591Ssam 	case PRC_QUENCH:
1426591Ssam 		break;
1436591Ssam 
1446591Ssam 	/* these are handled by ip */
1456591Ssam 	case PRC_IFDOWN:
1466591Ssam 	case PRC_HOSTDEAD:
1476591Ssam 	case PRC_HOSTUNREACH:
1486591Ssam 		break;
1496591Ssam 
1506591Ssam 	default:
1516591Ssam 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
1528641Sroot 		in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
1536591Ssam 	}
1546591Ssam }
1556591Ssam 
1564955Swnj udp_output(inp, m0)
1574926Swnj 	struct inpcb *inp;
1584926Swnj 	struct mbuf *m0;
1594784Swnj {
1604926Swnj 	register struct mbuf *m;
1614926Swnj 	register struct udpiphdr *ui;
16217165Skarels 	register struct socket *so;
16317165Skarels 	register int len = 0;
16416601Ssam 	register struct route *ro;
1654784Swnj 
1664926Swnj 	/*
1674926Swnj 	 * Calculate data length and get a mbuf
1685246Sroot 	 * for UDP and IP headers.
1694926Swnj 	 */
1704926Swnj 	for (m = m0; m; m = m->m_next)
1714926Swnj 		len += m->m_len;
172*21112Skarels 	MGET(m, M_DONTWAIT, MT_HEADER);
1736507Ssam 	if (m == 0) {
1746507Ssam 		m_freem(m0);
1756507Ssam 		return (ENOBUFS);
1766507Ssam 	}
1774784Swnj 
1784926Swnj 	/*
1795246Sroot 	 * Fill in mbuf with extended UDP header
1804926Swnj 	 * and addresses and length put into network format.
1814926Swnj 	 */
1824926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
1834926Swnj 	m->m_len = sizeof (struct udpiphdr);
1844926Swnj 	m->m_next = m0;
1854926Swnj 	ui = mtod(m, struct udpiphdr *);
1864926Swnj 	ui->ui_next = ui->ui_prev = 0;
1874926Swnj 	ui->ui_x1 = 0;
1884926Swnj 	ui->ui_pr = IPPROTO_UDP;
18915226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
1905050Swnj 	ui->ui_src = inp->inp_laddr;
1915050Swnj 	ui->ui_dst = inp->inp_faddr;
1925050Swnj 	ui->ui_sport = inp->inp_lport;
1935050Swnj 	ui->ui_dport = inp->inp_fport;
19415226Ssam 	ui->ui_ulen = ui->ui_len;
1954784Swnj 
1964926Swnj 	/*
1974926Swnj 	 * Stuff checksum and output datagram.
1984926Swnj 	 */
1994926Swnj 	ui->ui_sum = 0;
20019519Skarels 	if (udpcksum) {
20119519Skarels 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
20215539Skarels 		ui->ui_sum = -1;
203*21112Skarels 	}
2045050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
2055050Swnj 	((struct ip *)ui)->ip_ttl = MAXTTL;
20617165Skarels 	so = inp->inp_socket;
20717165Skarels 	if (so->so_options & SO_DONTROUTE)
20817165Skarels 		return (ip_output(m, (struct mbuf *)0, (struct route *)0,
20917165Skarels 		    (so->so_options & SO_BROADCAST) | IP_ROUTETOIF));
21017165Skarels 	/*
21117165Skarels 	 * Use cached route for previous datagram if
21217165Skarels 	 * this is also to the same destination.
21317165Skarels 	 *
21417165Skarels 	 * NB: We don't handle broadcasts because that
21517165Skarels 	 *     would require 3 subroutine calls.
21617165Skarels 	 */
21717165Skarels 	ro = &inp->inp_route;
21816601Ssam #define	satosin(sa)	((struct sockaddr_in *)(sa))
21917165Skarels 	if (ro->ro_rt &&
22017165Skarels 	    satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) {
22117165Skarels 		RTFREE(ro->ro_rt);
22217165Skarels 		ro->ro_rt = (struct rtentry *)0;
22317165Skarels 	}
22417165Skarels 	return (ip_output(m, (struct mbuf *)0, ro,
22517165Skarels 	    so->so_options & SO_BROADCAST));
2264784Swnj }
2274784Swnj 
22818368Skarels int	udp_sendspace = 2048;		/* really max datagram size */
22918368Skarels int	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
23018368Skarels 
2318602Sroot /*ARGSUSED*/
23212767Ssam udp_usrreq(so, req, m, nam, rights)
2334887Swnj 	struct socket *so;
2344784Swnj 	int req;
23512767Ssam 	struct mbuf *m, *nam, *rights;
2364784Swnj {
2374887Swnj 	struct inpcb *inp = sotoinpcb(so);
2386507Ssam 	int error = 0;
2394784Swnj 
24018368Skarels 	if (req == PRU_CONTROL)
24118368Skarels 		return (in_control(so, (int)m, (caddr_t)nam,
24218368Skarels 			(struct ifnet *)rights));
24312767Ssam 	if (rights && rights->m_len) {
24412767Ssam 		error = EINVAL;
24512767Ssam 		goto release;
24612767Ssam 	}
24711080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
24811080Ssam 		error = EINVAL;
24911080Ssam 		goto release;
25011080Ssam 	}
2514784Swnj 	switch (req) {
2524784Swnj 
2534784Swnj 	case PRU_ATTACH:
25411080Ssam 		if (inp != NULL) {
25511080Ssam 			error = EINVAL;
25611080Ssam 			break;
25711080Ssam 		}
2588273Sroot 		error = in_pcballoc(so, &udb);
2598273Sroot 		if (error)
2608273Sroot 			break;
26118368Skarels 		error = soreserve(so, udp_sendspace, udp_recvspace);
2628273Sroot 		if (error)
2638273Sroot 			break;
2644887Swnj 		break;
2654784Swnj 
2664784Swnj 	case PRU_DETACH:
26711080Ssam 		if (inp == NULL) {
26811080Ssam 			error = ENOTCONN;
26911080Ssam 			break;
27011080Ssam 		}
2715166Swnj 		in_pcbdetach(inp);
2724887Swnj 		break;
2734784Swnj 
2748273Sroot 	case PRU_BIND:
2758273Sroot 		error = in_pcbbind(inp, nam);
2768273Sroot 		break;
2778273Sroot 
2788273Sroot 	case PRU_LISTEN:
2798273Sroot 		error = EOPNOTSUPP;
2808273Sroot 		break;
2818273Sroot 
2824784Swnj 	case PRU_CONNECT:
28311080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
28411080Ssam 			error = EISCONN;
28511080Ssam 			break;
28611080Ssam 		}
2878273Sroot 		error = in_pcbconnect(inp, nam);
2886507Ssam 		if (error == 0)
2896507Ssam 			soisconnected(so);
2904887Swnj 		break;
2914784Swnj 
29213118Ssam 	case PRU_CONNECT2:
29313118Ssam 		error = EOPNOTSUPP;
29413118Ssam 		break;
29513118Ssam 
2964926Swnj 	case PRU_ACCEPT:
29711080Ssam 		error = EOPNOTSUPP;
29811080Ssam 		break;
2994926Swnj 
3004784Swnj 	case PRU_DISCONNECT:
30111080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
30211080Ssam 			error = ENOTCONN;
30311080Ssam 			break;
30411080Ssam 		}
3055166Swnj 		in_pcbdisconnect(inp);
3064887Swnj 		soisdisconnected(so);
3074784Swnj 		break;
3084784Swnj 
3094912Swnj 	case PRU_SHUTDOWN:
3104912Swnj 		socantsendmore(so);
3114912Swnj 		break;
3124912Swnj 
3135996Swnj 	case PRU_SEND: {
3145996Swnj 		struct in_addr laddr;
31516799Skarels 		int s;
3165996Swnj 
3178273Sroot 		if (nam) {
3185996Swnj 			laddr = inp->inp_laddr;
31911080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
32011080Ssam 				error = EISCONN;
32111080Ssam 				break;
32211080Ssam 			}
32316799Skarels 			/*
32416799Skarels 			 * Must block input while temporarily connected.
32516799Skarels 			 */
32616799Skarels 			s = splnet();
3278273Sroot 			error = in_pcbconnect(inp, nam);
32816799Skarels 			if (error) {
32916799Skarels 				splx(s);
3306507Ssam 				break;
33116799Skarels 			}
3324955Swnj 		} else {
33311080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
33411080Ssam 				error = ENOTCONN;
33511080Ssam 				break;
33611080Ssam 			}
3374955Swnj 		}
3386507Ssam 		error = udp_output(inp, m);
33911080Ssam 		m = NULL;
3408273Sroot 		if (nam) {
3415166Swnj 			in_pcbdisconnect(inp);
34218368Skarels 			inp->inp_laddr = laddr;
34316799Skarels 			splx(s);
3445996Swnj 		}
3455996Swnj 		}
3464784Swnj 		break;
3474784Swnj 
3484784Swnj 	case PRU_ABORT:
3495166Swnj 		in_pcbdetach(inp);
3504887Swnj 		sofree(so);
3514887Swnj 		soisdisconnected(so);
3524784Swnj 		break;
3534784Swnj 
3546511Ssam 	case PRU_SOCKADDR:
3558273Sroot 		in_setsockaddr(inp, nam);
3566511Ssam 		break;
3576511Ssam 
35814124Ssam 	case PRU_PEERADDR:
35914124Ssam 		in_setpeeraddr(inp, nam);
36014124Ssam 		break;
36114124Ssam 
36216988Skarels 	case PRU_SENSE:
36316988Skarels 		/*
36416988Skarels 		 * stat: don't bother with a blocksize.
36516988Skarels 		 */
36616988Skarels 		return (0);
36716988Skarels 
36812205Ssam 	case PRU_SENDOOB:
36912205Ssam 	case PRU_FASTTIMO:
37012205Ssam 	case PRU_SLOWTIMO:
37112205Ssam 	case PRU_PROTORCV:
37212205Ssam 	case PRU_PROTOSEND:
37312205Ssam 		error =  EOPNOTSUPP;
37412205Ssam 		break;
37513118Ssam 
37616799Skarels 	case PRU_RCVD:
37716799Skarels 	case PRU_RCVOOB:
37816799Skarels 		return (EOPNOTSUPP);	/* do not free mbuf's */
37916799Skarels 
38013118Ssam 	default:
38113118Ssam 		panic("udp_usrreq");
3824805Swnj 	}
38311080Ssam release:
38411080Ssam 	if (m != NULL)
38511080Ssam 		m_freem(m);
3866507Ssam 	return (error);
3874784Swnj }
388