xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 16601)
1*16601Ssam /*	udp_usrreq.c	6.4	84/06/22	*/
24784Swnj 
34784Swnj #include "../h/param.h"
44887Swnj #include "../h/dir.h"
54887Swnj #include "../h/user.h"
64784Swnj #include "../h/mbuf.h"
74805Swnj #include "../h/protosw.h"
84887Swnj #include "../h/socket.h"
94887Swnj #include "../h/socketvar.h"
1010897Ssam #include "../h/errno.h"
1110897Ssam 
126584Ssam #include "../net/if.h"
136354Ssam #include "../net/route.h"
1410897Ssam 
1510897Ssam #include "../netinet/in.h"
168411Swnj #include "../netinet/in_pcb.h"
178411Swnj #include "../netinet/in_systm.h"
188411Swnj #include "../netinet/ip.h"
198411Swnj #include "../netinet/ip_var.h"
208411Swnj #include "../netinet/ip_icmp.h"
218411Swnj #include "../netinet/udp.h"
228411Swnj #include "../netinet/udp_var.h"
234784Swnj 
244926Swnj /*
254926Swnj  * UDP protocol implementation.
264926Swnj  * Per RFC 768, August, 1980.
274926Swnj  */
284805Swnj udp_init()
294805Swnj {
304805Swnj 
314901Swnj 	udb.inp_next = udb.inp_prev = &udb;
324805Swnj }
334805Swnj 
3415539Skarels int	udpcksum = 0;
354926Swnj struct	sockaddr_in udp_in = { AF_INET };
364901Swnj 
374926Swnj udp_input(m0)
384926Swnj 	struct mbuf *m0;
394784Swnj {
404901Swnj 	register struct udpiphdr *ui;
414887Swnj 	register struct inpcb *inp;
424926Swnj 	register struct mbuf *m;
437844Sroot 	int len;
444784Swnj 
454926Swnj 	/*
465246Sroot 	 * Get IP and UDP header together in first mbuf.
474926Swnj 	 */
484926Swnj 	m = m0;
495308Sroot 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
505308Sroot 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
514926Swnj 		udpstat.udps_hdrops++;
525308Sroot 		return;
534926Swnj 	}
545050Swnj 	ui = mtod(m, struct udpiphdr *);
555246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
565220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
574926Swnj 
584926Swnj 	/*
595246Sroot 	 * Make mbuf data length reflect UDP length.
605246Sroot 	 * If not enough data to reflect UDP length, drop.
614926Swnj 	 */
627844Sroot 	len = ntohs((u_short)ui->ui_ulen);
634926Swnj 	if (((struct ip *)ui)->ip_len != len) {
644926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
654926Swnj 			udpstat.udps_badlen++;
664926Swnj 			goto bad;
674926Swnj 		}
6815539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
694926Swnj 		/* (struct ip *)ui->ip_len = len; */
704926Swnj 	}
714926Swnj 
724926Swnj 	/*
735246Sroot 	 * Checksum extended UDP header and data.
744926Swnj 	 */
7515539Skarels 	if (udpcksum && ui->ui_sum) {
764926Swnj 		ui->ui_next = ui->ui_prev = 0;
774926Swnj 		ui->ui_x1 = 0;
787844Sroot 		ui->ui_len = htons((u_short)len);
797844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
804926Swnj 			udpstat.udps_badsum++;
814901Swnj 			m_freem(m);
824901Swnj 			return;
834901Swnj 		}
844901Swnj 	}
854926Swnj 
864926Swnj 	/*
877844Sroot 	 * Locate pcb for datagram.
884926Swnj 	 */
894926Swnj 	inp = in_pcblookup(&udb,
906029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
916029Sroot 		INPLOOKUP_WILDCARD);
926584Ssam 	if (inp == 0) {
9310144Ssam 		/* don't send ICMP response for broadcast packet */
9410144Ssam 		if (in_lnaof(ui->ui_dst) == INADDR_ANY)
956584Ssam 			goto bad;
966584Ssam 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
976584Ssam 		return;
986584Ssam 	}
996584Ssam 
1004926Swnj 	/*
1014926Swnj 	 * Construct sockaddr format source address.
1024926Swnj 	 * Stuff source address and datagram in user buffer.
1034926Swnj 	 */
1044926Swnj 	udp_in.sin_port = ui->ui_sport;
1054926Swnj 	udp_in.sin_addr = ui->ui_src;
1065050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1075050Swnj 	m->m_off += sizeof (struct udpiphdr);
10812767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
10912767Ssam 	    m, (struct mbuf *)0) == 0)
1104926Swnj 		goto bad;
1115050Swnj 	sorwakeup(inp->inp_socket);
1124887Swnj 	return;
1134926Swnj bad:
1144887Swnj 	m_freem(m);
1154784Swnj }
1164784Swnj 
1176584Ssam udp_abort(inp)
1186584Ssam 	struct inpcb *inp;
1196584Ssam {
1206584Ssam 	struct socket *so = inp->inp_socket;
1216584Ssam 
1226584Ssam 	in_pcbdisconnect(inp);
1236584Ssam 	soisdisconnected(so);
1246584Ssam }
1256584Ssam 
1266591Ssam udp_ctlinput(cmd, arg)
1276591Ssam 	int cmd;
1286591Ssam 	caddr_t arg;
1296591Ssam {
1306591Ssam 	struct in_addr *sin;
1316591Ssam 	extern u_char inetctlerrmap[];
1326591Ssam 
1336591Ssam 	if (cmd < 0 || cmd > PRC_NCMDS)
1346591Ssam 		return;
1356591Ssam 	switch (cmd) {
1366591Ssam 
1376591Ssam 	case PRC_ROUTEDEAD:
1386591Ssam 		break;
1396591Ssam 
1406591Ssam 	case PRC_QUENCH:
1416591Ssam 		break;
1426591Ssam 
1436591Ssam 	/* these are handled by ip */
1446591Ssam 	case PRC_IFDOWN:
1456591Ssam 	case PRC_HOSTDEAD:
1466591Ssam 	case PRC_HOSTUNREACH:
1476591Ssam 		break;
1486591Ssam 
1496591Ssam 	default:
1506591Ssam 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
1518641Sroot 		in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
1526591Ssam 	}
1536591Ssam }
1546591Ssam 
1554955Swnj udp_output(inp, m0)
1564926Swnj 	struct inpcb *inp;
1574926Swnj 	struct mbuf *m0;
1584784Swnj {
1594926Swnj 	register struct mbuf *m;
1604926Swnj 	register struct udpiphdr *ui;
1617157Swnj 	register struct socket *so;
1624926Swnj 	register int len = 0;
163*16601Ssam 	register struct route *ro;
1644784Swnj 
1654926Swnj 	/*
1664926Swnj 	 * Calculate data length and get a mbuf
1675246Sroot 	 * for UDP and IP headers.
1684926Swnj 	 */
1694926Swnj 	for (m = m0; m; m = m->m_next)
1704926Swnj 		len += m->m_len;
1719644Ssam 	m = m_get(M_DONTWAIT, MT_HEADER);
1726507Ssam 	if (m == 0) {
1736507Ssam 		m_freem(m0);
1746507Ssam 		return (ENOBUFS);
1756507Ssam 	}
1764784Swnj 
1774926Swnj 	/*
1785246Sroot 	 * Fill in mbuf with extended UDP header
1794926Swnj 	 * and addresses and length put into network format.
1804926Swnj 	 */
1814926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
1824926Swnj 	m->m_len = sizeof (struct udpiphdr);
1834926Swnj 	m->m_next = m0;
1844926Swnj 	ui = mtod(m, struct udpiphdr *);
1854926Swnj 	ui->ui_next = ui->ui_prev = 0;
1864926Swnj 	ui->ui_x1 = 0;
1874926Swnj 	ui->ui_pr = IPPROTO_UDP;
18815226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
1895050Swnj 	ui->ui_src = inp->inp_laddr;
1905050Swnj 	ui->ui_dst = inp->inp_faddr;
1915050Swnj 	ui->ui_sport = inp->inp_lport;
1925050Swnj 	ui->ui_dport = inp->inp_fport;
19315226Ssam 	ui->ui_ulen = ui->ui_len;
1944784Swnj 
1954926Swnj 	/*
1964926Swnj 	 * Stuff checksum and output datagram.
1974926Swnj 	 */
1984926Swnj 	ui->ui_sum = 0;
19915539Skarels 	if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
20015539Skarels 		ui->ui_sum = -1;
2015050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
2025050Swnj 	((struct ip *)ui)->ip_ttl = MAXTTL;
2037157Swnj 	so = inp->inp_socket;
204*16601Ssam 	if (so->so_options & SO_DONTROUTE)
205*16601Ssam 		return (ip_output(m, (struct mbuf *)0, (struct route *)0,
206*16601Ssam 		    IP_ROUTETOIF));
207*16601Ssam 	/*
208*16601Ssam 	 * Use cached route for previous datagram if
209*16601Ssam 	 * this is also to the same destination.
210*16601Ssam 	 *
211*16601Ssam 	 * NB: We don't handle broadcasts because that
212*16601Ssam 	 *     would require 3 subroutine calls.
213*16601Ssam 	 */
214*16601Ssam 	ro = &inp->inp_route;
215*16601Ssam #define	satosin(sa)	((struct sockaddr_in *)(sa))
216*16601Ssam 	if (ro->ro_rt &&
217*16601Ssam 	    satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) {
218*16601Ssam 		RTFREE(ro->ro_rt);
219*16601Ssam 		ro->ro_rt = (struct rtentry *)0;
220*16601Ssam 	}
221*16601Ssam 	return (ip_output(m, (struct mbuf *)0, ro, so->so_state & SS_PRIV));
2224784Swnj }
2234784Swnj 
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 
23312767Ssam 	if (rights && rights->m_len) {
23412767Ssam 		error = EINVAL;
23512767Ssam 		goto release;
23612767Ssam 	}
23711080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
23811080Ssam 		error = EINVAL;
23911080Ssam 		goto release;
24011080Ssam 	}
2414784Swnj 	switch (req) {
2424784Swnj 
2434784Swnj 	case PRU_ATTACH:
24411080Ssam 		if (inp != NULL) {
24511080Ssam 			error = EINVAL;
24611080Ssam 			break;
24711080Ssam 		}
2488273Sroot 		error = in_pcballoc(so, &udb);
2498273Sroot 		if (error)
2508273Sroot 			break;
2519032Sroot 		error = soreserve(so, 2048, 2048);
2528273Sroot 		if (error)
2538273Sroot 			break;
2544887Swnj 		break;
2554784Swnj 
2564784Swnj 	case PRU_DETACH:
25711080Ssam 		if (inp == NULL) {
25811080Ssam 			error = ENOTCONN;
25911080Ssam 			break;
26011080Ssam 		}
2615166Swnj 		in_pcbdetach(inp);
2624887Swnj 		break;
2634784Swnj 
2648273Sroot 	case PRU_BIND:
2658273Sroot 		error = in_pcbbind(inp, nam);
2668273Sroot 		break;
2678273Sroot 
2688273Sroot 	case PRU_LISTEN:
2698273Sroot 		error = EOPNOTSUPP;
2708273Sroot 		break;
2718273Sroot 
2724784Swnj 	case PRU_CONNECT:
27311080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
27411080Ssam 			error = EISCONN;
27511080Ssam 			break;
27611080Ssam 		}
2778273Sroot 		error = in_pcbconnect(inp, nam);
2786507Ssam 		if (error == 0)
2796507Ssam 			soisconnected(so);
2804887Swnj 		break;
2814784Swnj 
28213118Ssam 	case PRU_CONNECT2:
28313118Ssam 		error = EOPNOTSUPP;
28413118Ssam 		break;
28513118Ssam 
2864926Swnj 	case PRU_ACCEPT:
28711080Ssam 		error = EOPNOTSUPP;
28811080Ssam 		break;
2894926Swnj 
2904784Swnj 	case PRU_DISCONNECT:
29111080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
29211080Ssam 			error = ENOTCONN;
29311080Ssam 			break;
29411080Ssam 		}
2955166Swnj 		in_pcbdisconnect(inp);
2964887Swnj 		soisdisconnected(so);
2974784Swnj 		break;
2984784Swnj 
2994912Swnj 	case PRU_SHUTDOWN:
3004912Swnj 		socantsendmore(so);
3014912Swnj 		break;
3024912Swnj 
3035996Swnj 	case PRU_SEND: {
3045996Swnj 		struct in_addr laddr;
3055996Swnj 
3068273Sroot 		if (nam) {
3075996Swnj 			laddr = inp->inp_laddr;
30811080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
30911080Ssam 				error = EISCONN;
31011080Ssam 				break;
31111080Ssam 			}
3128273Sroot 			error = in_pcbconnect(inp, nam);
3135224Swnj 			if (error)
3146507Ssam 				break;
3154955Swnj 		} else {
31611080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
31711080Ssam 				error = ENOTCONN;
31811080Ssam 				break;
31911080Ssam 			}
3204955Swnj 		}
3216507Ssam 		error = udp_output(inp, m);
32211080Ssam 		m = NULL;
3238273Sroot 		if (nam) {
3245166Swnj 			in_pcbdisconnect(inp);
3255996Swnj 			inp->inp_laddr = laddr;
3265996Swnj 		}
3275996Swnj 		}
3284784Swnj 		break;
3294784Swnj 
3304784Swnj 	case PRU_ABORT:
3315166Swnj 		in_pcbdetach(inp);
3324887Swnj 		sofree(so);
3334887Swnj 		soisdisconnected(so);
3344784Swnj 		break;
3354784Swnj 
3366511Ssam 	case PRU_SOCKADDR:
3378273Sroot 		in_setsockaddr(inp, nam);
3386511Ssam 		break;
3396511Ssam 
34014124Ssam 	case PRU_PEERADDR:
34114124Ssam 		in_setpeeraddr(inp, nam);
34214124Ssam 		break;
34314124Ssam 
34413051Ssam 	case PRU_CONTROL:
34513118Ssam 		m = NULL;
34613118Ssam 		error = EOPNOTSUPP;
34713118Ssam 		break;
34813051Ssam 
34913118Ssam 	case PRU_SENSE:
35013118Ssam 		m = NULL;
35113118Ssam 		/* fall thru... */
35213118Ssam 
35312205Ssam 	case PRU_RCVD:
35412205Ssam 	case PRU_RCVOOB:
35512205Ssam 	case PRU_SENDOOB:
35612205Ssam 	case PRU_FASTTIMO:
35712205Ssam 	case PRU_SLOWTIMO:
35812205Ssam 	case PRU_PROTORCV:
35912205Ssam 	case PRU_PROTOSEND:
36012205Ssam 		error =  EOPNOTSUPP;
36112205Ssam 		break;
36213118Ssam 
36313118Ssam 	default:
36413118Ssam 		panic("udp_usrreq");
3654805Swnj 	}
36611080Ssam release:
36711080Ssam 	if (m != NULL)
36811080Ssam 		m_freem(m);
3696507Ssam 	return (error);
3704784Swnj }
371