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