xref: /original-bsd/sys/netinet/udp_usrreq.c (revision 2ad443ff)
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.14 (Berkeley) 05/17/90
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_systm.h"
35 #include "ip.h"
36 #include "in_pcb.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 ip *ip;
66 	register struct udphdr *uh;
67 	register struct inpcb *inp;
68 	int len;
69 	struct ip save_ip;
70 
71 #ifndef notyet
72 	if (iphlen > sizeof (struct ip))
73 		ip_stripoptions(m, (struct mbuf *)0);
74 #endif
75 	/*
76 	 * Get IP and UDP header together in first mbuf.
77 	 */
78 	ip = mtod(m, struct ip *);
79 	if (m->m_len < iphlen + sizeof(struct udphdr)) {
80 		if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
81 			udpstat.udps_hdrops++;
82 			return;
83 		}
84 		ip = mtod(m, struct ip *);
85 	}
86 	uh = (struct udphdr *)((caddr_t)ip + iphlen);
87 
88 	/*
89 	 * Make mbuf data length reflect UDP length.
90 	 * If not enough data to reflect UDP length, drop.
91 	 */
92 	len = ntohs((u_short)uh->uh_ulen);
93 	if (ip->ip_len != len) {
94 		if (len > ip->ip_len) {
95 			udpstat.udps_badlen++;
96 			goto bad;
97 		}
98 		m_adj(m, len - ip->ip_len);
99 		/* ip->ip_len = len; */
100 	}
101 	/*
102 	 * Save a copy of the IP header in case we want restore it
103 	 * for sending an ICMP error message in response.
104 	 */
105 	save_ip = *ip;
106 
107 	/*
108 	 * Checksum extended UDP header and data.
109 	 */
110 	if (udpcksum && uh->uh_sum) {
111 		((struct ipovly *)ip)->ih_next = 0;
112 		((struct ipovly *)ip)->ih_prev = 0;
113 		((struct ipovly *)ip)->ih_x1 = 0;
114 		((struct ipovly *)ip)->ih_len = uh->uh_ulen;
115 		if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
116 			udpstat.udps_badsum++;
117 			m_freem(m);
118 			return;
119 		}
120 	}
121 
122 	/*
123 	 * Locate pcb for datagram.
124 	 */
125 	inp = in_pcblookup(&udb,
126 	    ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport,
127 	    INPLOOKUP_WILDCARD);
128 	if (inp == 0) {
129 		/* don't send ICMP response for broadcast packet */
130 		udpstat.udps_noport++;
131 		if (m->m_flags & M_BCAST)
132 			goto bad;
133 		*ip = save_ip;
134 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
135 		return;
136 	}
137 
138 	/*
139 	 * Construct sockaddr format source address.
140 	 * Stuff source address and datagram in user buffer.
141 	 */
142 	udp_in.sin_port = uh->uh_sport;
143 	udp_in.sin_addr = ip->ip_src;
144 iphlen = sizeof(struct ip);
145 	iphlen += sizeof(struct udphdr);
146 	m->m_len -= iphlen;
147 	m->m_pkthdr.len -= iphlen;
148 	m->m_data += iphlen;
149 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
150 	    m, (struct mbuf *)0) == 0)
151 		goto bad;
152 	udpstat.udps_ipackets++;
153 	sorwakeup(inp->inp_socket);
154 	return;
155 bad:
156 	m_freem(m);
157 }
158 
159 /*
160  * Notify a udp user of an asynchronous error;
161  * just wake up so that he can collect error status.
162  */
163 udp_notify(inp)
164 	register struct inpcb *inp;
165 {
166 
167 	sorwakeup(inp->inp_socket);
168 	sowwakeup(inp->inp_socket);
169 }
170 
171 udp_ctlinput(cmd, sa, ip)
172 	int cmd;
173 	struct sockaddr *sa;
174 	register struct ip *ip;
175 {
176 	register struct udphdr *uh;
177 	extern struct in_addr zeroin_addr;
178 	extern u_char inetctlerrmap[];
179 
180 	if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)
181 		return;
182 	if (ip) {
183 		uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
184 		in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
185 			cmd, udp_notify);
186 	} else
187 		in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
188 }
189 
190 udp_output(inp, m, addr, control)
191 	register struct inpcb *inp;
192 	register struct mbuf *m;
193 	struct mbuf *addr, *control;
194 {
195 	register struct udpiphdr *ui;
196 	register int len = m->m_pkthdr.len;
197 	struct in_addr laddr;
198 	int s, error = 0;
199 
200 	if (control)
201 		m_freem(control);		/* XXX */
202 
203 	if (addr) {
204 		laddr = inp->inp_laddr;
205 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
206 			error = EISCONN;
207 			goto release;
208 		}
209 		/*
210 		 * Must block input while temporarily connected.
211 		 */
212 		s = splnet();
213 		error = in_pcbconnect(inp, addr);
214 		if (error) {
215 			splx(s);
216 			goto release;
217 		}
218 	} else {
219 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
220 			error = ENOTCONN;
221 			goto release;
222 		}
223 	}
224 	/*
225 	 * Calculate data length and get a mbuf
226 	 * for UDP and IP headers.
227 	 */
228 	M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT);
229 
230 	/*
231 	 * Fill in mbuf with extended UDP header
232 	 * and addresses and length put into network format.
233 	 */
234 	ui = mtod(m, struct udpiphdr *);
235 	ui->ui_next = ui->ui_prev = 0;
236 	ui->ui_x1 = 0;
237 	ui->ui_pr = IPPROTO_UDP;
238 	ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
239 	ui->ui_src = inp->inp_laddr;
240 	ui->ui_dst = inp->inp_faddr;
241 	ui->ui_sport = inp->inp_lport;
242 	ui->ui_dport = inp->inp_fport;
243 	ui->ui_ulen = ui->ui_len;
244 
245 	/*
246 	 * Stuff checksum and output datagram.
247 	 */
248 	ui->ui_sum = 0;
249 	if (udpcksum) {
250 	    if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
251 		ui->ui_sum = 0xffff;
252 	}
253 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
254 	((struct ip *)ui)->ip_ttl = udp_ttl;
255 	udpstat.udps_opackets++;
256 	error = ip_output(m, inp->inp_options, &inp->inp_route,
257 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST));
258 
259 	if (addr) {
260 		in_pcbdisconnect(inp);
261 		inp->inp_laddr = laddr;
262 		splx(s);
263 	}
264 	return (error);
265 
266 release:
267 	m_freem(m);
268 	return (error);
269 }
270 
271 u_long	udp_sendspace = 9216;		/* really max datagram size */
272 u_long	udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
273 					/* 40 1K datagrams */
274 
275 /*ARGSUSED*/
276 udp_usrreq(so, req, m, addr, control)
277 	struct socket *so;
278 	int req;
279 	struct mbuf *m, *addr, *control;
280 {
281 	struct inpcb *inp = sotoinpcb(so);
282 	int error = 0;
283 
284 	if (req == PRU_CONTROL)
285 		return (in_control(so, (int)m, (caddr_t)addr,
286 			(struct ifnet *)control));
287 	if (inp == NULL && req != PRU_ATTACH) {
288 		error = EINVAL;
289 		goto release;
290 	}
291 	switch (req) {
292 
293 	case PRU_ATTACH:
294 		if (inp != NULL) {
295 			error = EINVAL;
296 			break;
297 		}
298 		error = in_pcballoc(so, &udb);
299 		if (error)
300 			break;
301 		error = soreserve(so, udp_sendspace, udp_recvspace);
302 		if (error)
303 			break;
304 		break;
305 
306 	case PRU_DETACH:
307 		in_pcbdetach(inp);
308 		break;
309 
310 	case PRU_BIND:
311 		error = in_pcbbind(inp, addr);
312 		break;
313 
314 	case PRU_LISTEN:
315 		error = EOPNOTSUPP;
316 		break;
317 
318 	case PRU_CONNECT:
319 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
320 			error = EISCONN;
321 			break;
322 		}
323 		error = in_pcbconnect(inp, addr);
324 		if (error == 0)
325 			soisconnected(so);
326 		break;
327 
328 	case PRU_CONNECT2:
329 		error = EOPNOTSUPP;
330 		break;
331 
332 	case PRU_ACCEPT:
333 		error = EOPNOTSUPP;
334 		break;
335 
336 	case PRU_DISCONNECT:
337 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
338 			error = ENOTCONN;
339 			break;
340 		}
341 		in_pcbdisconnect(inp);
342 		inp->inp_laddr.s_addr = INADDR_ANY;
343 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
344 		break;
345 
346 	case PRU_SHUTDOWN:
347 		socantsendmore(so);
348 		break;
349 
350 	case PRU_SEND:
351 		error = udp_output(inp, m, addr, control);
352 		m = NULL;
353 		control = NULL;
354 		break;
355 
356 	case PRU_ABORT:
357 		soisdisconnected(so);
358 		in_pcbdetach(inp);
359 		break;
360 
361 	case PRU_SOCKADDR:
362 		in_setsockaddr(inp, addr);
363 		break;
364 
365 	case PRU_PEERADDR:
366 		in_setpeeraddr(inp, addr);
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 (control) {
392 		printf("udp control data unexpectedly retained\n");
393 		m_freem(control);
394 	}
395 	if (m)
396 		m_freem(m);
397 	return (error);
398 }
399