xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 31398)
1 /*
2  * Copyright (c) 1982, 1986 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	7.2 (Berkeley) 06/04/87
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 #ifndef	COMPAT_42
42 int	udpcksum = 1;
43 #else
44 int	udpcksum = 0;		/* XXX */
45 #endif
46 int	udp_ttl = UDP_TTL;
47 
48 struct	sockaddr_in udp_in = { AF_INET };
49 
50 udp_input(m0, ifp)
51 	struct mbuf *m0;
52 	struct ifnet *ifp;
53 {
54 	register struct udpiphdr *ui;
55 	register struct inpcb *inp;
56 	register struct mbuf *m;
57 	int len;
58 	struct ip ip;
59 
60 	/*
61 	 * Get IP and UDP header together in first mbuf.
62 	 */
63 	m = m0;
64 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
65 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
66 		udpstat.udps_hdrops++;
67 		return;
68 	}
69 	ui = mtod(m, struct udpiphdr *);
70 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
71 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
72 
73 	/*
74 	 * Make mbuf data length reflect UDP length.
75 	 * If not enough data to reflect UDP length, drop.
76 	 */
77 	len = ntohs((u_short)ui->ui_ulen);
78 	if (((struct ip *)ui)->ip_len != len) {
79 		if (len > ((struct ip *)ui)->ip_len) {
80 			udpstat.udps_badlen++;
81 			goto bad;
82 		}
83 		m_adj(m, len - ((struct ip *)ui)->ip_len);
84 		/* ((struct ip *)ui)->ip_len = len; */
85 	}
86 	/*
87 	 * Save a copy of the IP header in case we want restore it for ICMP.
88 	 */
89 	ip = *(struct ip*)ui;
90 
91 	/*
92 	 * Checksum extended UDP header and data.
93 	 */
94 	if (udpcksum && ui->ui_sum) {
95 		ui->ui_next = ui->ui_prev = 0;
96 		ui->ui_x1 = 0;
97 		ui->ui_len = ui->ui_ulen;
98 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
99 			udpstat.udps_badsum++;
100 			m_freem(m);
101 			return;
102 		}
103 	}
104 
105 	/*
106 	 * Locate pcb for datagram.
107 	 */
108 	inp = in_pcblookup(&udb,
109 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
110 		INPLOOKUP_WILDCARD);
111 	if (inp == 0) {
112 		/* don't send ICMP response for broadcast packet */
113 		if (in_broadcast(ui->ui_dst))
114 			goto bad;
115 		*(struct ip *)ui = ip;
116 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
117 		    ifp);
118 		return;
119 	}
120 
121 	/*
122 	 * Construct sockaddr format source address.
123 	 * Stuff source address and datagram in user buffer.
124 	 */
125 	udp_in.sin_port = ui->ui_sport;
126 	udp_in.sin_addr = ui->ui_src;
127 	m->m_len -= sizeof (struct udpiphdr);
128 	m->m_off += sizeof (struct udpiphdr);
129 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
130 	    m, (struct mbuf *)0) == 0)
131 		goto bad;
132 	sorwakeup(inp->inp_socket);
133 	return;
134 bad:
135 	m_freem(m);
136 }
137 
138 /*
139  * Notify a udp user of an asynchronous error;
140  * just wake up so that he can collect error status.
141  */
142 udp_notify(inp)
143 	register struct inpcb *inp;
144 {
145 
146 	sorwakeup(inp->inp_socket);
147 	sowwakeup(inp->inp_socket);
148 }
149 
150 udp_ctlinput(cmd, sa)
151 	int cmd;
152 	struct sockaddr *sa;
153 {
154 	extern u_char inetctlerrmap[];
155 	struct sockaddr_in *sin;
156 	int in_rtchange();
157 
158 	if ((unsigned)cmd > PRC_NCMDS)
159 		return;
160 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
161 		return;
162 	sin = (struct sockaddr_in *)sa;
163 	if (sin->sin_addr.s_addr == INADDR_ANY)
164 		return;
165 
166 	switch (cmd) {
167 
168 	case PRC_QUENCH:
169 		break;
170 
171 	case PRC_ROUTEDEAD:
172 	case PRC_REDIRECT_NET:
173 	case PRC_REDIRECT_HOST:
174 	case PRC_REDIRECT_TOSNET:
175 	case PRC_REDIRECT_TOSHOST:
176 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
177 		break;
178 
179 	default:
180 		if (inetctlerrmap[cmd] == 0)
181 			return;		/* XXX */
182 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
183 			udp_notify);
184 	}
185 }
186 
187 udp_output(inp, m0)
188 	register struct inpcb *inp;
189 	struct mbuf *m0;
190 {
191 	register struct mbuf *m;
192 	register struct udpiphdr *ui;
193 	register int len = 0;
194 
195 	/*
196 	 * Calculate data length and get a mbuf
197 	 * for UDP and IP headers.
198 	 */
199 	for (m = m0; m; m = m->m_next)
200 		len += m->m_len;
201 	MGET(m, M_DONTWAIT, MT_HEADER);
202 	if (m == 0) {
203 		m_freem(m0);
204 		return (ENOBUFS);
205 	}
206 
207 	/*
208 	 * Fill in mbuf with extended UDP header
209 	 * and addresses and length put into network format.
210 	 */
211 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
212 	m->m_len = sizeof (struct udpiphdr);
213 	m->m_next = m0;
214 	ui = mtod(m, struct udpiphdr *);
215 	ui->ui_next = ui->ui_prev = 0;
216 	ui->ui_x1 = 0;
217 	ui->ui_pr = IPPROTO_UDP;
218 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
219 	ui->ui_src = inp->inp_laddr;
220 	ui->ui_dst = inp->inp_faddr;
221 	ui->ui_sport = inp->inp_lport;
222 	ui->ui_dport = inp->inp_fport;
223 	ui->ui_ulen = ui->ui_len;
224 
225 	/*
226 	 * Stuff checksum and output datagram.
227 	 */
228 	ui->ui_sum = 0;
229 	if (udpcksum) {
230 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
231 		ui->ui_sum = -1;
232 	}
233 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
234 	((struct ip *)ui)->ip_ttl = udp_ttl;
235 	return (ip_output(m, inp->inp_options, &inp->inp_route,
236 	    inp->inp_socket->so_options & (SO_DONTROUTE | 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 		in_pcbdetach(inp);
279 		break;
280 
281 	case PRU_BIND:
282 		error = in_pcbbind(inp, nam);
283 		break;
284 
285 	case PRU_LISTEN:
286 		error = EOPNOTSUPP;
287 		break;
288 
289 	case PRU_CONNECT:
290 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
291 			error = EISCONN;
292 			break;
293 		}
294 		error = in_pcbconnect(inp, nam);
295 		if (error == 0)
296 			soisconnected(so);
297 		break;
298 
299 	case PRU_CONNECT2:
300 		error = EOPNOTSUPP;
301 		break;
302 
303 	case PRU_ACCEPT:
304 		error = EOPNOTSUPP;
305 		break;
306 
307 	case PRU_DISCONNECT:
308 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
309 			error = ENOTCONN;
310 			break;
311 		}
312 		in_pcbdisconnect(inp);
313 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
314 		break;
315 
316 	case PRU_SHUTDOWN:
317 		socantsendmore(so);
318 		break;
319 
320 	case PRU_SEND: {
321 		struct in_addr laddr;
322 		int s;
323 
324 		if (nam) {
325 			laddr = inp->inp_laddr;
326 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
327 				error = EISCONN;
328 				break;
329 			}
330 			/*
331 			 * Must block input while temporarily connected.
332 			 */
333 			s = splnet();
334 			error = in_pcbconnect(inp, nam);
335 			if (error) {
336 				splx(s);
337 				break;
338 			}
339 		} else {
340 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
341 				error = ENOTCONN;
342 				break;
343 			}
344 		}
345 		error = udp_output(inp, m);
346 		m = NULL;
347 		if (nam) {
348 			in_pcbdisconnect(inp);
349 			inp->inp_laddr = laddr;
350 			splx(s);
351 		}
352 		}
353 		break;
354 
355 	case PRU_ABORT:
356 		in_pcbdetach(inp);
357 		sofree(so);
358 		soisdisconnected(so);
359 		break;
360 
361 	case PRU_SOCKADDR:
362 		in_setsockaddr(inp, nam);
363 		break;
364 
365 	case PRU_PEERADDR:
366 		in_setpeeraddr(inp, nam);
367 		break;
368 
369 	case PRU_SENSE:
370 		/*
371 		 * stat: don't bother with a blocksize.
372 		 */
373 		return (0);
374 
375 	case PRU_SENDOOB:
376 	case PRU_FASTTIMO:
377 	case PRU_SLOWTIMO:
378 	case PRU_PROTORCV:
379 	case PRU_PROTOSEND:
380 		error =  EOPNOTSUPP;
381 		break;
382 
383 	case PRU_RCVD:
384 	case PRU_RCVOOB:
385 		return (EOPNOTSUPP);	/* do not free mbuf's */
386 
387 	default:
388 		panic("udp_usrreq");
389 	}
390 release:
391 	if (m != NULL)
392 		m_freem(m);
393 	return (error);
394 }
395