xref: /original-bsd/sys/netinet/udp_usrreq.c (revision 78204ff3)
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.12 (Berkeley) 06/27/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 = 9216;		/* really max datagram size */
242 u_long	udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
243 					/* 40 1K datagrams */
244 
245 /*ARGSUSED*/
246 udp_usrreq(so, req, m, nam, rights, control)
247 	struct socket *so;
248 	int req;
249 	struct mbuf *m, *nam, *rights, *control;
250 {
251 	struct inpcb *inp = sotoinpcb(so);
252 	int error = 0;
253 
254 	if (req == PRU_CONTROL)
255 		return (in_control(so, (int)m, (caddr_t)nam,
256 			(struct ifnet *)rights));
257 	if (rights && rights->m_len) {
258 		error = EINVAL;
259 		goto release;
260 	}
261 	if (inp == NULL && req != PRU_ATTACH) {
262 		error = EINVAL;
263 		goto release;
264 	}
265 	switch (req) {
266 
267 	case PRU_ATTACH:
268 		if (inp != NULL) {
269 			error = EINVAL;
270 			break;
271 		}
272 		error = in_pcballoc(so, &udb);
273 		if (error)
274 			break;
275 		error = soreserve(so, udp_sendspace, udp_recvspace);
276 		if (error)
277 			break;
278 		break;
279 
280 	case PRU_DETACH:
281 		in_pcbdetach(inp);
282 		break;
283 
284 	case PRU_BIND:
285 		error = in_pcbbind(inp, nam);
286 		break;
287 
288 	case PRU_LISTEN:
289 		error = EOPNOTSUPP;
290 		break;
291 
292 	case PRU_CONNECT:
293 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
294 			error = EISCONN;
295 			break;
296 		}
297 		error = in_pcbconnect(inp, nam);
298 		if (error == 0)
299 			soisconnected(so);
300 		break;
301 
302 	case PRU_CONNECT2:
303 		error = EOPNOTSUPP;
304 		break;
305 
306 	case PRU_ACCEPT:
307 		error = EOPNOTSUPP;
308 		break;
309 
310 	case PRU_DISCONNECT:
311 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
312 			error = ENOTCONN;
313 			break;
314 		}
315 		in_pcbdisconnect(inp);
316 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
317 		break;
318 
319 	case PRU_SHUTDOWN:
320 		socantsendmore(so);
321 		break;
322 
323 	case PRU_SEND: {
324 		struct in_addr laddr;
325 		int s;
326 
327 		if (nam) {
328 			laddr = inp->inp_laddr;
329 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
330 				error = EISCONN;
331 				break;
332 			}
333 			/*
334 			 * Must block input while temporarily connected.
335 			 */
336 			s = splnet();
337 			error = in_pcbconnect(inp, nam);
338 			if (error) {
339 				splx(s);
340 				break;
341 			}
342 		} else {
343 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
344 				error = ENOTCONN;
345 				break;
346 			}
347 		}
348 		error = udp_output(inp, m);
349 		m = NULL;
350 		if (nam) {
351 			in_pcbdisconnect(inp);
352 			inp->inp_laddr = laddr;
353 			splx(s);
354 		}
355 		}
356 		break;
357 
358 	case PRU_ABORT:
359 		soisdisconnected(so);
360 		in_pcbdetach(inp);
361 		break;
362 
363 	case PRU_SOCKADDR:
364 		in_setsockaddr(inp, nam);
365 		break;
366 
367 	case PRU_PEERADDR:
368 		in_setpeeraddr(inp, nam);
369 		break;
370 
371 	case PRU_SENSE:
372 		/*
373 		 * stat: don't bother with a blocksize.
374 		 */
375 		return (0);
376 
377 	case PRU_SENDOOB:
378 	case PRU_FASTTIMO:
379 	case PRU_SLOWTIMO:
380 	case PRU_PROTORCV:
381 	case PRU_PROTOSEND:
382 		error =  EOPNOTSUPP;
383 		break;
384 
385 	case PRU_RCVD:
386 	case PRU_RCVOOB:
387 		return (EOPNOTSUPP);	/* do not free mbuf's */
388 
389 	default:
390 		panic("udp_usrreq");
391 	}
392 release:
393 	if (m != NULL)
394 		m_freem(m);
395 	return (error);
396 }
397