raw_ip6.c revision 1.3 1 /* $NetBSD: raw_ip6.c,v 1.3 1999/07/03 21:30:19 thorpej Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * 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. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 1982, 1986, 1988, 1993
34 * The Regents of the University of California. All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94
65 */
66
67 #include <sys/param.h>
68 #include <sys/malloc.h>
69 #include <sys/mbuf.h>
70 #include <sys/socket.h>
71 #include <sys/protosw.h>
72 #include <sys/socketvar.h>
73 #include <sys/errno.h>
74 #include <sys/systm.h>
75 #ifdef __NetBSD__
76 #include <sys/proc.h>
77 #endif
78
79 #include <net/if.h>
80 #include <net/route.h>
81 #include <net/if_types.h>
82
83 #include <netinet/in.h>
84 #include <netinet/in_var.h>
85 #include <netinet6/in6_systm.h>
86 #include <netinet6/ip6.h>
87 #include <netinet6/ip6_var.h>
88 #include <netinet6/ip6_mroute.h>
89 #include <netinet6/icmp6.h>
90 #include <netinet6/in6_pcb.h>
91 #include <netinet6/nd6.h>
92
93 #ifdef IPSEC
94 #include <netinet6/ipsec.h>
95 #endif /*IPSEC*/
96
97 #include <machine/stdarg.h>
98
99 #include "faith.h"
100
101 struct in6pcb rawin6pcb;
102 #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
103
104 /*
105 * Raw interface to IP6 protocol.
106 */
107
108 /*
109 * Initialize raw connection block queue.
110 */
111 void
112 rip6_init()
113 {
114 rawin6pcb.in6p_next = rawin6pcb.in6p_prev = &rawin6pcb;
115 }
116
117 /*
118 * Setup generic address and protocol structures
119 * for raw_input routine, then pass them along with
120 * mbuf chain.
121 */
122 int
123 rip6_input(mp, offp, proto)
124 struct mbuf **mp;
125 int *offp, proto;
126 {
127 struct mbuf *m = *mp;
128 register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
129 register struct in6pcb *in6p;
130 struct in6pcb *last = NULL;
131 struct sockaddr_in6 rip6src;
132 struct mbuf *opts = NULL;
133
134 #if defined(NFAITH) && 0 < NFAITH
135 if (m->m_pkthdr.rcvif) {
136 if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
137 /* send icmp6 host unreach? */
138 m_freem(m);
139 return IPPROTO_DONE;
140 }
141 }
142 #endif
143 bzero(&rip6src, sizeof(rip6src));
144 rip6src.sin6_len = sizeof(struct sockaddr_in6);
145 rip6src.sin6_family = AF_INET6;
146 rip6src.sin6_addr = ip6->ip6_src;
147 if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
148 rip6src.sin6_addr.s6_addr16[1] = 0;
149 if (m->m_pkthdr.rcvif) {
150 if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
151 rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index;
152 else
153 rip6src.sin6_scope_id = 0;
154 } else
155 rip6src.sin6_scope_id = 0;
156
157 for (in6p = rawin6pcb.in6p_next;
158 in6p != &rawin6pcb; in6p = in6p->in6p_next) {
159 if (in6p->in6p_ip6.ip6_nxt &&
160 in6p->in6p_ip6.ip6_nxt != proto)
161 continue;
162 if (!IN6_IS_ADDR_ANY(&in6p->in6p_laddr) &&
163 !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
164 continue;
165 if (!IN6_IS_ADDR_ANY(&in6p->in6p_faddr) &&
166 !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
167 continue;
168 if (in6p->in6p_cksum != -1
169 && in6_cksum(m, ip6->ip6_nxt, *offp,
170 sizeof(struct ip6_hdr) + ip6->ip6_plen - *offp)) {
171 /* XXX bark something */
172 continue;
173 }
174 if (last) {
175 struct mbuf *n;
176 if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
177 if (last->in6p_flags & IN6P_CONTROLOPTS)
178 ip6_savecontrol(last, &opts, ip6, n);
179 /* strip intermediate headers */
180 m_adj(n, *offp);
181 if (sbappendaddr(&last->in6p_socket->so_rcv,
182 (struct sockaddr *)&rip6src,
183 n, opts) == 0) {
184 /* should notify about lost packet */
185 m_freem(n);
186 if (opts)
187 m_freem(opts);
188 } else
189 sorwakeup(last->in6p_socket);
190 opts = NULL;
191 }
192 }
193 last = in6p;
194 }
195 if (last) {
196 if (last->in6p_flags & IN6P_CONTROLOPTS)
197 ip6_savecontrol(last, &opts, ip6, m);
198 /* strip intermediate headers */
199 m_adj(m, *offp);
200 if (sbappendaddr(&last->in6p_socket->so_rcv,
201 (struct sockaddr *)&rip6src, m, opts) == 0) {
202 m_freem(m);
203 if (opts)
204 m_freem(opts);
205 } else
206 sorwakeup(last->in6p_socket);
207 } else {
208 if (proto == IPPROTO_NONE)
209 m_freem(m);
210 else {
211 char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
212 icmp6_error(m, ICMP6_PARAM_PROB,
213 ICMP6_PARAMPROB_NEXTHEADER,
214 prvnxtp - mtod(m, char *));
215 }
216 ip6stat.ip6s_delivered--;
217 }
218 return IPPROTO_DONE;
219 }
220
221 /*
222 * Generate IPv6 header and pass packet to ip6_output.
223 * Tack on options user may have setup with control call.
224 */
225 int
226 #if __STDC__
227 rip6_output(struct mbuf *m, ...)
228 #else
229 rip6_output(m, va_alist)
230 struct mbuf *m;
231 va_dcl
232 #endif
233 {
234 struct socket *so;
235 struct sockaddr_in6 *dstsock;
236 struct mbuf *control;
237 struct in6_addr *dst;
238 struct ip6_hdr *ip6;
239 struct in6pcb *in6p;
240 u_int plen = m->m_pkthdr.len;
241 int error = 0;
242 struct ip6_pktopts opt, *optp = NULL;
243 struct ifnet *oifp = NULL;
244 int priv = 0;
245 va_list ap;
246
247 va_start(ap, m);
248 so = va_arg(ap, struct socket *);
249 dstsock = va_arg(ap, struct sockaddr_in6 *);
250 control = va_arg(ap, struct mbuf *);
251 va_end(ap);
252
253 in6p = sotoin6pcb(so);
254
255 {
256 struct proc *p = curproc; /* XXX */
257
258 if (p && !suser(p->p_ucred, &p->p_acflag))
259 priv = 1;
260 }
261 dst = &dstsock->sin6_addr;
262 if (control) {
263 if ((error = ip6_setpktoptions(control, &opt, priv)) != 0)
264 goto bad;
265 optp = &opt;
266 } else
267 optp = in6p->in6p_outputopts;
268
269 M_PREPEND(m, sizeof(*ip6), M_WAIT);
270 ip6 = mtod(m, struct ip6_hdr *);
271
272 /*
273 * Next header might not be ICMP6 but use its pseudo header anyway.
274 */
275 ip6->ip6_dst = *dst;
276
277 /*
278 * If the scope of the destination is link-local, embed the interface
279 * index in the address.
280 *
281 * XXX advanced-api value overrides sin6_scope_id
282 */
283 if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
284 struct in6_pktinfo *pi;
285
286 /*
287 * XXX Boundary check is assumed to be already done in
288 * in6_setpktoptions().
289 */
290 if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) {
291 ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
292 oifp = ifindex2ifnet[pi->ipi6_ifindex];
293 }
294 else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
295 in6p->in6p_moptions &&
296 in6p->in6p_moptions->im6o_multicast_ifp) {
297 ip6->ip6_dst.s6_addr16[1] =
298 htons(in6p->in6p_moptions->im6o_multicast_ifp->if_index);
299 oifp = ifindex2ifnet[in6p->in6p_moptions->im6o_multicast_ifp->if_index];
300 } else if (dstsock->sin6_scope_id) {
301 /* boundary check */
302 if (dstsock->sin6_scope_id < 0
303 || if_index < dstsock->sin6_scope_id) {
304 error = ENXIO; /* XXX EINVAL? */
305 goto bad;
306 }
307 ip6->ip6_dst.s6_addr16[1]
308 = htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/
309 }
310 }
311
312 if (IN6_IS_ADDR_ANY(&in6p->in6p_laddr)) {
313 struct in6_addr *in6a;
314
315 if ((in6a = in6_selectsrc(dstsock, optp,
316 in6p->in6p_moptions,
317 &in6p->in6p_route,
318 &error)) == 0) {
319 if (error == 0)
320 error = EADDRNOTAVAIL;
321 goto bad;
322 }
323 ip6->ip6_src = *in6a;
324 if (in6p->in6p_route.ro_rt)
325 oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index];
326 } else
327 ip6->ip6_src = in6p->in6p_laddr;
328
329 ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
330 ip6->ip6_vfc = IPV6_VERSION;
331 #if 0 /* ip6_plen will be filled in ip6_output. */
332 ip6->ip6_plen = htons((u_short)plen);
333 #endif
334 ip6->ip6_nxt = in6p->in6p_ip6.ip6_nxt;
335 if (oifp)
336 ip6->ip6_hlim = nd_ifinfo[oifp->if_index].chlim;
337 else
338 ip6->ip6_hlim = in6p->in6p_ip6.ip6_hlim;
339
340 if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
341 in6p->in6p_cksum != -1) {
342 struct mbuf *n;
343 int off;
344 u_int16_t *p;
345
346 #define offsetof(type, member) ((size_t)(&((type *)0)->member)) /* XXX */
347
348 /* compute checksum */
349 if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
350 off = offsetof(struct icmp6_hdr, icmp6_cksum);
351 else
352 off = in6p->in6p_cksum;
353 if (plen < off + 1) {
354 error = EINVAL;
355 goto bad;
356 }
357 off += sizeof(struct ip6_hdr);
358
359 n = m;
360 while (n && n->m_len <= off) {
361 off -= n->m_len;
362 n = n->m_next;
363 }
364 if (!n)
365 goto bad;
366 p = (u_int16_t *)(mtod(n, caddr_t) + off);
367 *p = 0;
368 *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
369 }
370
371 #ifdef IPSEC
372 m->m_pkthdr.rcvif = (struct ifnet *)so;
373 #endif /*IPSEC*/
374
375 error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions);
376 goto freectl;
377
378 bad:
379 if (m)
380 m_freem(m);
381
382 freectl:
383 if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt)
384 RTFREE(optp->ip6po_route.ro_rt);
385 if (control)
386 m_freem(control);
387 return(error);
388 }
389
390 /*
391 * Raw IPv6 socket option processing.
392 */
393 int
394 rip6_ctloutput(op, so, level, optname, m)
395 int op;
396 struct socket *so;
397 int level, optname;
398 struct mbuf **m;
399 {
400 int error = 0;
401
402 switch(level) {
403 case IPPROTO_IPV6:
404 switch(optname) {
405 case MRT6_INIT:
406 case MRT6_DONE:
407 case MRT6_ADD_MIF:
408 case MRT6_DEL_MIF:
409 case MRT6_ADD_MFC:
410 case MRT6_DEL_MFC:
411 case MRT6_PIM:
412 if (op == PRCO_SETOPT) {
413 error = ip6_mrouter_set(optname, so, *m);
414 if (*m)
415 (void)m_free(*m);
416 } else if (op == PRCO_GETOPT) {
417 error = ip6_mrouter_get(optname, so, m);
418 } else
419 error = EINVAL;
420 return (error);
421 }
422 return (ip6_ctloutput(op, so, level, optname, m));
423 /* NOTREACHED */
424
425 case IPPROTO_ICMPV6:
426 /*
427 * XXX: is it better to call icmp6_ctloutput() directly
428 * from protosw?
429 */
430 return(icmp6_ctloutput(op, so, level, optname, m));
431
432 default:
433 if (op == PRCO_SETOPT && *m)
434 (void)m_free(*m);
435 return(EINVAL);
436 }
437 }
438
439 extern u_long rip6_sendspace;
440 extern u_long rip6_recvspace;
441
442 int
443 rip6_usrreq(so, req, m, nam, control, p)
444 register struct socket *so;
445 int req;
446 struct mbuf *m, *nam, *control;
447 struct proc *p;
448 {
449 register struct in6pcb *in6p = sotoin6pcb(so);
450 int s;
451 int error = 0;
452 /* extern struct socket *ip6_mrouter; */ /* xxx */
453
454 if (req == PRU_CONTROL)
455 return (in6_control(so, (u_long)m, (caddr_t)nam,
456 (struct ifnet *)control, p));
457
458 switch (req) {
459 case PRU_ATTACH:
460 if (in6p)
461 panic("rip6_attach");
462 if (p == 0 || suser(p->p_ucred, &p->p_acflag)) {
463 error = EACCES;
464 break;
465 }
466 s = splnet();
467 if ((error = soreserve(so, rip6_sendspace, rip6_recvspace)) ||
468 (error = in6_pcballoc(so, &rawin6pcb))) {
469 splx(s);
470 break;
471 }
472 splx(s);
473 in6p = sotoin6pcb(so);
474 in6p->in6p_ip6.ip6_nxt = (int)nam;
475 in6p->in6p_cksum = -1;
476 #ifdef IPSEC
477 if ((error = ipsec_init_policy(&in6p->in6p_sp)) != 0)
478 break;
479 #endif /*IPSEC*/
480
481 MALLOC(in6p->in6p_icmp6filt, struct icmp6_filter *,
482 sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
483 ICMP6_FILTER_SETPASSALL(in6p->in6p_icmp6filt);
484 break;
485
486 case PRU_DISCONNECT:
487 if ((so->so_state & SS_ISCONNECTED) == 0) {
488 error = ENOTCONN;
489 break;
490 }
491 in6p->in6p_faddr = in6addr_any;
492 so->so_state &= ~SS_ISCONNECTED; /* XXX */
493 break;
494
495 case PRU_ABORT:
496 soisdisconnected(so);
497 /* Fallthrough */
498 case PRU_DETACH:
499 if (in6p == 0)
500 panic("rip6_detach");
501 if (so == ip6_mrouter)
502 ip6_mrouter_done();
503 /* xxx: RSVP */
504 if (in6p->in6p_icmp6filt) {
505 FREE(in6p->in6p_icmp6filt, M_PCB);
506 in6p->in6p_icmp6filt = NULL;
507 }
508 in6_pcbdetach(in6p);
509 break;
510
511 case PRU_BIND:
512 {
513 struct sockaddr_in6 *addr = mtod(nam, struct sockaddr_in6 *);
514 struct ifaddr *ia = NULL;
515
516 if (nam->m_len != sizeof(*addr)) {
517 error = EINVAL;
518 break;
519 }
520 if ((ifnet.tqh_first == 0) ||
521 (addr->sin6_family != AF_INET6) ||
522 (!IN6_IS_ADDR_ANY(&addr->sin6_addr) &&
523 (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)) {
524 error = EADDRNOTAVAIL;
525 break;
526 }
527 if (ia &&
528 ((struct in6_ifaddr *)ia)->ia6_flags &
529 (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
530 IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
531 error = EADDRNOTAVAIL;
532 break;
533 }
534 in6p->in6p_laddr = addr->sin6_addr;
535 break;
536 }
537
538 case PRU_CONNECT:
539 {
540 struct sockaddr_in6 *addr = mtod(nam, struct sockaddr_in6 *);
541 struct in6_addr *in6a = NULL;
542
543 if (nam->m_len != sizeof(*addr)) {
544 error = EINVAL;
545 break;
546 }
547 if (ifnet.tqh_first == 0) {
548 error = EADDRNOTAVAIL;
549 break;
550 }
551 if (addr->sin6_family != AF_INET6) {
552 error = EAFNOSUPPORT;
553 break;
554 }
555
556 /* Source address selection. XXX: need pcblookup? */
557 in6a = &in6p->in6p_laddr;
558 if (IN6_IS_ADDR_ANY(in6a) &&
559 (in6a = in6_selectsrc(addr, in6p->in6p_outputopts,
560 in6p->in6p_moptions, &in6p->in6p_route,
561 &error)) == NULL) {
562 if (error == 0)
563 error = EADDRNOTAVAIL;
564 break;
565 }
566 in6p->in6p_laddr = *in6a;
567 in6p->in6p_faddr = addr->sin6_addr;
568 soisconnected(so);
569 break;
570 }
571
572 case PRU_CONNECT2:
573 error = EOPNOTSUPP;
574 break;
575
576 /*
577 * Mark the connection as being incapable of futther input.
578 */
579 case PRU_SHUTDOWN:
580 socantsendmore(so);
581 break;
582 /*
583 * Ship a packet out. The appropriate raw output
584 * routine handles any messaging necessary.
585 */
586 case PRU_SEND:
587 {
588 struct sockaddr_in6 tmp;
589 struct sockaddr_in6 *dst;
590
591 if (so->so_state & SS_ISCONNECTED) {
592 if (nam) {
593 error = EISCONN;
594 break;
595 }
596 /* XXX */
597 bzero(&tmp, sizeof(tmp));
598 tmp.sin6_family = AF_INET6;
599 tmp.sin6_len = sizeof(struct sockaddr_in6);
600 bcopy(&in6p->in6p_faddr, &tmp.sin6_addr,
601 sizeof(struct in6_addr));
602 dst = &tmp;
603 } else {
604 if (nam == NULL) {
605 error = ENOTCONN;
606 break;
607 }
608 dst = mtod(nam, struct sockaddr_in6 *);
609 }
610 error = rip6_output(m, so, dst, control);
611 m = NULL;
612 break;
613 }
614
615 case PRU_SENSE:
616 /*
617 * stat: don't bother with a blocksize
618 */
619 return(0);
620 /*
621 * Not supported.
622 */
623 case PRU_RCVOOB:
624 case PRU_RCVD:
625 case PRU_LISTEN:
626 case PRU_ACCEPT:
627 case PRU_SENDOOB:
628 error = EOPNOTSUPP;
629 break;
630
631 case PRU_SOCKADDR:
632 in6_setsockaddr(in6p, nam);
633 break;
634
635 case PRU_PEERADDR:
636 in6_setpeeraddr(in6p, nam);
637 break;
638
639 default:
640 panic("rip6_usrreq");
641 }
642 if (m != NULL)
643 m_freem(m);
644 return(error);
645 }
646