xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 35794)
1 /*
2  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  *	@(#)udp_usrreq.c	7.9 (Berkeley) 10/12/88
18  */
19 
20 #include "param.h"
21 #include "dir.h"
22 #include "user.h"
23 #include "malloc.h"
24 #include "mbuf.h"
25 #include "protosw.h"
26 #include "socket.h"
27 #include "socketvar.h"
28 #include "errno.h"
29 #include "stat.h"
30 
31 #include "../net/if.h"
32 #include "../net/route.h"
33 
34 #include "in.h"
35 #include "in_pcb.h"
36 #include "in_systm.h"
37 #include "ip.h"
38 #include "ip_var.h"
39 #include "ip_icmp.h"
40 #include "udp.h"
41 #include "udp_var.h"
42 
43 /*
44  * UDP protocol implementation.
45  * Per RFC 768, August, 1980.
46  */
47 udp_init()
48 {
49 
50 	udb.inp_next = udb.inp_prev = &udb;
51 }
52 
53 #ifndef	COMPAT_42
54 int	udpcksum = 1;
55 #else
56 int	udpcksum = 0;		/* XXX */
57 #endif
58 int	udp_ttl = UDP_TTL;
59 
60 struct	sockaddr_in udp_in = { AF_INET };
61 
62 udp_input(m, iphlen)
63 	register struct mbuf *m;
64 	int iphlen;
65 {
66 	register struct udpiphdr *ui;
67 	register struct inpcb *inp;
68 	int len;
69 	struct ip ip;
70 
71 	/*
72 	 * Get IP and UDP header together in first mbuf.
73 	 * Note: IP leaves IP header in first mbuf.
74 	 */
75 	ui = mtod(m, struct udpiphdr *);
76 	if (iphlen > sizeof (struct ip))
77 		ip_stripoptions(m, (struct mbuf *)0);
78 	if (m->m_len < sizeof (struct udpiphdr)) {
79 		if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
80 			udpstat.udps_hdrops++;
81 			return;
82 		}
83 		ui = mtod(m, struct udpiphdr *);
84 	}
85 
86 	/*
87 	 * Make mbuf data length reflect UDP length.
88 	 * If not enough data to reflect UDP length, drop.
89 	 */
90 	len = ntohs((u_short)ui->ui_ulen);
91 	if (((struct ip *)ui)->ip_len != len) {
92 		if (len > ((struct ip *)ui)->ip_len) {
93 			udpstat.udps_badlen++;
94 			goto bad;
95 		}
96 		m_adj(m, len - ((struct ip *)ui)->ip_len);
97 		/* ((struct ip *)ui)->ip_len = len; */
98 	}
99 	/*
100 	 * Save a copy of the IP header in case we want restore it for ICMP.
101 	 */
102 	ip = *(struct ip *)ui;
103 
104 	/*
105 	 * Checksum extended UDP header and data.
106 	 */
107 	if (udpcksum && ui->ui_sum) {
108 		ui->ui_next = ui->ui_prev = 0;
109 		ui->ui_x1 = 0;
110 		ui->ui_len = ui->ui_ulen;
111 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
112 			udpstat.udps_badsum++;
113 			m_freem(m);
114 			return;
115 		}
116 	}
117 
118 	/*
119 	 * Locate pcb for datagram.
120 	 */
121 	inp = in_pcblookup(&udb,
122 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
123 		INPLOOKUP_WILDCARD);
124 	if (inp == 0) {
125 		/* don't send ICMP response for broadcast packet */
126 		if (m->m_flags & M_BCAST)
127 			goto bad;
128 		*(struct ip *)ui = ip;
129 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
130 		return;
131 	}
132 
133 	/*
134 	 * Construct sockaddr format source address.
135 	 * Stuff source address and datagram in user buffer.
136 	 */
137 	udp_in.sin_port = ui->ui_sport;
138 	udp_in.sin_addr = ui->ui_src;
139 	m->m_len -= sizeof (struct udpiphdr);
140 	m->m_pkthdr.len -= sizeof (struct udpiphdr);
141 	m->m_data += sizeof (struct udpiphdr);
142 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
143 	    m, (struct mbuf *)0) == 0)
144 		goto bad;
145 	sorwakeup(inp->inp_socket);
146 	return;
147 bad:
148 	m_freem(m);
149 }
150 
151 /*
152  * Notify a udp user of an asynchronous error;
153  * just wake up so that he can collect error status.
154  */
155 udp_notify(inp)
156 	register struct inpcb *inp;
157 {
158 
159 	sorwakeup(inp->inp_socket);
160 	sowwakeup(inp->inp_socket);
161 }
162 
163 udp_ctlinput(cmd, sa)
164 	int cmd;
165 	struct sockaddr *sa;
166 {
167 	extern u_char inetctlerrmap[];
168 	struct sockaddr_in *sin;
169 	int in_rtchange();
170 
171 	if ((unsigned)cmd > PRC_NCMDS)
172 		return;
173 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
174 		return;
175 	sin = (struct sockaddr_in *)sa;
176 	if (sin->sin_addr.s_addr == INADDR_ANY)
177 		return;
178 
179 	switch (cmd) {
180 
181 	case PRC_QUENCH:
182 		break;
183 
184 	case PRC_ROUTEDEAD:
185 	case PRC_REDIRECT_NET:
186 	case PRC_REDIRECT_HOST:
187 	case PRC_REDIRECT_TOSNET:
188 	case PRC_REDIRECT_TOSHOST:
189 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
190 		break;
191 
192 	default:
193 		if (inetctlerrmap[cmd] == 0)
194 			return;		/* XXX */
195 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
196 			udp_notify);
197 	}
198 }
199 
200 udp_output(inp, m)
201 	register struct inpcb *inp;
202 	register struct mbuf *m;
203 {
204 	register struct udpiphdr *ui;
205 	register int len = m->m_pkthdr.len;
206 
207 	/*
208 	 * Calculate data length and get a mbuf
209 	 * for UDP and IP headers.
210 	 */
211 	M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT);
212 
213 	/*
214 	 * Fill in mbuf with extended UDP header
215 	 * and addresses and length put into network format.
216 	 */
217 	ui = mtod(m, struct udpiphdr *);
218 	ui->ui_next = ui->ui_prev = 0;
219 	ui->ui_x1 = 0;
220 	ui->ui_pr = IPPROTO_UDP;
221 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
222 	ui->ui_src = inp->inp_laddr;
223 	ui->ui_dst = inp->inp_faddr;
224 	ui->ui_sport = inp->inp_lport;
225 	ui->ui_dport = inp->inp_fport;
226 	ui->ui_ulen = ui->ui_len;
227 
228 	/*
229 	 * Stuff checksum and output datagram.
230 	 */
231 	ui->ui_sum = 0;
232 	if (udpcksum) {
233 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
234 		ui->ui_sum = 0xffff;
235 	}
236 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
237 	((struct ip *)ui)->ip_ttl = udp_ttl;
238 	return (ip_output(m, inp->inp_options, &inp->inp_route,
239 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
240 }
241 
242 u_long	udp_sendspace = 2048;		/* really max datagram size */
243 u_long	udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
244 
245 /*ARGSUSED*/
246 udp_usrreq(so, req, m, nam, rights, control)
247 	struct socket *so;
248 	int req;
249 	struct mbuf *m, *nam, *rights, *control;
250 {
251 	struct inpcb *inp = sotoinpcb(so);
252 	int error = 0;
253 
254 	if (req == PRU_CONTROL)
255 		return (in_control(so, (int)m, (caddr_t)nam,
256 			(struct ifnet *)rights));
257 	if (rights && rights->m_len) {
258 		error = EINVAL;
259 		goto release;
260 	}
261 	if (inp == NULL && req != PRU_ATTACH) {
262 		error = EINVAL;
263 		goto release;
264 	}
265 	switch (req) {
266 
267 	case PRU_ATTACH:
268 		if (inp != NULL) {
269 			error = EINVAL;
270 			break;
271 		}
272 		error = in_pcballoc(so, &udb);
273 		if (error)
274 			break;
275 		error = soreserve(so, udp_sendspace, udp_recvspace);
276 		if (error)
277 			break;
278 		break;
279 
280 	case PRU_DETACH:
281 		in_pcbdetach(inp);
282 		break;
283 
284 	case PRU_BIND:
285 		error = in_pcbbind(inp, nam);
286 		break;
287 
288 	case PRU_LISTEN:
289 		error = EOPNOTSUPP;
290 		break;
291 
292 	case PRU_CONNECT:
293 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
294 			error = EISCONN;
295 			break;
296 		}
297 		error = in_pcbconnect(inp, nam);
298 		if (error == 0)
299 			soisconnected(so);
300 		break;
301 
302 	case PRU_CONNECT2:
303 		error = EOPNOTSUPP;
304 		break;
305 
306 	case PRU_ACCEPT:
307 		error = EOPNOTSUPP;
308 		break;
309 
310 	case PRU_DISCONNECT:
311 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
312 			error = ENOTCONN;
313 			break;
314 		}
315 		in_pcbdisconnect(inp);
316 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
317 		break;
318 
319 	case PRU_SHUTDOWN:
320 		socantsendmore(so);
321 		break;
322 
323 	case PRU_SEND: {
324 		struct in_addr laddr;
325 		int s;
326 
327 		if (nam) {
328 			laddr = inp->inp_laddr;
329 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
330 				error = EISCONN;
331 				break;
332 			}
333 			/*
334 			 * Must block input while temporarily connected.
335 			 */
336 			s = splnet();
337 			error = in_pcbconnect(inp, nam);
338 			if (error) {
339 				splx(s);
340 				break;
341 			}
342 		} else {
343 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
344 				error = ENOTCONN;
345 				break;
346 			}
347 		}
348 		error = udp_output(inp, m);
349 		m = NULL;
350 		if (nam) {
351 			in_pcbdisconnect(inp);
352 			inp->inp_laddr = laddr;
353 			splx(s);
354 		}
355 		}
356 		break;
357 
358 	case PRU_ABORT:
359 		soisdisconnected(so);
360 		in_pcbdetach(inp);
361 		break;
362 
363 	case PRU_SOCKADDR:
364 		in_setsockaddr(inp, nam);
365 		break;
366 
367 	case PRU_PEERADDR:
368 		in_setpeeraddr(inp, nam);
369 		break;
370 
371 	case PRU_SENSE:
372 		/*
373 		 * stat: don't bother with a blocksize.
374 		 */
375 		return (0);
376 
377 	case PRU_SENDOOB:
378 	case PRU_FASTTIMO:
379 	case PRU_SLOWTIMO:
380 	case PRU_PROTORCV:
381 	case PRU_PROTOSEND:
382 		error =  EOPNOTSUPP;
383 		break;
384 
385 	case PRU_RCVD:
386 	case PRU_RCVOOB:
387 		return (EOPNOTSUPP);	/* do not free mbuf's */
388 
389 	default:
390 		panic("udp_usrreq");
391 	}
392 release:
393 	if (m != NULL)
394 		m_freem(m);
395 	return (error);
396 }
397