xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 7157)
1 /*	udp_usrreq.c	4.29	82/06/12	*/
2 
3 #include "../h/param.h"
4 #include "../h/dir.h"
5 #include "../h/user.h"
6 #include "../h/mbuf.h"
7 #include "../h/protosw.h"
8 #include "../h/socket.h"
9 #include "../h/socketvar.h"
10 #include "../net/in.h"
11 #include "../net/if.h"
12 #include "../net/route.h"
13 #include "../net/in_pcb.h"
14 #include "../net/in_systm.h"
15 #include "../net/ip.h"
16 #include "../net/ip_var.h"
17 #include "../net/ip_icmp.h"
18 #include "../net/udp.h"
19 #include "../net/udp_var.h"
20 #include <errno.h>
21 
22 /*
23  * UDP protocol implementation.
24  * Per RFC 768, August, 1980.
25  */
26 udp_init()
27 {
28 
29 COUNT(UDP_INIT);
30 	udb.inp_next = udb.inp_prev = &udb;
31 }
32 
33 int	udpcksum;
34 struct	sockaddr_in udp_in = { AF_INET };
35 
36 udp_input(m0)
37 	struct mbuf *m0;
38 {
39 	register struct udpiphdr *ui;
40 	register struct inpcb *inp;
41 	register struct mbuf *m;
42 	int len, ulen;
43 
44 COUNT(UDP_INPUT);
45 	/*
46 	 * Get IP and UDP header together in first mbuf.
47 	 */
48 	m = m0;
49 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
50 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
51 		udpstat.udps_hdrops++;
52 		return;
53 	}
54 	ui = mtod(m, struct udpiphdr *);
55 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
56 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
57 
58 	/*
59 	 * Make mbuf data length reflect UDP length.
60 	 * If not enough data to reflect UDP length, drop.
61 	 */
62 	ulen = ntohs((u_short)ui->ui_ulen);
63 	len = sizeof (struct udphdr) + ulen;
64 	if (((struct ip *)ui)->ip_len != len) {
65 		if (len > ((struct ip *)ui)->ip_len) {
66 			udpstat.udps_badlen++;
67 			goto bad;
68 		}
69 		m_adj(m, ((struct ip *)ui)->ip_len - len);
70 		/* (struct ip *)ui->ip_len = len; */
71 	}
72 
73 	/*
74 	 * Checksum extended UDP header and data.
75 	 */
76 	if (udpcksum) {
77 		ui->ui_next = ui->ui_prev = 0;
78 		ui->ui_x1 = 0;
79 		ui->ui_len = htons((u_short)(sizeof (struct udphdr) + ulen));
80 		if (ui->ui_sum = in_cksum(m, len)) {
81 			udpstat.udps_badsum++;
82 			printf("udp cksum %x\n", ui->ui_sum);
83 			m_freem(m);
84 			return;
85 		}
86 	}
87 
88 	/*
89 	 * Locate pcb for datagram.  On wildcard match, update
90 	 * control block to anchor network and host address.
91 	 */
92 	inp = in_pcblookup(&udb,
93 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
94 		INPLOOKUP_WILDCARD);
95 	if (inp == 0) {
96 		struct in_addr broadcastaddr;
97 
98 		broadcastaddr = if_makeaddr(ui->ui_dst.s_net, INADDR_ANY);
99 		if (ui->ui_dst.s_addr == broadcastaddr.s_addr)
100 			goto bad;
101 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
102 		return;
103 	}
104 
105 	/*
106 	 * Construct sockaddr format source address.
107 	 * Stuff source address and datagram in user buffer.
108 	 */
109 	udp_in.sin_port = ui->ui_sport;
110 	udp_in.sin_addr = ui->ui_src;
111 	m->m_len -= sizeof (struct udpiphdr);
112 	m->m_off += sizeof (struct udpiphdr);
113 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
114 		goto bad;
115 	sorwakeup(inp->inp_socket);
116 	return;
117 bad:
118 	m_freem(m);
119 }
120 
121 udp_abort(inp)
122 	struct inpcb *inp;
123 {
124 	struct socket *so = inp->inp_socket;
125 
126 	in_pcbdisconnect(inp);
127 	soisdisconnected(so);
128 }
129 
130 udp_ctlinput(cmd, arg)
131 	int cmd;
132 	caddr_t arg;
133 {
134 	struct in_addr *sin;
135 	extern u_char inetctlerrmap[];
136 COUNT(UDP_CTLINPUT);
137 
138 	if (cmd < 0 || cmd > PRC_NCMDS)
139 		return;
140 	switch (cmd) {
141 
142 	case PRC_ROUTEDEAD:
143 		break;
144 
145 	case PRC_QUENCH:
146 		break;
147 
148 	/* these are handled by ip */
149 	case PRC_IFDOWN:
150 	case PRC_HOSTDEAD:
151 	case PRC_HOSTUNREACH:
152 		break;
153 
154 	default:
155 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
156 		in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
157 	}
158 }
159 
160 udp_output(inp, m0)
161 	struct inpcb *inp;
162 	struct mbuf *m0;
163 {
164 	register struct mbuf *m;
165 	register struct udpiphdr *ui;
166 	register struct socket *so;
167 	register int len = 0;
168 
169 COUNT(UDP_OUTPUT);
170 	/*
171 	 * Calculate data length and get a mbuf
172 	 * for UDP and IP headers.
173 	 */
174 	for (m = m0; m; m = m->m_next)
175 		len += m->m_len;
176 	m = m_get(M_DONTWAIT);
177 	if (m == 0) {
178 		m_freem(m0);
179 		return (ENOBUFS);
180 	}
181 
182 	/*
183 	 * Fill in mbuf with extended UDP header
184 	 * and addresses and length put into network format.
185 	 */
186 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
187 	m->m_len = sizeof (struct udpiphdr);
188 	m->m_next = m0;
189 	ui = mtod(m, struct udpiphdr *);
190 	ui->ui_next = ui->ui_prev = 0;
191 	ui->ui_x1 = 0;
192 	ui->ui_pr = IPPROTO_UDP;
193 	ui->ui_len = sizeof (struct udpiphdr) + len;
194 	ui->ui_src = inp->inp_laddr;
195 	ui->ui_dst = inp->inp_faddr;
196 	ui->ui_sport = inp->inp_lport;
197 	ui->ui_dport = inp->inp_fport;
198 	ui->ui_ulen = len;
199 #if vax
200 	ui->ui_ulen = htons(ui->ui_ulen);
201 #endif
202 
203 	/*
204 	 * Stuff checksum and output datagram.
205 	 */
206 	ui->ui_sum = 0;
207 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
208 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
209 	((struct ip *)ui)->ip_ttl = MAXTTL;
210 	so = inp->inp_socket;
211 	return (ip_output(m, (struct mbuf *)0,
212 	    (so->so_options & SO_DONTROUTE) ? &routetoif : (struct route *)0,
213 	    so->so_state & SS_PRIV));
214 }
215 
216 udp_usrreq(so, req, m, addr)
217 	struct socket *so;
218 	int req;
219 	struct mbuf *m;
220 	caddr_t addr;
221 {
222 	struct inpcb *inp = sotoinpcb(so);
223 	int error = 0;
224 
225 COUNT(UDP_USRREQ);
226 	if (inp == 0 && req != PRU_ATTACH)
227 		return (EINVAL);
228 	switch (req) {
229 
230 	case PRU_ATTACH:
231 		if (inp != 0)
232 			return (EINVAL);
233 		error = in_pcbattach(so, &udb, 2048, 2048,
234 				(struct sockaddr_in *)addr);
235 		break;
236 
237 	case PRU_DETACH:
238 		if (inp == 0)
239 			return (ENOTCONN);
240 		in_pcbdetach(inp);
241 		break;
242 
243 	case PRU_CONNECT:
244 		if (inp->inp_faddr.s_addr)
245 			return (EISCONN);
246 		error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
247 		if (error == 0)
248 			soisconnected(so);
249 		break;
250 
251 	case PRU_ACCEPT:
252 		return (EOPNOTSUPP);
253 
254 	case PRU_DISCONNECT:
255 		if (inp->inp_faddr.s_addr == 0)
256 			return (ENOTCONN);
257 		in_pcbdisconnect(inp);
258 		soisdisconnected(so);
259 		break;
260 
261 	case PRU_SHUTDOWN:
262 		socantsendmore(so);
263 		break;
264 
265 	case PRU_SEND: {
266 		struct in_addr laddr;
267 
268 		if (addr) {
269 			laddr = inp->inp_laddr;
270 			if (inp->inp_faddr.s_addr)
271 				return (EISCONN);
272 			error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
273 			if (error)
274 				break;
275 		} else {
276 			if (inp->inp_faddr.s_addr == 0)
277 				return (ENOTCONN);
278 		}
279 		error = udp_output(inp, m);
280 		if (addr) {
281 			in_pcbdisconnect(inp);
282 			inp->inp_laddr = laddr;
283 		}
284 		}
285 		break;
286 
287 	case PRU_ABORT:
288 		in_pcbdetach(inp);
289 		sofree(so);
290 		soisdisconnected(so);
291 		break;
292 
293 	case PRU_CONTROL:
294 		return (EOPNOTSUPP);
295 
296 	case PRU_SOCKADDR:
297 		in_setsockaddr((struct sockaddr_in *)addr, inp);
298 		break;
299 
300 	default:
301 		panic("udp_usrreq");
302 	}
303 	return (error);
304 }
305