xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 34855)
123199Smckusick /*
229158Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
332789Sbostic  * All rights reserved.
423199Smckusick  *
532789Sbostic  * Redistribution and use in source and binary forms are permitted
6*34855Sbostic  * provided that the above copyright notice and this paragraph are
7*34855Sbostic  * duplicated in all such forms and that any documentation,
8*34855Sbostic  * advertising materials, and other materials related to such
9*34855Sbostic  * distribution and use acknowledge that the software was developed
10*34855Sbostic  * by the University of California, Berkeley.  The name of the
11*34855Sbostic  * University may not be used to endorse or promote products derived
12*34855Sbostic  * from this software without specific prior written permission.
13*34855Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34855Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34855Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1632789Sbostic  *
17*34855Sbostic  *	@(#)udp_usrreq.c	7.7 (Berkeley) 06/29/88
1823199Smckusick  */
194784Swnj 
2017065Sbloom #include "param.h"
2117065Sbloom #include "dir.h"
2217065Sbloom #include "user.h"
2317065Sbloom #include "mbuf.h"
2417065Sbloom #include "protosw.h"
2517065Sbloom #include "socket.h"
2617065Sbloom #include "socketvar.h"
2717065Sbloom #include "errno.h"
2817065Sbloom #include "stat.h"
2910897Ssam 
306584Ssam #include "../net/if.h"
316354Ssam #include "../net/route.h"
3210897Ssam 
3317065Sbloom #include "in.h"
3417065Sbloom #include "in_pcb.h"
3517065Sbloom #include "in_systm.h"
3617065Sbloom #include "ip.h"
3717065Sbloom #include "ip_var.h"
3817065Sbloom #include "ip_icmp.h"
3917065Sbloom #include "udp.h"
4017065Sbloom #include "udp_var.h"
414784Swnj 
424926Swnj /*
434926Swnj  * UDP protocol implementation.
444926Swnj  * Per RFC 768, August, 1980.
454926Swnj  */
464805Swnj udp_init()
474805Swnj {
484805Swnj 
494901Swnj 	udb.inp_next = udb.inp_prev = &udb;
504805Swnj }
514805Swnj 
5226118Skarels #ifndef	COMPAT_42
5319519Skarels int	udpcksum = 1;
5426118Skarels #else
5526118Skarels int	udpcksum = 0;		/* XXX */
5626118Skarels #endif
5731398Skarels int	udp_ttl = UDP_TTL;
5826118Skarels 
594926Swnj struct	sockaddr_in udp_in = { AF_INET };
604901Swnj 
6126032Skarels udp_input(m0, ifp)
624926Swnj 	struct mbuf *m0;
6326032Skarels 	struct ifnet *ifp;
644784Swnj {
654901Swnj 	register struct udpiphdr *ui;
664887Swnj 	register struct inpcb *inp;
674926Swnj 	register struct mbuf *m;
687844Sroot 	int len;
6924823Skarels 	struct ip ip;
704784Swnj 
714926Swnj 	/*
725246Sroot 	 * Get IP and UDP header together in first mbuf.
734926Swnj 	 */
744926Swnj 	m = m0;
755308Sroot 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
765308Sroot 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
774926Swnj 		udpstat.udps_hdrops++;
785308Sroot 		return;
794926Swnj 	}
805050Swnj 	ui = mtod(m, struct udpiphdr *);
815246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
825220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
834926Swnj 
844926Swnj 	/*
855246Sroot 	 * Make mbuf data length reflect UDP length.
865246Sroot 	 * If not enough data to reflect UDP length, drop.
874926Swnj 	 */
887844Sroot 	len = ntohs((u_short)ui->ui_ulen);
894926Swnj 	if (((struct ip *)ui)->ip_len != len) {
904926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
914926Swnj 			udpstat.udps_badlen++;
924926Swnj 			goto bad;
934926Swnj 		}
9415539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
9524823Skarels 		/* ((struct ip *)ui)->ip_len = len; */
964926Swnj 	}
9724823Skarels 	/*
9824823Skarels 	 * Save a copy of the IP header in case we want restore it for ICMP.
9924823Skarels 	 */
10024823Skarels 	ip = *(struct ip*)ui;
1014926Swnj 
1024926Swnj 	/*
1035246Sroot 	 * Checksum extended UDP header and data.
1044926Swnj 	 */
10515539Skarels 	if (udpcksum && ui->ui_sum) {
1064926Swnj 		ui->ui_next = ui->ui_prev = 0;
1074926Swnj 		ui->ui_x1 = 0;
10821112Skarels 		ui->ui_len = ui->ui_ulen;
1097844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
1104926Swnj 			udpstat.udps_badsum++;
1114901Swnj 			m_freem(m);
1124901Swnj 			return;
1134901Swnj 		}
1144901Swnj 	}
1154926Swnj 
1164926Swnj 	/*
1177844Sroot 	 * Locate pcb for datagram.
1184926Swnj 	 */
1194926Swnj 	inp = in_pcblookup(&udb,
1206029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
1216029Sroot 		INPLOOKUP_WILDCARD);
1226584Ssam 	if (inp == 0) {
12310144Ssam 		/* don't send ICMP response for broadcast packet */
12421112Skarels 		if (in_broadcast(ui->ui_dst))
1256584Ssam 			goto bad;
12624823Skarels 		*(struct ip *)ui = ip;
12726032Skarels 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
12826032Skarels 		    ifp);
1296584Ssam 		return;
1306584Ssam 	}
1316584Ssam 
1324926Swnj 	/*
1334926Swnj 	 * Construct sockaddr format source address.
1344926Swnj 	 * Stuff source address and datagram in user buffer.
1354926Swnj 	 */
1364926Swnj 	udp_in.sin_port = ui->ui_sport;
1374926Swnj 	udp_in.sin_addr = ui->ui_src;
1385050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1395050Swnj 	m->m_off += sizeof (struct udpiphdr);
14012767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
14112767Ssam 	    m, (struct mbuf *)0) == 0)
1424926Swnj 		goto bad;
1435050Swnj 	sorwakeup(inp->inp_socket);
1444887Swnj 	return;
1454926Swnj bad:
1464887Swnj 	m_freem(m);
1474784Swnj }
1484784Swnj 
14926426Skarels /*
15026426Skarels  * Notify a udp user of an asynchronous error;
15126426Skarels  * just wake up so that he can collect error status.
15226426Skarels  */
15326426Skarels udp_notify(inp)
15426426Skarels 	register struct inpcb *inp;
15526426Skarels {
15626426Skarels 
15726426Skarels 	sorwakeup(inp->inp_socket);
15826426Skarels 	sowwakeup(inp->inp_socket);
15926426Skarels }
16026426Skarels 
16124823Skarels udp_ctlinput(cmd, sa)
1626591Ssam 	int cmd;
16324823Skarels 	struct sockaddr *sa;
1646591Ssam {
1656591Ssam 	extern u_char inetctlerrmap[];
16624823Skarels 	struct sockaddr_in *sin;
16721121Skarels 	int in_rtchange();
1686591Ssam 
16924823Skarels 	if ((unsigned)cmd > PRC_NCMDS)
1706591Ssam 		return;
17124823Skarels 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
17224823Skarels 		return;
17324823Skarels 	sin = (struct sockaddr_in *)sa;
17424823Skarels 	if (sin->sin_addr.s_addr == INADDR_ANY)
17524823Skarels 		return;
17624823Skarels 
1776591Ssam 	switch (cmd) {
1786591Ssam 
17924823Skarels 	case PRC_QUENCH:
1806591Ssam 		break;
1816591Ssam 
18224823Skarels 	case PRC_ROUTEDEAD:
18321121Skarels 	case PRC_REDIRECT_NET:
18421121Skarels 	case PRC_REDIRECT_HOST:
18524823Skarels 	case PRC_REDIRECT_TOSNET:
18624823Skarels 	case PRC_REDIRECT_TOSHOST:
18724823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
1886591Ssam 		break;
1896591Ssam 
1906591Ssam 	default:
19121121Skarels 		if (inetctlerrmap[cmd] == 0)
19221121Skarels 			return;		/* XXX */
19324823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
19426426Skarels 			udp_notify);
1956591Ssam 	}
1966591Ssam }
1976591Ssam 
1984955Swnj udp_output(inp, m0)
19926060Skarels 	register struct inpcb *inp;
2004926Swnj 	struct mbuf *m0;
2014784Swnj {
2024926Swnj 	register struct mbuf *m;
2034926Swnj 	register struct udpiphdr *ui;
20417165Skarels 	register int len = 0;
2054784Swnj 
2064926Swnj 	/*
2074926Swnj 	 * Calculate data length and get a mbuf
2085246Sroot 	 * for UDP and IP headers.
2094926Swnj 	 */
2104926Swnj 	for (m = m0; m; m = m->m_next)
2114926Swnj 		len += m->m_len;
21221112Skarels 	MGET(m, M_DONTWAIT, MT_HEADER);
2136507Ssam 	if (m == 0) {
2146507Ssam 		m_freem(m0);
2156507Ssam 		return (ENOBUFS);
2166507Ssam 	}
2174784Swnj 
2184926Swnj 	/*
2195246Sroot 	 * Fill in mbuf with extended UDP header
2204926Swnj 	 * and addresses and length put into network format.
2214926Swnj 	 */
2224926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
2234926Swnj 	m->m_len = sizeof (struct udpiphdr);
2244926Swnj 	m->m_next = m0;
2254926Swnj 	ui = mtod(m, struct udpiphdr *);
2264926Swnj 	ui->ui_next = ui->ui_prev = 0;
2274926Swnj 	ui->ui_x1 = 0;
2284926Swnj 	ui->ui_pr = IPPROTO_UDP;
22915226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
2305050Swnj 	ui->ui_src = inp->inp_laddr;
2315050Swnj 	ui->ui_dst = inp->inp_faddr;
2325050Swnj 	ui->ui_sport = inp->inp_lport;
2335050Swnj 	ui->ui_dport = inp->inp_fport;
23415226Ssam 	ui->ui_ulen = ui->ui_len;
2354784Swnj 
2364926Swnj 	/*
2374926Swnj 	 * Stuff checksum and output datagram.
2384926Swnj 	 */
2394926Swnj 	ui->ui_sum = 0;
24019519Skarels 	if (udpcksum) {
24119519Skarels 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
24233715Skarels 		ui->ui_sum = 0xffff;
24321112Skarels 	}
2445050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
24531398Skarels 	((struct ip *)ui)->ip_ttl = udp_ttl;
24626060Skarels 	return (ip_output(m, inp->inp_options, &inp->inp_route,
24726060Skarels 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
2484784Swnj }
2494784Swnj 
25034500Skarels u_long	udp_sendspace = 2048;		/* really max datagram size */
25134500Skarels u_long	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
25218368Skarels 
2538602Sroot /*ARGSUSED*/
25412767Ssam udp_usrreq(so, req, m, nam, rights)
2554887Swnj 	struct socket *so;
2564784Swnj 	int req;
25712767Ssam 	struct mbuf *m, *nam, *rights;
2584784Swnj {
2594887Swnj 	struct inpcb *inp = sotoinpcb(so);
2606507Ssam 	int error = 0;
2614784Swnj 
26218368Skarels 	if (req == PRU_CONTROL)
26318368Skarels 		return (in_control(so, (int)m, (caddr_t)nam,
26418368Skarels 			(struct ifnet *)rights));
26512767Ssam 	if (rights && rights->m_len) {
26612767Ssam 		error = EINVAL;
26712767Ssam 		goto release;
26812767Ssam 	}
26911080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
27011080Ssam 		error = EINVAL;
27111080Ssam 		goto release;
27211080Ssam 	}
2734784Swnj 	switch (req) {
2744784Swnj 
2754784Swnj 	case PRU_ATTACH:
27611080Ssam 		if (inp != NULL) {
27711080Ssam 			error = EINVAL;
27811080Ssam 			break;
27911080Ssam 		}
2808273Sroot 		error = in_pcballoc(so, &udb);
2818273Sroot 		if (error)
2828273Sroot 			break;
28318368Skarels 		error = soreserve(so, udp_sendspace, udp_recvspace);
2848273Sroot 		if (error)
2858273Sroot 			break;
2864887Swnj 		break;
2874784Swnj 
2884784Swnj 	case PRU_DETACH:
2895166Swnj 		in_pcbdetach(inp);
2904887Swnj 		break;
2914784Swnj 
2928273Sroot 	case PRU_BIND:
2938273Sroot 		error = in_pcbbind(inp, nam);
2948273Sroot 		break;
2958273Sroot 
2968273Sroot 	case PRU_LISTEN:
2978273Sroot 		error = EOPNOTSUPP;
2988273Sroot 		break;
2998273Sroot 
3004784Swnj 	case PRU_CONNECT:
30111080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
30211080Ssam 			error = EISCONN;
30311080Ssam 			break;
30411080Ssam 		}
3058273Sroot 		error = in_pcbconnect(inp, nam);
3066507Ssam 		if (error == 0)
3076507Ssam 			soisconnected(so);
3084887Swnj 		break;
3094784Swnj 
31013118Ssam 	case PRU_CONNECT2:
31113118Ssam 		error = EOPNOTSUPP;
31213118Ssam 		break;
31313118Ssam 
3144926Swnj 	case PRU_ACCEPT:
31511080Ssam 		error = EOPNOTSUPP;
31611080Ssam 		break;
3174926Swnj 
3184784Swnj 	case PRU_DISCONNECT:
31911080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
32011080Ssam 			error = ENOTCONN;
32111080Ssam 			break;
32211080Ssam 		}
3235166Swnj 		in_pcbdisconnect(inp);
32426404Skarels 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
3254784Swnj 		break;
3264784Swnj 
3274912Swnj 	case PRU_SHUTDOWN:
3284912Swnj 		socantsendmore(so);
3294912Swnj 		break;
3304912Swnj 
3315996Swnj 	case PRU_SEND: {
3325996Swnj 		struct in_addr laddr;
33316799Skarels 		int s;
3345996Swnj 
3358273Sroot 		if (nam) {
3365996Swnj 			laddr = inp->inp_laddr;
33711080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
33811080Ssam 				error = EISCONN;
33911080Ssam 				break;
34011080Ssam 			}
34116799Skarels 			/*
34216799Skarels 			 * Must block input while temporarily connected.
34316799Skarels 			 */
34416799Skarels 			s = splnet();
3458273Sroot 			error = in_pcbconnect(inp, nam);
34616799Skarels 			if (error) {
34716799Skarels 				splx(s);
3486507Ssam 				break;
34916799Skarels 			}
3504955Swnj 		} else {
35111080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
35211080Ssam 				error = ENOTCONN;
35311080Ssam 				break;
35411080Ssam 			}
3554955Swnj 		}
3566507Ssam 		error = udp_output(inp, m);
35711080Ssam 		m = NULL;
3588273Sroot 		if (nam) {
3595166Swnj 			in_pcbdisconnect(inp);
36018368Skarels 			inp->inp_laddr = laddr;
36116799Skarels 			splx(s);
3625996Swnj 		}
3635996Swnj 		}
3644784Swnj 		break;
3654784Swnj 
3664784Swnj 	case PRU_ABORT:
36731750Skarels 		soisdisconnected(so);
3685166Swnj 		in_pcbdetach(inp);
3694784Swnj 		break;
3704784Swnj 
3716511Ssam 	case PRU_SOCKADDR:
3728273Sroot 		in_setsockaddr(inp, nam);
3736511Ssam 		break;
3746511Ssam 
37514124Ssam 	case PRU_PEERADDR:
37614124Ssam 		in_setpeeraddr(inp, nam);
37714124Ssam 		break;
37814124Ssam 
37916988Skarels 	case PRU_SENSE:
38016988Skarels 		/*
38116988Skarels 		 * stat: don't bother with a blocksize.
38216988Skarels 		 */
38316988Skarels 		return (0);
38416988Skarels 
38512205Ssam 	case PRU_SENDOOB:
38612205Ssam 	case PRU_FASTTIMO:
38712205Ssam 	case PRU_SLOWTIMO:
38812205Ssam 	case PRU_PROTORCV:
38912205Ssam 	case PRU_PROTOSEND:
39012205Ssam 		error =  EOPNOTSUPP;
39112205Ssam 		break;
39213118Ssam 
39316799Skarels 	case PRU_RCVD:
39416799Skarels 	case PRU_RCVOOB:
39516799Skarels 		return (EOPNOTSUPP);	/* do not free mbuf's */
39616799Skarels 
39713118Ssam 	default:
39813118Ssam 		panic("udp_usrreq");
3994805Swnj 	}
40011080Ssam release:
40111080Ssam 	if (m != NULL)
40211080Ssam 		m_freem(m);
4036507Ssam 	return (error);
4044784Swnj }
405