xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 23199)
1 /*
2  * Copyright (c) 1982 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)udp_usrreq.c	6.14 (Berkeley) 06/08/85
7  */
8 
9 #include "param.h"
10 #include "dir.h"
11 #include "user.h"
12 #include "mbuf.h"
13 #include "protosw.h"
14 #include "socket.h"
15 #include "socketvar.h"
16 #include "errno.h"
17 #include "stat.h"
18 
19 #include "../net/if.h"
20 #include "../net/route.h"
21 
22 #include "in.h"
23 #include "in_pcb.h"
24 #include "in_systm.h"
25 #include "ip.h"
26 #include "ip_var.h"
27 #include "ip_icmp.h"
28 #include "udp.h"
29 #include "udp_var.h"
30 
31 /*
32  * UDP protocol implementation.
33  * Per RFC 768, August, 1980.
34  */
35 udp_init()
36 {
37 
38 	udb.inp_next = udb.inp_prev = &udb;
39 }
40 
41 int	udpcksum = 1;
42 struct	sockaddr_in udp_in = { AF_INET };
43 
44 udp_input(m0)
45 	struct mbuf *m0;
46 {
47 	register struct udpiphdr *ui;
48 	register struct inpcb *inp;
49 	register struct mbuf *m;
50 	int len;
51 
52 	/*
53 	 * Get IP and UDP header together in first mbuf.
54 	 */
55 	m = m0;
56 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
57 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
58 		udpstat.udps_hdrops++;
59 		return;
60 	}
61 	ui = mtod(m, struct udpiphdr *);
62 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
63 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
64 
65 	/*
66 	 * Make mbuf data length reflect UDP length.
67 	 * If not enough data to reflect UDP length, drop.
68 	 */
69 	len = ntohs((u_short)ui->ui_ulen);
70 	if (((struct ip *)ui)->ip_len != len) {
71 		if (len > ((struct ip *)ui)->ip_len) {
72 			udpstat.udps_badlen++;
73 			goto bad;
74 		}
75 		m_adj(m, len - ((struct ip *)ui)->ip_len);
76 		/* (struct ip *)ui->ip_len = len; */
77 	}
78 
79 	/*
80 	 * Checksum extended UDP header and data.
81 	 */
82 	if (udpcksum && ui->ui_sum) {
83 		ui->ui_next = ui->ui_prev = 0;
84 		ui->ui_x1 = 0;
85 		ui->ui_len = ui->ui_ulen;
86 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
87 			udpstat.udps_badsum++;
88 			m_freem(m);
89 			return;
90 		}
91 	}
92 
93 	/*
94 	 * Locate pcb for datagram.
95 	 */
96 	inp = in_pcblookup(&udb,
97 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
98 		INPLOOKUP_WILDCARD);
99 	if (inp == 0) {
100 		/* don't send ICMP response for broadcast packet */
101 		if (in_broadcast(ui->ui_dst))
102 			goto bad;
103 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
104 		return;
105 	}
106 
107 	/*
108 	 * Construct sockaddr format source address.
109 	 * Stuff source address and datagram in user buffer.
110 	 */
111 	udp_in.sin_port = ui->ui_sport;
112 	udp_in.sin_addr = ui->ui_src;
113 	m->m_len -= sizeof (struct udpiphdr);
114 	m->m_off += sizeof (struct udpiphdr);
115 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
116 	    m, (struct mbuf *)0) == 0)
117 		goto bad;
118 	sorwakeup(inp->inp_socket);
119 	return;
120 bad:
121 	m_freem(m);
122 }
123 
124 udp_abort(inp)
125 	struct inpcb *inp;
126 {
127 	struct socket *so = inp->inp_socket;
128 
129 	in_pcbdisconnect(inp);
130 	soisdisconnected(so);
131 }
132 
133 udp_ctlinput(cmd, arg)
134 	int cmd;
135 	caddr_t arg;
136 {
137 	struct in_addr *in;
138 	extern u_char inetctlerrmap[];
139 	int in_rtchange();
140 
141 	if (cmd < 0 || cmd > PRC_NCMDS)
142 		return;
143 	switch (cmd) {
144 
145 	case PRC_ROUTEDEAD:
146 		break;
147 
148 	case PRC_REDIRECT_NET:
149 	case PRC_REDIRECT_HOST:
150 		in = &((struct icmp *)arg)->icmp_ip.ip_dst;
151 		in_pcbnotify(&udb, in, 0, in_rtchange);
152 		break;
153 
154 	case PRC_IFDOWN:
155 		in = &((struct sockaddr_in *)arg)->sin_addr;
156 		goto notify;
157 
158 	case PRC_HOSTDEAD:
159 	case PRC_HOSTUNREACH:
160 		in = (struct in_addr *)arg;
161 		goto notify;
162 
163 	default:
164 		if (inetctlerrmap[cmd] == 0)
165 			return;		/* XXX */
166 		in = &((struct icmp *)arg)->icmp_ip.ip_dst;
167 notify:
168 		in_pcbnotify(&udb, in, (int)inetctlerrmap[cmd], udp_abort);
169 	}
170 }
171 
172 udp_output(inp, m0)
173 	struct inpcb *inp;
174 	struct mbuf *m0;
175 {
176 	register struct mbuf *m;
177 	register struct udpiphdr *ui;
178 	register struct socket *so;
179 	register int len = 0;
180 	register struct route *ro;
181 
182 	/*
183 	 * Calculate data length and get a mbuf
184 	 * for UDP and IP headers.
185 	 */
186 	for (m = m0; m; m = m->m_next)
187 		len += m->m_len;
188 	MGET(m, M_DONTWAIT, MT_HEADER);
189 	if (m == 0) {
190 		m_freem(m0);
191 		return (ENOBUFS);
192 	}
193 
194 	/*
195 	 * Fill in mbuf with extended UDP header
196 	 * and addresses and length put into network format.
197 	 */
198 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
199 	m->m_len = sizeof (struct udpiphdr);
200 	m->m_next = m0;
201 	ui = mtod(m, struct udpiphdr *);
202 	ui->ui_next = ui->ui_prev = 0;
203 	ui->ui_x1 = 0;
204 	ui->ui_pr = IPPROTO_UDP;
205 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
206 	ui->ui_src = inp->inp_laddr;
207 	ui->ui_dst = inp->inp_faddr;
208 	ui->ui_sport = inp->inp_lport;
209 	ui->ui_dport = inp->inp_fport;
210 	ui->ui_ulen = ui->ui_len;
211 
212 	/*
213 	 * Stuff checksum and output datagram.
214 	 */
215 	ui->ui_sum = 0;
216 	if (udpcksum) {
217 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
218 		ui->ui_sum = -1;
219 	}
220 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
221 	((struct ip *)ui)->ip_ttl = MAXTTL;
222 	so = inp->inp_socket;
223 	if (so->so_options & SO_DONTROUTE)
224 		return (ip_output(m, (struct mbuf *)0, (struct route *)0,
225 		    (so->so_options & SO_BROADCAST) | IP_ROUTETOIF));
226 	/*
227 	 * Use cached route for previous datagram if
228 	 * this is also to the same destination.
229 	 *
230 	 * NB: We don't handle broadcasts because that
231 	 *     would require 3 subroutine calls.
232 	 */
233 	ro = &inp->inp_route;
234 #define	satosin(sa)	((struct sockaddr_in *)(sa))
235 	if (ro->ro_rt &&
236 	    satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) {
237 		RTFREE(ro->ro_rt);
238 		ro->ro_rt = (struct rtentry *)0;
239 	}
240 	return (ip_output(m, (struct mbuf *)0, ro,
241 	    so->so_options & SO_BROADCAST));
242 }
243 
244 int	udp_sendspace = 2048;		/* really max datagram size */
245 int	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
246 
247 /*ARGSUSED*/
248 udp_usrreq(so, req, m, nam, rights)
249 	struct socket *so;
250 	int req;
251 	struct mbuf *m, *nam, *rights;
252 {
253 	struct inpcb *inp = sotoinpcb(so);
254 	int error = 0;
255 
256 	if (req == PRU_CONTROL)
257 		return (in_control(so, (int)m, (caddr_t)nam,
258 			(struct ifnet *)rights));
259 	if (rights && rights->m_len) {
260 		error = EINVAL;
261 		goto release;
262 	}
263 	if (inp == NULL && req != PRU_ATTACH) {
264 		error = EINVAL;
265 		goto release;
266 	}
267 	switch (req) {
268 
269 	case PRU_ATTACH:
270 		if (inp != NULL) {
271 			error = EINVAL;
272 			break;
273 		}
274 		error = in_pcballoc(so, &udb);
275 		if (error)
276 			break;
277 		error = soreserve(so, udp_sendspace, udp_recvspace);
278 		if (error)
279 			break;
280 		break;
281 
282 	case PRU_DETACH:
283 		if (inp == NULL) {
284 			error = ENOTCONN;
285 			break;
286 		}
287 		in_pcbdetach(inp);
288 		break;
289 
290 	case PRU_BIND:
291 		error = in_pcbbind(inp, nam);
292 		break;
293 
294 	case PRU_LISTEN:
295 		error = EOPNOTSUPP;
296 		break;
297 
298 	case PRU_CONNECT:
299 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
300 			error = EISCONN;
301 			break;
302 		}
303 		error = in_pcbconnect(inp, nam);
304 		if (error == 0)
305 			soisconnected(so);
306 		break;
307 
308 	case PRU_CONNECT2:
309 		error = EOPNOTSUPP;
310 		break;
311 
312 	case PRU_ACCEPT:
313 		error = EOPNOTSUPP;
314 		break;
315 
316 	case PRU_DISCONNECT:
317 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
318 			error = ENOTCONN;
319 			break;
320 		}
321 		in_pcbdisconnect(inp);
322 		soisdisconnected(so);
323 		break;
324 
325 	case PRU_SHUTDOWN:
326 		socantsendmore(so);
327 		break;
328 
329 	case PRU_SEND: {
330 		struct in_addr laddr;
331 		int s;
332 
333 		if (nam) {
334 			laddr = inp->inp_laddr;
335 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
336 				error = EISCONN;
337 				break;
338 			}
339 			/*
340 			 * Must block input while temporarily connected.
341 			 */
342 			s = splnet();
343 			error = in_pcbconnect(inp, nam);
344 			if (error) {
345 				splx(s);
346 				break;
347 			}
348 		} else {
349 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
350 				error = ENOTCONN;
351 				break;
352 			}
353 		}
354 		error = udp_output(inp, m);
355 		m = NULL;
356 		if (nam) {
357 			in_pcbdisconnect(inp);
358 			inp->inp_laddr = laddr;
359 			splx(s);
360 		}
361 		}
362 		break;
363 
364 	case PRU_ABORT:
365 		in_pcbdetach(inp);
366 		sofree(so);
367 		soisdisconnected(so);
368 		break;
369 
370 	case PRU_SOCKADDR:
371 		in_setsockaddr(inp, nam);
372 		break;
373 
374 	case PRU_PEERADDR:
375 		in_setpeeraddr(inp, nam);
376 		break;
377 
378 	case PRU_SENSE:
379 		/*
380 		 * stat: don't bother with a blocksize.
381 		 */
382 		return (0);
383 
384 	case PRU_SENDOOB:
385 	case PRU_FASTTIMO:
386 	case PRU_SLOWTIMO:
387 	case PRU_PROTORCV:
388 	case PRU_PROTOSEND:
389 		error =  EOPNOTSUPP;
390 		break;
391 
392 	case PRU_RCVD:
393 	case PRU_RCVOOB:
394 		return (EOPNOTSUPP);	/* do not free mbuf's */
395 
396 	default:
397 		panic("udp_usrreq");
398 	}
399 release:
400 	if (m != NULL)
401 		m_freem(m);
402 	return (error);
403 }
404