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