xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 6591)
1*6591Ssam /*	udp_usrreq.c	4.28	82/04/25	*/
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"
105094Swnj #include "../net/in.h"
116584Ssam #include "../net/if.h"
126354Ssam #include "../net/route.h"
135094Swnj #include "../net/in_pcb.h"
145094Swnj #include "../net/in_systm.h"
154901Swnj #include "../net/ip.h"
164901Swnj #include "../net/ip_var.h"
176584Ssam #include "../net/ip_icmp.h"
184887Swnj #include "../net/udp.h"
194887Swnj #include "../net/udp_var.h"
206507Ssam #include <errno.h>
214784Swnj 
224926Swnj /*
234926Swnj  * UDP protocol implementation.
244926Swnj  * Per RFC 768, August, 1980.
254926Swnj  */
264805Swnj udp_init()
274805Swnj {
284805Swnj 
295094Swnj COUNT(UDP_INIT);
304901Swnj 	udb.inp_next = udb.inp_prev = &udb;
314805Swnj }
324805Swnj 
334901Swnj int	udpcksum;
344926Swnj struct	sockaddr_in udp_in = { AF_INET };
354901Swnj 
364926Swnj udp_input(m0)
374926Swnj 	struct mbuf *m0;
384784Swnj {
394901Swnj 	register struct udpiphdr *ui;
404887Swnj 	register struct inpcb *inp;
414926Swnj 	register struct mbuf *m;
424926Swnj 	int len, ulen;
434784Swnj 
445094Swnj COUNT(UDP_INPUT);
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 	 */
624955Swnj 	ulen = ntohs((u_short)ui->ui_ulen);
635220Swnj 	len = sizeof (struct udphdr) + 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 		}
694926Swnj 		m_adj(m, ((struct ip *)ui)->ip_len - len);
704926Swnj 		/* (struct ip *)ui->ip_len = len; */
714926Swnj 	}
724926Swnj 
734926Swnj 	/*
745246Sroot 	 * Checksum extended UDP header and data.
754926Swnj 	 */
764901Swnj 	if (udpcksum) {
774926Swnj 		ui->ui_next = ui->ui_prev = 0;
784926Swnj 		ui->ui_x1 = 0;
795220Swnj 		ui->ui_len = htons((u_short)(sizeof (struct udphdr) + ulen));
805220Swnj 		if (ui->ui_sum = in_cksum(m, len)) {
814926Swnj 			udpstat.udps_badsum++;
824901Swnj 			printf("udp cksum %x\n", ui->ui_sum);
834901Swnj 			m_freem(m);
844901Swnj 			return;
854901Swnj 		}
864901Swnj 	}
874926Swnj 
884926Swnj 	/*
895996Swnj 	 * Locate pcb for datagram.  On wildcard match, update
905996Swnj 	 * control block to anchor network and host address.
914926Swnj 	 */
924926Swnj 	inp = in_pcblookup(&udb,
936029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
946029Sroot 		INPLOOKUP_WILDCARD);
956584Ssam 	if (inp == 0) {
966584Ssam 		struct in_addr broadcastaddr;
974926Swnj 
986584Ssam 		broadcastaddr = if_makeaddr(ui->ui_dst.s_net, INADDR_ANY);
99*6591Ssam 		if (ui->ui_dst.s_addr == broadcastaddr.s_addr)
1006584Ssam 			goto bad;
1016584Ssam 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
1026584Ssam 		return;
1036584Ssam 	}
1046584Ssam 
1054926Swnj 	/*
1064926Swnj 	 * Construct sockaddr format source address.
1074926Swnj 	 * Stuff source address and datagram in user buffer.
1084926Swnj 	 */
1094926Swnj 	udp_in.sin_port = ui->ui_sport;
1104926Swnj 	udp_in.sin_addr = ui->ui_src;
1115050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1125050Swnj 	m->m_off += sizeof (struct udpiphdr);
1135050Swnj 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
1144926Swnj 		goto bad;
1155050Swnj 	sorwakeup(inp->inp_socket);
1164887Swnj 	return;
1174926Swnj bad:
1184887Swnj 	m_freem(m);
1194784Swnj }
1204784Swnj 
1216584Ssam udp_abort(inp)
1226584Ssam 	struct inpcb *inp;
1236584Ssam {
1246584Ssam 	struct socket *so = inp->inp_socket;
1256584Ssam 
1266584Ssam 	in_pcbdisconnect(inp);
1276584Ssam 	soisdisconnected(so);
1286584Ssam }
1296584Ssam 
130*6591Ssam udp_ctlinput(cmd, arg)
131*6591Ssam 	int cmd;
132*6591Ssam 	caddr_t arg;
133*6591Ssam {
134*6591Ssam 	struct in_addr *sin;
135*6591Ssam 	extern u_char inetctlerrmap[];
136*6591Ssam COUNT(UDP_CTLINPUT);
137*6591Ssam 
138*6591Ssam 	if (cmd < 0 || cmd > PRC_NCMDS)
139*6591Ssam 		return;
140*6591Ssam 	switch (cmd) {
141*6591Ssam 
142*6591Ssam 	case PRC_ROUTEDEAD:
143*6591Ssam 		break;
144*6591Ssam 
145*6591Ssam 	case PRC_QUENCH:
146*6591Ssam 		break;
147*6591Ssam 
148*6591Ssam 	/* these are handled by ip */
149*6591Ssam 	case PRC_IFDOWN:
150*6591Ssam 	case PRC_HOSTDEAD:
151*6591Ssam 	case PRC_HOSTUNREACH:
152*6591Ssam 		break;
153*6591Ssam 
154*6591Ssam 	default:
155*6591Ssam 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
156*6591Ssam 		in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
157*6591Ssam 	}
158*6591Ssam }
159*6591Ssam 
1604955Swnj udp_output(inp, m0)
1614926Swnj 	struct inpcb *inp;
1624926Swnj 	struct mbuf *m0;
1634784Swnj {
1644926Swnj 	register struct mbuf *m;
1654926Swnj 	register struct udpiphdr *ui;
1664926Swnj 	register int len = 0;
1674784Swnj 
1685094Swnj COUNT(UDP_OUTPUT);
1694926Swnj 	/*
1704926Swnj 	 * Calculate data length and get a mbuf
1715246Sroot 	 * for UDP and IP headers.
1724926Swnj 	 */
1734926Swnj 	for (m = m0; m; m = m->m_next)
1744926Swnj 		len += m->m_len;
1755585Sroot 	m = m_get(M_DONTWAIT);
1766507Ssam 	if (m == 0) {
1776507Ssam 		m_freem(m0);
1786507Ssam 		return (ENOBUFS);
1796507Ssam 	}
1804784Swnj 
1814926Swnj 	/*
1825246Sroot 	 * Fill in mbuf with extended UDP header
1834926Swnj 	 * and addresses and length put into network format.
1844926Swnj 	 */
1854926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
1864926Swnj 	m->m_len = sizeof (struct udpiphdr);
1874926Swnj 	m->m_next = m0;
1884926Swnj 	ui = mtod(m, struct udpiphdr *);
1894926Swnj 	ui->ui_next = ui->ui_prev = 0;
1904926Swnj 	ui->ui_x1 = 0;
1914926Swnj 	ui->ui_pr = IPPROTO_UDP;
1925050Swnj 	ui->ui_len = sizeof (struct udpiphdr) + len;
1935050Swnj 	ui->ui_src = inp->inp_laddr;
1945050Swnj 	ui->ui_dst = inp->inp_faddr;
1955050Swnj 	ui->ui_sport = inp->inp_lport;
1965050Swnj 	ui->ui_dport = inp->inp_fport;
1974955Swnj 	ui->ui_ulen = htons((u_short)len);
1984784Swnj 
1994926Swnj 	/*
2004926Swnj 	 * Stuff checksum and output datagram.
2014926Swnj 	 */
2024926Swnj 	ui->ui_sum = 0;
2035094Swnj 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
2045050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
2055050Swnj 	((struct ip *)ui)->ip_ttl = MAXTTL;
2066507Ssam 	return (ip_output(m, (struct mbuf *)0, 0,
2076507Ssam 	    inp->inp_socket->so_state & SS_PRIV));
2084784Swnj }
2094784Swnj 
2104887Swnj udp_usrreq(so, req, m, addr)
2114887Swnj 	struct socket *so;
2124784Swnj 	int req;
2134784Swnj 	struct mbuf *m;
2144912Swnj 	caddr_t addr;
2154784Swnj {
2164887Swnj 	struct inpcb *inp = sotoinpcb(so);
2176507Ssam 	int error = 0;
2184784Swnj 
2195094Swnj COUNT(UDP_USRREQ);
2205051Swnj 	if (inp == 0 && req != PRU_ATTACH)
2215050Swnj 		return (EINVAL);
2224784Swnj 	switch (req) {
2234784Swnj 
2244784Swnj 	case PRU_ATTACH:
2254887Swnj 		if (inp != 0)
2264887Swnj 			return (EINVAL);
2276507Ssam 		error = in_pcbattach(so, &udb, 2048, 2048,
2286507Ssam 				(struct sockaddr_in *)addr);
2294887Swnj 		break;
2304784Swnj 
2314784Swnj 	case PRU_DETACH:
2324887Swnj 		if (inp == 0)
2334887Swnj 			return (ENOTCONN);
2345166Swnj 		in_pcbdetach(inp);
2354887Swnj 		break;
2364784Swnj 
2374784Swnj 	case PRU_CONNECT:
2384955Swnj 		if (inp->inp_faddr.s_addr)
2394887Swnj 			return (EISCONN);
2405166Swnj 		error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
2416507Ssam 		if (error == 0)
2426507Ssam 			soisconnected(so);
2434887Swnj 		break;
2444784Swnj 
2454926Swnj 	case PRU_ACCEPT:
2464926Swnj 		return (EOPNOTSUPP);
2474926Swnj 
2484784Swnj 	case PRU_DISCONNECT:
2494955Swnj 		if (inp->inp_faddr.s_addr == 0)
2504887Swnj 			return (ENOTCONN);
2515166Swnj 		in_pcbdisconnect(inp);
2524887Swnj 		soisdisconnected(so);
2534784Swnj 		break;
2544784Swnj 
2554912Swnj 	case PRU_SHUTDOWN:
2564912Swnj 		socantsendmore(so);
2574912Swnj 		break;
2584912Swnj 
2595996Swnj 	case PRU_SEND: {
2605996Swnj 		struct in_addr laddr;
2615996Swnj 
2624887Swnj 		if (addr) {
2635996Swnj 			laddr = inp->inp_laddr;
2645224Swnj 			if (inp->inp_faddr.s_addr)
2654887Swnj 				return (EISCONN);
2665166Swnj 			error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
2675224Swnj 			if (error)
2686507Ssam 				break;
2694955Swnj 		} else {
2704955Swnj 			if (inp->inp_faddr.s_addr == 0)
2714955Swnj 				return (ENOTCONN);
2724955Swnj 		}
2736507Ssam 		error = udp_output(inp, m);
2745996Swnj 		if (addr) {
2755166Swnj 			in_pcbdisconnect(inp);
2765996Swnj 			inp->inp_laddr = laddr;
2775996Swnj 		}
2785996Swnj 		}
2794784Swnj 		break;
2804784Swnj 
2814784Swnj 	case PRU_ABORT:
2825166Swnj 		in_pcbdetach(inp);
2834887Swnj 		sofree(so);
2844887Swnj 		soisdisconnected(so);
2854784Swnj 		break;
2864784Swnj 
2874784Swnj 	case PRU_CONTROL:
2884887Swnj 		return (EOPNOTSUPP);
2894784Swnj 
2906511Ssam 	case PRU_SOCKADDR:
2916511Ssam 		in_setsockaddr((struct sockaddr_in *)addr, inp);
2926511Ssam 		break;
2936511Ssam 
2944784Swnj 	default:
2954784Swnj 		panic("udp_usrreq");
2964805Swnj 	}
2976507Ssam 	return (error);
2984784Swnj }
299