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