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