+/*
+ * Copyright (c) 2003-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
/* $FreeBSD: src/sys/netinet6/ip6_input.c,v 1.11.2.10 2001/07/24 19:10:18 brooks Exp $ */
/* $KAME: ip6_input.c,v 1.194 2001/05/27 13:28:35 itojun Exp $ */
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
+#include <net/kpi_protocol.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet6/ipsec6.h>
#endif
extern int ipsec_bypass;
-extern lck_mtx_t *sadb_mutex;
#endif
#include <netinet6/ip6_fw.h>
#include <net/net_osdep.h>
+#if PF
+#include <net/pfvar.h>
+#endif /* PF */
+
extern struct domain inet6domain;
extern struct ip6protosw inet6sw[];
int in6_init2done = 0;
+#if IPFW2
/* firewall hooks */
ip6_fw_chk_t *ip6_fw_chk_ptr;
ip6_fw_ctl_t *ip6_fw_ctl_ptr;
int ip6_fw_enable = 1;
+#endif
struct ip6stat ip6stat;
lck_mtx_t *ip6_mutex;
lck_mtx_t *dad6_mutex;
lck_mtx_t *nd6_mutex;
-lck_mtx_t *prefix6_mutex;
+lck_mtx_t *prefix6_mutex;
+lck_mtx_t *scope6_mutex;
lck_attr_t *ip6_mutex_attr;
-lck_grp_t *ip6_mutex_grp;
-lck_grp_attr_t *ip6_mutex_grp_attr;
+lck_grp_t *ip6_mutex_grp;
+lck_grp_attr_t *ip6_mutex_grp_attr;
extern lck_mtx_t *inet6_domain_mutex;
#endif
extern int loopattach_done;
static void ip6_init2(void *);
-static struct mbuf *ip6_setdstifaddr(struct mbuf *, struct in6_ifaddr *);
+static struct ip6aux *ip6_setdstifaddr(struct mbuf *, struct in6_ifaddr *);
static int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *);
#if PULLDOWN_TEST
void stfattach(void);
#endif
+extern lck_mtx_t *domain_proto_mtx;
+
+
static void
ip6_proto_input(
- protocol_family_t protocol,
+ __unused protocol_family_t protocol,
mbuf_t packet)
{
ip6_input(packet);
struct ip6protosw *pr;
int i;
struct timeval tv;
- extern lck_mtx_t *domain_proto_mtx;
#if DIAGNOSTIC
if (sizeof(struct protosw) != sizeof(struct ip6protosw))
for (i = 0; i < IPPROTO_MAX; i++)
ip6_protox[i] = pr;
for (pr = (struct ip6protosw*)inet6domain.dom_protosw; pr; pr = pr->pr_next) {
- if(!((unsigned int)pr->pr_domain)) continue; /* If uninitialized, skip */
+ if(!(pr->pr_domain)) continue; /* If uninitialized, skip */
if (pr->pr_domain->dom_family == PF_INET6 &&
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) {
ip6_protox[pr->pr_protocol] = pr;
}
ip6_mutex_grp_attr = lck_grp_attr_alloc_init();
- lck_grp_attr_setdefault(ip6_mutex_grp_attr);
ip6_mutex_grp = lck_grp_alloc_init("ip6", ip6_mutex_grp_attr);
ip6_mutex_attr = lck_attr_alloc_init();
- lck_attr_setdefault(ip6_mutex_attr);
if ((ip6_mutex = lck_mtx_alloc_init(ip6_mutex_grp, ip6_mutex_attr)) == NULL) {
- printf("ip6_init: can't alloc ip6_mutex\n");
- return;
+ panic("ip6_init: can't alloc ip6_mutex\n");
}
if ((dad6_mutex = lck_mtx_alloc_init(ip6_mutex_grp, ip6_mutex_attr)) == NULL) {
- printf("ip6_init: can't alloc dad6_mutex\n");
- return;
+ panic("ip6_init: can't alloc dad6_mutex\n");
}
if ((nd6_mutex = lck_mtx_alloc_init(ip6_mutex_grp, ip6_mutex_attr)) == NULL) {
- printf("ip6_init: can't alloc nd6_mutex\n");
- return;
+ panic("ip6_init: can't alloc nd6_mutex\n");
}
if ((prefix6_mutex = lck_mtx_alloc_init(ip6_mutex_grp, ip6_mutex_attr)) == NULL) {
- printf("ip6_init: can't alloc prefix6_mutex\n");
- return;
+ panic("ip6_init: can't alloc prefix6_mutex\n");
+ }
+
+ if ((scope6_mutex = lck_mtx_alloc_init(ip6_mutex_grp, ip6_mutex_attr)) == NULL) {
+ panic("ip6_init: can't alloc scope6_mutex\n");
}
+
inet6domain.dom_flags = DOM_REENTRANT;
ip6intrq.ifq_maxlen = ip6qmaxlen;
+ in6_ifaddr_init();
nd6_init();
frag6_init();
icmp6_init();
timeout(ip6_init2, (caddr_t)0, 1 * hz);
lck_mtx_unlock(domain_proto_mtx);
- proto_register_input(PF_INET6, ip6_proto_input, NULL);
+ proto_register_input(PF_INET6, ip6_proto_input, NULL, 0);
lck_mtx_lock(domain_proto_mtx);
}
static void
-ip6_init2(dummy)
- void *dummy;
+ip6_init2(
+ __unused void *dummy)
{
/*
* to route local address of p2p link to loopback,
timeout(ip6_init2, (caddr_t)0, 1 * hz);
return;
}
- in6_ifattach(&loif[0], NULL, NULL);
+ (void) in6_ifattach(lo_ifp, NULL, NULL);
#ifdef __APPLE__
/* nd6_timer_init */
SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL);
#endif
-extern struct route_in6 ip6_forward_rt;
+/*
+ * ip6_forward_rt contains the route entry that was recently used during
+ * the forwarding of an IPv6 packet and thus acts as a route cache. Access
+ * to this variable is protected by the global lock ip6_mutex.
+ */
+static struct route_in6 ip6_forward_rt;
void
ip6_input(m)
#define M2MMAX (sizeof(ip6stat.ip6s_m2m)/sizeof(ip6stat.ip6s_m2m[0]))
if (m->m_next) {
if (m->m_flags & M_LOOP) {
- ip6stat.ip6s_m2m[loif[0].if_index]++; /* XXX */
+ ip6stat.ip6s_m2m[ifnet_index(lo_ifp)]++; /* XXX */
} else if (m->m_pkthdr.rcvif->if_index < M2MMAX)
ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++;
else
if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) {
struct mbuf *n;
- MGETHDR(n, M_DONTWAIT, MT_HEADER);
+ MGETHDR(n, M_DONTWAIT, MT_HEADER); /* MAC-OK */
if (n)
M_COPY_PKTHDR(n, m);
if (n && m->m_pkthdr.len > MHLEN) {
ip6stat.ip6s_nxthist[ip6->ip6_nxt]++;
+#if IPFW2
/*
* Check with the firewall...
*/
return;
}
}
+#endif
/*
* Check against address spoofing/corruption.
}
#endif
+#if PF
+ /* Invoke inbound packet filter */
+ lck_mtx_unlock(ip6_mutex);
+ if (pf_af_hook(m->m_pkthdr.rcvif, NULL, &m, AF_INET6, TRUE) != 0) {
+ if (m != NULL) {
+ panic("%s: unexpected packet %p\n", __func__, m);
+ /* NOTREACHED */
+ }
+ /* Already freed by callee */
+ return;
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ lck_mtx_lock(ip6_mutex);
+#endif /* PF */
+
/* drop packets if interface ID portion is already filled */
if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) &&
* Multicast check
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
- struct in6_multi *in6m = 0;
+ struct in6_multi *in6m = 0;
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
- in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast);
+ in6_ifstat_inc(ifp, ifs6_in_mcast);
/*
* See if we belong to the destination multicast group on the
* arrival interface.
*/
- IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m);
+ ifnet_lock_shared(ifp);
+ IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+ ifnet_lock_done(ifp);
if (in6m)
ours = 1;
+#if MROUTING
else if (!ip6_mrouter) {
+#else
+ else {
+#endif
ip6stat.ip6s_notmember++;
ip6stat.ip6s_cantforward++;
- in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ in6_ifstat_inc(ifp, ifs6_in_discard);
goto bad;
}
- deliverifp = m->m_pkthdr.rcvif;
+ deliverifp = ifp;
goto hbhcheck;
}
+ if (ip6_forward_rt.ro_rt != NULL)
+ RT_LOCK(ip6_forward_rt.ro_rt);
/*
* Unicast check
*/
- switch (ip6_ours_check_algorithm) {
- default:
- /*
- * XXX: I intentionally broke our indentation rule here,
- * since this switch-case is just for measurement and
- * therefore should soon be removed.
- */
if (ip6_forward_rt.ro_rt != NULL &&
- (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 &&
+ (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) &&
IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
- &((struct sockaddr_in6 *)(&ip6_forward_rt.ro_dst))->sin6_addr))
+ &((struct sockaddr_in6 *)(&ip6_forward_rt.ro_dst))->sin6_addr) &&
+ ip6_forward_rt.ro_rt->generation_id == route_generation) {
ip6stat.ip6s_forward_cachehit++;
- else {
+ } else {
struct sockaddr_in6 *dst6;
- if (ip6_forward_rt.ro_rt) {
- /* route is down or destination is different */
+ if (ip6_forward_rt.ro_rt != NULL) {
+ /* route is down/stale or destination is different */
ip6stat.ip6s_forward_cachemiss++;
+ RT_UNLOCK(ip6_forward_rt.ro_rt);
rtfree(ip6_forward_rt.ro_rt);
- ip6_forward_rt.ro_rt = 0;
+ ip6_forward_rt.ro_rt = NULL;
}
bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6));
#endif
rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+ if (ip6_forward_rt.ro_rt != NULL)
+ RT_LOCK(ip6_forward_rt.ro_rt);
}
#define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key))
* while it would be less efficient. Or, should we rather install a
* reject route for such a case?
*/
- if (ip6_forward_rt.ro_rt &&
+ if (ip6_forward_rt.ro_rt != NULL &&
(ip6_forward_rt.ro_rt->rt_flags &
(RTF_HOST|RTF_GATEWAY)) == RTF_HOST &&
#if RTF_WASCLONED
!(ip6_forward_rt.ro_rt->rt_flags & RTF_WASCLONED) &&
#endif
-#if RTF_CLONED
- !(ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) &&
-#endif
#if 0
/*
* The check below is redundant since the comparison of
ia6->ia_ifa.if_ipackets++;
ia6->ia_ifa.if_ibytes += m->m_pkthdr.len;
#endif
+ RT_UNLOCK(ip6_forward_rt.ro_rt);
goto hbhcheck;
} else {
+ RT_UNLOCK(ip6_forward_rt.ro_rt);
/* address is not ready, so discard the packet. */
nd6log((LOG_INFO,
"ip6_input: packet to an unready address %s->%s\n",
goto bad;
}
}
- } /* XXX indentation (see above) */
/*
* FAITH(Firewall Aided Internet Translator)
/* XXX do we need more sanity checks? */
ours = 1;
deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /* faith */
+ RT_UNLOCK(ip6_forward_rt.ro_rt);
goto hbhcheck;
}
}
#endif
+ if (ip6_forward_rt.ro_rt != NULL)
+ RT_UNLOCK(ip6_forward_rt.ro_rt);
/*
* Now there is no reason to process the packet if it's not our own
* to the upper layers.
*/
}
+ ifafree(&ia6->ia_ifa);
}
}
* ip6_mforward() returns a non-zero value, the packet
* must be discarded, else it may be accepted below.
*/
+#if MROUTING
if (ip6_mrouter && ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) {
ip6stat.ip6s_cantforward++;
m_freem(m);
lck_mtx_unlock(ip6_mutex);
return;
}
+#endif
if (!ours) {
m_freem(m);
lck_mtx_unlock(ip6_mutex);
return;
}
} else if (!ours) {
- ip6_forward(m, 0, 1);
+ ip6_forward(m, &ip6_forward_rt, 0, 1);
lck_mtx_unlock(ip6_mutex);
return;
}
* code - like udp/tcp/raw ip.
*/
if ((ipsec_bypass == 0) && (ip6_protox[nxt]->pr_flags & PR_LASTHDR) != 0) {
- lck_mtx_lock(sadb_mutex);
if (ipsec6_in_reject(m, NULL)) {
- ipsec6stat.in_polvio++;
- lck_mtx_unlock(sadb_mutex);
+ IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio);
goto badunlocked;
- }
- lck_mtx_unlock(sadb_mutex);
+ }
}
#endif
/*
- * Call IP filter on last header only
+ * Call IP filter
*/
- if ((ip6_protox[nxt]->pr_flags & PR_LASTHDR) != 0 && !TAILQ_EMPTY(&ipv6_filters)) {
+ if (!TAILQ_EMPTY(&ipv6_filters)) {
ipf_ref();
TAILQ_FOREACH(filter, &ipv6_filters, ipf_link) {
if (seen == 0) {
* set/grab in6_ifaddr correspond to IPv6 destination address.
* XXX backward compatibility wrapper
*/
-static struct mbuf *
-ip6_setdstifaddr(m, ia6)
- struct mbuf *m;
- struct in6_ifaddr *ia6;
+static struct ip6aux *
+ip6_setdstifaddr(struct mbuf *m, struct in6_ifaddr *ia6)
{
- struct mbuf *n;
+ struct ip6aux *n;
n = ip6_addaux(m);
if (n)
- mtod(n, struct ip6aux *)->ip6a_dstia6 = ia6;
- return n; /* NULL if failed to set */
+ n->ip6a_dstia6 = ia6;
+ return (struct ip6aux *)n; /* NULL if failed to set */
}
struct in6_ifaddr *
ip6_getdstifaddr(m)
struct mbuf *m;
{
- struct mbuf *n;
+ struct ip6aux *n;
n = ip6_findaux(m);
if (n)
- return mtod(n, struct ip6aux *)->ip6a_dstia6;
+ return n->ip6a_dstia6;
else
return NULL;
}
}
#endif
+ /* some OSes call this logic with IPv4 packet, for SO_TIMESTAMP */
+ if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
+ return;
+
/* RFC 2292 sec. 5 */
if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) {
struct in6_pktinfo pi6;
mp = &(*mp)->m_next;
}
+ if ((in6p->in6p_flags & IN6P_TCLASS) != 0) {
+ u_int32_t flowinfo;
+ int tclass;
+
+ flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
+ flowinfo >>= 20;
+
+ tclass = flowinfo & 0xff;
+ *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass),
+ IPV6_TCLASS, IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+
/*
* IPV6_HOPOPTS socket option. Recall that we required super-user
* privilege for the option (see ip6_ctloutput), but it might be too
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (off == sizeof(struct ip6_hdr))
- return(&ip6->ip6_nxt);
+ return((char *) &ip6->ip6_nxt);
else {
int len, nxt;
struct ip6_ext *ip6e = NULL;
nxt = ip6e->ip6e_nxt;
}
if (ip6e)
- return(&ip6e->ip6e_nxt);
+ return((char *) &ip6e->ip6e_nxt);
else
return NULL;
}
}
}
-struct mbuf *
-ip6_addaux(m)
- struct mbuf *m;
+struct ip6aux *
+ip6_addaux(
+ struct mbuf *m)
{
- struct mbuf *n;
-
-#if DIAGNOSTIC
- if (sizeof(struct ip6aux) > MHLEN)
- panic("assumption failed on sizeof(ip6aux)");
-#endif
- n = m_aux_find(m, AF_INET6, -1);
- if (n) {
- if (n->m_len < sizeof(struct ip6aux)) {
- printf("conflicting use of ip6aux");
- return NULL;
- }
- } else {
- n = m_aux_add(m, AF_INET6, -1);
- if (n) {
- n->m_len = sizeof(struct ip6aux);
- bzero(mtod(n, caddr_t), n->m_len);
+ struct m_tag *tag;
+
+ /* Check if one is already allocated */
+ tag = m_tag_locate(m, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_INET6, NULL);
+ if (tag == NULL) {
+ /* Allocate a tag */
+ tag = m_tag_alloc(KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_INET6,
+ sizeof (struct ip6aux), M_DONTWAIT);
+
+ /* Attach it to the mbuf */
+ if (tag) {
+ m_tag_prepend(m, tag);
}
}
- return n;
+
+ return tag ? (struct ip6aux*)(tag + 1) : NULL;
}
-struct mbuf *
-ip6_findaux(m)
- struct mbuf *m;
+struct ip6aux *
+ip6_findaux(
+ struct mbuf *m)
{
- struct mbuf *n;
-
- n = m_aux_find(m, AF_INET6, -1);
- if (n && n->m_len < sizeof(struct ip6aux)) {
- printf("conflicting use of ip6aux");
- n = NULL;
- }
- return n;
+ struct m_tag *tag;
+
+ tag = m_tag_locate(m, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_ENCAP, NULL);
+
+ return tag ? (struct ip6aux*)(tag + 1) : NULL;
}
void
-ip6_delaux(m)
- struct mbuf *m;
+ip6_delaux(
+ struct mbuf *m)
{
- struct mbuf *n;
+ struct m_tag *tag;
- n = m_aux_find(m, AF_INET6, -1);
- if (n)
- m_aux_delete(m, n);
+ tag = m_tag_locate(m, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_ENCAP, NULL);
+ if (tag) {
+ m_tag_delete(m, tag);
+ }
}
/*