xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 26060)
123199Smckusick /*
223199Smckusick  * Copyright (c) 1982 Regents of the University of California.
323199Smckusick  * All rights reserved.  The Berkeley software License Agreement
423199Smckusick  * specifies the terms and conditions for redistribution.
523199Smckusick  *
6*26060Skarels  *	@(#)udp_usrreq.c	6.17 (Berkeley) 02/03/86
723199Smckusick  */
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 
4426032Skarels udp_input(m0, ifp)
454926Swnj 	struct mbuf *m0;
4626032Skarels 	struct ifnet *ifp;
474784Swnj {
484901Swnj 	register struct udpiphdr *ui;
494887Swnj 	register struct inpcb *inp;
504926Swnj 	register struct mbuf *m;
517844Sroot 	int len;
5224823Skarels 	struct ip ip;
534784Swnj 
544926Swnj 	/*
555246Sroot 	 * Get IP and UDP header together in first mbuf.
564926Swnj 	 */
574926Swnj 	m = m0;
585308Sroot 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
595308Sroot 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
604926Swnj 		udpstat.udps_hdrops++;
615308Sroot 		return;
624926Swnj 	}
635050Swnj 	ui = mtod(m, struct udpiphdr *);
645246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
655220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
664926Swnj 
674926Swnj 	/*
685246Sroot 	 * Make mbuf data length reflect UDP length.
695246Sroot 	 * If not enough data to reflect UDP length, drop.
704926Swnj 	 */
717844Sroot 	len = ntohs((u_short)ui->ui_ulen);
724926Swnj 	if (((struct ip *)ui)->ip_len != len) {
734926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
744926Swnj 			udpstat.udps_badlen++;
754926Swnj 			goto bad;
764926Swnj 		}
7715539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
7824823Skarels 		/* ((struct ip *)ui)->ip_len = len; */
794926Swnj 	}
8024823Skarels 	/*
8124823Skarels 	 * Save a copy of the IP header in case we want restore it for ICMP.
8224823Skarels 	 */
8324823Skarels 	ip = *(struct ip*)ui;
844926Swnj 
854926Swnj 	/*
865246Sroot 	 * Checksum extended UDP header and data.
874926Swnj 	 */
8815539Skarels 	if (udpcksum && ui->ui_sum) {
894926Swnj 		ui->ui_next = ui->ui_prev = 0;
904926Swnj 		ui->ui_x1 = 0;
9121112Skarels 		ui->ui_len = ui->ui_ulen;
927844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
934926Swnj 			udpstat.udps_badsum++;
944901Swnj 			m_freem(m);
954901Swnj 			return;
964901Swnj 		}
974901Swnj 	}
984926Swnj 
994926Swnj 	/*
1007844Sroot 	 * Locate pcb for datagram.
1014926Swnj 	 */
1024926Swnj 	inp = in_pcblookup(&udb,
1036029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
1046029Sroot 		INPLOOKUP_WILDCARD);
1056584Ssam 	if (inp == 0) {
10610144Ssam 		/* don't send ICMP response for broadcast packet */
10721112Skarels 		if (in_broadcast(ui->ui_dst))
1086584Ssam 			goto bad;
10924823Skarels 		*(struct ip *)ui = ip;
11026032Skarels 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
11126032Skarels 		    ifp);
1126584Ssam 		return;
1136584Ssam 	}
1146584Ssam 
1154926Swnj 	/*
1164926Swnj 	 * Construct sockaddr format source address.
1174926Swnj 	 * Stuff source address and datagram in user buffer.
1184926Swnj 	 */
1194926Swnj 	udp_in.sin_port = ui->ui_sport;
1204926Swnj 	udp_in.sin_addr = ui->ui_src;
1215050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1225050Swnj 	m->m_off += sizeof (struct udpiphdr);
12312767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
12412767Ssam 	    m, (struct mbuf *)0) == 0)
1254926Swnj 		goto bad;
1265050Swnj 	sorwakeup(inp->inp_socket);
1274887Swnj 	return;
1284926Swnj bad:
1294887Swnj 	m_freem(m);
1304784Swnj }
1314784Swnj 
13224823Skarels udp_ctlinput(cmd, sa)
1336591Ssam 	int cmd;
13424823Skarels 	struct sockaddr *sa;
1356591Ssam {
1366591Ssam 	extern u_char inetctlerrmap[];
13724823Skarels 	struct sockaddr_in *sin;
13821121Skarels 	int in_rtchange();
1396591Ssam 
14024823Skarels 	if ((unsigned)cmd > PRC_NCMDS)
1416591Ssam 		return;
14224823Skarels 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
14324823Skarels 		return;
14424823Skarels 	sin = (struct sockaddr_in *)sa;
14524823Skarels 	if (sin->sin_addr.s_addr == INADDR_ANY)
14624823Skarels 		return;
14724823Skarels 
1486591Ssam 	switch (cmd) {
1496591Ssam 
15024823Skarels 	case PRC_QUENCH:
1516591Ssam 		break;
1526591Ssam 
15324823Skarels 	case PRC_ROUTEDEAD:
15421121Skarels 	case PRC_REDIRECT_NET:
15521121Skarels 	case PRC_REDIRECT_HOST:
15624823Skarels 	case PRC_REDIRECT_TOSNET:
15724823Skarels 	case PRC_REDIRECT_TOSHOST:
15824823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
1596591Ssam 		break;
1606591Ssam 
1616591Ssam 	default:
16221121Skarels 		if (inetctlerrmap[cmd] == 0)
16321121Skarels 			return;		/* XXX */
16424823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
16524823Skarels 			(int (*)())0);
1666591Ssam 	}
1676591Ssam }
1686591Ssam 
1694955Swnj udp_output(inp, m0)
170*26060Skarels 	register struct inpcb *inp;
1714926Swnj 	struct mbuf *m0;
1724784Swnj {
1734926Swnj 	register struct mbuf *m;
1744926Swnj 	register struct udpiphdr *ui;
17517165Skarels 	register int len = 0;
1764784Swnj 
1774926Swnj 	/*
1784926Swnj 	 * Calculate data length and get a mbuf
1795246Sroot 	 * for UDP and IP headers.
1804926Swnj 	 */
1814926Swnj 	for (m = m0; m; m = m->m_next)
1824926Swnj 		len += m->m_len;
18321112Skarels 	MGET(m, M_DONTWAIT, MT_HEADER);
1846507Ssam 	if (m == 0) {
1856507Ssam 		m_freem(m0);
1866507Ssam 		return (ENOBUFS);
1876507Ssam 	}
1884784Swnj 
1894926Swnj 	/*
1905246Sroot 	 * Fill in mbuf with extended UDP header
1914926Swnj 	 * and addresses and length put into network format.
1924926Swnj 	 */
1934926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
1944926Swnj 	m->m_len = sizeof (struct udpiphdr);
1954926Swnj 	m->m_next = m0;
1964926Swnj 	ui = mtod(m, struct udpiphdr *);
1974926Swnj 	ui->ui_next = ui->ui_prev = 0;
1984926Swnj 	ui->ui_x1 = 0;
1994926Swnj 	ui->ui_pr = IPPROTO_UDP;
20015226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
2015050Swnj 	ui->ui_src = inp->inp_laddr;
2025050Swnj 	ui->ui_dst = inp->inp_faddr;
2035050Swnj 	ui->ui_sport = inp->inp_lport;
2045050Swnj 	ui->ui_dport = inp->inp_fport;
20515226Ssam 	ui->ui_ulen = ui->ui_len;
2064784Swnj 
2074926Swnj 	/*
2084926Swnj 	 * Stuff checksum and output datagram.
2094926Swnj 	 */
2104926Swnj 	ui->ui_sum = 0;
21119519Skarels 	if (udpcksum) {
21219519Skarels 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
21315539Skarels 		ui->ui_sum = -1;
21421112Skarels 	}
2155050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
2165050Swnj 	((struct ip *)ui)->ip_ttl = MAXTTL;
217*26060Skarels 	return (ip_output(m, inp->inp_options, &inp->inp_route,
218*26060Skarels 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
2194784Swnj }
2204784Swnj 
22118368Skarels int	udp_sendspace = 2048;		/* really max datagram size */
22218368Skarels int	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
22318368Skarels 
2248602Sroot /*ARGSUSED*/
22512767Ssam udp_usrreq(so, req, m, nam, rights)
2264887Swnj 	struct socket *so;
2274784Swnj 	int req;
22812767Ssam 	struct mbuf *m, *nam, *rights;
2294784Swnj {
2304887Swnj 	struct inpcb *inp = sotoinpcb(so);
2316507Ssam 	int error = 0;
2324784Swnj 
23318368Skarels 	if (req == PRU_CONTROL)
23418368Skarels 		return (in_control(so, (int)m, (caddr_t)nam,
23518368Skarels 			(struct ifnet *)rights));
23612767Ssam 	if (rights && rights->m_len) {
23712767Ssam 		error = EINVAL;
23812767Ssam 		goto release;
23912767Ssam 	}
24011080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
24111080Ssam 		error = EINVAL;
24211080Ssam 		goto release;
24311080Ssam 	}
2444784Swnj 	switch (req) {
2454784Swnj 
2464784Swnj 	case PRU_ATTACH:
24711080Ssam 		if (inp != NULL) {
24811080Ssam 			error = EINVAL;
24911080Ssam 			break;
25011080Ssam 		}
2518273Sroot 		error = in_pcballoc(so, &udb);
2528273Sroot 		if (error)
2538273Sroot 			break;
25418368Skarels 		error = soreserve(so, udp_sendspace, udp_recvspace);
2558273Sroot 		if (error)
2568273Sroot 			break;
2574887Swnj 		break;
2584784Swnj 
2594784Swnj 	case PRU_DETACH:
26011080Ssam 		if (inp == NULL) {
26111080Ssam 			error = ENOTCONN;
26211080Ssam 			break;
26311080Ssam 		}
2645166Swnj 		in_pcbdetach(inp);
2654887Swnj 		break;
2664784Swnj 
2678273Sroot 	case PRU_BIND:
2688273Sroot 		error = in_pcbbind(inp, nam);
2698273Sroot 		break;
2708273Sroot 
2718273Sroot 	case PRU_LISTEN:
2728273Sroot 		error = EOPNOTSUPP;
2738273Sroot 		break;
2748273Sroot 
2754784Swnj 	case PRU_CONNECT:
27611080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
27711080Ssam 			error = EISCONN;
27811080Ssam 			break;
27911080Ssam 		}
2808273Sroot 		error = in_pcbconnect(inp, nam);
2816507Ssam 		if (error == 0)
2826507Ssam 			soisconnected(so);
2834887Swnj 		break;
2844784Swnj 
28513118Ssam 	case PRU_CONNECT2:
28613118Ssam 		error = EOPNOTSUPP;
28713118Ssam 		break;
28813118Ssam 
2894926Swnj 	case PRU_ACCEPT:
29011080Ssam 		error = EOPNOTSUPP;
29111080Ssam 		break;
2924926Swnj 
2934784Swnj 	case PRU_DISCONNECT:
29411080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
29511080Ssam 			error = ENOTCONN;
29611080Ssam 			break;
29711080Ssam 		}
2985166Swnj 		in_pcbdisconnect(inp);
2994887Swnj 		soisdisconnected(so);
3004784Swnj 		break;
3014784Swnj 
3024912Swnj 	case PRU_SHUTDOWN:
3034912Swnj 		socantsendmore(so);
3044912Swnj 		break;
3054912Swnj 
3065996Swnj 	case PRU_SEND: {
3075996Swnj 		struct in_addr laddr;
30816799Skarels 		int s;
3095996Swnj 
3108273Sroot 		if (nam) {
3115996Swnj 			laddr = inp->inp_laddr;
31211080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
31311080Ssam 				error = EISCONN;
31411080Ssam 				break;
31511080Ssam 			}
31616799Skarels 			/*
31716799Skarels 			 * Must block input while temporarily connected.
31816799Skarels 			 */
31916799Skarels 			s = splnet();
3208273Sroot 			error = in_pcbconnect(inp, nam);
32116799Skarels 			if (error) {
32216799Skarels 				splx(s);
3236507Ssam 				break;
32416799Skarels 			}
3254955Swnj 		} else {
32611080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
32711080Ssam 				error = ENOTCONN;
32811080Ssam 				break;
32911080Ssam 			}
3304955Swnj 		}
3316507Ssam 		error = udp_output(inp, m);
33211080Ssam 		m = NULL;
3338273Sroot 		if (nam) {
3345166Swnj 			in_pcbdisconnect(inp);
33518368Skarels 			inp->inp_laddr = laddr;
33616799Skarels 			splx(s);
3375996Swnj 		}
3385996Swnj 		}
3394784Swnj 		break;
3404784Swnj 
3414784Swnj 	case PRU_ABORT:
3425166Swnj 		in_pcbdetach(inp);
3434887Swnj 		sofree(so);
3444887Swnj 		soisdisconnected(so);
3454784Swnj 		break;
3464784Swnj 
3476511Ssam 	case PRU_SOCKADDR:
3488273Sroot 		in_setsockaddr(inp, nam);
3496511Ssam 		break;
3506511Ssam 
35114124Ssam 	case PRU_PEERADDR:
35214124Ssam 		in_setpeeraddr(inp, nam);
35314124Ssam 		break;
35414124Ssam 
35516988Skarels 	case PRU_SENSE:
35616988Skarels 		/*
35716988Skarels 		 * stat: don't bother with a blocksize.
35816988Skarels 		 */
35916988Skarels 		return (0);
36016988Skarels 
36112205Ssam 	case PRU_SENDOOB:
36212205Ssam 	case PRU_FASTTIMO:
36312205Ssam 	case PRU_SLOWTIMO:
36412205Ssam 	case PRU_PROTORCV:
36512205Ssam 	case PRU_PROTOSEND:
36612205Ssam 		error =  EOPNOTSUPP;
36712205Ssam 		break;
36813118Ssam 
36916799Skarels 	case PRU_RCVD:
37016799Skarels 	case PRU_RCVOOB:
37116799Skarels 		return (EOPNOTSUPP);	/* do not free mbuf's */
37216799Skarels 
37313118Ssam 	default:
37413118Ssam 		panic("udp_usrreq");
3754805Swnj 	}
37611080Ssam release:
37711080Ssam 	if (m != NULL)
37811080Ssam 		m_freem(m);
3796507Ssam 	return (error);
3804784Swnj }
381