xref: /netbsd-src/sys/netinet/raw_ip.c (revision 81b108b45f75f89f1e3ffad9fb6f074e771c0935)
1 /*	$NetBSD: raw_ip.c,v 1.34 1996/09/16 17:45:17 mycroft Exp $	*/
2 
3 /*
4  * Copyright (c) 1982, 1986, 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)raw_ip.c	8.2 (Berkeley) 1/4/94
36  */
37 
38 #include <sys/param.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/socket.h>
42 #include <sys/protosw.h>
43 #include <sys/socketvar.h>
44 #include <sys/errno.h>
45 #include <sys/systm.h>
46 #include <sys/proc.h>
47 
48 #include <net/if.h>
49 #include <net/route.h>
50 
51 #include <netinet/in.h>
52 #include <netinet/in_systm.h>
53 #include <netinet/ip.h>
54 #include <netinet/ip_var.h>
55 #include <netinet/ip_mroute.h>
56 #include <netinet/in_pcb.h>
57 #include <netinet/in_var.h>
58 
59 #include <machine/stdarg.h>
60 
61 struct inpcbtable rawcbtable;
62 
63 int	 rip_bind __P((struct inpcb *, struct mbuf *));
64 int	 rip_connect __P((struct inpcb *, struct mbuf *));
65 void	 rip_disconnect __P((struct inpcb *));
66 
67 /*
68  * Nominal space allocated to a raw ip socket.
69  */
70 #define	RIPSNDQ		8192
71 #define	RIPRCVQ		8192
72 
73 /*
74  * Raw interface to IP protocol.
75  */
76 
77 /*
78  * Initialize raw connection block q.
79  */
80 void
81 rip_init()
82 {
83 
84 	in_pcbinit(&rawcbtable, 1, 1);
85 }
86 
87 /*
88  * Setup generic address and protocol structures
89  * for raw_input routine, then pass them along with
90  * mbuf chain.
91  */
92 void
93 #if __STDC__
94 rip_input(struct mbuf *m, ...)
95 #else
96 rip_input(m, va_alist)
97 	struct mbuf *m;
98 	va_dcl
99 #endif
100 {
101 	register struct ip *ip = mtod(m, struct ip *);
102 	register struct inpcb *inp;
103 	struct socket *last = 0;
104 	struct sockaddr_in ripsrc;
105 
106 	ripsrc.sin_family = AF_INET;
107 	ripsrc.sin_len = sizeof(struct sockaddr_in);
108 	ripsrc.sin_addr = ip->ip_src;
109 	ripsrc.sin_port = 0;
110 	bzero((caddr_t)ripsrc.sin_zero, sizeof(ripsrc.sin_zero));
111 
112 	for (inp = rawcbtable.inpt_queue.cqh_first;
113 	    inp != (struct inpcb *)&rawcbtable.inpt_queue;
114 	    inp = inp->inp_queue.cqe_next) {
115 		if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
116 			continue;
117 		if (!in_nullhost(inp->inp_laddr) &&
118 		    !in_hosteq(inp->inp_laddr, ip->ip_dst))
119 			continue;
120 		if (!in_nullhost(inp->inp_faddr) &&
121 		    !in_hosteq(inp->inp_faddr, ip->ip_src))
122 			continue;
123 		if (last) {
124 			struct mbuf *n;
125 			if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
126 				if (sbappendaddr(&last->so_rcv,
127 				    sintosa(&ripsrc), n,
128 				    (struct mbuf *)0) == 0)
129 					/* should notify about lost packet */
130 					m_freem(n);
131 				else
132 					sorwakeup(last);
133 			}
134 		}
135 		last = inp->inp_socket;
136 	}
137 	if (last) {
138 		if (sbappendaddr(&last->so_rcv, sintosa(&ripsrc), m,
139 		    (struct mbuf *)0) == 0)
140 			m_freem(m);
141 		else
142 			sorwakeup(last);
143 	} else {
144 		m_freem(m);
145 		ipstat.ips_noproto++;
146 		ipstat.ips_delivered--;
147 	}
148 }
149 
150 /*
151  * Generate IP header and pass packet to ip_output.
152  * Tack on options user may have setup with control call.
153  */
154 int
155 #if __STDC__
156 rip_output(struct mbuf *m, ...)
157 #else
158 rip_output(m, va_alist)
159 	struct mbuf *m;
160 	va_dcl
161 #endif
162 {
163 	register struct inpcb *inp;
164 	register struct ip *ip;
165 	struct mbuf *opts;
166 	int flags;
167 	va_list ap;
168 
169 	va_start(ap, m);
170 	inp = va_arg(ap, struct inpcb *);
171 	va_end(ap);
172 
173 	flags =
174 	    (inp->inp_socket->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;
175 
176 	/*
177 	 * If the user handed us a complete IP packet, use it.
178 	 * Otherwise, allocate an mbuf for a header and fill it in.
179 	 */
180 	if ((inp->inp_flags & INP_HDRINCL) == 0) {
181 		M_PREPEND(m, sizeof(struct ip), M_WAIT);
182 		ip = mtod(m, struct ip *);
183 		ip->ip_tos = 0;
184 		ip->ip_off = 0;
185 		ip->ip_p = inp->inp_ip.ip_p;
186 		ip->ip_len = m->m_pkthdr.len;
187 		ip->ip_src = inp->inp_laddr;
188 		ip->ip_dst = inp->inp_faddr;
189 		ip->ip_ttl = MAXTTL;
190 		opts = inp->inp_options;
191 	} else {
192 		ip = mtod(m, struct ip *);
193 		if (ip->ip_id == 0)
194 			ip->ip_id = htons(ip_id++);
195 		opts = NULL;
196 		/* XXX prevent ip_output from overwriting header fields */
197 		flags |= IP_RAWOUTPUT;
198 		ipstat.ips_rawout++;
199 	}
200 	return (ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions));
201 }
202 
203 /*
204  * Raw IP socket option processing.
205  */
206 int
207 rip_ctloutput(op, so, level, optname, m)
208 	int op;
209 	struct socket *so;
210 	int level, optname;
211 	struct mbuf **m;
212 {
213 	register struct inpcb *inp = sotoinpcb(so);
214 	int error = 0;
215 
216 	if (level != IPPROTO_IP) {
217 		error = ENOPROTOOPT;
218 		if (op == PRCO_SETOPT && *m != 0)
219 			(void) m_free(*m);
220 	} else switch (op) {
221 
222 	case PRCO_SETOPT:
223 		switch (optname) {
224 		case IP_HDRINCL:
225 			if (*m == 0 || (*m)->m_len < sizeof (int))
226 				error = EINVAL;
227 			else {
228 				if (*mtod(*m, int *))
229 					inp->inp_flags |= INP_HDRINCL;
230 				else
231 					inp->inp_flags &= ~INP_HDRINCL;
232 			}
233 			if (*m != 0)
234 				(void) m_free(*m);
235 			break;
236 
237 #ifdef MROUTING
238 		case MRT_INIT:
239 		case MRT_DONE:
240 		case MRT_ADD_VIF:
241 		case MRT_DEL_VIF:
242 		case MRT_ADD_MFC:
243 		case MRT_DEL_MFC:
244 		case MRT_ASSERT:
245 			error = ip_mrouter_set(so, optname, m);
246 			break;
247 #endif
248 
249 		default:
250 			error = ip_ctloutput(op, so, level, optname, m);
251 			break;
252 		}
253 		break;
254 
255 	case PRCO_GETOPT:
256 		switch (optname) {
257 		case IP_HDRINCL:
258 			*m = m_get(M_WAIT, M_SOOPTS);
259 			(*m)->m_len = sizeof (int);
260 			*mtod(*m, int *) = inp->inp_flags & INP_HDRINCL ? 1 : 0;
261 			break;
262 
263 #ifdef MROUTING
264 		case MRT_VERSION:
265 		case MRT_ASSERT:
266 			error = ip_mrouter_get(so, optname, m);
267 			break;
268 #endif
269 
270 		default:
271 			error = ip_ctloutput(op, so, level, optname, m);
272 			break;
273 		}
274 		break;
275 	}
276 	return (error);
277 }
278 
279 int
280 rip_bind(inp, nam)
281 	struct inpcb *inp;
282 	struct mbuf *nam;
283 {
284 	struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
285 
286 	if (nam->m_len != sizeof(*addr))
287 		return (EINVAL);
288 	if (ifnet.tqh_first == 0)
289 		return (EADDRNOTAVAIL);
290 	if (addr->sin_family != AF_INET &&
291 	    addr->sin_family != AF_IMPLINK)
292 		return (EAFNOSUPPORT);
293 	if (!in_nullhost(addr->sin_addr) &&
294 	    ifa_ifwithaddr(sintosa(addr)) == 0)
295 		return (EADDRNOTAVAIL);
296 	inp->inp_laddr = addr->sin_addr;
297 	return (0);
298 }
299 
300 int
301 rip_connect(inp, nam)
302 	struct inpcb *inp;
303 	struct mbuf *nam;
304 {
305 	struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
306 
307 	if (nam->m_len != sizeof(*addr))
308 		return (EINVAL);
309 	if (ifnet.tqh_first == 0)
310 		return (EADDRNOTAVAIL);
311 	if (addr->sin_family != AF_INET &&
312 	    addr->sin_family != AF_IMPLINK)
313 		return (EAFNOSUPPORT);
314 	inp->inp_faddr = addr->sin_addr;
315 	return (0);
316 }
317 
318 void
319 rip_disconnect(inp)
320 	struct inpcb *inp;
321 {
322 
323 	inp->inp_faddr = zeroin_addr;
324 }
325 
326 u_long	rip_sendspace = RIPSNDQ;
327 u_long	rip_recvspace = RIPRCVQ;
328 
329 /*ARGSUSED*/
330 int
331 rip_usrreq(so, req, m, nam, control, p)
332 	register struct socket *so;
333 	int req;
334 	struct mbuf *m, *nam, *control;
335 	struct proc *p;
336 {
337 	register struct inpcb *inp;
338 	int s;
339 	register int error = 0;
340 #ifdef MROUTING
341 	extern struct socket *ip_mrouter;
342 #endif
343 
344 	if (req == PRU_CONTROL)
345 		return (in_control(so, (long)m, (caddr_t)nam,
346 		    (struct ifnet *)control, p));
347 
348 	s = splsoftnet();
349 	inp = sotoinpcb(so);
350 #ifdef DIAGNOSTIC
351 	if (req != PRU_SEND && req != PRU_SENDOOB && control)
352 		panic("rip_usrreq: unexpected control mbuf");
353 #endif
354 	if (inp == 0 && req != PRU_ATTACH) {
355 		error = EINVAL;
356 		goto release;
357 	}
358 
359 	switch (req) {
360 
361 	case PRU_ATTACH:
362 		if (inp != 0) {
363 			error = EISCONN;
364 			break;
365 		}
366 		if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag))) {
367 			error = EACCES;
368 			break;
369 		}
370 		if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
371 			error = soreserve(so, rip_sendspace, rip_recvspace);
372 			if (error)
373 				break;
374 		}
375 		error = in_pcballoc(so, &rawcbtable);
376 		if (error)
377 			break;
378 		inp = sotoinpcb(so);
379 		inp->inp_ip.ip_p = (long)nam;
380 		break;
381 
382 	case PRU_DETACH:
383 #ifdef MROUTING
384 		if (so == ip_mrouter)
385 			ip_mrouter_done();
386 #endif
387 		in_pcbdetach(inp);
388 		break;
389 
390 	case PRU_BIND:
391 		error = rip_bind(inp, nam);
392 		break;
393 
394 	case PRU_LISTEN:
395 		error = EOPNOTSUPP;
396 		break;
397 
398 	case PRU_CONNECT:
399 		error = rip_connect(inp, nam);
400 		if (error)
401 			break;
402 		soisconnected(so);
403 		break;
404 
405 	case PRU_CONNECT2:
406 		error = EOPNOTSUPP;
407 		break;
408 
409 	case PRU_DISCONNECT:
410 		soisdisconnected(so);
411 		rip_disconnect(inp);
412 		break;
413 
414 	/*
415 	 * Mark the connection as being incapable of further input.
416 	 */
417 	case PRU_SHUTDOWN:
418 		socantsendmore(so);
419 		break;
420 
421 	case PRU_RCVD:
422 		error = EOPNOTSUPP;
423 		break;
424 
425 	/*
426 	 * Ship a packet out.  The appropriate raw output
427 	 * routine handles any massaging necessary.
428 	 */
429 	case PRU_SEND:
430 		if (control && control->m_len) {
431 			m_freem(control);
432 			m_freem(m);
433 			error = EINVAL;
434 			break;
435 		}
436 	{
437 		if (nam) {
438 			if ((so->so_state & SS_ISCONNECTED) != 0) {
439 				error = EISCONN;
440 				goto die;
441 			}
442 			error = rip_connect(inp, nam);
443 			if (error) {
444 			die:
445 				m_freem(m);
446 				break;
447 			}
448 		} else {
449 			if ((so->so_state & SS_ISCONNECTED) == 0) {
450 				error = ENOTCONN;
451 				goto die;
452 			}
453 		}
454 		error = rip_output(m, inp);
455 		if (nam)
456 			rip_disconnect(inp);
457 	}
458 		break;
459 
460 	case PRU_SENSE:
461 		/*
462 		 * stat: don't bother with a blocksize.
463 		 */
464 		splx(s);
465 		return (0);
466 
467 	case PRU_RCVOOB:
468 		error = EOPNOTSUPP;
469 		break;
470 
471 	case PRU_SENDOOB:
472 		m_freem(control);
473 		m_freem(m);
474 		error = EOPNOTSUPP;
475 		break;
476 
477 	case PRU_SOCKADDR:
478 		in_setsockaddr(inp, nam);
479 		break;
480 
481 	case PRU_PEERADDR:
482 		in_setpeeraddr(inp, nam);
483 		break;
484 
485 	default:
486 		panic("rip_usrreq");
487 	}
488 
489 release:
490 	splx(s);
491 	return (error);
492 }
493