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