Esempio n. 1
0
u_int16_t
inet6_cksum(struct mbuf *m, unsigned int nxt, unsigned int off,
    unsigned int len)
{
	u_int16_t *w;
	int sum = 0;
	int mlen = 0;
	int byte_swapped = 0;
	struct ip6_hdr *ip6;
	union {
		u_int16_t phs[4];
		struct {
			u_int32_t	ph_len;
			u_int8_t	ph_zero[3];
			u_int8_t	ph_nxt;
		} ph __attribute__((__packed__));
	} uph;
	union {
		u_int8_t	c[2];
		u_int16_t	s;
	} s_util;
	union {
		u_int16_t s[2];
		u_int32_t l;
	} l_util;

	/* sanity check */
	if (m->m_pkthdr.len < off + len) {
		panic("inet6_cksum: mbuf len (%d) < off+len (%d+%d)\n",
		    m->m_pkthdr.len, off, len);
	}

	if (nxt != 0) {
		bzero(&uph, sizeof (uph));

		/*
		 * First create IP6 pseudo header and calculate a summary.
		 */
		ip6 = mtod(m, struct ip6_hdr *);
		w = (u_int16_t *)&ip6->ip6_src;
		uph.ph.ph_len = htonl(len);
		uph.ph.ph_nxt = nxt;

		/* IPv6 source address */
		sum += w[0];
		if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
			sum += w[1];
		sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
		sum += w[6]; sum += w[7];
		/* IPv6 destination address */
		sum += w[8];
		if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
			sum += w[9];
		sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13];
		sum += w[14]; sum += w[15];
		/* Payload length and upper layer identifier */
		sum += uph.phs[0];  sum += uph.phs[1];
		sum += uph.phs[2];  sum += uph.phs[3];
	}
Esempio n. 2
0
/*
 * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
 * is unspecified (=0), needs to be specified, and the default zone ID can be
 * used, the default value will be used.
 * This routine then generates the kernel-internal form: if the address scope
 * of is interface-local or link-local, embed the interface index in the
 * address.
 */
int
sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
{
	struct ifnet *ifp;
	u_int32_t zoneid;

	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
		zoneid = scope6_addr2default(&sin6->sin6_addr);

	if (zoneid != 0 &&
	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
		/*
		 * At this moment, we only check interface-local and
		 * link-local scope IDs, and use interface indices as the
		 * zone IDs assuming a one-to-one mapping between interfaces
		 * and links.
		 */
		if (V_if_index < zoneid)
			return (ENXIO);
		ifp = ifnet_byindex(zoneid);
		if (ifp == NULL) /* XXX: this can happen for some OS */
			return (ENXIO);

		/* XXX assignment to 16bit from 32bit variable */
		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);

		sin6->sin6_scope_id = 0;
	}

	return 0;
}
Esempio n. 3
0
/*
 * generate standard sockaddr_in6 from embedded form.
 */
int
sa6_recoverscope(struct sockaddr_in6 *sin6)
{
	char ip6buf[INET6_ADDRSTRLEN];
	u_int32_t zoneid;

	if (sin6->sin6_scope_id != 0) {
		log(LOG_NOTICE,
		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
		/* XXX: proceed anyway... */
	}
	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
		/*
		 * KAME assumption: link id == interface id
		 */
		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
		if (zoneid) {
			/* sanity check */
			if (zoneid < 0 || V_if_index < zoneid)
				return (ENXIO);
			if (!ifnet_byindex(zoneid))
				return (ENXIO);
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = zoneid;
		}
	}

	return 0;
}
Esempio n. 4
0
/*
 * generate standard sockaddr_in6 from embedded form.
 */
int
sa6_recoverscope(struct sockaddr_in6 *sin6)
{
	u_int32_t zoneid;

	if (sin6->sin6_scope_id != 0) {
		log(LOG_NOTICE,
		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
		    ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
		/* XXX: proceed anyway... */
	}
	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
		/*
		 * KAME assumption: link id == interface id
		 */
		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
		if (zoneid) {
			/* sanity check */
			if (zoneid < 0 || if_indexlim <= zoneid)
				return (ENXIO);
#ifdef __FreeBSD__
			if (ifnet_byindex(zoneid) == NULL)
#else
			if (ifindex2ifnet[zoneid] == NULL)
#endif
				return (ENXIO);
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = zoneid;
		}
	}

	return 0;
}
Esempio n. 5
0
/*
 * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
 * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
 * in the in6_addr structure, in6 will be modified. 
 *
 * ret_id - unnecessary?
 */
int
in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
{
	int scope;
	u_int32_t zoneid = 0;
	struct scope6_id *sid = SID(ifp);

#ifdef DIAGNOSTIC
	if (sid == NULL) { /* should not happen */
		panic("in6_setscope: scope array is NULL");
		/* NOTREACHED */
	}
#endif

	/*
	 * special case: the loopback address can only belong to a loopback
	 * interface.
	 */
	if (IN6_IS_ADDR_LOOPBACK(in6)) {
		if (!(ifp->if_flags & IFF_LOOPBACK))
			return (EINVAL);
		else {
			if (ret_id != NULL)
				*ret_id = 0; /* there's no ambiguity */
			return (0);
		}
	}

	scope = in6_addrscope(in6);

	switch (scope) {
	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
		break;

	case IPV6_ADDR_SCOPE_LINKLOCAL:
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
		break;

	case IPV6_ADDR_SCOPE_SITELOCAL:
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
		break;

	case IPV6_ADDR_SCOPE_ORGLOCAL:
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
		break;

	default:
		zoneid = 0;	/* XXX: treat as global. */
		break;
	}

	if (ret_id != NULL)
		*ret_id = zoneid;

	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */

	return (0);
}
Esempio n. 6
0
/*
 * generate standard sockaddr_in6 from embedded form.
 */
int
sa6_recoverscope(struct sockaddr_in6 *sin6)
{
	uint32_t zoneid;

	if (sin6->sin6_scope_id != 0) {
		log(LOG_NOTICE,
		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
		    ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
		/* XXX: proceed anyway... */
	}
	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
		/*
		 * KAME assumption: link id == interface id
		 */
		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
		if (zoneid) {
			int s = pserialize_read_enter();
			if (!if_byindex(zoneid)) {
				pserialize_read_exit(s);
				return (ENXIO);
			}
			pserialize_read_exit(s);
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = zoneid;
		}
	}

	return 0;
}
Esempio n. 7
0
/*
 * Return the scope identifier or zero.
 */
uint16_t
in6_getscope(struct in6_addr *in6)
{

	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
		return (in6->s6_addr16[1]);

	return (0);
}
Esempio n. 8
0
/*
 * Just clear the embedded scope identifier.  Return 0 if the original address
 * is intact; return non 0 if the address is modified.
 */
int
in6_clearscope(struct in6_addr *in6)
{
	int modified = 0;

	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
		if (in6->s6_addr16[1] != 0)
			modified = 1;
		in6->s6_addr16[1] = 0;
	}

	return (modified);
}
Esempio n. 9
0
/*
 * Performs IPv6 route table lookup on @dst. Returns 0 on success.
 * Stores extended nexthop info into provided @pnh6 structure.
 * Note that
 * - nh_ifp cannot be safely dereferenced unless NHR_REF is specified.
 * - in that case you need to call fib6_free_nh_ext()
 * - nh_ifp represents logical transmit interface (rt_ifp) by default
 * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
 * - mtu from logical transmit interface will be returned.
 * - scope will be embedded in nh_addr
 */
int
fib6_lookup_nh_ext(uint32_t fibnum, const struct in6_addr *dst,uint32_t scopeid,
    uint32_t flags, uint32_t flowid, struct nhop6_extended *pnh6)
{
	struct rib_head *rh;
	struct radix_node *rn;
	struct sockaddr_in6 sin6;
	struct rtentry *rte;

	KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_ext: bad fibnum"));
	rh = rt_tables_get_rnh(fibnum, AF_INET6);
	if (rh == NULL)
		return (ENOENT);

	/* Prepare lookup key */
	memset(&sin6, 0, sizeof(sin6));
	sin6.sin6_len = sizeof(struct sockaddr_in6);
	sin6.sin6_addr = *dst;
	/* Assume scopeid is valid and embed it directly */
	if (IN6_IS_SCOPE_LINKLOCAL(dst))
		sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);

	RIB_RLOCK(rh);
	rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
		rte = RNTORT(rn);
#ifdef RADIX_MPATH
		rte = rt_mpath_select(rte, flowid);
		if (rte == NULL) {
			RIB_RUNLOCK(rh);
			return (ENOENT);
		}
#endif
		/* Ensure route & ifp is UP */
		if (RT_LINK_IS_UP(rte->rt_ifp)) {
			fib6_rte_to_nh_extended(rte, &sin6.sin6_addr, flags,
			    pnh6);
			if ((flags & NHR_REF) != 0) {
				/* TODO: Do lwref on egress ifp's */
			}
			RIB_RUNLOCK(rh);

			return (0);
		}
	}
	RIB_RUNLOCK(rh);

	return (ENOENT);
}
Esempio n. 10
0
File: in6_pcb.c Progetto: Prajna/xnu
struct sockaddr *
in6_sockaddr(
	in_port_t port,
	struct in6_addr *addr_p)
{
	struct sockaddr_in6 *sin6;

	MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK);
	if (sin6 == NULL)
		return NULL;
	bzero(sin6, sizeof *sin6);
	sin6->sin6_family = AF_INET6;
	sin6->sin6_len = sizeof(*sin6);
	sin6->sin6_port = port;
	sin6->sin6_addr = *addr_p;
	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
		sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
	else
		sin6->sin6_scope_id = 0;	/*XXX*/
	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
		sin6->sin6_addr.s6_addr16[1] = 0;

	return (struct sockaddr *)sin6;
}
Esempio n. 11
0
/*
 * generate standard sockaddr_in6 from embedded form.
 */
int
sa6_recoverscope(struct sockaddr_in6 *sin6, boolean_t attachcheck)
{
	u_int32_t zoneid;

	if (sin6->sin6_scope_id != 0) {
		log(LOG_NOTICE,
		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
		    ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
		/* XXX: proceed anyway... */
	}
	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
		/*
		 * KAME assumption: link id == interface id
		 */
		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
		if (zoneid) {
			/* sanity check */
			if (if_index < zoneid)
				return (ENXIO);
			/*
			 * We use the attachcheck parameter to skip the
			 * interface attachment check.
			 * Some callers might hold the ifnet_head lock in
			 * exclusive mode. This means that:
			 * 1) the interface can't go away -- hence we don't
			 *    need to perform this check
			 * 2) we can't perform this check because the lock is
			 *    in exclusive mode and trying to lock it in shared
			 *    mode would cause a deadlock.
			 */
			if (attachcheck) {
				ifnet_head_lock_shared();
				if (ifindex2ifnet[zoneid] == NULL) {
					ifnet_head_done();
					return (ENXIO);
				}
				ifnet_head_done();
			}
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = zoneid;
		}
	}

	return (0);
}
Esempio n. 12
0
/*
 * Performs IPv6 route table lookup on @dst. Returns 0 on success.
 * Stores basic nexthop info into provided @pnh6 structure.
 * Note that
 * - nh_ifp represents logical transmit interface (rt_ifp) by default
 * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
 * - mtu from logical transmit interface will be returned.
 * - nh_ifp cannot be safely dereferenced
 * - nh_ifp represents rt_ifp (e.g. if looking up address on
 *   interface "ix0" pointer to "ix0" interface will be returned instead
 *   of "lo0")
 * - howewer mtu from "transmit" interface will be returned.
 * - scope will be embedded in nh_addr
 */
int
fib6_lookup_nh_basic(uint32_t fibnum, const struct in6_addr *dst, uint32_t scopeid,
    uint32_t flags, uint32_t flowid, struct nhop6_basic *pnh6)
{
	struct radix_node_head *rh;
	struct radix_node *rn;
	struct sockaddr_in6 sin6;
	struct rtentry *rte;

	KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_basic: bad fibnum"));
	rh = rt_tables_get_rnh(fibnum, AF_INET6);
	if (rh == NULL)
		return (ENOENT);

	/* Prepare lookup key */
	memset(&sin6, 0, sizeof(sin6));
	sin6.sin6_addr = *dst;
	sin6.sin6_len = sizeof(struct sockaddr_in6);
	/* Assume scopeid is valid and embed it directly */
	if (IN6_IS_SCOPE_LINKLOCAL(dst))
		sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);

	RADIX_NODE_HEAD_RLOCK(rh);
	rn = rh->rnh_matchaddr((void *)&sin6, rh);
	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
		rte = RNTORT(rn);
		/* Ensure route & ifp is UP */
		if (RT_LINK_IS_UP(rte->rt_ifp)) {
			fib6_rte_to_nh_basic(rte, &sin6.sin6_addr, flags, pnh6);
			RADIX_NODE_HEAD_RUNLOCK(rh);
			return (0);
		}
	}
	RADIX_NODE_HEAD_RUNLOCK(rh);

	return (ENOENT);
}
Esempio n. 13
0
/*
 * generate standard sockaddr_in6 from embedded form.
 */
int
sa6_recoverscope(struct sockaddr_in6 *sin6)
{
	char ip6buf[INET6_ADDRSTRLEN];
	u_int32_t zoneid;

	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
		/*
		 * KAME assumption: link id == interface id
		 */
		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
		if (zoneid) {
			/* sanity check */
			if (V_if_index < zoneid)
				return (ENXIO);
#if 0
			/* XXX: Disabled due to possible deadlock. */
			if (!ifnet_byindex(zoneid))
				return (ENXIO);
#endif
			if (sin6->sin6_scope_id != 0 &&
			    zoneid != sin6->sin6_scope_id) {
				log(LOG_NOTICE,
				    "%s: embedded scope mismatch: %s%%%d. "
				    "sin6_scope_id was overridden.", __func__,
				    ip6_sprintf(ip6buf, &sin6->sin6_addr),
				    sin6->sin6_scope_id);
			}
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = zoneid;
		}
	}

	return 0;
}
Esempio n. 14
0
static struct ipsecrequest *
ipsec_nextisr(
	struct mbuf *m,
	struct ipsecrequest *isr,
	int af,
	struct secasindex *saidx,
	int *error
)
{
#define	IPSEC_OSTAT(name)	do {		\
	if (isr->saidx.proto == IPPROTO_ESP)	\
		ESPSTAT_INC(esps_##name);	\
	else if (isr->saidx.proto == IPPROTO_AH)\
		AHSTAT_INC(ahs_##name);		\
	else					\
		IPCOMPSTAT_INC(ipcomps_##name);	\
} while (0)
	struct secasvar *sav;

	IPSECREQUEST_LOCK_ASSERT(isr);

	IPSEC_ASSERT(af == AF_INET || af == AF_INET6,
		("invalid address family %u", af));
again:
	/*
	 * Craft SA index to search for proper SA.  Note that
	 * we only fillin unspecified SA peers for transport
	 * mode; for tunnel mode they must already be filled in.
	 */
	*saidx = isr->saidx;
	if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
		/* Fillin unspecified SA peers only for transport mode */
		if (af == AF_INET) {
			struct sockaddr_in *sin;
			struct ip *ip = mtod(m, struct ip *);

			if (saidx->src.sa.sa_len == 0) {
				sin = &saidx->src.sin;
				sin->sin_len = sizeof(*sin);
				sin->sin_family = AF_INET;
				sin->sin_port = IPSEC_PORT_ANY;
				sin->sin_addr = ip->ip_src;
			}
			if (saidx->dst.sa.sa_len == 0) {
				sin = &saidx->dst.sin;
				sin->sin_len = sizeof(*sin);
				sin->sin_family = AF_INET;
				sin->sin_port = IPSEC_PORT_ANY;
				sin->sin_addr = ip->ip_dst;
			}
		} else {
			struct sockaddr_in6 *sin6;
			struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);

			if (saidx->src.sin6.sin6_len == 0) {
				sin6 = (struct sockaddr_in6 *)&saidx->src;
				sin6->sin6_len = sizeof(*sin6);
				sin6->sin6_family = AF_INET6;
				sin6->sin6_port = IPSEC_PORT_ANY;
				sin6->sin6_addr = ip6->ip6_src;
				if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
					/* fix scope id for comparing SPD */
					sin6->sin6_addr.s6_addr16[1] = 0;
					sin6->sin6_scope_id =
					    ntohs(ip6->ip6_src.s6_addr16[1]);
				}
			}
			if (saidx->dst.sin6.sin6_len == 0) {
				sin6 = (struct sockaddr_in6 *)&saidx->dst;
				sin6->sin6_len = sizeof(*sin6);
				sin6->sin6_family = AF_INET6;
				sin6->sin6_port = IPSEC_PORT_ANY;
				sin6->sin6_addr = ip6->ip6_dst;
				if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
					/* fix scope id for comparing SPD */
					sin6->sin6_addr.s6_addr16[1] = 0;
					sin6->sin6_scope_id =
					    ntohs(ip6->ip6_dst.s6_addr16[1]);
				}
			}
		}
	}
Esempio n. 15
0
/*
 * Massage IPv4/IPv6 headers for AH processing.
 */
static int
ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
{
	struct mbuf *m = *m0;
	unsigned char *ptr;
	int off, count;

#ifdef INET
	struct ip *ip;
#endif /* INET */

#ifdef INET6
	struct ip6_ext *ip6e;
	struct ip6_hdr ip6;
	int alloc, ad, nxt;
#endif /* INET6 */

	switch (proto) {
#ifdef INET
	case AF_INET:
		/*
		 * This is the least painful way of dealing with IPv4 header
		 * and option processing -- just make sure they're in
		 * contiguous memory.
		 */
		*m0 = m = m_pullup(m, skip);
		if (m == NULL) {
			DPRINTF(("ah_massage_headers: m_pullup failed\n"));
			return ENOBUFS;
		}

		/* Fix the IP header */
		ip = mtod(m, struct ip *);
		if (ip4_ah_cleartos)
			ip->ip_tos = 0;
		ip->ip_ttl = 0;
		ip->ip_sum = 0;
		ip->ip_off = htons(ntohs(ip->ip_off) & ip4_ah_offsetmask);

		/*
		 * On FreeBSD, ip_off and ip_len assumed in host endian;
		 * they are converted (if necessary) by ip_input().
		 * On NetBSD, ip_off and ip_len are in network byte order.
		 * They must be massaged back to network byte order
		 * before verifying the  HMAC. Moreover, on FreeBSD,
		 * we should add `skip' back into the massaged ip_len
		 * (presumably ip_input() deducted it before we got here?)
		 * whereas on NetBSD, we should not.
		 */
#ifdef __FreeBSD__
  #define TOHOST(x) (x)
#else
  #define TOHOST(x) (ntohs(x))
#endif
		if (!out) {
			u_int16_t inlen = TOHOST(ip->ip_len);

#ifdef __FreeBSD__
			ip->ip_len = htons(inlen + skip);
#else  /*!__FreeBSD__ */
			ip->ip_len = htons(inlen);
#endif /*!__FreeBSD__ */

			if (alg == CRYPTO_MD5_KPDK || alg == CRYPTO_SHA1_KPDK)
				ip->ip_off  &= IP_OFF_CONVERT(IP_DF);
			else
				ip->ip_off = 0;
		} else {
			if (alg == CRYPTO_MD5_KPDK || alg == CRYPTO_SHA1_KPDK)
				ip->ip_off &= IP_OFF_CONVERT(IP_DF);
			else
				ip->ip_off = 0;
		}

		ptr = mtod(m, unsigned char *);

		/* IPv4 option processing */
		for (off = sizeof(struct ip); off < skip;) {
			if (ptr[off] == IPOPT_EOL || ptr[off] == IPOPT_NOP ||
			    off + 1 < skip)
				;
			else {
				DPRINTF(("ah_massage_headers: illegal IPv4 "
				    "option length for option %d\n",
				    ptr[off]));

				m_freem(m);
				return EINVAL;
			}

			switch (ptr[off]) {
			case IPOPT_EOL:
				off = skip;  /* End the loop. */
				break;

			case IPOPT_NOP:
				off++;
				break;

			case IPOPT_SECURITY:	/* 0x82 */
			case 0x85:	/* Extended security. */
			case 0x86:	/* Commercial security. */
			case 0x94:	/* Router alert */
			case 0x95:	/* RFC1770 */
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("ah_massage_headers: "
					    "illegal IPv4 option length for "
					    "option %d\n", ptr[off]));

					m_freem(m);
					return EINVAL;
				}

				off += ptr[off + 1];
				break;

			case IPOPT_LSRR:
			case IPOPT_SSRR:
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("ah_massage_headers: "
					    "illegal IPv4 option length for "
					    "option %d\n", ptr[off]));

					m_freem(m);
					return EINVAL;
				}

				/*
				 * On output, if we have either of the
				 * source routing options, we should
				 * swap the destination address of the
				 * IP header with the last address
				 * specified in the option, as that is
				 * what the destination's IP header
				 * will look like.
				 */
				if (out)
					bcopy(ptr + off + ptr[off + 1] -
					    sizeof(struct in_addr),
					    &(ip->ip_dst), sizeof(struct in_addr));

				/* Fall through */
			default:
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("ah_massage_headers: "
					    "illegal IPv4 option length for "
					    "option %d\n", ptr[off]));
					m_freem(m);
					return EINVAL;
				}

				/* Zeroize all other options. */
				count = ptr[off + 1];
				memcpy(ptr + off, ipseczeroes, count);
				off += count;
				break;
			}

			/* Sanity check. */
			if (off > skip)	{
				DPRINTF(("ah_massage_headers(): malformed "
				    "IPv4 options header\n"));

				m_freem(m);
				return EINVAL;
			}
		}

		break;
#endif /* INET */

#ifdef INET6
	case AF_INET6:  /* Ugly... */
		/* Copy and "cook" the IPv6 header. */
		m_copydata(m, 0, sizeof(ip6), &ip6);

		/* We don't do IPv6 Jumbograms. */
		if (ip6.ip6_plen == 0) {
			DPRINTF(("ah_massage_headers: unsupported IPv6 jumbogram\n"));
			m_freem(m);
			return EMSGSIZE;
		}

		ip6.ip6_flow = 0;
		ip6.ip6_hlim = 0;
		ip6.ip6_vfc &= ~IPV6_VERSION_MASK;
		ip6.ip6_vfc |= IPV6_VERSION;

		/* Scoped address handling. */
		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;

		/* Done with IPv6 header. */
		m_copyback(m, 0, sizeof(struct ip6_hdr), &ip6);

		/* Let's deal with the remaining headers (if any). */
		if (skip - sizeof(struct ip6_hdr) > 0) {
			if (m->m_len <= skip) {
				ptr = (unsigned char *) malloc(
				    skip - sizeof(struct ip6_hdr),
				    M_XDATA, M_NOWAIT);
				if (ptr == NULL) {
					DPRINTF(("ah_massage_headers: failed "
					    "to allocate memory for IPv6 "
					    "headers\n"));
					m_freem(m);
					return ENOBUFS;
				}

				/*
				 * Copy all the protocol headers after
				 * the IPv6 header.
				 */
				m_copydata(m, sizeof(struct ip6_hdr),
				    skip - sizeof(struct ip6_hdr), ptr);
				alloc = 1;
			} else {
				/* No need to allocate memory. */
				ptr = mtod(m, unsigned char *) +
				    sizeof(struct ip6_hdr);
				alloc = 0;
			}
		} else
			break;

		nxt = ip6.ip6_nxt & 0xff; /* Next header type. */

		for (off = 0; off < skip - sizeof(struct ip6_hdr);)
			switch (nxt) {
			case IPPROTO_HOPOPTS:
			case IPPROTO_DSTOPTS:
				ip6e = (struct ip6_ext *) (ptr + off);

				/*
				 * Process the mutable/immutable
				 * options -- borrows heavily from the
				 * KAME code.
				 */
				for (count = off + sizeof(struct ip6_ext);
				     count < off + ((ip6e->ip6e_len + 1) << 3);) {
					if (ptr[count] == IP6OPT_PAD1) {
						count++;
						continue; /* Skip padding. */
					}

					/* Sanity check. */
					if (count > off +
					    ((ip6e->ip6e_len + 1) << 3)) {
						m_freem(m);

						/* Free, if we allocated. */
						if (alloc)
							free(ptr, M_XDATA);
						return EINVAL;
					}

					ad = ptr[count + 1];

					/* If mutable option, zeroize. */
					if (ptr[count] & IP6OPT_MUTABLE)
						memcpy(ptr + count, ipseczeroes,
						    ptr[count + 1]);

					count += ad;

					/* Sanity check. */
					if (count >
					    skip - sizeof(struct ip6_hdr)) {
						m_freem(m);

						/* Free, if we allocated. */
						if (alloc)
							free(ptr, M_XDATA);
						return EINVAL;
					}
				}

				/* Advance. */
				off += ((ip6e->ip6e_len + 1) << 3);
				nxt = ip6e->ip6e_nxt;
				break;

			case IPPROTO_ROUTING:
				/*
				 * Always include routing headers in
				 * computation.
				 */
				{
					struct ip6_rthdr *rh;

					ip6e = (struct ip6_ext *) (ptr + off);
					rh = (struct ip6_rthdr *)(ptr + off);
					/*
					 * must adjust content to make it look like
					 * its final form (as seen at the final
					 * destination).
					 * we only know how to massage type 0 routing
					 * header.
					 */
					if (out && rh->ip6r_type == IPV6_RTHDR_TYPE_0) {
						struct ip6_rthdr0 *rh0;
						struct in6_addr *addr, finaldst;
						int i;

						rh0 = (struct ip6_rthdr0 *)rh;
						addr = (struct in6_addr *)(rh0 + 1);

						for (i = 0; i < rh0->ip6r0_segleft; i++)
							in6_clearscope(&addr[i]);

						finaldst = addr[rh0->ip6r0_segleft - 1];
						memmove(&addr[1], &addr[0],
							sizeof(struct in6_addr) *
							(rh0->ip6r0_segleft - 1));

						m_copydata(m, 0, sizeof(ip6), &ip6);
						addr[0] = ip6.ip6_dst;
						ip6.ip6_dst = finaldst;
						m_copyback(m, 0, sizeof(ip6), &ip6);

						rh0->ip6r0_segleft = 0;
					}

					/* advance */
					off += ((ip6e->ip6e_len + 1) << 3);
					nxt = ip6e->ip6e_nxt;
					break;
				}

			default:
				DPRINTF(("ah_massage_headers: unexpected "
				    "IPv6 header type %d", off));
				if (alloc)
					free(ptr, M_XDATA);
				m_freem(m);
				return EINVAL;
			}

		/* Copyback and free, if we allocated. */
		if (alloc) {
			m_copyback(m, sizeof(struct ip6_hdr),
			    skip - sizeof(struct ip6_hdr), ptr);
			free(ptr, M_XDATA);
		}

		break;
#endif /* INET6 */
	}
Esempio n. 16
0
/*
 * ipsec_common_input gets called when an IPsec-protected packet
 * is received by IPv4 or IPv6.  Its job is to find the right SA
 * and call the appropriate transform.  The transform callback
 * takes care of further processing (like ingress filtering).
 */
int
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
{
	char buf[INET6_ADDRSTRLEN];
	union sockaddr_union dst_address;
	struct secasvar *sav;
	u_int32_t spi;
	int error;
#ifdef INET
#ifdef IPSEC_NAT_T
	struct m_tag *tag;
#endif
#endif

	IPSEC_ISTAT(sproto, input);

	IPSEC_ASSERT(m != NULL, ("null packet"));

	IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
		sproto == IPPROTO_IPCOMP,
		("unexpected security protocol %u", sproto));

	if ((sproto == IPPROTO_ESP && !V_esp_enable) ||
	    (sproto == IPPROTO_AH && !V_ah_enable) ||
	    (sproto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
		m_freem(m);
		IPSEC_ISTAT(sproto, pdrops);
		return EOPNOTSUPP;
	}

	if (m->m_pkthdr.len - skip < 2 * sizeof (u_int32_t)) {
		m_freem(m);
		IPSEC_ISTAT(sproto, hdrops);
		DPRINTF(("%s: packet too small\n", __func__));
		return EINVAL;
	}

	/* Retrieve the SPI from the relevant IPsec header */
	if (sproto == IPPROTO_ESP)
		m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
	else if (sproto == IPPROTO_AH)
		m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
		    (caddr_t) &spi);
	else if (sproto == IPPROTO_IPCOMP) {
		u_int16_t cpi;
		m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
		    (caddr_t) &cpi);
		spi = ntohl(htons(cpi));
	}

	/*
	 * Find the SA and (indirectly) call the appropriate
	 * kernel crypto routine. The resulting mbuf chain is a valid
	 * IP packet ready to go through input processing.
	 */
	bzero(&dst_address, sizeof (dst_address));
	dst_address.sa.sa_family = af;
	switch (af) {
#ifdef INET
	case AF_INET:
		dst_address.sin.sin_len = sizeof(struct sockaddr_in);
		m_copydata(m, offsetof(struct ip, ip_dst),
		    sizeof(struct in_addr),
		    (caddr_t) &dst_address.sin.sin_addr);
#ifdef IPSEC_NAT_T
		/* Find the source port for NAT-T; see udp*_espdecap. */
		tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL);
		if (tag != NULL)
			dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1];
#endif /* IPSEC_NAT_T */
		break;
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
		m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
		    sizeof(struct in6_addr),
		    (caddr_t) &dst_address.sin6.sin6_addr);
		/* We keep addresses in SADB without embedded scope id */
		if (IN6_IS_SCOPE_LINKLOCAL(&dst_address.sin6.sin6_addr)) {
			/* XXX: sa6_recoverscope() */
			dst_address.sin6.sin6_scope_id =
			    ntohs(dst_address.sin6.sin6_addr.s6_addr16[1]);
			dst_address.sin6.sin6_addr.s6_addr16[1] = 0;
		}
		break;
#endif /* INET6 */
	default:
		DPRINTF(("%s: unsupported protocol family %u\n", __func__, af));
		m_freem(m);
		IPSEC_ISTAT(sproto, nopf);
		return EPFNOSUPPORT;
	}

	/* NB: only pass dst since key_allocsa follows RFC2401 */
	sav = KEY_ALLOCSA(&dst_address, sproto, spi);
	if (sav == NULL) {
		DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n",
		    __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
		    (u_long) ntohl(spi), sproto));
		IPSEC_ISTAT(sproto, notdb);
		m_freem(m);
		return ENOENT;
	}

	if (sav->tdb_xform == NULL) {
		DPRINTF(("%s: attempted to use uninitialized SA %s/%08lx/%u\n",
		    __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
		    (u_long) ntohl(spi), sproto));
		IPSEC_ISTAT(sproto, noxform);
		KEY_FREESAV(&sav);
		m_freem(m);
		return ENXIO;
	}

	/*
	 * Call appropriate transform and return -- callback takes care of
	 * everything else.
	 */
	error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff);
	KEY_FREESAV(&sav);
	return error;
}
Esempio n. 17
0
/*
 * Input an Neighbor Solicitation Message.
 *
 * Based on RFC 2461
 * Based on RFC 2462 (duplicated address detection)
 */
void
nd6_ns_input(struct mbuf *m, int off, int icmp6len)
{
	struct ifnet *ifp = m->m_pkthdr.rcvif;
	struct ifnet *cmpifp;
	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
	struct nd_neighbor_solicit *nd_ns;
	struct in6_addr saddr6 = ip6->ip6_src;
	struct in6_addr daddr6 = ip6->ip6_dst;
	struct in6_addr taddr6;
	struct in6_addr myaddr6;
	char *lladdr = NULL;
	struct ifaddr *ifa = NULL;
	int lladdrlen = 0;
	int anycast = 0, proxy = 0, tentative = 0;
	int tlladdr;
	union nd_opts ndopts;
	struct sockaddr_dl *proxydl = NULL;

	/*
	 * Collapse interfaces to the bridge for comparison and
	 * mac (llinfo) purposes.
	 */
	cmpifp = ifp;
	if (ifp->if_bridge)
		cmpifp = ifp->if_bridge;

#ifndef PULLDOWN_TEST
	IP6_EXTHDR_CHECK(m, off, icmp6len,);
	nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
#else
	IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
	if (nd_ns == NULL) {
		icmp6stat.icp6s_tooshort++;
		return;
	}
#endif
	ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
	taddr6 = nd_ns->nd_ns_target;

	if (ip6->ip6_hlim != 255) {
		nd6log((LOG_ERR,
		    "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n",
		    ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
		    ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
		goto bad;
	}

	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
		/* dst has to be solicited node multicast address. */
		if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL &&
		    /* don't check ifindex portion */
		    daddr6.s6_addr32[1] == 0 &&
		    daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE &&
		    daddr6.s6_addr8[12] == 0xff) {
			; /* good */
		} else {
			nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
			    "(wrong ip6 dst)\n"));
			goto bad;
		}
	} else if (!nd6_onlink_ns_rfc4861) {
		/*
		 * Make sure the source address is from a neighbor's address.
		 *
		 * XXX probably only need to check cmpifp.
		 */
		if (in6ifa_ifplocaladdr(cmpifp, &saddr6) == NULL &&
		    in6ifa_ifplocaladdr(ifp, &saddr6) == NULL) {
			nd6log((LOG_INFO, "nd6_ns_input: "
			    "NS packet from non-neighbor\n"));
			goto bad;
		}
	}

	if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
		nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"));
		goto bad;
	}

	if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
		taddr6.s6_addr16[1] = htons(ifp->if_index);

	icmp6len -= sizeof(*nd_ns);
	nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
	if (nd6_options(&ndopts) < 0) {
		nd6log((LOG_INFO,
		    "nd6_ns_input: invalid ND option, ignored\n"));
		/* nd6_options have incremented stats */
		goto freeit;
	}

	if (ndopts.nd_opts_src_lladdr) {
		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
	}

	if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
		nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
		    "(link-layer address option)\n"));
		goto bad;
	}

	/*
	 * Attaching target link-layer address to the NA?
	 * (RFC 2461 7.2.4)
	 *
	 * NS IP dst is unicast/anycast			MUST NOT add
	 * NS IP dst is solicited-node multicast	MUST add
	 *
	 * In implementation, we add target link-layer address by default.
	 * We do not add one in MUST NOT cases.
	 */
#if 0 /* too much! */
	ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6);
	if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST))
		tlladdr = 0;
	else
#endif
	if (!IN6_IS_ADDR_MULTICAST(&daddr6))
		tlladdr = 0;
	else
		tlladdr = 1;

	/*
	 * Target address (taddr6) must be either:
	 * (1) Valid unicast/anycast address for my receiving interface.
	 * (2) Unicast or anycast address for which I'm offering proxy
	 *     service.
	 * (3) "tentative" address on which DAD is being performed.
	 */
	/* (1) and (3) check. */
#ifdef CARP
	if (ifp->if_carp)
		ifa = carp_iamatch6(ifp->if_carp, &taddr6);
	if (!ifa)
		ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
#else
	ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
#endif

	/*
	 * (2) Check proxying.  Requires ip6_forwarding to be turned on.
	 *
	 *     If the packet is anycast the target route must be on a
	 *     different interface because the anycast will get anything
	 *     on the current interface.
	 *
	 *     If the packet is unicast the target route may be on the
	 *     same interface.  If the gateway is a (typically manually
	 *     configured) link address we can directly offer it.
	 *     XXX for now we don't do this but instead offer ours and
	 *     presumably relay.
	 *
	 *     WARNING! Since this is a subnet proxy the interface proxying
	 *     the ND6 must be in promiscuous mode or it will not see the
	 *     solicited multicast requests for various hosts being proxied.
	 *
	 *     WARNING! Since this is a subnet proxy we have to treat bridge
	 *     interfaces as being the bridge itself so we do not proxy-nd6
	 *     between bridge interfaces (which are effectively switched).
	 *
	 *     (In the specific-host-proxy case via RTF_ANNOUNCE, which is
	 *     a bitch to configure, a specific multicast route is already
	 *     added for that host <-- NOT RECOMMENDED).
	 */
	if (!ifa && ip6_forwarding) {
		struct rtentry *rt;
		struct sockaddr_in6 tsin6;
		struct ifnet *rtifp;

		bzero(&tsin6, sizeof tsin6);
		tsin6.sin6_len = sizeof(struct sockaddr_in6);
		tsin6.sin6_family = AF_INET6;
		tsin6.sin6_addr = taddr6;

		rt = rtpurelookup((struct sockaddr *)&tsin6);
		rtifp = rt ? rt->rt_ifp : NULL;
		if (rtifp && rtifp->if_bridge)
			rtifp = rtifp->if_bridge;

		if (rt != NULL &&
		    (cmpifp != rtifp ||
		     (cmpifp == rtifp && (m->m_flags & M_MCAST) == 0))
		) {
			ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(cmpifp,
				IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
			nd6log((LOG_INFO,
			       "nd6_ns_input: nd6 proxy %s(%s)<-%s ifa %p\n",
			       if_name(cmpifp), if_name(ifp),
			       if_name(rtifp), ifa));
			if (ifa) {
				proxy = 1;
				/*
				 * Manual link address on same interface
				 * w/announce flag will proxy-arp using
				 * target mac, else our mac is used.
				 */
				if (cmpifp == rtifp &&
				    (rt->rt_flags & RTF_ANNOUNCE) &&
				    rt->rt_gateway->sa_family == AF_LINK) {
					proxydl = SDL(rt->rt_gateway);
				}
			}
		}
		if (rt != NULL)
			--rt->rt_refcnt;
	}
	if (ifa == NULL) {
		/*
		 * We've got an NS packet, and we don't have that adddress
		 * assigned for us.  We MUST silently ignore it.
		 * See RFC2461 7.2.3.
		 */
		goto freeit;
	}
	myaddr6 = *IFA_IN6(ifa);
	anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
	tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
	if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
		goto freeit;

	if (lladdr && ((cmpifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
		nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s "
		    "(if %d, NS packet %d)\n",
		    ip6_sprintf(&taddr6), cmpifp->if_addrlen, lladdrlen - 2));
		goto bad;
	}

	if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
		nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n",
		    ip6_sprintf(&saddr6)));
		goto freeit;
	}

	/*
	 * We have neighbor solicitation packet, with target address equals to
	 * one of my tentative address.
	 *
	 * src addr	how to process?
	 * ---		---
	 * multicast	of course, invalid (rejected in ip6_input)
	 * unicast	somebody is doing address resolution -> ignore
	 * unspec	dup address detection
	 *
	 * The processing is defined in RFC 2462.
	 */
	if (tentative) {
		/*
		 * If source address is unspecified address, it is for
		 * duplicated address detection.
		 *
		 * If not, the packet is for addess resolution;
		 * silently ignore it.
		 */
		if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
			nd6_dad_ns_input(ifa);

		goto freeit;
	}

	/*
	 * If the source address is unspecified address, entries must not
	 * be created or updated.
	 * It looks that sender is performing DAD.  Output NA toward
	 * all-node multicast address, to tell the sender that I'm using
	 * the address.
	 * S bit ("solicited") must be zero.
	 */
	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
		saddr6 = kin6addr_linklocal_allnodes;
		saddr6.s6_addr16[1] = htons(cmpifp->if_index);
		nd6_na_output(cmpifp, &saddr6, &taddr6,
		    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
		    (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
		    tlladdr, (struct sockaddr *)proxydl);
		goto freeit;
	}

	nd6_cache_lladdr(cmpifp, &saddr6, lladdr, lladdrlen,
	    ND_NEIGHBOR_SOLICIT, 0);

	nd6_na_output(ifp, &saddr6, &taddr6,
	    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
	    (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
	    tlladdr, (struct sockaddr *)proxydl);
freeit:
	m_freem(m);
	return;

bad:
	nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)));
	nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)));
	nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)));
	icmp6stat.icp6s_badns++;
	m_freem(m);
}
Esempio n. 18
0
static void
ipsec_setsockaddrs_inpcb(struct inpcb *inp, union sockaddr_union *src,
    union sockaddr_union *dst, u_int dir)
{

#ifdef INET6
	if (inp->inp_vflag & INP_IPV6) {
		struct sockaddr_in6 *sin6;

		bzero(&src->sin6, sizeof(src->sin6));
		bzero(&dst->sin6, sizeof(dst->sin6));
		src->sin6.sin6_family = AF_INET6;
		src->sin6.sin6_len = sizeof(struct sockaddr_in6);
		dst->sin6.sin6_family = AF_INET6;
		dst->sin6.sin6_len = sizeof(struct sockaddr_in6);

		if (dir == IPSEC_DIR_OUTBOUND)
			sin6 = &src->sin6;
		else
			sin6 = &dst->sin6;
		sin6->sin6_addr = inp->in6p_laddr;
		sin6->sin6_port = inp->inp_lport;
		if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_laddr)) {
			/* XXXAE: use in6p_zoneid */
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = ntohs(
			    inp->in6p_laddr.s6_addr16[1]);
		}

		if (dir == IPSEC_DIR_OUTBOUND)
			sin6 = &dst->sin6;
		else
			sin6 = &src->sin6;
		sin6->sin6_addr = inp->in6p_faddr;
		sin6->sin6_port = inp->inp_fport;
		if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_faddr)) {
			/* XXXAE: use in6p_zoneid */
			sin6->sin6_addr.s6_addr16[1] = 0;
			sin6->sin6_scope_id = ntohs(
			    inp->in6p_faddr.s6_addr16[1]);
		}
	}
#endif
#ifdef INET
	if (inp->inp_vflag & INP_IPV4) {
		struct sockaddr_in *sin;

		bzero(&src->sin, sizeof(src->sin));
		bzero(&dst->sin, sizeof(dst->sin));
		src->sin.sin_family = AF_INET;
		src->sin.sin_len = sizeof(struct sockaddr_in);
		dst->sin.sin_family = AF_INET;
		dst->sin.sin_len = sizeof(struct sockaddr_in);

		if (dir == IPSEC_DIR_OUTBOUND)
			sin = &src->sin;
		else
			sin = &dst->sin;
		sin->sin_addr = inp->inp_laddr;
		sin->sin_port = inp->inp_lport;

		if (dir == IPSEC_DIR_OUTBOUND)
			sin = &dst->sin;
		else
			sin = &src->sin;
		sin->sin_addr = inp->inp_faddr;
		sin->sin_port = inp->inp_fport;
	}
#endif
}
Esempio n. 19
0
/*
 * Massage IPv4/IPv6 headers for AH processing.
 */
static int
ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
{
	struct mbuf *m = *m0;
	unsigned char *ptr;
	int off, count;

#ifdef INET
	struct ip *ip;
#endif /* INET */

#ifdef INET6
	struct ip6_ext *ip6e;
	struct ip6_hdr ip6;
	int alloc, len, ad;
#endif /* INET6 */

	switch (proto) {
#ifdef INET
	case AF_INET:
		/*
		 * This is the least painful way of dealing with IPv4 header
		 * and option processing -- just make sure they're in
		 * contiguous memory.
		 */
		*m0 = m = m_pullup(m, skip);
		if (m == NULL) {
			DPRINTF(("%s: m_pullup failed\n", __func__));
			return ENOBUFS;
		}

		/* Fix the IP header */
		ip = mtod(m, struct ip *);
		if (V_ah_cleartos)
			ip->ip_tos = 0;
		ip->ip_ttl = 0;
		ip->ip_sum = 0;

		/*
		 * On input, fix ip_len which has been byte-swapped
		 * at ip_input().
		 */
		if (!out) {
			ip->ip_len = htons(ip->ip_len + skip);

			if (alg == CRYPTO_MD5_KPDK || alg == CRYPTO_SHA1_KPDK)
				ip->ip_off = htons(ip->ip_off & IP_DF);
			else
				ip->ip_off = 0;
		} else {
			if (alg == CRYPTO_MD5_KPDK || alg == CRYPTO_SHA1_KPDK)
				ip->ip_off = htons(ntohs(ip->ip_off) & IP_DF);
			else
				ip->ip_off = 0;
		}

		ptr = mtod(m, unsigned char *) + sizeof(struct ip);

		/* IPv4 option processing */
		for (off = sizeof(struct ip); off < skip;) {
			if (ptr[off] == IPOPT_EOL || ptr[off] == IPOPT_NOP ||
			    off + 1 < skip)
				;
			else {
				DPRINTF(("%s: illegal IPv4 option length for "
					"option %d\n", __func__, ptr[off]));

				m_freem(m);
				return EINVAL;
			}

			switch (ptr[off]) {
			case IPOPT_EOL:
				off = skip;  /* End the loop. */
				break;

			case IPOPT_NOP:
				off++;
				break;

			case IPOPT_SECURITY:	/* 0x82 */
			case 0x85:	/* Extended security. */
			case 0x86:	/* Commercial security. */
			case 0x94:	/* Router alert */
			case 0x95:	/* RFC1770 */
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("%s: illegal IPv4 option "
						"length for option %d\n",
						__func__, ptr[off]));

					m_freem(m);
					return EINVAL;
				}

				off += ptr[off + 1];
				break;

			case IPOPT_LSRR:
			case IPOPT_SSRR:
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("%s: illegal IPv4 option "
						"length for option %d\n",
						__func__, ptr[off]));

					m_freem(m);
					return EINVAL;
				}

				/*
				 * On output, if we have either of the
				 * source routing options, we should
				 * swap the destination address of the
				 * IP header with the last address
				 * specified in the option, as that is
				 * what the destination's IP header
				 * will look like.
				 */
				if (out)
					bcopy(ptr + off + ptr[off + 1] -
					    sizeof(struct in_addr),
					    &(ip->ip_dst), sizeof(struct in_addr));

				/* Fall through */
			default:
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("%s: illegal IPv4 option "
						"length for option %d\n",
						__func__, ptr[off]));
					m_freem(m);
					return EINVAL;
				}

				/* Zeroize all other options. */
				count = ptr[off + 1];
				bcopy(ipseczeroes, ptr, count);
				off += count;
				break;
			}

			/* Sanity check. */
			if (off > skip)	{
				DPRINTF(("%s: malformed IPv4 options header\n",
					__func__));

				m_freem(m);
				return EINVAL;
			}
		}

		break;
#endif /* INET */

#ifdef INET6
	case AF_INET6:  /* Ugly... */
		/* Copy and "cook" the IPv6 header. */
		m_copydata(m, 0, sizeof(ip6), (caddr_t) &ip6);

		/* We don't do IPv6 Jumbograms. */
		if (ip6.ip6_plen == 0) {
			DPRINTF(("%s: unsupported IPv6 jumbogram\n", __func__));
			m_freem(m);
			return EMSGSIZE;
		}

		ip6.ip6_flow = 0;
		ip6.ip6_hlim = 0;
		ip6.ip6_vfc &= ~IPV6_VERSION_MASK;
		ip6.ip6_vfc |= IPV6_VERSION;

		/* Scoped address handling. */
		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;

		/* Done with IPv6 header. */
		m_copyback(m, 0, sizeof(struct ip6_hdr), (caddr_t) &ip6);

		/* Let's deal with the remaining headers (if any). */
		if (skip - sizeof(struct ip6_hdr) > 0) {
			if (m->m_len <= skip) {
				ptr = (unsigned char *) malloc(
				    skip - sizeof(struct ip6_hdr),
				    M_XDATA, M_NOWAIT);
				if (ptr == NULL) {
					DPRINTF(("%s: failed to allocate memory"
						"for IPv6 headers\n",__func__));
					m_freem(m);
					return ENOBUFS;
				}

				/*
				 * Copy all the protocol headers after
				 * the IPv6 header.
				 */
				m_copydata(m, sizeof(struct ip6_hdr),
				    skip - sizeof(struct ip6_hdr), ptr);
				alloc = 1;
			} else {
				/* No need to allocate memory. */
				ptr = mtod(m, unsigned char *) +
				    sizeof(struct ip6_hdr);
				alloc = 0;
			}
		} else
			break;

		off = ip6.ip6_nxt & 0xff; /* Next header type. */

		for (len = 0; len < skip - sizeof(struct ip6_hdr);)
			switch (off) {
			case IPPROTO_HOPOPTS:
			case IPPROTO_DSTOPTS:
				ip6e = (struct ip6_ext *) (ptr + len);

				/*
				 * Process the mutable/immutable
				 * options -- borrows heavily from the
				 * KAME code.
				 */
				for (count = len + sizeof(struct ip6_ext);
				     count < len + ((ip6e->ip6e_len + 1) << 3);) {
					if (ptr[count] == IP6OPT_PAD1) {
						count++;
						continue; /* Skip padding. */
					}

					/* Sanity check. */
					if (count > len +
					    ((ip6e->ip6e_len + 1) << 3)) {
						m_freem(m);

						/* Free, if we allocated. */
						if (alloc)
							free(ptr, M_XDATA);
						return EINVAL;
					}

					ad = ptr[count + 1];

					/* If mutable option, zeroize. */
					if (ptr[count] & IP6OPT_MUTABLE)
						bcopy(ipseczeroes, ptr + count,
						    ptr[count + 1]);

					count += ad;

					/* Sanity check. */
					if (count >
					    skip - sizeof(struct ip6_hdr)) {
						m_freem(m);

						/* Free, if we allocated. */
						if (alloc)
							free(ptr, M_XDATA);
						return EINVAL;
					}
				}

				/* Advance. */
				len += ((ip6e->ip6e_len + 1) << 3);
				off = ip6e->ip6e_nxt;
				break;

			case IPPROTO_ROUTING:
				/*
				 * Always include routing headers in
				 * computation.
				 */
				ip6e = (struct ip6_ext *) (ptr + len);
				len += ((ip6e->ip6e_len + 1) << 3);
				off = ip6e->ip6e_nxt;
				break;

			default:
				DPRINTF(("%s: unexpected IPv6 header type %d",
					__func__, off));
				if (alloc)
					free(ptr, M_XDATA);
				m_freem(m);
				return EINVAL;
			}

		/* Copyback and free, if we allocated. */
		if (alloc) {
			m_copyback(m, sizeof(struct ip6_hdr),
			    skip - sizeof(struct ip6_hdr), ptr);
			free(ptr, M_XDATA);
		}

		break;
#endif /* INET6 */
	}
Esempio n. 20
0
/*
 * Massage IPv4/IPv6 headers for AH processing.
 */
int
ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
{
	struct mbuf *m = *m0;
	unsigned char *ptr;
	int off, count;

#ifdef INET
	struct ip *ip;
#endif /* INET */

#ifdef INET6
	struct ip6_ext *ip6e;
	struct ip6_hdr ip6;
	int alloc, len, ad;
#endif /* INET6 */

	switch (proto) {
#ifdef INET
	case AF_INET:
		/*
		 * This is the least painful way of dealing with IPv4 header
		 * and option processing -- just make sure they're in
		 * contiguous memory.
		 */
		*m0 = m = m_pullup(m, skip);
		if (m == NULL) {
			DPRINTF(("ah_massage_headers(): m_pullup() failed\n"));
			ahstat.ahs_hdrops++;
			return ENOBUFS;
		}

		/* Fix the IP header */
		ip = mtod(m, struct ip *);
		ip->ip_tos = 0;
		ip->ip_ttl = 0;
		ip->ip_sum = 0;

		/*
		 * On input, fix ip_len which has been byte-swapped
		 * at ip_input().
		 */
		if (alg == CRYPTO_MD5_KPDK || alg == CRYPTO_SHA1_KPDK)
			ip->ip_off &= htons(IP_DF);
		else
			ip->ip_off = 0;

		ptr = mtod(m, unsigned char *) + sizeof(struct ip);

		/* IPv4 option processing */
		for (off = sizeof(struct ip); off < skip;) {
			if (ptr[off] == IPOPT_EOL || ptr[off] == IPOPT_NOP ||
			    off + 1 < skip)
				;
			else {
				DPRINTF(("ah_massage_headers(): illegal IPv4 "
				    "option length for option %d\n",
				    ptr[off]));

				ahstat.ahs_hdrops++;
				m_freem(m);
				return EINVAL;
			}

			switch (ptr[off]) {
			case IPOPT_EOL:
				off = skip;  /* End the loop. */
				break;

			case IPOPT_NOP:
				off++;
				break;

			case IPOPT_SECURITY:	/* 0x82 */
			case 0x85:	/* Extended security. */
			case 0x86:	/* Commercial security. */
			case 0x94:	/* Router alert */
			case 0x95:	/* RFC1770 */
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("ah_massage_headers(): "
					    "illegal IPv4 option length for "
					    "option %d\n", ptr[off]));

					ahstat.ahs_hdrops++;
					m_freem(m);
					return EINVAL;
				}

				off += ptr[off + 1];
				break;

			case IPOPT_LSRR:
			case IPOPT_SSRR:
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("ah_massage_headers(): "
					    "illegal IPv4 option length for "
					    "option %d\n", ptr[off]));

					ahstat.ahs_hdrops++;
					m_freem(m);
					return EINVAL;
				}

				/*
				 * On output, if we have either of the
				 * source routing options, we should
				 * swap the destination address of the
				 * IP header with the last address
				 * specified in the option, as that is
				 * what the destination's IP header
				 * will look like.
				 */
				if (out)
					bcopy(ptr + off + ptr[off + 1] -
					    sizeof(struct in_addr),
					    &(ip->ip_dst), sizeof(struct in_addr));

				/* Fall through */
			default:
				/* Sanity check for option length. */
				if (ptr[off + 1] < 2) {
					DPRINTF(("ah_massage_headers(): "
					    "illegal IPv4 option length for "
					    "option %d\n", ptr[off]));
					ahstat.ahs_hdrops++;
					m_freem(m);
					return EINVAL;
				}

				/* Zeroize all other options. */
				count = ptr[off + 1];
				bcopy(ipseczeroes, ptr, count);
				off += count;
				break;
			}

			/* Sanity check. */
			if (off > skip)	{
				DPRINTF(("ah_massage_headers(): malformed "
				    "IPv4 options header\n"));

				ahstat.ahs_hdrops++;
				m_freem(m);
				return EINVAL;
			}
		}

		break;
#endif /* INET */

#ifdef INET6
	case AF_INET6:  /* Ugly... */
		/* Copy and "cook" the IPv6 header. */
		m_copydata(m, 0, sizeof(ip6), (caddr_t) &ip6);

		/* We don't do IPv6 Jumbograms. */
		if (ip6.ip6_plen == 0) {
			DPRINTF(("ah_massage_headers(): unsupported IPv6 "
			    "jumbogram"));
			ahstat.ahs_hdrops++;
			m_freem(m);
			return EMSGSIZE;
		}

		ip6.ip6_flow = 0;
		ip6.ip6_hlim = 0;
		ip6.ip6_vfc &= ~IPV6_VERSION_MASK;
		ip6.ip6_vfc |= IPV6_VERSION;

		/* Scoped address handling. */
		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;

		/* Done with IPv6 header. */
		m_copyback(m, 0, sizeof(struct ip6_hdr), &ip6);

		/* Let's deal with the remaining headers (if any). */
		if (skip - sizeof(struct ip6_hdr) > 0) {
			if (m->m_len <= skip) {
				MALLOC(ptr, unsigned char *,
				    skip - sizeof(struct ip6_hdr),
				    M_XDATA, M_NOWAIT);
				if (ptr == NULL) {
					DPRINTF(("ah_massage_headers(): failed to allocate memory for IPv6 headers\n"));
					ahstat.ahs_hdrops++;
					m_freem(m);
					return ENOBUFS;
				}

				/*
				 * Copy all the protocol headers after
				 * the IPv6 header.
				 */
				m_copydata(m, sizeof(struct ip6_hdr),
				    skip - sizeof(struct ip6_hdr), ptr);
				alloc = 1;
			} else {
				/* No need to allocate memory. */
				ptr = mtod(m, unsigned char *) +
				    sizeof(struct ip6_hdr);
				alloc = 0;
			}
		} else
Esempio n. 21
0
struct mbuf*
ip6_tryforward(struct mbuf *m)
{
	struct sockaddr_in6 dst;
	struct nhop6_basic nh;
	struct m_tag *fwd_tag;
	struct ip6_hdr *ip6;
	struct ifnet *rcvif;
	uint32_t plen;
	int error;

	/*
	 * Fallback conditions to ip6_input for slow path processing.
	 */
	ip6 = mtod(m, struct ip6_hdr *);
	if (ip6->ip6_nxt == IPPROTO_HOPOPTS ||
	    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
	    IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) ||
	    IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src) ||
	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) ||
	    in6_localip(&ip6->ip6_dst))
		return (m);
	/*
	 * Check that the amount of data in the buffers
	 * is as at least much as the IPv6 header would have us expect.
	 * Trim mbufs if longer than we expect.
	 * Drop packet if shorter than we expect.
	 */
	rcvif = m->m_pkthdr.rcvif;
	plen = ntohs(ip6->ip6_plen);
	if (plen == 0) {
		/*
		 * Jumbograms must have hop-by-hop header and go via
		 * slow path.
		 */
		IP6STAT_INC(ip6s_badoptions);
		goto dropin;
	}
	if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) {
		IP6STAT_INC(ip6s_tooshort);
		in6_ifstat_inc(rcvif, ifs6_in_truncated);
		goto dropin;
	}
	if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) {
		if (m->m_len == m->m_pkthdr.len) {
			m->m_len = sizeof(struct ip6_hdr) + plen;
			m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen;
		} else
			m_adj(m, sizeof(struct ip6_hdr) + plen -
			    m->m_pkthdr.len);
	}

	/*
	 * Hop limit.
	 */
#ifdef IPSTEALTH
	if (!V_ip6stealth)
#endif
	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
		icmp6_error(m, ICMP6_TIME_EXCEEDED,
		    ICMP6_TIME_EXCEED_TRANSIT, 0);
		m = NULL;
		goto dropin;
	}

	bzero(&dst, sizeof(dst));
	dst.sin6_family = AF_INET6;
	dst.sin6_len = sizeof(dst);
	dst.sin6_addr = ip6->ip6_dst;

	/*
	 * Incoming packet firewall processing.
	 */
	if (!PFIL_HOOKED(&V_inet6_pfil_hook))
		goto passin;
	if (pfil_run_hooks(&V_inet6_pfil_hook, &m, rcvif, PFIL_IN,
	    NULL) != 0 || m == NULL)
		goto dropin;
	/*
	 * If packet filter sets the M_FASTFWD_OURS flag, this means
	 * that new destination or next hop is our local address.
	 * So, we can just go back to ip6_input.
	 * XXX: should we decrement ip6_hlim in such case?
	 *
	 * Also it can forward packet to another destination, e.g.
	 * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf.
	 */
	if (m->m_flags & M_FASTFWD_OURS)
		return (m);

	ip6 = mtod(m, struct ip6_hdr *);
	if ((m->m_flags & M_IP6_NEXTHOP) &&
	    (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
		/*
		 * Now we will find route to forwarded by pfil destination.
		 */
		bcopy((fwd_tag + 1), &dst, sizeof(dst));
		m->m_flags &= ~M_IP6_NEXTHOP;
		m_tag_delete(m, fwd_tag);
	} else {
		/* Update dst since pfil could change it */
		dst.sin6_addr = ip6->ip6_dst;
	}
passin:
	/*
	 * Find route to destination.
	 */
	if (ip6_findroute(&nh, &dst, m) != 0) {
		m = NULL;
		in6_ifstat_inc(rcvif, ifs6_in_noroute);
		goto dropin;
	}
	/*
	 * We used slow path processing for packets with scoped addresses.
	 * So, scope checks aren't needed here.
	 */
	if (m->m_pkthdr.len > nh.nh_mtu) {
		in6_ifstat_inc(nh.nh_ifp, ifs6_in_toobig);
		icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu);
		m = NULL;
		goto dropout;
	}

	/*
	 * Outgoing packet firewall processing.
	 */
	if (!PFIL_HOOKED(&V_inet6_pfil_hook))
		goto passout;
	if (pfil_run_hooks(&V_inet6_pfil_hook, &m, nh.nh_ifp, PFIL_OUT,
	    NULL) != 0 || m == NULL)
		goto dropout;
	/*
	 * If packet filter sets the M_FASTFWD_OURS flag, this means
	 * that new destination or next hop is our local address.
	 * So, we can just go back to ip6_input.
	 *
	 * Also it can forward packet to another destination, e.g.
	 * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf.
	 */
	if (m->m_flags & M_FASTFWD_OURS) {
		/*
		 * XXX: we did one hop and should decrement hop limit. But
		 * now we are the destination and just don't pay attention.
		 */
		return (m);
	}
	/*
	 * Again. A packet filter could change the destination address.
	 */
	ip6 = mtod(m, struct ip6_hdr *);
	if (m->m_flags & M_IP6_NEXTHOP)
		fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
	else
		fwd_tag = NULL;

	if (fwd_tag != NULL ||
	    !IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) {
		if (fwd_tag != NULL) {
			bcopy((fwd_tag + 1), &dst, sizeof(dst));
			m->m_flags &= ~M_IP6_NEXTHOP;
			m_tag_delete(m, fwd_tag);
		} else
			dst.sin6_addr = ip6->ip6_dst;
		/*
		 * Redo route lookup with new destination address
		 */
		if (ip6_findroute(&nh, &dst, m) != 0) {
			m = NULL;
			goto dropout;
		}
	}
passout:
#ifdef IPSTEALTH
	if (!V_ip6stealth)
#endif
	{
		ip6->ip6_hlim -= IPV6_HLIMDEC;
	}

	m_clrprotoflags(m);	/* Avoid confusing lower layers. */
	IP_PROBE(send, NULL, NULL, ip6, nh.nh_ifp, NULL, ip6);

	/*
	 * XXX: we need to use destination address with embedded scope
	 * zone id, because LLTABLE uses such form of addresses for lookup.
	 */
	dst.sin6_addr = nh.nh_addr;
	if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6_addr))
		dst.sin6_addr.s6_addr16[1] = htons(nh.nh_ifp->if_index & 0xffff);

	error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m,
	    (struct sockaddr *)&dst, NULL);
	if (error != 0) {
		in6_ifstat_inc(nh.nh_ifp, ifs6_out_discard);
		IP6STAT_INC(ip6s_cantforward);
	} else {
		in6_ifstat_inc(nh.nh_ifp, ifs6_out_forward);
		IP6STAT_INC(ip6s_forward);
	}
	return (NULL);
dropin:
	in6_ifstat_inc(rcvif, ifs6_in_discard);
	goto drop;
dropout:
	in6_ifstat_inc(nh.nh_ifp, ifs6_out_discard);
drop:
	if (m != NULL)
		m_freem(m);
	return (NULL);
}
Esempio n. 22
0
/*
 * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
 * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
 * in the in6_addr structure, in6 will be modified.
 *
 * ret_id - unnecessary?
 */
int
in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
{
	int scope;
	u_int32_t zoneid = 0;
	struct scope6_id *sid;

	/*
	 * special case: the loopback address can only belong to a loopback
	 * interface.
	 */
	if (IN6_IS_ADDR_LOOPBACK(in6)) {
		if (!(ifp->if_flags & IFF_LOOPBACK)) {
			return (EINVAL);
		} else {
			if (ret_id != NULL)
				*ret_id = 0; /* there's no ambiguity */
			return (0);
		}
	}

	scope = in6_addrscope(in6);

	if_inet6data_lock_shared(ifp);
	if (IN6_IFEXTRA(ifp) == NULL) {
		if_inet6data_lock_done(ifp);
		if (ret_id)
			*ret_id = 0;
		return (EINVAL);
	}
	sid = SID(ifp);
	switch (scope) {
	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
		break;

	case IPV6_ADDR_SCOPE_LINKLOCAL:
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
		break;

	case IPV6_ADDR_SCOPE_SITELOCAL:
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
		break;

	case IPV6_ADDR_SCOPE_ORGLOCAL:
		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
		break;
	default:
		zoneid = 0;	/* XXX: treat as global. */
		break;
	}
	if_inet6data_lock_done(ifp);

	if (ret_id != NULL)
		*ret_id = zoneid;

	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */

	return (0);
}