-/* $KAME: ip6_output.c,v 1.94 2000/04/04 14:45:44 itojun Exp $ */
+/* $FreeBSD: src/sys/netinet6/ip6_output.c,v 1.43 2002/10/31 19:45:48 ume Exp $ */
+/* $KAME: ip6_output.c,v 1.279 2002/01/26 06:12:30 jinmei Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
*/
-#if __FreeBSD__
-#include "opt_ip6fw.h"
-#endif
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
-#include "opt_inet.h"
-#if __NetBSD__ /*XXX*/
-#include "opt_ipsec.h"
-#endif
-#endif
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
#include <sys/kernel.h>
-#endif
#include <sys/proc.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
-#if defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#endif
+#include <netinet/ip_var.h>
+#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet6/ip6_var.h>
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802) || defined (__APPLE__)
#include <netinet/in_pcb.h>
-#else
-#include <netinet6/in6_pcb.h>
-#endif
#include <netinet6/nd6.h>
#if IPSEC
#include <netinet6/ipsec.h>
+#if INET6
+#include <netinet6/ipsec6.h>
+#endif
#include <netkey/key.h>
-#include <netkey/key_debug.h>
+extern int ipsec_bypass;
#endif /* IPSEC */
-#ifndef __bsdi__
-#include "loop.h"
-#endif
+#include <netinet6/ip6_fw.h>
#include <net/net_osdep.h>
-#if IPV6FIREWALL
-#include <netinet6/ip6_fw.h>
-#endif
-
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#ifndef __APPLE__
static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
#endif
+
+static u_long lo_dl_tag = 0;
+extern u_long route_generation;
+
+
struct ip6_exthdrs {
struct mbuf *ip6e_ip6;
struct mbuf *ip6e_hbh;
struct mbuf *ip6e_dest2;
};
-static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **, int));
-static int ip6_getpcbopt __P((struct ip6_pktopts *, int, void **, int *));
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined(__APPLE__)
static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
struct socket *, struct sockopt *sopt));
-#else
-static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
- struct socket *));
-#endif
-static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *));
+static int ip6_setmoptions __P((int, struct inpcb *, struct mbuf *));
static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
struct ip6_frag **));
static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
-#if defined(__bsdi__) || defined(__OpenBSD__)
-extern struct ifnet loif;
-#endif
-
-#if __NetBSD__
-extern struct ifnet **ifindex2ifnet;
-extern struct ifnet loif[NLOOP];
-#endif
-#if MIP6
-int (*mip6_output_hook)(struct mbuf *m, struct ip6_pktopts **opt);
-#endif /* MIP6 */
-static u_long lo_dl_tag = 0;
+extern int ip_createmoptions(struct ip_moptions **imop);
+extern int ip_addmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
+extern int ip_dropmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
/*
* IP6 output. The packet in mbuf chain m contains a skeletal IP6
* This function may modify ver and hlim only.
* The mbuf chain containing the packet will be freed.
* The mbuf opt, if present, will not be freed.
+ *
+ * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and
+ * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one,
+ * which is rt_rmx.rmx_mtu.
*/
int
ip6_output(m0, opt, ro, flags, im6o, ifpp)
struct ifnet **ifpp; /* XXX: just for statistics */
{
struct ip6_hdr *ip6, *mhip6;
- struct ifnet *ifp;
+ struct ifnet *ifp, *origifp;
struct mbuf *m = m0;
int hlen, tlen, len, off;
struct route_in6 ip6route;
struct sockaddr_in6 *dst;
int error = 0;
- struct in6_ifaddr *ia;
+ struct in6_ifaddr *ia = NULL;
u_long mtu;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs;
struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0;
int needipsec = 0;
-
-
#if IPSEC
int needipsectun = 0;
- struct socket *so;
+ struct socket *so = NULL;
struct secpolicy *sp = NULL;
/* for AH processing. stupid to have "socket" variable in IP layer... */
- so = ipsec_getsocket(m);
- ipsec_setsocket(m, NULL);
+ if (ipsec_bypass == 0)
+ {
+ so = ipsec_getsocket(m);
+ (void)ipsec_setsocket(m, NULL);
+ }
ip6 = mtod(m, struct ip6_hdr *);
#endif /* IPSEC */
-#define MAKE_EXTHDR(hp,mp) \
- { \
+#define MAKE_EXTHDR(hp, mp) \
+ do { \
if (hp) { \
struct ip6_ext *eh = (struct ip6_ext *)(hp); \
error = ip6_copyexthdr((mp), (caddr_t)(hp), \
if (error) \
goto freehdrs; \
} \
- }
+ } while (0)
bzero(&exthdrs, sizeof(exthdrs));
-#if MIP6
- /*
- * Mobile IPv6
- *
- * Call Mobile IPv6 to check if there are any Destination Header
- * options to add.
- */
- if (mip6_output_hook) {
- error = (*mip6_output_hook)(m, &opt);
- if (error)
- goto freehdrs;
- }
-#endif /* MIP6 */
-
if (opt) {
/* Hop-by-Hop options header */
MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
- if (opt->ip6po_rthdr) {
- /*
- * Destination options header(1st part)
- * This only makes sence with a routing header.
- */
- MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
- }
+ /* Destination options header(1st part) */
+ MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
/* Routing header */
MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
/* Destination options header(2nd part) */
}
#if IPSEC
+ if (ipsec_bypass != 0)
+ goto skip_ipsec;
+
/* get a security policy for this packet */
if (so == NULL)
sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
if (sp == NULL) {
ipsec6stat.out_inval++;
- goto bad;
+ goto freehdrs;
}
error = 0;
* This packet is just discarded.
*/
ipsec6stat.out_polvio++;
- goto bad;
+ goto freehdrs;
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
if (sp->req == NULL) {
/* acquire a policy */
error = key_spdacquire(sp);
- goto bad;
+ goto freehdrs;
}
needipsec = 1;
break;
default:
printf("ip6_output: Invalid policy found. %d\n", sp->policy);
}
+ skip_ipsec:
#endif /* IPSEC */
/*
/*
* we treat dest2 specially. this makes IPsec processing
- * much easier.
+ * much easier. the goal here is to make mprev point the
+ * mbuf prior to dest2.
*
* result: IPv6 dest2 payload
* m and mprev will point to IPv6 header.
ip6->ip6_nxt = IPPROTO_DSTOPTS;
}
-#define MAKE_CHAIN(m,mp,p,i)\
- {\
+#define MAKE_CHAIN(m, mp, p, i)\
+ do {\
if (m) {\
if (!hdrsplit) \
panic("assumption failed: hdr not split"); \
(mp)->m_next = (m);\
(mp) = (m);\
}\
- }
+ } while (0)
/*
* result: IPv6 hbh dest1 rthdr dest2 payload
* m will point to IPv6 header. mprev will point to the
break;
default:
printf("ip6_output (ipsec): error code %d\n", error);
- /*fall through*/
+ /* fall through */
case ENOENT:
/* don't show these error codes to the user */
error = 0;
(struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
struct ip6_rthdr *));
struct ip6_rthdr0 *rh0;
- struct in6_addr *addr;
finaldst = ip6->ip6_dst;
- switch(rh->ip6r_type) {
+ switch (rh->ip6r_type) {
case IPV6_RTHDR_TYPE_0:
rh0 = (struct ip6_rthdr0 *)rh;
- addr = (struct in6_addr *)(rh0 + 1);
-
- ip6->ip6_dst = *addr;
- bcopy((caddr_t)(addr + 1), (caddr_t)addr,
- sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1)
+ ip6->ip6_dst = rh0->ip6r0_addr[0];
+ bcopy((caddr_t)&rh0->ip6r0_addr[1],
+ (caddr_t)&rh0->ip6r0_addr[0],
+ sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1)
);
- *(addr + rh0->ip6r0_segleft - 1) = finaldst;
+ rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst;
break;
default: /* is it possible? */
error = EINVAL;
* and is still up. If not, free it and try again.
*/
if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
- !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
- RTFREE(ro->ro_rt);
+ dst->sin6_family != AF_INET6 ||
+ !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst) ||
+ ro->ro_rt->generation_id != route_generation)) {
+ rtfree(ro->ro_rt);
ro->ro_rt = (struct rtentry *)0;
}
if (ro->ro_rt == 0) {
dst->sin6_family = AF_INET6;
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_addr = ip6->ip6_dst;
+#if SCOPEDROUTING
+ /* XXX: sin6_scope_id should already be fixed at this point */
+ if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr))
+ dst->sin6_scope_id = ntohs(dst->sin6_addr.s6_addr16[1]);
+#endif
}
#if IPSEC
if (needipsec && needipsectun) {
break;
default:
printf("ip6_output (ipsec): error code %d\n", error);
- /*fall through*/
+ /* fall through */
case ENOENT:
/* don't show these error codes to the user */
error = 0;
exthdrs.ip6e_ip6 = m;
}
-#endif /*IPSEC*/
+#endif /* IPSEC */
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
/* Unicast */
* ifp must point it.
*/
if (ro->ro_rt == 0) {
-#ifndef __bsdi__
/*
* non-bsdi always clone routes, if parent is
* PRF_CLONING.
*/
rtalloc((struct route *)ro);
-#else
- if (ro == &ip6route) /* xxx kazu */
- rtalloc((struct route *)ro);
- else
- rtcalloc((struct route *)ro);
-#endif
}
if (ro->ro_rt == 0) {
ip6stat.ip6s_noroute++;
in6_ifstat_inc(ifp, ifs6_out_discard);
goto bad;
} else {
-#ifdef __bsdi__
- ifp = loifp;
-#else
ifp = &loif[0];
-#endif
}
}
if (ifp == NULL) {
if (ro->ro_rt == 0) {
ro->ro_rt = rtalloc1((struct sockaddr *)
- &ro->ro_dst, 0
-#if __FreeBSD__ || defined (__APPLE__)
- , 0UL
-#endif
- );
+ &ro->ro_dst, 0, 0UL);
}
if (ro->ro_rt == 0) {
ip6stat.ip6s_noroute++;
if (ifpp)
*ifpp = ifp;
- /*
- * Upper-layer reachability confirmation
- */
- if (opt && (opt->ip6po_flags & IP6PO_REACHCONF))
- nd6_nud_hint(ro->ro_rt, NULL);
-
/*
* Determine path MTU.
*/
if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
!IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr,
&finaldst))) {
- RTFREE(ro_pmtu->ro_rt);
+ rtfree(ro_pmtu->ro_rt);
ro_pmtu->ro_rt = (struct rtentry *)0;
}
if (ro_pmtu->ro_rt == 0) {
sin6_fin->sin6_len = sizeof(struct sockaddr_in6);
sin6_fin->sin6_addr = finaldst;
-#ifdef __bsdi__ /* bsdi needs rtcalloc to clone a route. */
- rtcalloc((struct route *)ro_pmtu);
-#else
rtalloc((struct route *)ro_pmtu);
-#endif
}
}
if (ro_pmtu->ro_rt != NULL) {
u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu;
mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
- if (mtu > ifmtu) {
+ if (mtu > ifmtu || mtu == 0) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
* interface was brought up. Change the MTU in the
* route to match the interface MTU (as long as the
* field isn't locked).
+ *
+ * if MTU on the route is 0, we need to fix the MTU.
+ * this case happens with path MTU discovery timeouts.
*/
mtu = ifmtu;
if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
/*
* advanced API (IPV6_USE_MIN_MTU) overrides mtu setting
*/
- if (mtu > IPV6_MMTU) {
- if ((opt && (opt->ip6po_flags & IP6PO_MINMTU)) ||
- (flags & IPV6_MINMTU)) {
- mtu = IPV6_MMTU;
- }
- }
+ if ((flags & IPV6_MINMTU) != 0 && mtu > IPV6_MMTU)
+ mtu = IPV6_MMTU;
- /*
- * Fake link-local scope-class addresses
- */
- if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
+ /* Fake scoped addresses */
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ /*
+ * If source or destination address is a scoped address, and
+ * the packet is going to be sent to a loopback interface,
+ * we should keep the original interface.
+ */
+
+ /*
+ * XXX: this is a very experimental and temporary solution.
+ * We eventually have sockaddr_in6 and use the sin6_scope_id
+ * field of the structure here.
+ * We rely on the consistency between two scope zone ids
+ * of source and destination, which should already be assured.
+ * Larger scopes than link will be supported in the future.
+ */
+ origifp = NULL;
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
- ip6->ip6_src.s6_addr16[1] = 0;
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
- ip6->ip6_dst.s6_addr16[1] = 0;
+ origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])];
+ else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])];
+ /*
+ * XXX: origifp can be NULL even in those two cases above.
+ * For example, if we remove the (only) link-local address
+ * from the loopback interface, and try to send a link-local
+ * address without link-id information. Then the source
+ * address is ::1, and the destination address is the
+ * link-local address with its s6_addr16[1] being zero.
+ * What is worse, if the packet goes to the loopback interface
+ * by a default rejected route, the null pointer would be
+ * passed to looutput, and the kernel would hang.
+ * The following last resort would prevent such disaster.
+ */
+ if (origifp == NULL)
+ origifp = ifp;
}
+ else
+ origifp = ifp;
+#ifndef SCOPEDROUTING
+ /*
+ * clear embedded scope identifiers if necessary.
+ * in6_clearscope will touch the addresses only when necessary.
+ */
+ in6_clearscope(&ip6->ip6_src);
+ in6_clearscope(&ip6->ip6_dst);
+#endif
-#if IPV6FIREWALL
/*
* Check with the firewall...
*/
- if (ip6_fw_chk_ptr) {
+ if (ip6_fw_enable && ip6_fw_chk_ptr) {
u_short port = 0;
+ m->m_pkthdr.rcvif = NULL; /* XXX */
/* If ipfw says divert, we have to just drop packet */
if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) {
m_freem(m);
goto done;
}
}
-#endif
/*
* If the outgoing packet contains a hop-by-hop options header,
* (RFC 2460, section 4.)
*/
if (exthdrs.ip6e_hbh) {
- struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh,
- struct ip6_hbh *);
+ struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
u_int32_t dummy1; /* XXX unused */
u_int32_t dummy2; /* XXX unused */
+#if DIAGNOSTIC
+ if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len)
+ panic("ip6e_hbh is not continuous");
+#endif
/*
* XXX: if we have to send an ICMPv6 error to the sender,
* we need the M_LOOP flag since icmp6_error() expects
#endif
)
{
-#if defined(__NetBSD__) && defined(IFA_STATS)
- if (IFA_STATS) {
- struct in6_ifaddr *ia6;
- ip6 = mtod(m, struct ip6_hdr *);
- ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
- if (ia6) {
- ia->ia_ifa.ifa_data.ifad_outbytes +=
- m->m_pkthdr.len;
- }
- }
+ /* Record statistics for this interface address. */
+ if (ia && !(flags & IPV6_FORWARDING)) {
+#ifndef __APPLE__
+ ia->ia_ifa.if_opackets++;
+ ia->ia_ifa.if_obytes += m->m_pkthdr.len;
#endif
-#if OLDIP6OUTPUT
- error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
- ro->ro_rt);
-#else
- error = nd6_output(ifp, m, dst, ro->ro_rt);
+ }
+#ifdef IPSEC
+ /* clean ipsec history once it goes out of the node */
+ ipsec_delaux(m);
#endif
+
+ error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
goto done;
} else if (mtu < IPV6_MMTU) {
/*
hlen = unfragpartlen;
if (mtu > IPV6_MAXPACKET)
mtu = IPV6_MAXPACKET;
+
len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
if (len < 8) {
error = EMSGSIZE;
/*
* Loop through length of segment after first fragment,
- * make new header and copy data of each part and link onto chain.
+ * make new header and copy data of each part and link onto
+ * chain.
*/
m0 = m;
for (off = hlen; off < tlen; off += len) {
ip6stat.ip6s_odropped++;
goto sendorfree;
}
+ m->m_pkthdr.rcvif = NULL;
m->m_flags = m0->m_flags & M_COPYFLAGS;
*mnext = m;
mnext = &m->m_nextpkt;
m0 = m->m_nextpkt;
m->m_nextpkt = 0;
if (error == 0) {
-#if defined(__NetBSD__) && defined(IFA_STATS)
- if (IFA_STATS) {
- struct in6_ifaddr *ia6;
- ip6 = mtod(m, struct ip6_hdr *);
- ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
- if (ia6) {
- ia->ia_ifa.ifa_data.ifad_outbytes +=
- m->m_pkthdr.len;
- }
- }
+ /* Record statistics for this interface address. */
+ if (ia) {
+#ifndef __APPLE__
+ ia->ia_ifa.if_opackets++;
+ ia->ia_ifa.if_obytes += m->m_pkthdr.len;
#endif
-#if OLDIP6OUTPUT
- error = (*ifp->if_output)(ifp, m,
- (struct sockaddr *)dst,
- ro->ro_rt);
-#else
- error = nd6_output(ifp, m, dst, ro->ro_rt);
+ }
+#if IPSEC
+ /* clean ipsec history once it goes out of the node */
+ ipsec_delaux(m);
#endif
+ error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
} else
m_freem(m);
}
ip6stat.ip6s_fragmented++;
done:
- if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */
- RTFREE(ro->ro_rt);
+ if (ro == &ip6route && ro->ro_rt) { /* brace necessary for rtfree */
+ rtfree(ro->ro_rt);
} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
- RTFREE(ro_pmtu->ro_rt);
+ rtfree(ro_pmtu->ro_rt);
}
#if IPSEC
{
struct mbuf *mopt;
u_char *optbuf;
+ u_int32_t v;
#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
mopt = exthdrs->ip6e_hbh;
if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
- caddr_t oldoptp = mtod(mopt, caddr_t);
+ /*
+ * XXX assumption:
+ * - exthdrs->ip6e_hbh is not referenced from places
+ * other than exthdrs.
+ * - exthdrs->ip6e_hbh is not an mbuf chain.
+ */
int oldoptlen = mopt->m_len;
+ struct mbuf *n;
- if (mopt->m_flags & M_EXT)
- return(ENOBUFS); /* XXX */
- MCLGET(mopt, M_DONTWAIT);
- if ((mopt->m_flags & M_EXT) == 0)
+ /*
+ * XXX: give up if the whole (new) hbh header does
+ * not fit even in an mbuf cluster.
+ */
+ if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
return(ENOBUFS);
- bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen);
- optbuf = mtod(mopt, caddr_t) + oldoptlen;
- mopt->m_len = oldoptlen + JUMBOOPTLEN;
+ /*
+ * As a consequence, we must always prepare a cluster
+ * at this point.
+ */
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_freem(n);
+ n = NULL;
+ }
+ }
+ if (!n)
+ return(ENOBUFS);
+ n->m_len = oldoptlen + JUMBOOPTLEN;
+ bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t),
+ oldoptlen);
+ optbuf = mtod(n, caddr_t) + oldoptlen;
+ m_freem(mopt);
+ mopt = exthdrs->ip6e_hbh = n;
} else {
optbuf = mtod(mopt, u_char *) + mopt->m_len;
mopt->m_len += JUMBOOPTLEN;
/* fill in the option. */
optbuf[2] = IP6OPT_JUMBO;
optbuf[3] = 4;
- *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN);
+ v = (u_int32_t)htonl(plen + JUMBOOPTLEN);
+ bcopy(&v, &optbuf[4], sizeof(u_int32_t));
/* finally, adjust the packet header length */
exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
;
if ((mlast->m_flags & M_EXT) == 0 &&
- M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) {
+ M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) {
/* use the trailing space of the last mbuf for the fragment hdr */
*frghdrp =
(struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len);
return(0);
}
+extern int load_ipfw();
+
/*
* IP6 socket option processing.
*/
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
int
ip6_ctloutput(so, sopt)
struct socket *so;
struct sockopt *sopt;
-#else
-int
-ip6_ctloutput(op, so, level, optname, mp)
- int op;
- struct socket *so;
- int level, optname;
- struct mbuf **mp;
-#endif
{
- int privileged, optdatalen;
- void *optdata;
- struct ip6_recvpktopts *rcvopts;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- register struct inpcb *in6p = sotoinpcb(so);
+ int privileged;
+ struct inpcb *in6p = sotoinpcb(so);
int error, optval;
int level, op, optname;
int optlen;
struct proc *p;
- if (sopt) {
+ if (sopt == NULL)
+ panic("ip6_ctloutput: arg soopt is NULL");
+ else {
level = sopt->sopt_level;
op = sopt->sopt_dir;
optname = sopt->sopt_name;
optlen = sopt->sopt_valsize;
p = sopt->sopt_p;
- } else {
- panic("ip6_ctloutput: arg soopt is NULL");
}
-#else
-#if HAVE_NRL_INPCB
- register struct inpcb *inp = sotoinpcb(so);
-#else
- register struct in6pcb *in6p = sotoin6pcb(so);
-#endif
- register struct mbuf *m = *mp;
- int error, optval;
- int optlen;
-#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) && !defined (__APPLE__)
- struct proc *p = curproc; /* XXX */
-#endif
-
- optlen = m ? m->m_len : 0;
-#endif
error = optval = 0;
-#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) && !defined (__APPLE__)
privileged = (p == 0 || suser(p->p_ucred, &p->p_acflag)) ? 0 : 1;
-#else
-#if HAVE_NRL_INPCB
- privileged = (inp->inp_socket->so_state & SS_PRIV);
-#else
- privileged = (in6p->in6p_socket->so_state & SS_PRIV);
-#endif
-#endif
-
-#if defined(HAVE_NRL_INPCB)
- rcvopts = inp->inp_inputopts6;
-#else
- rcvopts = in6p->in6p_inputopts;
-#endif
if (level == IPPROTO_IPV6) {
switch (op) {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
case SOPT_SET:
-#else
- case PRCO_SETOPT:
-#endif
switch (optname) {
case IPV6_PKTOPTIONS:
- {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
+ {
struct mbuf *m;
- error = sooptgetm(sopt, &m); /* XXX */
+ error = soopt_getm(sopt, &m); /* XXX */
if (error != NULL)
break;
- error = sooptmcopyin(sopt, m); /* XXX */
+ error = soopt_mcopyin(sopt, m); /* XXX */
if (error != NULL)
break;
error = ip6_pcbopts(&in6p->in6p_outputopts,
m, so, sopt);
m_freem(m); /* XXX */
-#else
-#if HAVE_NRL_INPCB
- error = ip6_pcbopts(&inp->inp_outputopts6,
- m, so);
-#else
- error = ip6_pcbopts(&in6p->in6p_outputopts,
- m, so);
-#endif /* HAVE_NRL_INPCB */
-#endif /* FreeBSD >= 3 */
break;
}
+
/*
* Use of some Hop-by-Hop options or some
* Destination options, might require special
* receiving ANY hbh/dst options in order to avoid
* overhead of parsing options in the kernel.
*/
- case IPV6_RECVHOPOPTS:
- case IPV6_RECVDSTOPTS:
- case IPV6_RECVRTHDRDSTOPTS:
- if (!privileged) {
- error = EPERM;
- break;
- }
- /* fall through */
case IPV6_UNICAST_HOPS:
- case IPV6_HOPLIMIT:
case IPV6_CHECKSUM:
case IPV6_FAITH:
- case IPV6_RECVPKTINFO:
- case IPV6_RECVHOPLIMIT:
- case IPV6_RECVRTHDR:
- case IPV6_USE_MIN_MTU:
-#ifdef notyet /* To be implemented */
- case IPV6_RECVPATHMTU:
-#endif
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- case IPV6_BINDV6ONLY:
-#endif
- if (optlen != sizeof(int))
+ case IPV6_V6ONLY:
+ if (optlen != sizeof(int)) {
error = EINVAL;
- else {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- error = sooptcopyin(sopt, &optval,
- sizeof optval, sizeof optval);
- if (error)
- break;
-#else
- optval = *mtod(m, int *);
-#endif
- switch (optname) {
-
- case IPV6_UNICAST_HOPS:
- if (optval < -1 || optval >= 256)
- error = EINVAL;
- else {
- /* -1 = kernel default */
-#if HAVE_NRL_INPCB
- inp->inp_hops = optval;
-#else
- in6p->in6p_hops = optval;
+ break;
+ }
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+ switch (optname) {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- if ((in6p->in6p_vflag &
- INP_IPV4) != 0)
- in6p->inp_ip_ttl = optval;
-#endif
-#endif
- }
- break;
-#if HAVE_NRL_INPCB
-#define OPTSET(bit) \
- if (optval) \
- inp->inp_flags |= (bit); \
- else \
- inp->inp_flags &= ~(bit);
-#else
+ case IPV6_UNICAST_HOPS:
+ if (optval < -1 || optval >= 256)
+ error = EINVAL;
+ else {
+ /* -1 = kernel default */
+ in6p->in6p_hops = optval;
+
+ if ((in6p->in6p_vflag &
+ INP_IPV4) != 0)
+ in6p->inp_ip_ttl = optval;
+ }
+ break;
#define OPTSET(bit) \
+do { \
if (optval) \
in6p->in6p_flags |= (bit); \
else \
- in6p->in6p_flags &= ~(bit);
-#endif
-#if HAVE_NRL_INPCB
-#define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0)
-#else
+ in6p->in6p_flags &= ~(bit); \
+} while (0)
#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0)
-#endif
-
- case IPV6_RECVPKTINFO:
- OPTSET(IN6P_PKTINFO);
- if (OPTBIT(IN6P_PKTINFO) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVPKTINFO);
- break;
-
- case IPV6_HOPLIMIT:
- {
-#if COMPAT_RFC2292
- OPTSET(IN6P_HOPLIMIT);
- if (OPTBIT(IN6P_HOPLIMIT) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPLIMIT);
- break;
-#else /* new advanced API (2292bis) */
- struct ip6_pktopts **optp;
-#if HAVE_NRL_INPCB
- optp = &inp->inp_outputopts6;
-#else
- optp = &in6p->in6p_outputopts;
-#endif
-
- error = ip6_pcbopt(IPV6_HOPLIMIT,
- (u_char *)&optval,
- sizeof(optval),
- optp,
- privileged);
- break;
-#endif
- }
-
- case IPV6_RECVHOPLIMIT:
- OPTSET(IN6P_HOPLIMIT);
- if (OPTBIT(IN6P_HOPLIMIT) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPLIMIT);
- break;
-
- case IPV6_RECVHOPOPTS:
- OPTSET(IN6P_HOPOPTS);
- if (OPTBIT(IN6P_HOPOPTS) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPOPTS);
- break;
-
- case IPV6_RECVDSTOPTS:
- OPTSET(IN6P_DSTOPTS);
- if (OPTBIT(IN6P_DSTOPTS) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVDSTOPTS);
- break;
-
- case IPV6_RECVRTHDRDSTOPTS:
- OPTSET(IN6P_RTHDRDSTOPTS);
- if (OPTBIT(IN6P_RTHDRDSTOPTS) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDRDSTOPTS);
- break;
- case IPV6_RECVRTHDR:
- OPTSET(IN6P_RTHDR);
- if (OPTBIT(IN6P_RTHDR) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDR);
- break;
-
- case IPV6_CHECKSUM:
-#if HAVE_NRL_INPCB
- inp->inp_csumoffset = optval;
-#else
- in6p->in6p_cksum = optval;
-#endif
- break;
-
- case IPV6_FAITH:
- OPTSET(IN6P_FAITH);
- break;
+ case IPV6_CHECKSUM:
+ in6p->in6p_cksum = optval;
+ break;
- case IPV6_USE_MIN_MTU:
- OPTSET(IN6P_MINMTU);
- break;
+ case IPV6_FAITH:
+ OPTSET(IN6P_FAITH);
+ break;
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(INET6_BINDV6ONLY)) || defined (__APPLE__)
- case IPV6_BINDV6ONLY:
- OPTSET(IN6P_BINDV6ONLY);
+ case IPV6_V6ONLY:
+ /*
+ * make setsockopt(IPV6_V6ONLY)
+ * available only prior to bind(2).
+ * see ipng mailing list, Jun 22 2001.
+ */
+ if (in6p->in6p_lport ||
+ !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr))
+ {
+ error = EINVAL;
break;
-#endif
}
+ OPTSET(IN6P_IPV6_V6ONLY);
+ if (optval)
+ in6p->in6p_vflag &= ~INP_IPV4;
+ else
+ in6p->in6p_vflag |= INP_IPV4;
+ break;
}
break;
+
case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
case IPV6_HOPOPTS:
- case IPV6_RTHDR:
case IPV6_DSTOPTS:
- case IPV6_RTHDRDSTOPTS:
- if (optlen == sizeof(int)) {
- /* RFC 2292 */
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- error = sooptcopyin(sopt, &optval,
- sizeof optval, sizeof optval);
- if (error == 0)
- break;
-#else
- optval = *mtod(m, int *);
-#endif
- switch(optname) {
- case IPV6_PKTINFO:
- OPTSET(IN6P_PKTINFO);
- if (OPTBIT(IN6P_PKTINFO) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVPKTINFO);
- break;
- case IPV6_HOPOPTS:
- /*
- * Check super-user privilege.
- * See comments for
- * IPV6_RECVHOPOPTS.
- */
- if (!privileged)
- return(EPERM);
- OPTSET(IN6P_HOPOPTS);
- if (OPTBIT(IN6P_HOPOPTS) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPOPTS);
- break;
- case IPV6_DSTOPTS:
- if (!privileged)
- return(EPERM);
- OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
- if (OPTBIT(IN6P_DSTOPTS) == 0) {
- ip6_reset_rcvopt(rcvopts, IPV6_RECVDSTOPTS);
- ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDRDSTOPTS);
- }
- break;
- case IPV6_RTHDR:
- OPTSET(IN6P_RTHDR);
- if (OPTBIT(IN6P_RTHDR) == 0)
- ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDR);
- break;
- }
+ case IPV6_RTHDR:
+ /* RFC 2292 */
+ if (optlen != sizeof(int)) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+ switch (optname) {
+ case IPV6_PKTINFO:
+ OPTSET(IN6P_PKTINFO);
+ break;
+ case IPV6_HOPLIMIT:
+ OPTSET(IN6P_HOPLIMIT);
+ break;
+ case IPV6_HOPOPTS:
+ /*
+ * Check super-user privilege.
+ * See comments for IPV6_RECVHOPOPTS.
+ */
+ if (!privileged)
+ return(EPERM);
+ OPTSET(IN6P_HOPOPTS);
+ break;
+ case IPV6_DSTOPTS:
+ if (!privileged)
+ return(EPERM);
+ OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
+ break;
+ case IPV6_RTHDR:
+ OPTSET(IN6P_RTHDR);
break;
- } else {
- /* new advanced API (2292bis) */
- u_char *optbuf;
- int optlen;
- struct ip6_pktopts **optp;
-
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- optbuf = sopt->sopt_val;
- optlen = sopt->sopt_valsize;
-#else /* !fbsd3 */
- if (m && m->m_next) {
- error = EINVAL; /* XXX */
- break;
- }
- if (m) {
- optbuf = mtod(m, u_char *);
- optlen = m->m_len;
- } else {
- optbuf = NULL;
- optlen = 0;
- }
-#endif
-
-#if HAVE_NRL_INPCB
- optp = &inp->inp_outputopts6;
-#else
- optp = &in6p->in6p_outputopts;
-#endif
-
- error = ip6_pcbopt(optname,
- optbuf, optlen,
- optp, privileged);
}
break;
#undef OPTSET
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
{
struct mbuf *m;
if (sopt->sopt_valsize > MLEN) {
m->m_len = sopt->sopt_valsize;
error = sooptcopyin(sopt, mtod(m, char *),
m->m_len, m->m_len);
- error = ip6_setmoptions(sopt->sopt_name,
- &in6p->in6p_moptions,
- m);
+ error = ip6_setmoptions(sopt->sopt_name, in6p, m);
(void)m_free(m);
}
-#else
-#if HAVE_NRL_INPCB
- error = ip6_setmoptions(optname,
- &inp->inp_moptions6, m);
- /*
- * XXX: setting the flag would be redundant
- * except at the first time. Also, we
- * actually don't have to reset the flag,
- * since ip6_freemoptions() would simply
- * return when the inp_moptions6 is NULL.
- */
- if (inp->inp_moptions6)
- inp->inp_flags |= INP_IPV6_MCAST;
- else
- inp->inp_flags &= ~INP_IPV6_MCAST;
-#else
- error = ip6_setmoptions(optname,
- &in6p->in6p_moptions, m);
-#endif
-#endif
break;
-#ifndef __bsdi__
- case IPV6_PORTRANGE:
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- error = sooptcopyin(sopt, &optval, sizeof optval,
- sizeof optval);
- if (error)
- break;
-#else
- optval = *mtod(m, int *);
-#endif
+ case IPV6_PORTRANGE:
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
-#if HAVE_NRL_INPCB
-# define in6p inp
-# define in6p_flags inp_flags
-#endif
- switch (optval) {
- case IPV6_PORTRANGE_DEFAULT:
- in6p->in6p_flags &= ~(IN6P_LOWPORT);
- in6p->in6p_flags &= ~(IN6P_HIGHPORT);
- break;
+ switch (optval) {
+ case IPV6_PORTRANGE_DEFAULT:
+ in6p->in6p_flags &= ~(IN6P_LOWPORT);
+ in6p->in6p_flags &= ~(IN6P_HIGHPORT);
+ break;
- case IPV6_PORTRANGE_HIGH:
- in6p->in6p_flags &= ~(IN6P_LOWPORT);
- in6p->in6p_flags |= IN6P_HIGHPORT;
- break;
+ case IPV6_PORTRANGE_HIGH:
+ in6p->in6p_flags &= ~(IN6P_LOWPORT);
+ in6p->in6p_flags |= IN6P_HIGHPORT;
+ break;
- case IPV6_PORTRANGE_LOW:
- in6p->in6p_flags &= ~(IN6P_HIGHPORT);
- in6p->in6p_flags |= IN6P_LOWPORT;
- break;
+ case IPV6_PORTRANGE_LOW:
+ in6p->in6p_flags &= ~(IN6P_HIGHPORT);
+ in6p->in6p_flags |= IN6P_LOWPORT;
+ break;
- default:
- error = EINVAL;
+ default:
+ error = EINVAL;
+ break;
+ }
break;
- }
-#if HAVE_NRL_INPCB
-# undef in6p
-# undef in6p_flags
-#endif
- break;
-#endif
#if IPSEC
case IPV6_IPSEC_POLICY:
{
caddr_t req = NULL;
size_t len = 0;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
struct mbuf *m;
-#endif
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- if (error = sooptgetm(sopt, &m)) /* XXX */
+ if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
break;
- if (error = sooptmcopyin(sopt, m)) /* XXX */
+ if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
break;
-#endif
if (m) {
req = mtod(m, caddr_t);
len = m->m_len;
}
-#if HAVE_NRL_INPCB
- error = ipsec6_set_policy(inp, optname, req,
- len, privileged);
-#else
error = ipsec6_set_policy(in6p, optname, req,
len, privileged);
-#endif
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
m_freem(m);
-#endif
}
break;
-#endif /* IPSEC */
+#endif /* KAME IPSEC */
-#if IPV6FIREWALL
case IPV6_FW_ADD:
case IPV6_FW_DEL:
case IPV6_FW_FLUSH:
case IPV6_FW_ZERO:
- {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- struct mbuf *m;
- struct mbuf **mp = &m;
-#endif
-
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- if (ip6_fw_ctl_ptr == NULL)
- return EINVAL;
- if (error = sooptgetm(sopt, &m)) /* XXX */
- break;
- if (error = sooptmcopyin(sopt, m)) /* XXX */
- break;
-#else
- if (ip6_fw_ctl_ptr == NULL) {
- if (m) (void)m_free(m);
+ {
+ if (ip6_fw_ctl_ptr == NULL && load_ipfw() != 0)
return EINVAL;
+
+ error = (*ip6_fw_ctl_ptr)(sopt);
}
-#endif
- error = (*ip6_fw_ctl_ptr)(optname, mp);
- m = *mp;
- }
break;
-#endif
default:
error = ENOPROTOOPT;
break;
}
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
- if (m)
- (void)m_free(m);
-#endif
break;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
case SOPT_GET:
-#else
- case PRCO_GETOPT:
-#endif
switch (optname) {
case IPV6_PKTOPTIONS:
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- if (in6p->in6p_inputopts &&
- in6p->in6p_inputopts->head) {
- error = sooptmcopyout(sopt,
- in6p->in6p_inputopts->head);
+ if (in6p->in6p_options) {
+ struct mbuf *m;
+ m = m_copym(in6p->in6p_options,
+ 0, M_COPYALL, M_WAIT);
+ error = soopt_mcopyout(sopt, m);
+ if (error == 0)
+ m_freem(m);
} else
sopt->sopt_valsize = 0;
-#elif defined(HAVE_NRL_INPCB)
- if (inp->inp_options) {
- *mp = m_copym(inp->inp_options, 0,
- M_COPYALL, M_WAIT);
- } else {
- *mp = m_get(M_WAIT, MT_SOOPTS);
- (*mp)->m_len = 0;
- }
-#else
- if (in6p->in6p_inputopts &&
- in6p->in6p_inputopts->head) {
- *mp = m_copym(in6p->in6p_inputopts->head,
- 0, M_COPYALL, M_WAIT);
- } else {
- *mp = m_get(M_WAIT, MT_SOOPTS);
- (*mp)->m_len = 0;
- }
-#endif
break;
- case IPV6_RECVHOPOPTS:
- case IPV6_RECVDSTOPTS:
- case IPV6_RECVRTHDRDSTOPTS:
- if (!privileged) {
- error = EPERM;
- break;
- }
- /* fall through */
case IPV6_UNICAST_HOPS:
case IPV6_CHECKSUM:
- case IPV6_RECVPKTINFO:
- case IPV6_RECVHOPLIMIT:
- case IPV6_RECVRTHDR:
- case IPV6_USE_MIN_MTU:
-#ifdef notyet /* To be implemented */
- case IPV6_RECVPATHMTU:
-#endif
-
case IPV6_FAITH:
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(INET6_BINDV6ONLY)) || defined(__APPLE__)
- case IPV6_BINDV6ONLY:
-#endif
-#ifndef __bsdi__
+ case IPV6_V6ONLY:
case IPV6_PORTRANGE:
-#endif
switch (optname) {
case IPV6_UNICAST_HOPS:
-#if HAVE_NRL_INPCB
- optval = inp->inp_hops;
-#else
optval = in6p->in6p_hops;
-#endif
- break;
-
- case IPV6_RECVPKTINFO:
- optval = OPTBIT(IN6P_PKTINFO);
- break;
-
- case IPV6_RECVHOPLIMIT:
- optval = OPTBIT(IN6P_HOPLIMIT);
- break;
-
- case IPV6_RECVHOPOPTS:
- optval = OPTBIT(IN6P_HOPOPTS);
- break;
-
- case IPV6_RECVDSTOPTS:
- optval = OPTBIT(IN6P_DSTOPTS);
- break;
-
- case IPV6_RECVRTHDRDSTOPTS:
- optval = OPTBIT(IN6P_RTHDRDSTOPTS);
break;
case IPV6_CHECKSUM:
-#if HAVE_NRL_INPCB
- optval = inp->inp_csumoffset;
-#else
optval = in6p->in6p_cksum;
-#endif
- break;
-
- case IPV6_USE_MIN_MTU:
- optval = OPTBIT(IN6P_MINMTU);
break;
case IPV6_FAITH:
optval = OPTBIT(IN6P_FAITH);
break;
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(INET6_BINDV6ONLY)) || defined (__APPLE__)
- case IPV6_BINDV6ONLY:
- optval = OPTBIT(IN6P_BINDV6ONLY);
+ case IPV6_V6ONLY:
+ optval = OPTBIT(IN6P_IPV6_V6ONLY);
break;
-#endif
-#ifndef __bsdi__
case IPV6_PORTRANGE:
{
int flags;
-#if HAVE_NRL_INPCB
- flags = inp->inp_flags;
-#else
flags = in6p->in6p_flags;
-#endif
if (flags & IN6P_HIGHPORT)
optval = IPV6_PORTRANGE_HIGH;
else if (flags & IN6P_LOWPORT)
optval = 0;
break;
}
-#endif
}
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
error = sooptcopyout(sopt, &optval,
sizeof optval);
-#else
- *mp = m = m_get(M_WAIT, MT_SOOPTS);
- m->m_len = sizeof(int);
- *mtod(m, int *) = optval;
-#endif
break;
case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
case IPV6_HOPOPTS:
case IPV6_RTHDR:
case IPV6_DSTOPTS:
- case IPV6_RTHDRDSTOPTS:
-#if COMPAT_RFC2292
if (optname == IPV6_HOPOPTS ||
optname == IPV6_DSTOPTS ||
!privileged)
return(EPERM);
- switch(optname) {
+ switch (optname) {
case IPV6_PKTINFO:
- optbit = OPTBIT(IN6P_PKTINFO);
+ optval = OPTBIT(IN6P_PKTINFO);
break;
case IPV6_HOPLIMIT:
optval = OPTBIT(IN6P_HOPLIMIT);
break;
case IPV6_HOPOPTS:
- optbit = OPTBIT(IN6P_HOPOPTS);
+ if (!privileged)
+ return(EPERM);
+ optval = OPTBIT(IN6P_HOPOPTS);
break;
case IPV6_RTHDR:
- optbit = OPTBIT(IN6P_RTHDR);
+ optval = OPTBIT(IN6P_RTHDR);
break;
case IPV6_DSTOPTS:
- optbit = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
+ if (!privileged)
+ return(EPERM);
+ optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
break;
- case IPV6_RTHDRDSTOPTS: /* in 2292bis only */
- return(EOPNOTSUPP);
}
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
error = sooptcopyout(sopt, &optval,
sizeof optval);
-#else
- *mp = m = m_get(M_WAIT, MT_SOOPTS);
- m->m_len = sizeof(int);
- *mtod(m, int *) = optval;
-#endif /* FreeBSD3 */
-#else /* new advanced API */
-#if HAVE_NRL_INPCB
-#define in6p inp
-#define in6p_outputopts inp_outputopts6
-#endif
- error = ip6_getpcbopt(in6p->in6p_outputopts,
- optname, &optdata,
- &optdatalen);
- if (error == 0) {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- /* note that optdatalen maybe 0 */
- error = sooptcopyout(sopt, optdata,
- optdatalen);
-#else /* !FreeBSD3 */
- if (optdatalen > MCLBYTES)
- return(EMSGSIZE); /* XXX */
- *mp = m = m_get(M_WAIT, MT_SOOPTS);
- if (optdatalen > MLEN)
- MCLGET(m, M_WAIT);
- m->m_len = optdatalen;
- bcopy(optdata, mtod(m, void *),
- optdatalen);
-#endif /* FreeBSD3 */
- }
-#if HAVE_NRL_INPCB
-#undef in6p
-#undef in6p_outputopts
-#endif
-#endif /* COMPAT_RFC2292 */
break;
case IPV6_MULTICAST_IF:
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
{
struct mbuf *m;
error = ip6_getmoptions(sopt->sopt_name,
mtod(m, char *), m->m_len);
m_freem(m);
}
-#elif defined(HAVE_NRL_INPCB)
- error = ip6_getmoptions(optname, inp->inp_moptions6, mp);
-#else
- error = ip6_getmoptions(optname, in6p->in6p_moptions, mp);
-#endif
break;
#if IPSEC
{
caddr_t req = NULL;
size_t len = 0;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
struct mbuf *m = NULL;
struct mbuf **mp = &m;
- error = sooptgetm(sopt, &m); /* XXX */
+ error = soopt_getm(sopt, &m); /* XXX */
if (error != NULL)
break;
- error = sooptmcopyin(sopt, m); /* XXX */
+ error = soopt_mcopyin(sopt, m); /* XXX */
if (error != NULL)
break;
-#endif
if (m) {
req = mtod(m, caddr_t);
len = m->m_len;
}
-#if HAVE_NRL_INPCB
- error = ipsec6_get_policy(inp, req, len, mp);
-#else
error = ipsec6_get_policy(in6p, req, len, mp);
-#endif
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
if (error == 0)
- error = sooptmcopyout(sopt, m); /*XXX*/
- m_freem(m);
-#endif
+ error = soopt_mcopyout(sopt, m); /*XXX*/
+ if (error == 0 && m)
+ m_freem(m);
break;
}
-#endif /* IPSEC */
+#endif /* KAME IPSEC */
-#if IPV6FIREWALL
case IPV6_FW_GET:
- {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- struct mbuf *m;
- struct mbuf **mp = &m;
-#endif
-
- if (ip6_fw_ctl_ptr == NULL)
- {
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
- if (m)
- (void)m_free(m);
-#endif
+ {
+ if (ip6_fw_ctl_ptr == NULL && load_ipfw() != 0)
return EINVAL;
+
+ error = (*ip6_fw_ctl_ptr)(sopt);
}
- error = (*ip6_fw_ctl_ptr)(optname, mp);
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
- if (error == 0)
- error = sooptmcopyout(sopt, m); /* XXX */
- if (m)
- m_freem(m);
-#endif
- }
break;
-#endif
default:
error = ENOPROTOOPT;
}
} else {
error = EINVAL;
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
- if (op == PRCO_SETOPT && *mp)
- (void)m_free(*mp);
-#endif
}
return(error);
}
* specifying behavior of outgoing packets.
*/
static int
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
ip6_pcbopts(pktopt, m, so, sopt)
-#else
-ip6_pcbopts(pktopt, m, so)
-#endif
struct ip6_pktopts **pktopt;
- register struct mbuf *m;
+ struct mbuf *m;
struct socket *so;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
struct sockopt *sopt;
-#endif
{
- register struct ip6_pktopts *opt = *pktopt;
+ struct ip6_pktopts *opt = *pktopt;
int error = 0;
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
struct proc *p = sopt->sopt_p;
-#else
- struct proc *p = curproc; /* XXX */
-#endif
int priv = 0;
/* turn off any old options. */
if (opt) {
#if DIAGNOSTIC
- if (opt->ip6po_pktinfo || opt->ip6po_nexthop ||
- opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 ||
- opt->ip6po_rhinfo.ip6po_rhi_rthdr)
- printf("ip6_pcbopts: all specified options are cleared.\n");
+ if (opt->ip6po_pktinfo || opt->ip6po_nexthop ||
+ opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 ||
+ opt->ip6po_rhinfo.ip6po_rhi_rthdr)
+ printf("ip6_pcbopts: all specified options are cleared.\n");
#endif
ip6_clearpktopts(opt, 1, -1);
- }
- else
+ } else
opt = _MALLOC(sizeof(*opt), M_IP6OPT, M_WAITOK);
*pktopt = NULL;
if (!m || m->m_len == 0) {
/*
- * Only turning off any previous options.
+ * Only turning off any previous options, regardless of
+ * whether the opt is just created or given.
*/
if (opt)
- _FREE(opt, M_IP6OPT);
+ FREE(opt, M_IP6OPT);
return(0);
}
/* set options specified by user. */
-#if 0
if (p && !suser(p->p_ucred, &p->p_acflag))
priv = 1;
-#endif
if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
+ FREE(opt, M_IP6OPT);
return(error);
}
*pktopt = opt;
}
/*
- * Set up an IP6 option in pcb for insertion in output packets or
- * specifying behavior of outgoing packets.
- * XXX: The logic of this function is very similar to ip6_setpktoptions().
+ * initialize ip6_pktopts. beware that there are non-zero default values in
+ * the struct.
*/
-static int
-ip6_pcbopt(optname, buf, len, pktopt, priv)
- int optname, len, priv;
- u_char *buf;
- struct ip6_pktopts **pktopt;
-{
+void
+init_ip6pktopts(opt)
struct ip6_pktopts *opt;
- struct in6_pktinfo *pktinfo;
-
- if (*pktopt == NULL) {
- *pktopt = _MALLOC(sizeof(struct ip6_pktopts), M_IP6OPT,
- M_WAITOK);
- bzero(*pktopt, sizeof(struct ip6_pktopts));
- (*pktopt)->ip6po_hlim = -1;
- }
- opt = *pktopt;
-
- switch(optname) {
- case IPV6_PKTINFO:
- if (len == 0) { /* just remove the option */
- ip6_clearpktopts(opt, 1, IPV6_PKTINFO);
- break;
- }
-
- if (len != sizeof(struct in6_pktinfo))
- return EINVAL;
- pktinfo = (struct in6_pktinfo *)buf;
-
- /*
- * An application can clear any sticky IPV6_PKTINFO option by
- * doing a "regular" setsockopt with ipi6_addr being
- * in6addr_any and ipi6_ifindex being zero.
- * [rfc2292bis-01, Section 6]
- * XXX: Is this a good feature?? (jinmei@kame.net)
- */
- if (pktinfo->ipi6_ifindex == 0 &&
- IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
- ip6_clearpktopts(opt, 1, IPV6_PKTINFO);
- break;
- }
-
- /* XXX: this overrides the original data space */
- if (pktinfo->ipi6_ifindex &&
- IN6_IS_ADDR_LINKLOCAL(&pktinfo->ipi6_addr))
- pktinfo->ipi6_addr.s6_addr16[1] =
- htons(pktinfo->ipi6_ifindex);
-
- if (pktinfo->ipi6_ifindex > if_index ||
- pktinfo->ipi6_ifindex < 0)
- return(ENXIO);
-
- /*
- * Check if the requested source address is indeed a unicast
- * address assigned to the node.
- */
- if (!IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
- struct ifaddr *ia;
- struct sockaddr_in6 sin6;
-
- bzero(&sin6, sizeof(sin6));
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr = pktinfo->ipi6_addr;
- ia = ifa_ifwithaddr(sin6tosa(&sin6));
- if (ia == NULL)
- return(EADDRNOTAVAIL);
- }
-
- if (opt->ip6po_pktinfo == NULL)
- opt->ip6po_pktinfo = _MALLOC(sizeof(struct in6_pktinfo),
- M_IP6OPT, M_WAITOK);
- bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
-
- break;
- case IPV6_HOPLIMIT:
- {
- int *hlimp;
-
- if (len != sizeof(int))
- return(EINVAL);
- hlimp = (int *)buf;
- if (*hlimp < -1 || *hlimp > 255)
- return(EINVAL);
-
- opt->ip6po_hlim = *hlimp;
- break;
- }
- case IPV6_NEXTHOP:
- if (!priv)
- return(EPERM);
-
- if (len == 0) { /* just remove the option */
- ip6_clearpktopts(opt, 1, IPV6_NEXTHOP);
- break;
- }
-
- /* check if cmsg_len is large enough for sa_len */
- if (len < sizeof(u_char) ||
- len < *buf)
- return(EINVAL);
-
- /* turn off the previous option */
- ip6_clearpktopts(opt, 1, IPV6_NEXTHOP);
-
- opt->ip6po_nexthop = _MALLOC(*buf, M_IP6OPT, M_WAITOK);
- bcopy(buf, opt->ip6po_nexthop, *buf);
- break;
- case IPV6_HOPOPTS:
- {
- struct ip6_hbh *hbh;
- int hbhlen;
-
- /*
- * XXX: We don't allow a non-privileged user to set ANY HbH
- * options, since per-option restriction has too much
- * overhead.
- */
- if (!priv)
- return(EPERM);
-
- if (len == 0) {
- ip6_clearpktopts(opt, 1, IPV6_HOPOPTS);
- break; /* just remove the option */
- }
-
- if (len < sizeof(struct ip6_hbh))
- return(EINVAL);
- hbh = (struct ip6_hbh *)buf;
- hbhlen = (hbh->ip6h_len + 1) << 3;
- if (len != hbhlen)
- return(EINVAL);
-
- /* turn off the previous option */
- ip6_clearpktopts(opt, 1, IPV6_HOPOPTS);
-
- opt->ip6po_hbh = _MALLOC(hbhlen, M_IP6OPT, M_WAITOK);
- bcopy(buf, opt->ip6po_hbh, hbhlen);
-
- break;
- }
- case IPV6_DSTOPTS:
- case IPV6_RTHDRDSTOPTS:
- {
- struct ip6_dest *dest, *newdest;
- int destlen;
-
- if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */
- return(EPERM);
-
- if (len == 0) {
- ip6_clearpktopts(opt, 1, optname);
- break; /* just remove the option */
- }
-
- if (len < sizeof(struct ip6_dest))
- return(EINVAL);
- dest = (struct ip6_dest *)buf;
- destlen = (dest->ip6d_len + 1) << 3;
- if (len != destlen)
- return(EINVAL);
-
- /* turn off the previous option */
- ip6_clearpktopts(opt, 1, optname);
-
- newdest = _MALLOC(destlen, M_IP6OPT, M_WAITOK);
- bcopy(buf, newdest, destlen);
-
- if (optname == IPV6_DSTOPTS)
- opt->ip6po_dest2 = newdest;
- else
- opt->ip6po_dest1 = newdest;
-
- break;
- }
- case IPV6_RTHDR:
- {
- struct ip6_rthdr *rth;
- int rthlen;
-
- if (len == 0) {
- ip6_clearpktopts(opt, 1, IPV6_RTHDR);
- break; /* just remove the option */
- }
-
- if (len < sizeof(struct ip6_rthdr))
- return(EINVAL);
- rth = (struct ip6_rthdr *)buf;
- rthlen = (rth->ip6r_len + 1) << 3;
- if (len != rthlen)
- return(EINVAL);
-
- switch(rth->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- if (rth->ip6r_len == 0) /* must contain one addr */
- return(EINVAL);
- if (rth->ip6r_len % 2) /* length must be even */
- return(EINVAL);
- if (rth->ip6r_len / 2 != rth->ip6r_segleft)
- return(EINVAL);
- break;
- default:
- return(EINVAL); /* not supported */
- }
-
- /* turn off the previous option */
- ip6_clearpktopts(opt, 1, IPV6_RTHDR);
-
- opt->ip6po_rthdr = _MALLOC(rthlen, M_IP6OPT, M_WAITOK);
- bcopy(buf, opt->ip6po_rthdr, rthlen);
-
- break;
- }
- default:
- return(ENOPROTOOPT);
- } /* end of switch */
-
- return(0);
-}
-
-static int
-ip6_getpcbopt(pktopt, optname, datap, datalenp)
- struct ip6_pktopts *pktopt;
- int optname, *datalenp;
- void **datap;
{
- void *optdata = NULL;
- struct ip6_ext *ip6e;
- int optdatalen = 0;
- if (pktopt == NULL)
- goto end;
-
- switch(optname) {
- case IPV6_PKTINFO:
- if (pktopt->ip6po_pktinfo) {
- optdata = (void *)pktopt->ip6po_pktinfo;
- optdatalen = sizeof(struct in6_pktinfo);
- }
- break;
- case IPV6_HOPLIMIT:
- optdata = (void *)&pktopt->ip6po_hlim;
- optdatalen = sizeof(int);
- break;
- case IPV6_HOPOPTS:
- if (pktopt->ip6po_hbh) {
- optdata = (void *)pktopt->ip6po_hbh;
- ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
- break;
- case IPV6_RTHDR:
- if (pktopt->ip6po_rthdr) {
- optdata = (void *)pktopt->ip6po_rthdr;
- ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
- break;
- case IPV6_RTHDRDSTOPTS:
- if (pktopt->ip6po_dest1) {
- optdata = (void *)pktopt->ip6po_dest1;
- ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
- break;
- case IPV6_DSTOPTS:
- if (pktopt->ip6po_dest2) {
- optdata = (void *)pktopt->ip6po_dest2;
- ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
- break;
- }
-
- end:
- *datap = optdata;
- *datalenp = optdatalen;
-
- return(0);
+ bzero(opt, sizeof(*opt));
+ opt->ip6po_hlim = -1; /* -1 means default hop limit */
}
void
if (pktopt == NULL)
return;
- if (optname == -1 || optname == IPV6_PKTINFO) {
+ if (optname == -1) {
if (needfree && pktopt->ip6po_pktinfo)
- _FREE(pktopt->ip6po_pktinfo, M_IP6OPT);
+ FREE(pktopt->ip6po_pktinfo, M_IP6OPT);
pktopt->ip6po_pktinfo = NULL;
}
- if (optname == -1 || optname == IPV6_HOPLIMIT)
+ if (optname == -1)
pktopt->ip6po_hlim = -1;
- if (optname == -1 || optname == IPV6_NEXTHOP) {
+ if (optname == -1) {
if (needfree && pktopt->ip6po_nexthop)
- _FREE(pktopt->ip6po_nexthop, M_IP6OPT);
+ FREE(pktopt->ip6po_nexthop, M_IP6OPT);
pktopt->ip6po_nexthop = NULL;
}
- if (optname == -1 || optname == IPV6_HOPOPTS) {
+ if (optname == -1) {
if (needfree && pktopt->ip6po_hbh)
- _FREE(pktopt->ip6po_hbh, M_IP6OPT);
+ FREE(pktopt->ip6po_hbh, M_IP6OPT);
pktopt->ip6po_hbh = NULL;
}
- if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
+ if (optname == -1) {
if (needfree && pktopt->ip6po_dest1)
- _FREE(pktopt->ip6po_dest1, M_IP6OPT);
+ FREE(pktopt->ip6po_dest1, M_IP6OPT);
pktopt->ip6po_dest1 = NULL;
}
- if (optname == -1 || optname == IPV6_RTHDR) {
+ if (optname == -1) {
if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
- _FREE(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
+ FREE(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
if (pktopt->ip6po_route.ro_rt) {
- RTFREE(pktopt->ip6po_route.ro_rt);
+ rtfree(pktopt->ip6po_route.ro_rt);
pktopt->ip6po_route.ro_rt = NULL;
}
}
- if (optname == -1 || optname == IPV6_DSTOPTS) {
+ if (optname == -1) {
if (needfree && pktopt->ip6po_dest2)
- _FREE(pktopt->ip6po_dest2, M_IP6OPT);
+ FREE(pktopt->ip6po_dest2, M_IP6OPT);
pktopt->ip6po_dest2 = NULL;
}
}
-#define PKTOPT_EXTHDRCPY(type) if (src->type) {\
+#define PKTOPT_EXTHDRCPY(type) \
+do {\
+ if (src->type) {\
int hlen =\
(((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\
dst->type = _MALLOC(hlen, M_IP6OPT, canwait);\
if (dst->type == NULL && canwait == M_NOWAIT)\
goto bad;\
bcopy(src->type, dst->type, hlen);\
- }
+ }\
+} while (0)
struct ip6_pktopts *
ip6_copypktopts(src, canwait)
dst = _MALLOC(sizeof(*dst), M_IP6OPT, canwait);
if (dst == NULL && canwait == M_NOWAIT)
- goto bad;
+ return (NULL);
bzero(dst, sizeof(*dst));
dst->ip6po_hlim = src->ip6po_hlim;
- dst->ip6po_flags = src->ip6po_flags;
if (src->ip6po_pktinfo) {
dst->ip6po_pktinfo = _MALLOC(sizeof(*dst->ip6po_pktinfo),
M_IP6OPT, canwait);
return(dst);
bad:
- printf("ip6_copypktopts: copy failed");
- if (dst->ip6po_pktinfo) _FREE(dst->ip6po_pktinfo, M_IP6OPT);
- if (dst->ip6po_nexthop) _FREE(dst->ip6po_nexthop, M_IP6OPT);
- if (dst->ip6po_hbh) _FREE(dst->ip6po_hbh, M_IP6OPT);
- if (dst->ip6po_dest1) _FREE(dst->ip6po_dest1, M_IP6OPT);
- if (dst->ip6po_dest2) _FREE(dst->ip6po_dest2, M_IP6OPT);
- if (dst->ip6po_rthdr) _FREE(dst->ip6po_rthdr, M_IP6OPT);
+ if (dst->ip6po_pktinfo) FREE(dst->ip6po_pktinfo, M_IP6OPT);
+ if (dst->ip6po_nexthop) FREE(dst->ip6po_nexthop, M_IP6OPT);
+ if (dst->ip6po_hbh) FREE(dst->ip6po_hbh, M_IP6OPT);
+ if (dst->ip6po_dest1) FREE(dst->ip6po_dest1, M_IP6OPT);
+ if (dst->ip6po_dest2) FREE(dst->ip6po_dest2, M_IP6OPT);
+ if (dst->ip6po_rthdr) FREE(dst->ip6po_rthdr, M_IP6OPT);
+ FREE(dst, M_IP6OPT);
return(NULL);
}
#undef PKTOPT_EXTHDRCPY
ip6_clearpktopts(pktopt, 1, -1);
- _FREE(pktopt, M_IP6OPT);
+ FREE(pktopt, M_IP6OPT);
}
/*
* Set the IP6 multicast options in response to user setsockopt().
*/
static int
-ip6_setmoptions(optname, im6op, m)
+ip6_setmoptions(optname, in6p, m)
int optname;
- struct ip6_moptions **im6op;
+ struct inpcb* in6p;
struct mbuf *m;
{
int error = 0;
u_int loop, ifindex;
struct ipv6_mreq *mreq;
struct ifnet *ifp;
+ struct ip6_moptions **im6op = &in6p->in6p_moptions;
struct ip6_moptions *im6o = *im6op;
+ struct ip_moptions *imo;
struct route_in6 ro;
struct sockaddr_in6 *dst;
struct in6_multi_mship *imm;
-
- struct proc *p = current_proc(); /* ### */
+ struct proc *p = current_proc(); /* XXX */
if (im6o == NULL) {
/*
im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
LIST_INIT(&im6o->im6o_memberships);
}
+
+ if (in6p->inp_moptions == NULL) {
+ /*
+ * No IPv4 multicast option buffer attached to the pcb;
+ * call ip_createmoptions to allocate one and initialize
+ * to default values.
+ */
+ error = ip_createmoptions(&in6p->inp_moptions);
+ if (error != 0)
+ return error;
+ }
+ imo = in6p->inp_moptions;
switch (optname) {
error = EINVAL;
break;
}
- ifindex = *(mtod(m, u_int *));
+ bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex));
if (ifindex < 0 || if_index < ifindex) {
error = ENXIO; /* XXX EINVAL? */
break;
break;
}
im6o->im6o_multicast_ifp = ifp;
+ imo->imo_multicast_ifp = ifp;
break;
case IPV6_MULTICAST_HOPS:
error = EINVAL;
break;
}
- optval = *(mtod(m, u_int *));
+ bcopy(mtod(m, u_int *), &optval, sizeof(optval));
if (optval < -1 || optval >= 256)
error = EINVAL;
- else if (optval == -1)
+ else if (optval == -1) {
im6o->im6o_multicast_hlim = ip6_defmcasthlim;
- else
+ imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
+ } else {
im6o->im6o_multicast_hlim = optval;
+ imo->imo_multicast_ttl = optval;
+ }
break;
}
* Set the loopback flag for outgoing multicast packets.
* Must be zero or one.
*/
- if (m == NULL || m->m_len != sizeof(u_int) ||
- (loop = *(mtod(m, u_int *))) > 1) {
+ if (m == NULL || m->m_len != sizeof(u_int)) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(mtod(m, u_int *), &loop, sizeof(loop));
+ if (loop > 1) {
error = EINVAL;
break;
}
im6o->im6o_multicast_loop = loop;
+ imo->imo_multicast_loop = loop;
break;
case IPV6_JOIN_GROUP:
break;
}
mreq = mtod(m, struct ipv6_mreq *);
+ /*
+ * If the interface is specified, validate it.
+ */
+ if (mreq->ipv6mr_interface < 0
+ || if_index < mreq->ipv6mr_interface) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
/*
* We use the unspecified address to specify to accept
* all multicast addresses. Only super user is allowed
* to do this.
*/
-#if ISFB31
- if (suser(p->p_ucred, &p->p_acflag)) {
+ if (suser(p->p_ucred, &p->p_acflag))
+ {
error = EACCES;
break;
}
-#endif
+ } else if (IN6_IS_ADDR_V4MAPPED(&mreq->ipv6mr_multiaddr)) {
+ struct ip_mreq v4req;
+
+ v4req.imr_multiaddr.s_addr = mreq->ipv6mr_multiaddr.s6_addr32[3];
+ v4req.imr_interface.s_addr = INADDR_ANY;
+
+ /* Find an IPv4 address on the specified interface. */
+ if (mreq->ipv6mr_interface != 0) {
+ struct in_ifaddr *ifa;
+
+ ifp = ifindex2ifnet[mreq->ipv6mr_interface];
+
+ TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
+ if (ifa->ia_ifp == ifp) {
+ v4req.imr_interface = IA_SIN(ifa)->sin_addr;
+ break;
+ }
+ }
+
+ if (v4req.imr_multiaddr.s_addr == 0) {
+ /* Interface has no IPv4 address. */
+ error = EINVAL;
+ break;
+ }
+ }
+
+ error = ip_addmembership(imo, &v4req);
+ break;
} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
error = EINVAL;
break;
}
-
- /*
- * If the interface is specified, validate it.
- */
- if (mreq->ipv6mr_interface < 0
- || if_index < mreq->ipv6mr_interface) {
- error = ENXIO; /* XXX EINVAL? */
- break;
- }
/*
* If no interface was explicitly specified, choose an
* appropriate one according to the given multicast address.
* XXX: is it a good approach?
*/
if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) {
-#ifdef __bsdi__
- ifp = loifp;
-#else
ifp = &loif[0];
-#endif
} else {
ro.ro_rt = NULL;
dst = (struct sockaddr_in6 *)&ro.ro_dst;
}
if ((imm->i6mm_maddr =
in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
- _FREE(imm, M_IPMADDR);
+ FREE(imm, M_IPMADDR);
break;
}
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
break;
}
mreq = mtod(m, struct ipv6_mreq *);
- if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
- if (suser(p->p_ucred, &p->p_acflag)) {
- error = EACCES;
- break;
- }
- } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
- error = EINVAL;
- break;
- }
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
break;
}
ifp = ifindex2ifnet[mreq->ipv6mr_interface];
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
+ if (suser(p->p_ucred, &p->p_acflag)) {
+ error = EACCES;
+ break;
+ }
+ } else if (IN6_IS_ADDR_V4MAPPED(&mreq->ipv6mr_multiaddr)) {
+ struct ip_mreq v4req;
+
+ v4req.imr_multiaddr.s_addr = mreq->ipv6mr_multiaddr.s6_addr32[3];
+ v4req.imr_interface.s_addr = INADDR_ANY;
+
+ if (ifp != NULL) {
+ struct in_ifaddr *ifa;
+
+ TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
+ if (ifa->ia_ifp == ifp) {
+ v4req.imr_interface = IA_SIN(ifa)->sin_addr;
+ break;
+ }
+ }
+ }
+
+ error = ip_dropmembership(imo, &v4req);
+ break;
+ } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
/*
* Put interface index into the multicast address,
* if the address has link-local scope.
*/
LIST_REMOVE(imm, i6mm_chain);
in6_delmulti(imm->i6mm_maddr);
- _FREE(imm, M_IPMADDR);
+ FREE(imm, M_IPMADDR);
break;
default:
im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
im6o->im6o_memberships.lh_first == NULL) {
- _FREE(*im6op, M_IPMOPTS);
+ FREE(*im6op, M_IPMOPTS);
*im6op = NULL;
}
+ if (imo->imo_multicast_ifp == NULL &&
+ imo->imo_multicast_vif == -1 &&
+ imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL &&
+ imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP &&
+ imo->imo_num_memberships == 0) {
+ ip_freemoptions(imo);
+ in6p->inp_moptions = 0;
+ }
return(error);
}
static int
ip6_getmoptions(optname, im6o, mp)
int optname;
- register struct ip6_moptions *im6o;
- register struct mbuf **mp;
+ struct ip6_moptions *im6o;
+ struct mbuf **mp;
{
u_int *hlim, *loop, *ifindex;
-#if __FreeBSD__ || defined (__APPLE__)
*mp = m_get(M_WAIT, MT_HEADER); /*XXX*/
-#else
- *mp = m_get(M_WAIT, MT_SOOPTS);
-#endif
switch (optname) {
*/
void
ip6_freemoptions(im6o)
- register struct ip6_moptions *im6o;
+ struct ip6_moptions *im6o;
{
struct in6_multi_mship *imm;
LIST_REMOVE(imm, i6mm_chain);
if (imm->i6mm_maddr)
in6_delmulti(imm->i6mm_maddr);
- _FREE(imm, M_IPMADDR);
+ FREE(imm, M_IPMADDR);
}
- _FREE(im6o, M_IPMOPTS);
+ FREE(im6o, M_IPMOPTS);
}
/*
struct ip6_pktopts *opt;
int priv, needcopy;
{
- register struct cmsghdr *cm = 0;
+ struct cmsghdr *cm = 0;
if (control == 0 || opt == 0)
return(EINVAL);
- bzero(opt, sizeof(*opt));
- opt->ip6po_hlim = -1; /* -1 means to use default hop limit */
+ init_ip6pktopts(opt);
/*
* XXX: Currently, we assume all the optional information is stored
if (cm->cmsg_level != IPPROTO_IPV6)
continue;
- switch(cm->cmsg_type) {
+ /*
+ * XXX should check if RFC2292 API is mixed with 2292bis API
+ */
+ switch (cm->cmsg_type) {
case IPV6_PKTINFO:
if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
return(EINVAL);
opt->ip6po_pktinfo =
_MALLOC(sizeof(struct in6_pktinfo),
M_IP6OPT, M_WAITOK);
- *opt->ip6po_pktinfo =
- *(struct in6_pktinfo *)CMSG_DATA(cm);
+ bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo,
+ sizeof(struct in6_pktinfo));
} else
opt->ip6po_pktinfo =
(struct in6_pktinfo *)CMSG_DATA(cm);
/*
* Check if the requested source address is indeed a
- * unicast address assigned to the node.
+ * unicast address assigned to the node, and can be
+ * used as the packet's source address.
*/
if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
- struct ifaddr *ia;
+ struct in6_ifaddr *ia6;
struct sockaddr_in6 sin6;
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_addr =
opt->ip6po_pktinfo->ipi6_addr;
- ia = ifa_ifwithaddr(sin6tosa(&sin6));
- if (ia == NULL)
+ ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
+ if (ia6 == NULL ||
+ (ia6->ia6_flags & (IN6_IFF_ANYCAST |
+ IN6_IFF_NOTREADY)) != 0)
return(EADDRNOTAVAIL);
}
break;
case IPV6_NEXTHOP:
if (!priv)
return(EPERM);
-
+
if (cm->cmsg_len < sizeof(u_char) ||
/* check if cmsg_len is large enough for sa_len */
cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
case IPV6_DSTOPTS:
{
- struct ip6_dest *dest;
+ struct ip6_dest *dest, **newdest;
int destlen;
if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
if (cm->cmsg_len != CMSG_LEN(destlen))
return(EINVAL);
- /*
- * If there is no routing header yet, the destination
- * options header should be put on the 1st part.
- * Otherwise, the header should be on the 2nd part.
- * (See RFC 2460, section 4.1)
+ /*
+ * The old advacned API is ambiguous on this
+ * point. Our approach is to determine the
+ * position based according to the existence
+ * of a routing header. Note, however, that
+ * this depends on the order of the extension
+ * headers in the ancillary data; the 1st part
+ * of the destination options header must
+ * appear before the routing header in the
+ * ancillary data, too.
+ * RFC2292bis solved the ambiguity by
+ * introducing separate cmsg types.
*/
- if (opt->ip6po_rthdr == NULL) {
- if (needcopy) {
- opt->ip6po_dest1 =
- _MALLOC(destlen, M_IP6OPT,
- M_WAITOK);
- bcopy(dest, opt->ip6po_dest1, destlen);
- } else
- opt->ip6po_dest1 = dest;
- } else {
- if (needcopy) {
- opt->ip6po_dest2 =
- _MALLOC(destlen, M_IP6OPT,
- M_WAITOK);
- bcopy(dest, opt->ip6po_dest2, destlen);
- } else
- opt->ip6po_dest2 = dest;
- }
+ if (opt->ip6po_rthdr == NULL)
+ newdest = &opt->ip6po_dest1;
+ else
+ newdest = &opt->ip6po_dest2;
+
+ if (needcopy) {
+ *newdest = _MALLOC(destlen, M_IP6OPT, M_WAITOK);
+ bcopy(dest, *newdest, destlen);
+ } else
+ *newdest = dest;
+
break;
}
if (cm->cmsg_len != CMSG_LEN(rthlen))
return(EINVAL);
- switch(rth->ip6r_type) {
+ switch (rth->ip6r_type) {
case IPV6_RTHDR_TYPE_0:
/* must contain one addr */
if (rth->ip6r_len == 0)
break;
}
- case IPV6_REACHCONF:
-#if 1
- /*
- * it looks dangerous to allow IPV6_REACHCONF to
- * normal user. it affects the ND state (system state)
- * and can affect communication by others - jinmei
- */
- if (!priv)
- return(EPERM);
-#endif
-
- if (cm->cmsg_len != CMSG_LEN(0))
- return(EINVAL);
- opt->ip6po_flags |= IP6PO_REACHCONF;
- break;
-
- case IPV6_USE_MIN_MTU:
- if (cm->cmsg_len != CMSG_LEN(0))
- return(EINVAL);
- opt->ip6po_flags |= IP6PO_MINMTU;
- break;
-
default:
return(ENOPROTOOPT);
}
void
ip6_mloopback(ifp, m, dst)
struct ifnet *ifp;
- register struct mbuf *m;
- register struct sockaddr_in6 *dst;
+ struct mbuf *m;
+ struct sockaddr_in6 *dst;
{
- struct mbuf *copym;
+ struct mbuf *copym;
+ struct ip6_hdr *ip6;
copym = m_copy(m, 0, M_COPYALL);
- if (copym != NULL) {
+ if (copym == NULL)
+ return;
+
+ /*
+ * Make sure to deep-copy IPv6 header portion in case the data
+ * is in an mbuf cluster, so that we can safely override the IPv6
+ * header portion later.
+ */
+ if ((copym->m_flags & M_EXT) != 0 ||
+ copym->m_len < sizeof(struct ip6_hdr)) {
+ copym = m_pullup(copym, sizeof(struct ip6_hdr));
+ if (copym == NULL)
+ return;
+ }
+
+#if DIAGNOSTIC
+ if (copym->m_len < sizeof(*ip6)) {
+ m_freem(copym);
+ return;
+ }
+#endif
+
+ ip6 = mtod(copym, struct ip6_hdr *);
+#ifndef SCOPEDROUTING
+ /*
+ * clear embedded scope identifiers if necessary.
+ * in6_clearscope will touch the addresses only when necessary.
+ */
+ in6_clearscope(&ip6->ip6_src);
+ in6_clearscope(&ip6->ip6_dst);
+#endif
+
#ifdef __APPLE__
- /*
- * TedW:
- * We need to send all loopback traffic down to dlil in case
- * a filter has tapped-in.
- */
-
- if (lo_dl_tag == 0)
- dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET6, &lo_dl_tag);
-
- if (lo_dl_tag)
- dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *) dst, 0);
- else {
- printf("Warning: ip6_mloopback call to dlil_find_dltag failed!\n");
- m_freem(copym);
- }
+
+ /* Makes sure the HW checksum flags are cleaned before sending the packet */
+
+ copym->m_pkthdr.rcvif = (struct ifnet *)0;
+ copym->m_pkthdr.csum_data = 0;
+ copym->m_pkthdr.csum_flags = 0;
+
+ if (lo_dl_tag == 0)
+ dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET, &lo_dl_tag);
+
+ if (lo_dl_tag) {
+ copym->m_pkthdr.rcvif = ifp;
+ dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *)dst, 0);
+ } else
+ m_free(copym);
#else
- (void)if_simloop(ifp, copym, (struct sockaddr *)dst, NULL);
- (void)looutput(ifp, copym, (struct sockaddr *)dst, NULL);
+ (void)if_simloop(ifp, copym, dst->sin6_family, NULL);
#endif
- }
}
/*
/*
* Compute IPv6 extension header length.
*/
-#if HAVE_NRL_INPCB
-# define in6pcb inpcb
-# define in6p_outputopts inp_outputopts6
-#endif
int
ip6_optlen(in6p)
struct in6pcb *in6p;
return len;
#undef elen
}
-#if HAVE_NRL_INPCB
-# undef in6pcb
-# undef in6p_outputopts
-#endif