diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-01-06 23:26:59 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-01-06 23:26:59 +0000 |
commit | 003294c571f5404cf3bb2ab3cf33eba8643b932b (patch) | |
tree | 0efc8463ff2d3c2555c4a6a830fe508568cc5e07 /sys/netinet6/raw_ipv6.c | |
parent | 21e642825078d715499c3110ace9c6e61f6c6db4 (diff) |
first few files of NRL ipv6. This NRL release was officially exported
to me by US DOD officials, with the crypto already removed.
Diffstat (limited to 'sys/netinet6/raw_ipv6.c')
-rw-r--r-- | sys/netinet6/raw_ipv6.c | 1059 |
1 files changed, 1059 insertions, 0 deletions
diff --git a/sys/netinet6/raw_ipv6.c b/sys/netinet6/raw_ipv6.c new file mode 100644 index 00000000000..fb95a4858b2 --- /dev/null +++ b/sys/netinet6/raw_ipv6.c @@ -0,0 +1,1059 @@ +/* +%%% copyright-nrl-95 +This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee, +Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All +rights under this copyright have been assigned to the US Naval Research +Laboratory (NRL). The NRL Copyright Notice and License Agreement Version +1.1 (January 17, 1995) applies to this software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. + +*/ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)raw_ip.c 8.7 (Berkeley) 5/15/95 + * $Id: raw_ipv6.c,v 1.1 1999/01/06 23:26:58 deraadt Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> +#if __NetBSD__ || __FreeBSD__ +#include <sys/proc.h> +#endif /* __NetBSD__ || __FreeBSD__ */ +#if __FreeBSD__ +#include <vm/vm_zone.h> +#endif /* __FreeBSD__ */ + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/ip_mroute.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_pcb.h> + +#include <netinet6/in6.h> +#include <netinet6/in6_var.h> +#include <netinet6/ipv6.h> +#include <netinet6/ipv6_var.h> +#include <netinet6/icmpv6.h> + +#if __OpenBSD__ && defined(NRL_IPSEC) +#define IPSEC 1 +#endif /* __OpenBSD__ && defined(NRL_IPSEC) */ + +#ifdef IPSEC +#include <sys/osdep.h> +#include <net/netproc.h> +#include <net/netproc_var.h> +#endif /* IPSEC */ + +#include <sys/debug.h> + +/* + * Globals + */ + +#if __NetBSD__ || __OpenBSD__ +struct inpcbtable rawin6pcbtable; +#else /* __NetBSD__ || __OpenBSD__ */ +struct inpcb rawin6pcb; +#endif /* __NetBSD__ || __OpenBSD__ */ +struct sockaddr_in6 ripv6src = { sizeof(struct sockaddr_in6), AF_INET6 }; + +/* + * Nominal space allocated to a raw ip socket. + */ + +#define RIPV6SNDQ 8192 +#define RIPV6RCVQ 8192 +u_long ripv6_sendspace = RIPV6SNDQ; +u_long ripv6_recvspace = RIPV6RCVQ; + +/* + * External globals + */ +#if __FreeBSD__ +static struct inpcbhead ri6pcb; +static struct inpcbinfo ri6pcbinfo; +#endif /* __FreeBSD__ */ + +extern struct ipv6stat ipv6stat; + +struct mbuf *ipv6_headertocontrol(struct mbuf *m, size_t extra, int inp_flags); +int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *); + +#define RETURN_ERROR(x) { \ + DPRINTF(EVENT, ("%s: returning %s\n", DEBUG_STATUS, #x)); \ + return x; \ +} +#define RETURN_VALUE(x) { \ + DPRINTF(EVENT, ("%s: returning %d\n", DEBUG_STATUS, x)); \ + return x; \ +} + +/*---------------------------------------------------------------------- + * Raw IPv6 PCB initialization. + ----------------------------------------------------------------------*/ + +void +ripv6_init() +{ +#if __FreeBSD__ + LIST_INIT(&ri6pcb); + ri6pcbinfo.listhead = &ri6pcb; + /* + * XXX We don't use the hash list for raw IP, but it's easier + * to allocate a one entry hash list than it is to check all + * over the place for hashbase == NULL. + */ + ri6pcbinfo.hashbase = hashinit(1, M_PCB, &ri6pcbinfo.hashmask); + ri6pcbinfo.porthashbase = hashinit(1, M_PCB, &ri6pcbinfo.porthashmask); + ri6pcbinfo.ipi_zone = zinit("ri6pcb", sizeof(struct inpcb), + nmbclusters / 4, ZONE_INTERRUPT, 0); +#else /* __FreeBSD__ */ +#if __NetBSD__ + in_pcbinit(&rawin6pcbtable, 1, 1); +#else /* __NetBSD__ */ +#if __OpenBSD__ + in_pcbinit(&rawin6pcbtable, 1); +#else /* __OpenBSD__ */ + rawin6pcb.inp_next = rawin6pcb.inp_prev = &rawin6pcb; +#endif /* __OpenBSD__ */ +#endif /* __NetBSD__ */ +#endif /* __FreeBSD__ */ +} + +/* At the point where this function gets called, we don't know the nexthdr of + the current header to be processed, only its offset. So we have to go find + it the hard way. In the case where there's no chained headers, this is not + really painful. + + The good news is that all fields have been sanity checked. + + Assumes m has already been pulled up by extra. -cmetz +*/ +#if __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ +static __inline__ int ipv6_findnexthdr(struct mbuf *m, size_t extra) +#else /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ */ +static int ipv6_findnexthdr(struct mbuf *m, size_t extra) +#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ */ +{ + caddr_t p = mtod(m, caddr_t); + int nexthdr = IPPROTO_IPV6; + unsigned int hl; + + do { + switch(nexthdr) { + case IPPROTO_IPV6: + hl = sizeof(struct ipv6); + + if ((extra -= hl) < 0) + return -1; + + nexthdr = ((struct ipv6 *)p)->ipv6_nexthdr; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + if (extra < sizeof(struct ipv6_opthdr)) + return -1; + + hl = sizeof(struct ipv6_opthdr) + + ((struct ipv6_opthdr *)p)->oh_extlen << 3; + + if ((extra -= hl) < 0) + return -1; + + nexthdr = ((struct ipv6_opthdr *)p)->oh_nexthdr; + break; + case IPPROTO_ROUTING: + if (extra < sizeof(struct ipv6_srcroute0)) + return -1; + + hl = sizeof(struct ipv6_srcroute0) + + ((struct ipv6_srcroute0 *)p)->i6sr_len << 3; + + if ((extra -= hl) < 0) + return -1; + + nexthdr = ((struct ipv6_srcroute0 *)p)->i6sr_nexthdr; + break; +#ifdef IPSEC + case IPPROTO_AH: + if (extra < sizeof(struct ipv6_srcroute0)) + return -1; + + hl = sizeof(struct ipv6_srcroute0) + + ((struct ipv6_srcroute0 *)p)->i6sr_len << 3; + + if ((extra -= hl) < 0) + return -1; + + nexthdr = ((struct ipv6_srcroute0 *)p)->i6sr_nexthdr; + break; +#endif /* IPSEC */ + default: + return -1; + } + p += hl; + } while(extra > 0); + + return nexthdr; +} + +/*---------------------------------------------------------------------- + * If no HLP's are found for an IPv6 datagram, this routine is called. + ----------------------------------------------------------------------*/ +void +#if __OpenBSD__ +ripv6_input(struct mbuf *m, ...) +#else /* __OpenBSD__ */ +ripv6_input(m,extra) + struct mbuf *m; + int extra; +#endif /* __OpenBSD__ */ +{ + register struct ipv6 *ipv6 = mtod(m, struct ipv6 *); /* Will have been + pulled up by + ipv6_input(). */ + register struct inpcb *inp; + int nexthdr, icmpv6type; + int foundone = 0; + struct mbuf *m2 = NULL, *opts = NULL; + struct sockaddr_in6 srcsa; +#ifdef IPSEC + struct sockaddr_in6 dstsa; +#endif /* IPSEC */ +#if __OpenBSD__ + int extra; + va_list ap; + + va_start(ap, m); + extra = va_arg(ap, int); + va_end(ap); +#endif /* __OpenBSD__ */ + + DPRINTF(FINISHED, ("ripv6_input(m=%08x, extra=%d)\n", OSDEP_PCAST(m), extra)); + DP(FINISHED, m->m_pkthdr.len, d); + + DDO(FINISHED,printf("In ripv6_input(), header is:\n");dump_mchain(m)); + DPRINTF(EVENT, ("In ripv6_input()\n")); + DPRINTF(EVENT, ("Header is: ")); + DDO(GROSSEVENT, dump_ipv6(ipv6)); + + bzero(&srcsa, sizeof(struct sockaddr_in6)); + srcsa.sin6_family = AF_INET6; + srcsa.sin6_len = sizeof(struct sockaddr_in6); + srcsa.sin6_addr = ipv6->ipv6_src; + +#if IPSEC + bzero(&dstsa, sizeof(struct sockaddr_in6)); + dstsa.sin6_family = AF_INET6; + dstsa.sin6_len = sizeof(struct sockaddr_in6); + dstsa.sin6_addr = ipv6->ipv6_dst; +#endif /* IPSEC */ + +#if 0 + /* Will be done already by the previous input functions */ + if (m->m_len < extra)) { + if (!(m = m_pullup2(m, extra))) + return; + ipv6 = mtod(m, struct ipv6 *); + } +#endif /* 0 */ + + if ((nexthdr = ipv6_findnexthdr(m, extra)) < 0) { + DPRINTF(ERROR, ("ripv6_input: ipv6_findnexthdr failed\n")); + goto ret; + } + + DP(FINISHED, nexthdr, d); + + if (nexthdr == IPPROTO_ICMPV6) { + if (m->m_len < extra + sizeof(struct icmpv6hdr)) { + if (!(m = m_pullup2(m, extra + sizeof(struct icmpv6hdr)))) { + DPRINTF(ERROR, ("ripv6_input: m_pullup2 failed\n")); + goto ret; + } + ipv6 = mtod(m, struct ipv6 *); + } + icmpv6type = ((struct icmpv6hdr *)(mtod(m, caddr_t) + extra))->icmpv6_type; + } else + icmpv6type = -1; + + /* + * Locate raw PCB for incoming datagram. + */ +#if __FreeBSD__ + for (inp = ri6pcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) { +#else /* __FreeBSD__ */ +#if __NetBSD__ || __OpenBSD__ + for (inp = rawin6pcbtable.inpt_queue.cqh_first; + inp != (struct inpcb *)&rawin6pcbtable.inpt_queue; + inp = inp->inp_queue.cqe_next) { +#else /* __NetBSD__ || __OpenBSD__ */ + for (inp = rawin6pcb.inp_next; inp != &rawin6pcb; inp = inp->inp_next) { +#endif /* __NetBSD__ || __OpenBSD__ */ +#endif /* __FreeBSD__ */ + if (inp->inp_ipv6.ipv6_nexthdr && inp->inp_ipv6.ipv6_nexthdr != nexthdr) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) && + !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &ipv6->ipv6_dst)) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) && + !IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ipv6->ipv6_src)) + continue; + if ((icmpv6type >= 0) && + ICMPV6_FILTER_WILLBLOCK(icmpv6type, &inp->inp_filter)) + continue; + + DPRINTF(IDL_EVENT, ("Found a raw pcb (>1)\n")); + foundone = 1; + +#ifdef IPSEC + /* Perform input-side policy check. Drop packet if policy says to drop it. + + Note: For ICMPv6 packets, we also checked policy in ipv6_icmp_input(). + + XXX - state arg should NOT be NULL, it should be the netproc state + carried up the stack - cmetz */ + if (!netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa, + (struct sockaddr *)&dstsa, nexthdr, m, NULL, NULL)) +#endif /* IPSEC */ + + DP(FINISHED, m->m_pkthdr.len, d); + /* Note the inefficiency here; this is a consequence of the interfaces of + the functions being used. The raw code is not performance critical + enough to require an immediate fix. - cmetz */ + if ((m2 = m_copym(m, 0, (int)M_COPYALL, M_DONTWAIT))) { + m_adj(m2, extra); + DP(FINISHED, m2->m_pkthdr.len, d); + if (inp->inp_flags & INP_CONTROLOPTS) + opts = ipv6_headertocontrol(m, extra, inp->inp_flags); + else + opts = NULL; + if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&srcsa, m2, + opts)) { + sorwakeup(inp->inp_socket); + } else { + m_freem(m2); + }; + }; + }; + + if (!foundone) { + /* + * We should send an ICMPv6 protocol unreachable here, + * though original UCB 4.4-lite BSD's IPv4 does not do so. + */ + ipv6stat.ips_noproto++; + ipv6stat.ips_delivered--; + } + +ret: + if (m) + m_freem(m); + + DPRINTF(FINISHED, ("ripv6_input\n")); +} + +/*---------------------------------------------------------------------- + * Output function for raw IPv6. Called from ripv6_usrreq(), and + * ipv6_icmp_usrreq(). + ----------------------------------------------------------------------*/ + +#if __OpenBSD__ +int ripv6_output(struct mbuf *m, ...) +#else /* __OpenBSD__ */ +int +ripv6_output(m, so, dst, control) + struct mbuf *m; + struct socket *so; + struct in6_addr *dst; + struct mbuf *control; +#endif /* __OpenBSD__ */ +{ + register struct ipv6 *ipv6; + register struct inpcb *inp; + int flags; + struct ifnet *forceif = NULL; +#if __OpenBSD__ + va_list ap; + struct socket *so; + struct in6_addr *dst; + struct mbuf *control; + + va_start(ap, m); + so = va_arg(ap, struct socket *); + dst = va_arg(ap, struct in6_addr *); + control = va_arg(ap, struct mbuf *); + va_end(ap); +#endif /* __OpenBSD__ */ + + inp = sotoinpcb(so); + flags = (so->so_options & SO_DONTROUTE); + + if (inp->inp_flags & INP_HDRINCL) + { + flags |= IPV6_RAWOUTPUT; + ipv6stat.ips_rawout++; + /* Maybe m_pullup() ipv6 header here for ipv6_output(), which + expects it to be contiguous. */ + } + else + { + M_PREPEND(m, sizeof(struct ipv6), M_WAIT); + ipv6 = mtod(m, struct ipv6 *); + ipv6->ipv6_nexthdr = inp->inp_ipv6.ipv6_nexthdr; + ipv6->ipv6_hoplimit = MAXHOPLIMIT; + ipv6->ipv6_src = inp->inp_laddr6; + ipv6->ipv6_dst = *dst; + ipv6->ipv6_versfl = 0; /* Or possibly user flow label, in host order. */ + /* + * Question: How do I handle options? + * + * Answer: I put them in here, but how? + */ + } + + { + int payload = sizeof(struct ipv6); + int nexthdr = mtod(m, struct ipv6 *)->ipv6_nexthdr; + int error; + + if (control) + if ((error = ipv6_controltoheader(&m, control, &forceif, &payload))) { + m_freem(m); + return error; + } + + if (inp->inp_csumoffset >= 0) { + uint16_t *csum; + + if (!(m = m_pullup2(m, payload + inp->inp_csumoffset))) { + DPRINTF(IDL_ERROR, ("ripv6_output: m_pullup2(m, %d) failed\n", payload + inp->inp_csumoffset)); + m_freem(m); + return ENOBUFS; + }; + + csum = (uint16_t *)(mtod(m, uint8_t *) + payload + inp->inp_csumoffset); + + *csum = 0; + *csum = in6_cksum(m, nexthdr, m->m_pkthdr.len - payload, payload); + }; + }; + + return ipv6_output(m,&inp->inp_route6,flags,inp->inp_moptions6, forceif, so); +} + +/*---------------------------------------------------------------------- + * Handles [gs]etsockopt() calls. + ----------------------------------------------------------------------*/ + +#if __FreeBSD__ +int ripv6_ctloutput(struct socket *so, struct sockopt *sopt) +{ + register struct inpcb *inp = sotoinpcb(so); + int op; + int level; + int optname; + int optval; + + DPRINTF(FINISHED, ("ripv6_ctloutput(so=%08x, sopt=%08x)\n", + OSDEP_PCAST(so), OSDEP_PCAST(sopt))); + + switch(sopt->sopt_dir) { + case SOPT_GET: + op = PRCO_GETOPT; + break; + case SOPT_SET: + op = PRCO_SETOPT; + break; + default: + RETURN_ERROR(EINVAL); + }; + + level = sopt->sopt_level; + optname = sopt->sopt_name; +#else /* __FreeBSD__ */ +int +ripv6_ctloutput (op, so, level, optname, m) + int op; + struct socket *so; + int level, optname; + struct mbuf **m; +{ + register struct inpcb *inp = sotoinpcb(so); + + DPRINTF(FINISHED, ("ripv6_ctloutput(op=%x,so,level=%x,optname=%x,m)\n", op, level, optname)); +#endif /* __FreeBSD__ */ + + if ((level != IPPROTO_IP) && (level != IPPROTO_IPV6) && (level != IPPROTO_ICMPV6)) { +#if !__FreeBSD__ + if (op == PRCO_SETOPT && *m) + (void)m_free(*m); +#endif /* !__FreeBSD__ */ + RETURN_ERROR(EINVAL); + } + + switch (optname) { + case IPV6_CHECKSUM: + if (op == PRCO_SETOPT || op == PRCO_GETOPT) { +#if __FreeBSD__ + if (!sopt->sopt_val || (sopt->sopt_valsize != sizeof(int))) + RETURN_ERROR(EINVAL); + if (op == PRCO_SETOPT) { + int error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)); + if (error) + RETURN_VALUE(error); + inp->inp_csumoffset = optval; + + return 0; + } else + return sooptcopyout(sopt, &inp->inp_csumoffset, sizeof(int)); +#else /* __FreeBSD__ */ + if (!m || !*m || (*m)->m_len != sizeof(int)) + RETURN_ERROR(EINVAL); + if (op == PRCO_SETOPT) { + inp->inp_csumoffset = *(mtod(*m, int *)); + m_freem(*m); + } else { + (*m)->m_len = sizeof(int); + *(mtod(*m, int *)) = inp->inp_csumoffset; + }; +#endif /* __FreeBSD__ */ + return 0; + }; + break; + case ICMPV6_FILTER: + if (op == PRCO_SETOPT || op == PRCO_GETOPT) { +#if __FreeBSD__ + if (!sopt->sopt_val || (sopt->sopt_valsize != + sizeof(struct icmpv6_filter))) + RETURN_ERROR(EINVAL); + if (op == PRCO_SETOPT) { + struct icmpv6_filter icmpv6_filter; + int error = sooptcopyin(sopt, &icmpv6_filter, + sizeof(struct icmpv6_filter), sizeof(struct icmpv6_filter)); + if (error) + return error; + + inp->inp_filter = icmpv6_filter; + + return 0; + } else + return sooptcopyout(sopt, &inp->inp_filter, + sizeof(struct icmpv6_filter)); +#else /* __FreeBSD__ */ + if (!m || !*m || (*m)->m_len != sizeof(struct icmpv6_filter)) + RETURN_ERROR(EINVAL); + if (op == PRCO_SETOPT) { + inp->inp_filter = *mtod(*m, struct icmpv6_filter *); + m_freem(*m); + } else { + (*m)->m_len = sizeof(struct icmpv6_filter); + *mtod(*m, struct icmpv6_filter *) = inp->inp_filter; + }; + return 0; +#endif /* __FreeBSD__ */ + }; + break; + +/* Should this be obsoleted? */ + case IP_HDRINCL: + if (op == PRCO_SETOPT || op == PRCO_GETOPT) + { +#if __FreeBSD__ + if (!sopt->sopt_val || (sopt->sopt_valsize != sizeof(int))) + RETURN_ERROR(EINVAL); + if (op == PRCO_SETOPT) { + int error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)); + if (error) + return error; + + if (optval) + inp->inp_flags |= INP_HDRINCL; + else + inp->inp_flags &= ~INP_HDRINCL; + + return 0; + } else { + optval = (inp->inp_flags & INP_HDRINCL) ? 1 : 0; + return sooptcopyout(sopt, &optval, sizeof(int)); + }; +#else /* __FreeBSD__ */ + if (m == 0 || *m == 0 || (*m)->m_len != sizeof(int)) + RETURN_ERROR(EINVAL); + if (op == PRCO_SETOPT) + { + if (*mtod(*m, int *)) + inp->inp_flags |= INP_HDRINCL; + else inp->inp_flags &= ~INP_HDRINCL; + m_free(*m); + } + else + { + (*m)->m_len = sizeof(int); + *(mtod(*m, int *)) = (inp->inp_flags & INP_HDRINCL) ? 1 : 0; + } + return 0; +#endif /* __FreeBSD__ */ + } + break; + +#ifdef MRT_INIT + default: + if (optname >= MRT_INIT) { +#else /* MRT_INIT */ + case DVMRP_INIT: + case DVMRP_DONE: + case DVMRP_ADD_VIF: + case DVMRP_DEL_VIF: + case DVMRP_ADD_LGRP: + case DVMRP_DEL_LGRP: + case DVMRP_ADD_MRT: + case DVMRP_DEL_MRT: + { +#endif /* MRT_INIT */ +#ifdef MROUTING +/* Be careful here! */ +/* if (op == PRCO_SETOPT) + { + error = ipv6_mrouter_cmd(optname, so, *m); + if (*m) + (void)m_free(*m); + } + else error = EINVAL; + return (error);*/ + RETURN_ERROR(EOPNOTSUPP); +#else /* MROUTING */ +#if !__FreeBSD__ + if (op == PRCO_SETOPT && *m) + (void)m_free(*m); +#endif /* !__FreeBSD__ */ + RETURN_ERROR(EOPNOTSUPP); +#endif /* MROUTING */ + }; + } +#if __FreeBSD__ + return ipv6_ctloutput(so, sopt); +#else /* __FreeBSD__ */ + return ipv6_ctloutput(op, so, level, optname, m); +#endif /* __FreeBSD__ */ +} + +#if __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ +#define MAYBESTATIC static +#define MAYBEINLINE __inline__ +#else /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */ +#define MAYBESTATIC +#define MAYBEINLINE +#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */ + +#if __NetBSD__ || __FreeBSD__ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto, + struct proc *p) +#else /* __NetBSD__ || __FreeBSD__ */ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto) +#endif /* __NetBSD__ || __FreeBSD__ */ +{ + register struct inpcb *inp = sotoinpcb(so); + register int error = 0; + + if (inp) + panic("ripv6_attach - Already got PCB"); + +#if __NetBSD__ || __FreeBSD__ + if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag))) +#else /* __NetBSD__ || __FreeBSD__ */ + if ((so->so_state & SS_PRIV) == 0) +#endif /* __NetBSD__ || __FreeBSD__ */ + { + error = EACCES; + return error; + } + if ((error = soreserve(so, ripv6_sendspace, ripv6_recvspace)) || + +#if __FreeBSD__ + (error = in_pcballoc(so, &ri6pcbinfo, p))) +#else /* __FreeBSD__ */ +#if __NetBSD__ || __OpenBSD__ + (error = in_pcballoc(so, &rawin6pcbtable))) +#else /* __NetBSD__ || __OpenBSD__ */ + (error = in_pcballoc(so, &rawin6pcb))) +#endif /* __NetBSD__ || __OpenBSD__ */ +#endif /* __FreeBSD__ */ + + return error; + + inp = sotoinpcb(so); +#ifdef __alpha__ + inp->inp_ipv6.ipv6_nexthdr = (u_long)proto; /*nam; Nam contains protocol + type, apparently. */ +#else + inp->inp_ipv6.ipv6_nexthdr = (int)proto; /*nam; Nam contains protocol + type, apparently. */ +#endif + if (inp->inp_ipv6.ipv6_nexthdr == IPPROTO_ICMPV6) + inp->inp_csumoffset = 2; + return error; +} + +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_detach(struct socket *so) +{ + register struct inpcb *inp = sotoinpcb(so); + + if (inp == 0) + panic("ripv6_detach"); +#ifdef MROUTING + /* More MROUTING stuff. */ +#endif + in_pcbdetach(inp); + return 0; +} + +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_abort(struct socket *so) +{ + soisdisconnected(so); + return ripv6_usrreq_detach(so); +} + +static MAYBEINLINE int ripv6_usrreq_disconnect(struct socket *so) +{ + if ((so->so_state & SS_ISCONNECTED) == 0) + return ENOTCONN; + return ripv6_usrreq_abort(so); +} + +#if __NetBSD__ || __FreeBSD__ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so, + struct sockaddr *nam, struct proc *p) +#else /* __NetBSD__ || __FreeBSD__ */ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so, + struct sockaddr *nam) +#endif /* __NetBSD__ || __FreeBSD__ */ +{ + register struct inpcb *inp = sotoinpcb(so); + register struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; + + /* 'ifnet' is declared in one of the net/ header files. */ +#if __NetBSD__ || __OpenBSD__ || __FreeBSD__ + if ((ifnet.tqh_first == 0) || +#else /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ */ + if ((ifnet == 0) || +#endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ */ + (addr->sin6_family != AF_INET6) || /* I only allow AF_INET6 */ + (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && + ifa_ifwithaddr((struct sockaddr *)addr) == 0 ) ) + + return EADDRNOTAVAIL; + + inp->inp_laddr6 = addr->sin6_addr; + return 0; +} + +#if __NetBSD__ || __FreeBSD__ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so, + struct sockaddr *nam, struct proc *p) +#else /* __NetBSD__ || __FreeBSD__ */ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so, + struct sockaddr *nam) +#endif /* __NetBSD__ || __FreeBSD__ */ +{ + register struct inpcb *inp = sotoinpcb(so); + register struct sockaddr_in6 *addr = (struct sockaddr_in6 *) nam; + + if (addr->sin6_family != AF_INET6) + return EAFNOSUPPORT; + +#if __NetBSD__ || __FreeBSD__ || __OpenBSD__ + if (ifnet.tqh_first == 0) { +#else /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */ + if (ifnet == 0) { +#endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */ + return EADDRNOTAVAIL; /* This is a weird way to say there + are no interfaces, no? */ + } + + inp->inp_faddr6 = addr->sin6_addr; /* Will structure assignment + work with this compiler? */ + soisconnected(so); + return 0; +} + +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_shutdown(struct socket *so) +{ + socantsendmore(so); + return 0; +} + +#if __NetBSD__ || __FreeBSD__ +/* + * Note that flags and p are not used, but required by protosw in + * FreeBSD. + */ +int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m, + struct sockaddr *addr, struct mbuf *control, + struct proc *p) +#else /* __NetBSD__ || __FreeBSD__ */ +int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m, + struct sockaddr *addr, struct mbuf *control) +#endif /* __NetBSD__ || __FreeBSD__ */ +{ + register struct inpcb *inp = sotoinpcb(so); + register int error = 0; + struct in6_addr *dst; + + if (inp == 0) { + m_freem(m); + return EINVAL; + } + + /* + * Check "connected" status, and if there is a supplied destination + * address. + */ + if (so->so_state & SS_ISCONNECTED) + { + if (addr) + return EISCONN; + + dst = &(inp->inp_faddr6); + } + else + { + if (addr == NULL) + return ENOTCONN; + + dst = &((struct sockaddr_in6 *)addr)->sin6_addr; + } + + error = ripv6_output(m,so,dst,control); + /* m = NULL; */ + return error; +} + +#if __NetBSD__ || __FreeBSD__ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, u_long cmd, + caddr_t data, struct ifnet *ifp, struct proc *p) +#else /* __NetBSD__ || __FreeBSD__ */ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, int cmd, + caddr_t data, struct ifnet *ifp) +#endif /* __NetBSD__ || __FreeBSD__ */ +{ +/* Notice that IPv4 raw sockets don't pass PRU_CONTROL. I wonder + if they panic as well? */ +#if __NetBSD__ || __FreeBSD__ + return in6_control(so, cmd, data, ifp, 0, p); +#else /* __NetBSD__ || __FreeBSD__ */ + return in6_control(so, cmd, data, ifp, 0); +#endif /* __NetBSD__ || __FreeBSD__ */ +} + +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sense(struct socket *so, + struct stat *sb) +{ + /* services stat(2) call. */ + return 0; +} + +#if __FreeBSD__ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so, + struct sockaddr **nam) +#else /* __FreeBSD__ */ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so, + struct mbuf *nam) +#endif /* __FreeBSD__ */ +{ + register struct inpcb *inp = sotoinpcb(so); + return in6_setsockaddr(inp, nam); +} + +#if __FreeBSD__ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so, + struct sockaddr **nam) +#else /* __FreeBSD__ */ +MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so, + struct mbuf *nam) +#endif /* __FreeBSD__ */ +{ + register struct inpcb *inp = sotoinpcb(so); + return in6_setpeeraddr(inp, nam); +} + +#if __FreeBSD__ +struct pr_usrreqs ripv6_usrreqs = { + ripv6_usrreq_abort, pru_accept_notsupp, ripv6_usrreq_attach, + ripv6_usrreq_bind, ripv6_usrreq_connect, pru_connect2_notsupp, + ripv6_usrreq_control, ripv6_usrreq_detach, ripv6_usrreq_detach, + pru_listen_notsupp, ripv6_usrreq_peeraddr, pru_rcvd_notsupp, + pru_rcvoob_notsupp, ripv6_usrreq_send, ripv6_usrreq_sense, + ripv6_usrreq_shutdown, ripv6_usrreq_sockaddr, sosend, soreceive, sopoll +}; +#endif /* __FreeBSD__ */ + +/*---------------------------------------------------------------------- + * Handles PRU_* for raw IPv6 sockets. + ----------------------------------------------------------------------*/ +#if !__FreeBSD__ +int +#if __NetBSD__ +ripv6_usrreq(so, req, m, nam, control, p) +#else /* __NetBSD__ */ +ripv6_usrreq(so, req, m, nam, control) +#endif /* __NetBSD__ */ + struct socket *so; + int req; + struct mbuf *m, *nam, *control; +#if __NetBSD__ + struct proc *p; +#endif /* __NetBSD__ */ +{ + register int error = 0; + + DPRINTF(IDL_EVENT, ("ripv6_usrreq(so, req, m, nam, control)\n")); + +#ifdef MROUTING + /* + * Ummm, like, multicast routing stuff goes here, huh huh huh. + * + * Seriously, this would be for user-level multicast routing daemons. With + * multicast being a requirement for IPv6, code like what might go here + * may go away. + */ +#endif + + switch (req) + { + case PRU_ATTACH: +#if __NetBSD__ + error = ripv6_usrreq_attach(so, 0, p); /* XXX */ +#else /* __NetBSD__ */ + error = ripv6_usrreq_attach(so, 0); /* XXX */ +#endif /* __NetBSD__ */ + break; + case PRU_DISCONNECT: + error = ripv6_usrreq_disconnect(so); + break; + /* NOT */ + /* FALLTHROUGH */ + case PRU_ABORT: + error = ripv6_usrreq_abort(so); + break; + /* NOT */ + /* FALLTHROUGH */ + case PRU_DETACH: + error = ripv6_usrreq_detach(so); + break; + case PRU_BIND: + if (nam->m_len != sizeof(struct sockaddr_in6)) + return EINVAL; + /* + * Be strict regarding sockaddr_in6 fields. + */ +#if __NetBSD__ + error = ripv6_usrreq_bind(so, mtod(nam, struct sockaddr *), p); +#else /* __NetBSD__ */ + error = ripv6_usrreq_bind(so, mtod(nam, struct sockaddr *)); +#endif /* __NetBSD__ */ + break; + case PRU_CONNECT: + /* + * Be strict regarding sockaddr_in6 fields. + */ + if (nam->m_len != sizeof(struct sockaddr_in6)) + return EINVAL; +#if __NetBSD__ + error = ripv6_usrreq_connect(so, mtod(nam, struct sockaddr *), p); +#else /* __NetBSD__ */ + error = ripv6_usrreq_connect(so, mtod(nam, struct sockaddr *)); +#endif /* __NetBSD__ */ + break; + case PRU_SHUTDOWN: + error = ripv6_usrreq_shutdown(so); + break; + case PRU_SEND: + /* + * Be strict regarding sockaddr_in6 fields. + */ + if (nam->m_len != sizeof(struct sockaddr_in6)) + return EINVAL; +#if __NetBSD__ + error = ripv6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control, p); +#else /* __NetBSD__ */ + error = ripv6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control); +#endif /* __NetBSD__ */ + m = NULL; + break; + case PRU_CONTROL: +#if __NetBSD__ + return ripv6_usrreq_control(so, (u_long)m, (caddr_t) nam, + (struct ifnet *) control, p); +#else /* __NetBSD__ */ + return ripv6_usrreq_control(so, (int)m, (caddr_t) nam, + (struct ifnet *) control); +#endif /* __NetBSD__ */ + case PRU_SENSE: + return ripv6_usrreq_sense(so, NULL); /* XXX */ + case PRU_CONNECT2: + case PRU_RCVOOB: + case PRU_LISTEN: + case PRU_SENDOOB: + case PRU_RCVD: + case PRU_ACCEPT: + error = EOPNOTSUPP; + break; + case PRU_SOCKADDR: + error = ripv6_usrreq_sockaddr(so, nam); + break; + case PRU_PEERADDR: + error = ripv6_usrreq_peeraddr(so, nam); + break; + default: + panic ("ripv6_usrreq - unknown req\n"); + } + if (m != NULL) + m_freem(m); + return error; +} +#endif /* !__FreeBSD__ */ |