-/* $FreeBSD: src/sys/netinet6/ip6_output.c,v 1.13.2.10 2001/07/15 18:18:34 ume Exp $ */
-/* $KAME: ip6_output.c,v 1.180 2001/05/21 05:37:50 jinmei 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.
#include <netinet/in.h>
#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
static u_long lo_dl_tag = 0;
+extern u_long route_generation;
+
struct ip6_exthdrs {
struct mbuf *ip6e_ip6;
static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
struct socket *, struct sockopt *sopt));
-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,
static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
+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
* header (with pri, len, nxt, hlim, src, dst).
/*
* 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.
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;
* 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))) {
+ 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;
}
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 */
* 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 add destination, which should already be assured
- * larger scopes than link will be supported in the near
- * future.
+ * 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))
*/
if (ip6_fw_enable && ip6_fw_chk_ptr) {
u_short port = 0;
- m->m_pkthdr.rcvif = NULL; /*XXX*/
+ 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);
/*
* 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) {
return(0);
}
+extern int load_ipfw();
+
/*
* IP6 socket option processing.
*/
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");
}
error = optval = 0;
error = EINVAL;
break;
}
- /*
- * XXX: BINDV6ONLY should be integrated
- * into V6ONLY.
- */
- OPTSET(IN6P_BINDV6ONLY);
OPTSET(IN6P_IPV6_V6ONLY);
+ if (optval)
+ in6p->in6p_vflag &= ~INP_IPV4;
+ else
+ in6p->in6p_vflag |= INP_IPV4;
break;
}
break;
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);
}
break;
if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
break;
- if (error = soopt_mcopyin(sopt, m)) /* XXX */
+ if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
break;
if (m) {
req = mtod(m, caddr_t);
case IPV6_FW_DEL:
case IPV6_FW_FLUSH:
case IPV6_FW_ZERO:
- {
- struct mbuf *m;
- struct mbuf **mp = &m;
-
- if (ip6_fw_ctl_ptr == NULL)
+ {
+ if (ip6_fw_ctl_ptr == NULL && load_ipfw() != 0)
return EINVAL;
- /* XXX */
- if ((error = soopt_getm(sopt, &m)) != 0)
- break;
- /* XXX */
- if ((error = soopt_mcopyin(sopt, m)) != 0)
- break;
- error = (*ip6_fw_ctl_ptr)(optname, mp);
- m = *mp;
- }
+
+ error = (*ip6_fw_ctl_ptr)(sopt);
+ }
break;
default:
break;
case IPV6_V6ONLY:
- /* XXX: see the setopt case. */
- optval = OPTBIT(IN6P_BINDV6ONLY);
+ optval = OPTBIT(IN6P_IPV6_V6ONLY);
break;
case IPV6_PORTRANGE:
#endif /* KAME IPSEC */
case IPV6_FW_GET:
- {
- struct mbuf *m;
- struct mbuf **mp = &m;
-
- if (ip6_fw_ctl_ptr == NULL)
- {
+ {
+ 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 (error == 0)
- error = soopt_mcopyout(sopt, m); /* XXX */
- if (error == 0 && m)
- m_freem(m);
- }
break;
default:
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);
priv = 1;
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;
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;
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);
+ FREE(dst, M_IP6OPT);
return(NULL);
}
#undef PKTOPT_EXTHDRCPY
* 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;
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) {
break;
}
im6o->im6o_multicast_ifp = ifp;
+ imo->imo_multicast_ifp = ifp;
break;
case IPV6_MULTICAST_HOPS:
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;
}
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
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;
+
+ /* 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.
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.
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);
}
if (lo_dl_tag == 0)
dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET, &lo_dl_tag);
- if (lo_dl_tag)
- dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *)&dst, 0);
- else
+ 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, dst->sin6_family, NULL);
+ (void)if_simloop(ifp, copym, dst->sin6_family, NULL);
#endif
}