xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 32789)
123199Smckusick /*
229158Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
3*32789Sbostic  * All rights reserved.
423199Smckusick  *
5*32789Sbostic  * Redistribution and use in source and binary forms are permitted
6*32789Sbostic  * provided that this notice is preserved and that due credit is given
7*32789Sbostic  * to the University of California at Berkeley. The name of the University
8*32789Sbostic  * may not be used to endorse or promote products derived from this
9*32789Sbostic  * software without specific prior written permission. This software
10*32789Sbostic  * is provided ``as is'' without express or implied warranty.
11*32789Sbostic  *
12*32789Sbostic  *	@(#)udp_usrreq.c	7.4 (Berkeley) 12/07/87
1323199Smckusick  */
144784Swnj 
1517065Sbloom #include "param.h"
1617065Sbloom #include "dir.h"
1717065Sbloom #include "user.h"
1817065Sbloom #include "mbuf.h"
1917065Sbloom #include "protosw.h"
2017065Sbloom #include "socket.h"
2117065Sbloom #include "socketvar.h"
2217065Sbloom #include "errno.h"
2317065Sbloom #include "stat.h"
2410897Ssam 
256584Ssam #include "../net/if.h"
266354Ssam #include "../net/route.h"
2710897Ssam 
2817065Sbloom #include "in.h"
2917065Sbloom #include "in_pcb.h"
3017065Sbloom #include "in_systm.h"
3117065Sbloom #include "ip.h"
3217065Sbloom #include "ip_var.h"
3317065Sbloom #include "ip_icmp.h"
3417065Sbloom #include "udp.h"
3517065Sbloom #include "udp_var.h"
364784Swnj 
374926Swnj /*
384926Swnj  * UDP protocol implementation.
394926Swnj  * Per RFC 768, August, 1980.
404926Swnj  */
414805Swnj udp_init()
424805Swnj {
434805Swnj 
444901Swnj 	udb.inp_next = udb.inp_prev = &udb;
454805Swnj }
464805Swnj 
4726118Skarels #ifndef	COMPAT_42
4819519Skarels int	udpcksum = 1;
4926118Skarels #else
5026118Skarels int	udpcksum = 0;		/* XXX */
5126118Skarels #endif
5231398Skarels int	udp_ttl = UDP_TTL;
5326118Skarels 
544926Swnj struct	sockaddr_in udp_in = { AF_INET };
554901Swnj 
5626032Skarels udp_input(m0, ifp)
574926Swnj 	struct mbuf *m0;
5826032Skarels 	struct ifnet *ifp;
594784Swnj {
604901Swnj 	register struct udpiphdr *ui;
614887Swnj 	register struct inpcb *inp;
624926Swnj 	register struct mbuf *m;
637844Sroot 	int len;
6424823Skarels 	struct ip ip;
654784Swnj 
664926Swnj 	/*
675246Sroot 	 * Get IP and UDP header together in first mbuf.
684926Swnj 	 */
694926Swnj 	m = m0;
705308Sroot 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
715308Sroot 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
724926Swnj 		udpstat.udps_hdrops++;
735308Sroot 		return;
744926Swnj 	}
755050Swnj 	ui = mtod(m, struct udpiphdr *);
765246Sroot 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
775220Swnj 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
784926Swnj 
794926Swnj 	/*
805246Sroot 	 * Make mbuf data length reflect UDP length.
815246Sroot 	 * If not enough data to reflect UDP length, drop.
824926Swnj 	 */
837844Sroot 	len = ntohs((u_short)ui->ui_ulen);
844926Swnj 	if (((struct ip *)ui)->ip_len != len) {
854926Swnj 		if (len > ((struct ip *)ui)->ip_len) {
864926Swnj 			udpstat.udps_badlen++;
874926Swnj 			goto bad;
884926Swnj 		}
8915539Skarels 		m_adj(m, len - ((struct ip *)ui)->ip_len);
9024823Skarels 		/* ((struct ip *)ui)->ip_len = len; */
914926Swnj 	}
9224823Skarels 	/*
9324823Skarels 	 * Save a copy of the IP header in case we want restore it for ICMP.
9424823Skarels 	 */
9524823Skarels 	ip = *(struct ip*)ui;
964926Swnj 
974926Swnj 	/*
985246Sroot 	 * Checksum extended UDP header and data.
994926Swnj 	 */
10015539Skarels 	if (udpcksum && ui->ui_sum) {
1014926Swnj 		ui->ui_next = ui->ui_prev = 0;
1024926Swnj 		ui->ui_x1 = 0;
10321112Skarels 		ui->ui_len = ui->ui_ulen;
1047844Sroot 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
1054926Swnj 			udpstat.udps_badsum++;
1064901Swnj 			m_freem(m);
1074901Swnj 			return;
1084901Swnj 		}
1094901Swnj 	}
1104926Swnj 
1114926Swnj 	/*
1127844Sroot 	 * Locate pcb for datagram.
1134926Swnj 	 */
1144926Swnj 	inp = in_pcblookup(&udb,
1156029Sroot 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
1166029Sroot 		INPLOOKUP_WILDCARD);
1176584Ssam 	if (inp == 0) {
11810144Ssam 		/* don't send ICMP response for broadcast packet */
11921112Skarels 		if (in_broadcast(ui->ui_dst))
1206584Ssam 			goto bad;
12124823Skarels 		*(struct ip *)ui = ip;
12226032Skarels 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
12326032Skarels 		    ifp);
1246584Ssam 		return;
1256584Ssam 	}
1266584Ssam 
1274926Swnj 	/*
1284926Swnj 	 * Construct sockaddr format source address.
1294926Swnj 	 * Stuff source address and datagram in user buffer.
1304926Swnj 	 */
1314926Swnj 	udp_in.sin_port = ui->ui_sport;
1324926Swnj 	udp_in.sin_addr = ui->ui_src;
1335050Swnj 	m->m_len -= sizeof (struct udpiphdr);
1345050Swnj 	m->m_off += sizeof (struct udpiphdr);
13512767Ssam 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
13612767Ssam 	    m, (struct mbuf *)0) == 0)
1374926Swnj 		goto bad;
1385050Swnj 	sorwakeup(inp->inp_socket);
1394887Swnj 	return;
1404926Swnj bad:
1414887Swnj 	m_freem(m);
1424784Swnj }
1434784Swnj 
14426426Skarels /*
14526426Skarels  * Notify a udp user of an asynchronous error;
14626426Skarels  * just wake up so that he can collect error status.
14726426Skarels  */
14826426Skarels udp_notify(inp)
14926426Skarels 	register struct inpcb *inp;
15026426Skarels {
15126426Skarels 
15226426Skarels 	sorwakeup(inp->inp_socket);
15326426Skarels 	sowwakeup(inp->inp_socket);
15426426Skarels }
15526426Skarels 
15624823Skarels udp_ctlinput(cmd, sa)
1576591Ssam 	int cmd;
15824823Skarels 	struct sockaddr *sa;
1596591Ssam {
1606591Ssam 	extern u_char inetctlerrmap[];
16124823Skarels 	struct sockaddr_in *sin;
16221121Skarels 	int in_rtchange();
1636591Ssam 
16424823Skarels 	if ((unsigned)cmd > PRC_NCMDS)
1656591Ssam 		return;
16624823Skarels 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
16724823Skarels 		return;
16824823Skarels 	sin = (struct sockaddr_in *)sa;
16924823Skarels 	if (sin->sin_addr.s_addr == INADDR_ANY)
17024823Skarels 		return;
17124823Skarels 
1726591Ssam 	switch (cmd) {
1736591Ssam 
17424823Skarels 	case PRC_QUENCH:
1756591Ssam 		break;
1766591Ssam 
17724823Skarels 	case PRC_ROUTEDEAD:
17821121Skarels 	case PRC_REDIRECT_NET:
17921121Skarels 	case PRC_REDIRECT_HOST:
18024823Skarels 	case PRC_REDIRECT_TOSNET:
18124823Skarels 	case PRC_REDIRECT_TOSHOST:
18224823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
1836591Ssam 		break;
1846591Ssam 
1856591Ssam 	default:
18621121Skarels 		if (inetctlerrmap[cmd] == 0)
18721121Skarels 			return;		/* XXX */
18824823Skarels 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
18926426Skarels 			udp_notify);
1906591Ssam 	}
1916591Ssam }
1926591Ssam 
1934955Swnj udp_output(inp, m0)
19426060Skarels 	register struct inpcb *inp;
1954926Swnj 	struct mbuf *m0;
1964784Swnj {
1974926Swnj 	register struct mbuf *m;
1984926Swnj 	register struct udpiphdr *ui;
19917165Skarels 	register int len = 0;
2004784Swnj 
2014926Swnj 	/*
2024926Swnj 	 * Calculate data length and get a mbuf
2035246Sroot 	 * for UDP and IP headers.
2044926Swnj 	 */
2054926Swnj 	for (m = m0; m; m = m->m_next)
2064926Swnj 		len += m->m_len;
20721112Skarels 	MGET(m, M_DONTWAIT, MT_HEADER);
2086507Ssam 	if (m == 0) {
2096507Ssam 		m_freem(m0);
2106507Ssam 		return (ENOBUFS);
2116507Ssam 	}
2124784Swnj 
2134926Swnj 	/*
2145246Sroot 	 * Fill in mbuf with extended UDP header
2154926Swnj 	 * and addresses and length put into network format.
2164926Swnj 	 */
2174926Swnj 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
2184926Swnj 	m->m_len = sizeof (struct udpiphdr);
2194926Swnj 	m->m_next = m0;
2204926Swnj 	ui = mtod(m, struct udpiphdr *);
2214926Swnj 	ui->ui_next = ui->ui_prev = 0;
2224926Swnj 	ui->ui_x1 = 0;
2234926Swnj 	ui->ui_pr = IPPROTO_UDP;
22415226Ssam 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
2255050Swnj 	ui->ui_src = inp->inp_laddr;
2265050Swnj 	ui->ui_dst = inp->inp_faddr;
2275050Swnj 	ui->ui_sport = inp->inp_lport;
2285050Swnj 	ui->ui_dport = inp->inp_fport;
22915226Ssam 	ui->ui_ulen = ui->ui_len;
2304784Swnj 
2314926Swnj 	/*
2324926Swnj 	 * Stuff checksum and output datagram.
2334926Swnj 	 */
2344926Swnj 	ui->ui_sum = 0;
23519519Skarels 	if (udpcksum) {
23619519Skarels 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
23715539Skarels 		ui->ui_sum = -1;
23821112Skarels 	}
2395050Swnj 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
24031398Skarels 	((struct ip *)ui)->ip_ttl = udp_ttl;
24126060Skarels 	return (ip_output(m, inp->inp_options, &inp->inp_route,
24226060Skarels 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
2434784Swnj }
2444784Swnj 
24518368Skarels int	udp_sendspace = 2048;		/* really max datagram size */
24618368Skarels int	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
24718368Skarels 
2488602Sroot /*ARGSUSED*/
24912767Ssam udp_usrreq(so, req, m, nam, rights)
2504887Swnj 	struct socket *so;
2514784Swnj 	int req;
25212767Ssam 	struct mbuf *m, *nam, *rights;
2534784Swnj {
2544887Swnj 	struct inpcb *inp = sotoinpcb(so);
2556507Ssam 	int error = 0;
2564784Swnj 
25718368Skarels 	if (req == PRU_CONTROL)
25818368Skarels 		return (in_control(so, (int)m, (caddr_t)nam,
25918368Skarels 			(struct ifnet *)rights));
26012767Ssam 	if (rights && rights->m_len) {
26112767Ssam 		error = EINVAL;
26212767Ssam 		goto release;
26312767Ssam 	}
26411080Ssam 	if (inp == NULL && req != PRU_ATTACH) {
26511080Ssam 		error = EINVAL;
26611080Ssam 		goto release;
26711080Ssam 	}
2684784Swnj 	switch (req) {
2694784Swnj 
2704784Swnj 	case PRU_ATTACH:
27111080Ssam 		if (inp != NULL) {
27211080Ssam 			error = EINVAL;
27311080Ssam 			break;
27411080Ssam 		}
2758273Sroot 		error = in_pcballoc(so, &udb);
2768273Sroot 		if (error)
2778273Sroot 			break;
27818368Skarels 		error = soreserve(so, udp_sendspace, udp_recvspace);
2798273Sroot 		if (error)
2808273Sroot 			break;
2814887Swnj 		break;
2824784Swnj 
2834784Swnj 	case PRU_DETACH:
2845166Swnj 		in_pcbdetach(inp);
2854887Swnj 		break;
2864784Swnj 
2878273Sroot 	case PRU_BIND:
2888273Sroot 		error = in_pcbbind(inp, nam);
2898273Sroot 		break;
2908273Sroot 
2918273Sroot 	case PRU_LISTEN:
2928273Sroot 		error = EOPNOTSUPP;
2938273Sroot 		break;
2948273Sroot 
2954784Swnj 	case PRU_CONNECT:
29611080Ssam 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
29711080Ssam 			error = EISCONN;
29811080Ssam 			break;
29911080Ssam 		}
3008273Sroot 		error = in_pcbconnect(inp, nam);
3016507Ssam 		if (error == 0)
3026507Ssam 			soisconnected(so);
3034887Swnj 		break;
3044784Swnj 
30513118Ssam 	case PRU_CONNECT2:
30613118Ssam 		error = EOPNOTSUPP;
30713118Ssam 		break;
30813118Ssam 
3094926Swnj 	case PRU_ACCEPT:
31011080Ssam 		error = EOPNOTSUPP;
31111080Ssam 		break;
3124926Swnj 
3134784Swnj 	case PRU_DISCONNECT:
31411080Ssam 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
31511080Ssam 			error = ENOTCONN;
31611080Ssam 			break;
31711080Ssam 		}
3185166Swnj 		in_pcbdisconnect(inp);
31926404Skarels 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
3204784Swnj 		break;
3214784Swnj 
3224912Swnj 	case PRU_SHUTDOWN:
3234912Swnj 		socantsendmore(so);
3244912Swnj 		break;
3254912Swnj 
3265996Swnj 	case PRU_SEND: {
3275996Swnj 		struct in_addr laddr;
32816799Skarels 		int s;
3295996Swnj 
3308273Sroot 		if (nam) {
3315996Swnj 			laddr = inp->inp_laddr;
33211080Ssam 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
33311080Ssam 				error = EISCONN;
33411080Ssam 				break;
33511080Ssam 			}
33616799Skarels 			/*
33716799Skarels 			 * Must block input while temporarily connected.
33816799Skarels 			 */
33916799Skarels 			s = splnet();
3408273Sroot 			error = in_pcbconnect(inp, nam);
34116799Skarels 			if (error) {
34216799Skarels 				splx(s);
3436507Ssam 				break;
34416799Skarels 			}
3454955Swnj 		} else {
34611080Ssam 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
34711080Ssam 				error = ENOTCONN;
34811080Ssam 				break;
34911080Ssam 			}
3504955Swnj 		}
3516507Ssam 		error = udp_output(inp, m);
35211080Ssam 		m = NULL;
3538273Sroot 		if (nam) {
3545166Swnj 			in_pcbdisconnect(inp);
35518368Skarels 			inp->inp_laddr = laddr;
35616799Skarels 			splx(s);
3575996Swnj 		}
3585996Swnj 		}
3594784Swnj 		break;
3604784Swnj 
3614784Swnj 	case PRU_ABORT:
36231750Skarels 		soisdisconnected(so);
3635166Swnj 		in_pcbdetach(inp);
3644784Swnj 		break;
3654784Swnj 
3666511Ssam 	case PRU_SOCKADDR:
3678273Sroot 		in_setsockaddr(inp, nam);
3686511Ssam 		break;
3696511Ssam 
37014124Ssam 	case PRU_PEERADDR:
37114124Ssam 		in_setpeeraddr(inp, nam);
37214124Ssam 		break;
37314124Ssam 
37416988Skarels 	case PRU_SENSE:
37516988Skarels 		/*
37616988Skarels 		 * stat: don't bother with a blocksize.
37716988Skarels 		 */
37816988Skarels 		return (0);
37916988Skarels 
38012205Ssam 	case PRU_SENDOOB:
38112205Ssam 	case PRU_FASTTIMO:
38212205Ssam 	case PRU_SLOWTIMO:
38312205Ssam 	case PRU_PROTORCV:
38412205Ssam 	case PRU_PROTOSEND:
38512205Ssam 		error =  EOPNOTSUPP;
38612205Ssam 		break;
38713118Ssam 
38816799Skarels 	case PRU_RCVD:
38916799Skarels 	case PRU_RCVOOB:
39016799Skarels 		return (EOPNOTSUPP);	/* do not free mbuf's */
39116799Skarels 
39213118Ssam 	default:
39313118Ssam 		panic("udp_usrreq");
3944805Swnj 	}
39511080Ssam release:
39611080Ssam 	if (m != NULL)
39711080Ssam 		m_freem(m);
3986507Ssam 	return (error);
3994784Swnj }
400