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