Пример #1
0
struct ifaddr *
ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway,
    u_int rtableid)
{
	struct ifaddr	*ifa;

#ifdef IPSEC
	/*
	 * If the destination is a PF_KEY address, we'll look
	 * for the existence of a encap interface number or address
	 * in the options list of the gateway. By default, we'll return
	 * enc0.
	 */
	if (dst && (dst->sa_family == PF_KEY))
		return (encap_findgwifa(gateway));
#endif

	if ((flags & RTF_GATEWAY) == 0) {
		/*
		 * If we are adding a route to an interface,
		 * and the interface is a pt to pt link
		 * we should search for the destination
		 * as our clue to the interface.  Otherwise
		 * we can use the local address.
		 */
		ifa = NULL;
		if (flags & RTF_HOST)
			ifa = ifa_ifwithdstaddr(dst, rtableid);
		if (ifa == NULL)
			ifa = ifa_ifwithaddr(gateway, rtableid);
	} else {
		/*
		 * If we are adding a route to a remote net
		 * or host, the gateway may still be on the
		 * other end of a pt to pt link.
		 */
		ifa = ifa_ifwithdstaddr(gateway, rtableid);
	}
	if (ifa == NULL)
		ifa = ifa_ifwithnet(gateway, rtableid);
	if (ifa == NULL) {
		struct rtentry	*rt = rtalloc1(gateway, 0, rtable_l2(rtableid));
		if (rt == NULL)
			return (NULL);
		rt->rt_refcnt--;
		/* The gateway must be local if the same address family. */
		if ((rt->rt_flags & RTF_GATEWAY) &&
		    rt_key(rt)->sa_family == dst->sa_family)
			return (0);
		if ((ifa = rt->rt_ifa) == NULL)
			return (NULL);
	}
	if (ifa->ifa_addr->sa_family != dst->sa_family) {
		struct ifaddr	*oifa = ifa;
		ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
		if (ifa == NULL)
			ifa = oifa;
	}
	return (ifa);
}
Пример #2
0
/*
 * Provide an opportunity for a TOE driver to offload.
 */
int
tcp_offload_connect(struct socket *so, struct sockaddr *nam)
{
	struct ifnet *ifp;
	struct toedev *tod;
	struct rtentry *rt;
	int error = EOPNOTSUPP;

	INP_WLOCK_ASSERT(sotoinpcb(so));
	KASSERT(nam->sa_family == AF_INET || nam->sa_family == AF_INET6,
	    ("%s: called with sa_family %d", __func__, nam->sa_family));

	if (registered_toedevs == 0)
		return (error);

	rt = rtalloc1(nam, 0, 0);
	if (rt)
		RT_UNLOCK(rt);
	else
		return (EHOSTUNREACH);

	ifp = rt->rt_ifp;

	if (nam->sa_family == AF_INET && !(ifp->if_capenable & IFCAP_TOE4))
		goto done;
	if (nam->sa_family == AF_INET6 && !(ifp->if_capenable & IFCAP_TOE6))
		goto done;

	tod = TOEDEV(ifp);
	if (tod != NULL)
		error = tod->tod_connect(tod, so, rt, nam);
done:
	RTFREE(rt);
	return (error);
}
Пример #3
0
/*
 * Packet routing routines.
 */
void
rtalloc(struct route *ro)
{
	if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
		return;				 /* XXX */
	ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0);
}
Пример #4
0
struct hcentry *
hc_get(struct sockaddr *sa)
{
	u_long hash;
	struct hcentry *hc;
	struct hctable *hct;
	int s;

	hct = &hctable[sa->sa_family];
	if (hct->hct_nentries == 0)
		return 0;
	hash = hct->hct_cb->hccb_hash(sa, hct->hct_nentries);
	hc = hct->hct_heads[hash].lh_first;
	for (; hc; hc = hc->hc_link.le_next) {
		if (cmpsa(hc->hc_host, sa) == 0)
			break;
	}
	if (hc == 0)
		return 0;
	s = splnet();
	if (hc->hc_rt && (hc->hc_rt->rt_flags & RTF_UP) == 0) {
		RTFREE(hc->hc_rt);
		hc->hc_rt = 0;
	}
	if (hc->hc_rt == 0) {
		hc->hc_rt = rtalloc1(hc->hc_host, 1, 0);
	}
	hc_ref(hc);
	splx(s);
	/* XXX move to front of list? */
	return hc;
}
Пример #5
0
/*
 * Start output on the mpe interface.
 */
void
mpestart(struct ifnet *ifp)
{
	struct mbuf 		*m;
	struct sockaddr		*sa = (struct sockaddr *)&mpedst;
	int			 s;
	sa_family_t		 af;
	struct rtentry		*rt;

	for (;;) {
		s = splnet();
		IFQ_DEQUEUE(&ifp->if_snd, m);
		splx(s);

		if (m == NULL)
			return;

		af = *mtod(m, sa_family_t *);
		m_adj(m, sizeof(af));
		switch (af) {
		case AF_INET:
			bzero(sa, sizeof(struct sockaddr_in));
			satosin(sa)->sin_family = af;
			satosin(sa)->sin_len = sizeof(struct sockaddr_in);
			bcopy(mtod(m, caddr_t), &satosin(sa)->sin_addr,
			    sizeof(in_addr_t));
			m_adj(m, sizeof(in_addr_t));
			break;
		default:
			m_freem(m);
			continue;
		}

		rt = rtalloc1(sa, RT_REPORT, 0);
		if (rt == NULL) {
			/* no route give up */
			m_freem(m);
			continue;
		}

#if NBPFILTER > 0
		if (ifp->if_bpf) {
			/* remove MPLS label before passing packet to bpf */
			m->m_data += sizeof(struct shim_hdr);
			m->m_len -= sizeof(struct shim_hdr);
			m->m_pkthdr.len -= sizeof(struct shim_hdr);
			bpf_mtap_af(ifp->if_bpf, af, m, BPF_DIRECTION_OUT);
			m->m_data -= sizeof(struct shim_hdr);
			m->m_len += sizeof(struct shim_hdr);
			m->m_pkthdr.len += sizeof(struct shim_hdr);
		}
#endif
		/* XXX lie, but mpls_output will only look at sa_family */
		sa->sa_family = AF_MPLS;

		mpls_output(rt->rt_ifp, m, sa, rt);
		RTFREE(rt);
	}
}
Пример #6
0
void
rtalloc_mpath(struct route *ro, int hash)
{
	struct radix_node *rn0, *rn;
	int n;

	/*
	 * XXX we don't attempt to lookup cached route again; what should
	 * be done for sendto(3) case?
	 */
	if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
		return;				 /* XXX */
#ifdef __FreeBSD__
	ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL);
#else
	ro->ro_rt = rtalloc1(&ro->ro_dst, 1);
#endif
	/* if the route does not exist or it is not multipath, don't care */
	if (!ro->ro_rt || !rn_mpath_next((struct radix_node *)ro->ro_rt))
		return;

	/* beyond here, we use rn as the master copy */
	rn0 = rn = (struct radix_node *)ro->ro_rt;
	n = rn_mpath_count(rn0);

	/* gw selection by Modulo-N Hash (RFC2991) XXX need improvement? */
	hash += hashjitter;
	hash %= n;
	while (hash-- > 0 && rn) {
		/* stay within the multipath routes */
		if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask)
			break;
		rn = rn->rn_dupedkey;
	}

	/* XXX try filling rt_gwroute and avoid unreachable gw  */

	/* if gw selection fails, use the first match (default) */
	if (!rn)
		return;

	rtfree(ro->ro_rt);
	ro->ro_rt = (struct rtentry *)rn;
	ro->ro_rt->rt_refcnt++;
}
Пример #7
0
/*
 * allocate a route, potentially using multipath to select the peer.
 */
void
rtalloc_mpath(struct route *ro, u_int32_t *srcaddrp)
{
#if defined(INET) || defined(INET6)
	struct radix_node *rn;
	int hash, npaths, threshold;
#endif

	/*
	 * return a cached entry if it is still valid, otherwise we increase
	 * the risk of disrupting local flows.
	 */
	if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
		return;
	ro->ro_rt = rtalloc1(&ro->ro_dst, RT_REPORT, ro->ro_tableid);

	/* if the route does not exist or it is not multipath, don't care */
	if (!ro->ro_rt || !(ro->ro_rt->rt_flags & RTF_MPATH))
		return;

	/* check if multipath routing is enabled for the specified protocol */
	if (!(0
#ifdef INET
	    || (ipmultipath && ro->ro_dst.sa_family == AF_INET)
#endif
#ifdef INET6
	    || (ip6_multipath && ro->ro_dst.sa_family == AF_INET6)
#endif
	    ))
		return;

#if defined(INET) || defined(INET6)
	/* gw selection by Hash-Threshold (RFC 2992) */
	rn = (struct radix_node *)ro->ro_rt;
	npaths = rn_mpath_count(rn);
	hash = rn_mpath_hash(ro, srcaddrp) & 0xffff;
	threshold = 1 + (0xffff / npaths);
	while (hash > threshold && rn) {
		/* stay within the multipath routes */
		if (rn_mpath_next(rn, 0) == NULL)
			break;
		rn = rn->rn_dupedkey;
		hash -= threshold;
	}

	/* XXX try filling rt_gwroute and avoid unreachable gw  */

	/* if gw selection fails, use the first match (default) */
	if (!rn)
		return;

	rtfree(ro->ro_rt);
	ro->ro_rt = (struct rtentry *)rn;
	ro->ro_rt->rt_refcnt++;
#endif
}
Пример #8
0
int
tcp_offload_connect(struct socket *so, struct sockaddr *nam)
{
	struct ifnet *ifp;
	struct toedev *tdev;
	struct rtentry *rt;
	int error;

	if (toedev_registration_count == 0)
		return (EINVAL);
	
	/*
	 * Look up the route used for the connection to 
	 * determine if it uses an interface capable of
	 * offloading the connection.
	 */
	rt = rtalloc1(nam, 0 /*report*/, 0 /*ignflags*/);
	if (rt) 
		RT_UNLOCK(rt);
	else 
		return (EHOSTUNREACH);

	ifp = rt->rt_ifp;
	if ((ifp->if_capenable & IFCAP_TOE) == 0) {
		error = EINVAL;
		goto fail;
	}
	
	tdev = TOEDEV(ifp);
	if (tdev == NULL) {
		error = EPERM;
		goto fail;
	}
	
	if (tdev->tod_can_offload(tdev, so) == 0) {
		error = EPERM;
		goto fail;
	}
	
	return (tdev->tod_connect(tdev, so, rt, nam));
fail:
	RTFREE(rt);
	return (error);
}
Пример #9
0
/*
 * Force a routing table entry to the specified
 * destination to go through the given gateway.
 * Normally called as a result of a routing redirect
 * message from the network layer.
 *
 * N.B.: must be called at splsoftnet
 */
void
rtredirect(struct sockaddr *dst, struct sockaddr *gateway,
    struct sockaddr *netmask, int flags, struct sockaddr *src,
    struct rtentry **rtp, u_int rdomain)
{
	struct rtentry		*rt;
	int			 error = 0;
	u_int32_t		*stat = NULL;
	struct rt_addrinfo	 info;
	struct ifaddr		*ifa;
	struct ifnet		*ifp = NULL;

	splsoftassert(IPL_SOFTNET);

	/* verify the gateway is directly reachable */
	if ((ifa = ifa_ifwithnet(gateway, rdomain)) == NULL) {
		error = ENETUNREACH;
		goto out;
	}
	ifp = ifa->ifa_ifp;
	rt = rtalloc1(dst, 0, rdomain);
	/*
	 * If the redirect isn't from our current router for this dst,
	 * it's either old or wrong.  If it redirects us to ourselves,
	 * we have a routing loop, perhaps as a result of an interface
	 * going down recently.
	 */
#define	equal(a1, a2) \
	((a1)->sa_len == (a2)->sa_len && \
	 bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
	if (!(flags & RTF_DONE) && rt &&
	     (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
		error = EINVAL;
	else if (ifa_ifwithaddr(gateway, rdomain) != NULL)
		error = EHOSTUNREACH;
	if (error)
		goto done;
	/*
	 * Create a new entry if we just got back a wildcard entry
	 * or the lookup failed.  This is necessary for hosts
	 * which use routing redirects generated by smart gateways
	 * to dynamically build the routing tables.
	 */
	if ((rt == NULL) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
		goto create;
	/*
	 * Don't listen to the redirect if it's
	 * for a route to an interface. 
	 */
	if (rt->rt_flags & RTF_GATEWAY) {
		if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
			/*
			 * Changing from route to net => route to host.
			 * Create new route, rather than smashing route to net.
			 */
create:
			if (rt)
				rtfree(rt);
			flags |= RTF_GATEWAY | RTF_DYNAMIC;
			bzero(&info, sizeof(info));
			info.rti_info[RTAX_DST] = dst;
			info.rti_info[RTAX_GATEWAY] = gateway;
			info.rti_info[RTAX_NETMASK] = netmask;
			info.rti_ifa = ifa;
			info.rti_flags = flags;
			rt = NULL;
			error = rtrequest1(RTM_ADD, &info, RTP_DEFAULT, &rt,
			    rdomain);
			if (rt != NULL)
				flags = rt->rt_flags;
			stat = &rtstat.rts_dynamic;
		} else {
			/*
			 * Smash the current notion of the gateway to
			 * this destination.  Should check about netmask!!!
			 */
			rt->rt_flags |= RTF_MODIFIED;
			flags |= RTF_MODIFIED;
			stat = &rtstat.rts_newgateway;
			rt_setgate(rt, rt_key(rt), gateway, rdomain);
		}
	} else
		error = EHOSTUNREACH;
done:
	if (rt) {
		if (rtp && !error)
			*rtp = rt;
		else
			rtfree(rt);
	}
out:
	if (error)
		rtstat.rts_badredirect++;
	else if (stat != NULL)
		(*stat)++;
	bzero((caddr_t)&info, sizeof(info));
	info.rti_info[RTAX_DST] = dst;
	info.rti_info[RTAX_GATEWAY] = gateway;
	info.rti_info[RTAX_NETMASK] = netmask;
	info.rti_info[RTAX_AUTHOR] = src;
	rt_missmsg(RTM_REDIRECT, &info, flags, ifp, error, rdomain);
}
Пример #10
0
int
rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt,
    u_int tableid)
{
	int			 s = splsoftnet(); int error = 0;
	struct rtentry		*rt, *crt;
	struct radix_node	*rn;
	struct radix_node_head	*rnh;
	struct ifaddr		*ifa;
	struct sockaddr		*ndst;
	struct sockaddr_rtlabel	*sa_rl;
#define senderr(x) { error = x ; goto bad; }

	if ((rnh = rt_gettable(info->rti_info[RTAX_DST]->sa_family, tableid)) ==
	    NULL)
		senderr(EAFNOSUPPORT);
	if (info->rti_flags & RTF_HOST)
		info->rti_info[RTAX_NETMASK] = NULL;
	switch (req) {
	case RTM_DELETE:
		if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_NETMASK], rnh)) == NULL)
			senderr(ESRCH);
		rt = (struct rtentry *)rn;
#ifndef SMALL_KERNEL
		/*
		 * if we got multipath routes, we require users to specify
		 * a matching RTAX_GATEWAY.
		 */
		if (rn_mpath_capable(rnh)) {
			rt = rt_mpath_matchgate(rt,
			    info->rti_info[RTAX_GATEWAY]);
			rn = (struct radix_node *)rt;
			if (!rt)
				senderr(ESRCH);
		}
#endif
		if ((rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_NETMASK], rnh, rn)) == NULL)
			senderr(ESRCH);
		rt = (struct rtentry *)rn;

		/* clean up any cloned children */
		if ((rt->rt_flags & RTF_CLONING) != 0)
			rtflushclone(rnh, rt);

		if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
			panic ("rtrequest delete");

		if (rt->rt_gwroute) {
			rt = rt->rt_gwroute; RTFREE(rt);
			(rt = (struct rtentry *)rn)->rt_gwroute = NULL;
		}

		if (rt->rt_parent) {
			rt->rt_parent->rt_refcnt--;
			rt->rt_parent = NULL;
		}

#ifndef SMALL_KERNEL
		if (rn_mpath_capable(rnh)) {
			if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST],
			    info->rti_info[RTAX_NETMASK], rnh)) != NULL &&
			    rn_mpath_next(rn) == NULL)
				((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH;
		}
#endif

		rt->rt_flags &= ~RTF_UP;
		if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
			ifa->ifa_rtrequest(RTM_DELETE, rt, info);
		rttrash++;

		if (ret_nrt)
			*ret_nrt = rt;
		else if (rt->rt_refcnt <= 0) {
			rt->rt_refcnt++;
			rtfree(rt);
		}
		break;

	case RTM_RESOLVE:
		if (ret_nrt == NULL || (rt = *ret_nrt) == NULL)
			senderr(EINVAL);
		if ((rt->rt_flags & RTF_CLONING) == 0)
			senderr(EINVAL);
		ifa = rt->rt_ifa;
		info->rti_flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC);
		info->rti_flags |= RTF_CLONED;
		info->rti_info[RTAX_GATEWAY] = rt->rt_gateway;
		if ((info->rti_info[RTAX_NETMASK] = rt->rt_genmask) == NULL)
			info->rti_flags |= RTF_HOST;
		goto makeroute;

	case RTM_ADD:
		if (info->rti_ifa == 0 && (error = rt_getifa(info)))
			senderr(error);
		ifa = info->rti_ifa;
makeroute:
		rt = pool_get(&rtentry_pool, PR_NOWAIT);
		if (rt == NULL)
			senderr(ENOBUFS);
		Bzero(rt, sizeof(*rt));
		rt->rt_flags = RTF_UP | info->rti_flags;
		LIST_INIT(&rt->rt_timer);
		if (rt_setgate(rt, info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_GATEWAY], tableid)) {
			pool_put(&rtentry_pool, rt);
			senderr(ENOBUFS);
		}
		ndst = rt_key(rt);
		if (info->rti_info[RTAX_NETMASK] != NULL) {
			rt_maskedcopy(info->rti_info[RTAX_DST], ndst,
			    info->rti_info[RTAX_NETMASK]);
		} else
			Bcopy(info->rti_info[RTAX_DST], ndst,
			    info->rti_info[RTAX_DST]->sa_len);
#ifndef SMALL_KERNEL
		/* do not permit exactly the same dst/mask/gw pair */
		if (rn_mpath_capable(rnh) &&
		    rt_mpath_conflict(rnh, rt, info->rti_info[RTAX_NETMASK],
		    info->rti_flags & RTF_MPATH)) {
			if (rt->rt_gwroute)
				rtfree(rt->rt_gwroute);
			Free(rt_key(rt));
			pool_put(&rtentry_pool, rt);
			senderr(EEXIST);
		}
#endif

		if (info->rti_info[RTAX_LABEL] != NULL) {
			sa_rl = (struct sockaddr_rtlabel *)
			    info->rti_info[RTAX_LABEL];
			rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label);
		}

		ifa->ifa_refcnt++;
		rt->rt_ifa = ifa;
		rt->rt_ifp = ifa->ifa_ifp;
		if (req == RTM_RESOLVE) {
			/*
			 * Copy both metrics and a back pointer to the cloned
			 * route's parent.
			 */
			rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
			rt->rt_parent = *ret_nrt;	 /* Back ptr. to parent. */
			rt->rt_parent->rt_refcnt++;
		}
		rn = rnh->rnh_addaddr((caddr_t)ndst,
		    (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes);
		if (rn == NULL && (crt = rtalloc1(ndst, 0, tableid)) != NULL) {
			/* overwrite cloned route */
			if ((crt->rt_flags & RTF_CLONED) != 0) {
				rtdeletemsg(crt, tableid);
				rn = rnh->rnh_addaddr((caddr_t)ndst,
				    (caddr_t)info->rti_info[RTAX_NETMASK],
				    rnh, rt->rt_nodes);
			}
			RTFREE(crt);
		}
		if (rn == 0) {
			IFAFREE(ifa);
			if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent)
				rtfree(rt->rt_parent);
			if (rt->rt_gwroute)
				rtfree(rt->rt_gwroute);
			Free(rt_key(rt));
			pool_put(&rtentry_pool, rt);
			senderr(EEXIST);
		}

#ifndef SMALL_KERNEL
		if (rn_mpath_capable(rnh) &&
		    (rn = rnh->rnh_lookup(info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_NETMASK], rnh)) != NULL) {
			if (rn_mpath_next(rn) == NULL)
				((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH;
			else
				((struct rtentry *)rn)->rt_flags |= RTF_MPATH;
		}
#endif

		if (ifa->ifa_rtrequest)
			ifa->ifa_rtrequest(req, rt, info);
		if (ret_nrt) {
			*ret_nrt = rt;
			rt->rt_refcnt++;
		}
		if ((rt->rt_flags & RTF_CLONING) != 0) {
			/* clean up any cloned children */
			rtflushclone(rnh, rt);
		}

		if_group_routechange(info->rti_info[RTAX_DST],
			info->rti_info[RTAX_NETMASK]);
		break;
	}
bad:
	splx(s);
	return (error);
}
Пример #11
0
/*
 * Input a Neighbor Solicitation Message.
 *
 * Based on RFC 2461
 * Based on RFC 2462 (duplicate address detection)
 */
void
nd6_ns_input(struct mbuf *m, int off, int icmp6len)
{
	struct ifnet *ifp = m->m_pkthdr.rcvif;
	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;
	int rflag;
	union nd_opts ndopts;
	struct sockaddr_dl proxydl;
	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];

	rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0;
	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif)
		rflag = 0;
#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_INC(icp6s_tooshort);
		return;
	}
#endif
	ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
	taddr6 = nd_ns->nd_ns_target;
	if (in6_setscope(&taddr6, ifp, NULL) != 0)
		goto bad;

	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(ip6bufs, &ip6->ip6_src),
		    ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp)));
		goto bad;
	}

	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
		/* dst has to be a 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 (!V_nd6_onlink_ns_rfc4861) {
		struct sockaddr_in6 src_sa6;

		/*
		 * According to recent IETF discussions, it is not a good idea
		 * to accept a NS from an address which would not be deemed
		 * to be a neighbor otherwise.  This point is expected to be
		 * clarified in future revisions of the specification.
		 */
		bzero(&src_sa6, sizeof(src_sa6));
		src_sa6.sin6_family = AF_INET6;
		src_sa6.sin6_len = sizeof(src_sa6);
		src_sa6.sin6_addr = saddr6;
		if (nd6_is_addr_neighbor(&src_sa6, ifp) == 0) {
			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;
	}

	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 (!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 address for which I'm offering proxy service, or
	 * (3) "tentative" address on which DAD is being performed.
	 */
	/* (1) and (3) check. */
	if (ifp->if_carp)
		ifa = (*carp_iamatch6_p)(ifp, &taddr6);
	if (ifa == NULL)
		ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);

	/* (2) check. */
	if (ifa == NULL) {
		struct rtentry *rt;
		struct sockaddr_in6 tsin6;
		int need_proxy;
#ifdef RADIX_MPATH
		struct route_in6 ro;
#endif

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

#ifdef RADIX_MPATH
		bzero(&ro, sizeof(ro));
		ro.ro_dst = tsin6;
		rtalloc_mpath((struct route *)&ro, RTF_ANNOUNCE);
		rt = ro.ro_rt;
#else
		rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0);
#endif
		need_proxy = (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
		    rt->rt_gateway->sa_family == AF_LINK);
		if (rt != NULL) {
			/*
			 * Make a copy while we can be sure that rt_gateway
			 * is still stable before unlocking to avoid lock
			 * order problems.  proxydl will only be used if
			 * proxy will be set in the next block.
			 */
			if (need_proxy)
				proxydl = *SDL(rt->rt_gateway);
			RTFREE_LOCKED(rt);
		}
		if (need_proxy) {
			/*
			 * proxy NDP for single entry
			 */
			ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
				IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
			if (ifa)
				proxy = 1;
		}
	}
	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 && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
		nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s "
		    "(if %d, NS packet %d)\n",
		    ip6_sprintf(ip6bufs, &taddr6),
		    ifp->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(ip6bufs, &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
		 * duplicate 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)) {
		struct in6_addr in6_all;

		in6_all = in6addr_linklocal_allnodes;
		if (in6_setscope(&in6_all, ifp, NULL) != 0)
			goto bad;
		nd6_na_output(ifp, &in6_all, &taddr6,
		    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
		    rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL);
		goto freeit;
	}

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

	nd6_na_output(ifp, &saddr6, &taddr6,
	    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
	    rflag | ND_NA_FLAG_SOLICITED, tlladdr,
	    proxy ? (struct sockaddr *)&proxydl : NULL);
 freeit:
	if (ifa != NULL)
		ifa_free(ifa);
	m_freem(m);
	return;

 bad:
	nd6log((LOG_ERR, "nd6_ns_input: src=%s\n",
		ip6_sprintf(ip6bufs, &saddr6)));
	nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n",
		ip6_sprintf(ip6bufs, &daddr6)));
	nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n",
		ip6_sprintf(ip6bufs, &taddr6)));
	ICMP6STAT_INC(icp6s_badns);
	if (ifa != NULL)
		ifa_free(ifa);
	m_freem(m);
}
Пример #12
0
/*
 * llcintr() handles all LLC frames (except ISO CLNS ones for the time being)
 *           and tries to pass them on to the appropriate network layer entity.
 */
void
llcintr()
{
	register struct mbuf *m;
	register int i;
	register int frame_kind;
	register u_char cmdrsp;
	struct llc_linkcb *linkp;
	struct npaidbentry *sapinfo = NULL;
	struct sdl_hdr *sdlhdr;
	struct llc *frame;
	long expected_len;

	struct ifnet   *ifp;
	struct rtentry *llrt;
	struct rtentry *nlrt;

	for (;;) {
		i = splimp();
		IF_DEQUEUE(&llcintrq, m);
		splx(i);
		if (m == 0)
			break;
#ifdef		DIAGNOSTIC
		if ((m->m_flags & M_PKTHDR) == 0)
			panic("llcintr no HDR");
#endif
		/*
		 * Get ifp this packet was received on
		 */
		ifp = m->m_pkthdr.rcvif;

		sdlhdr = mtod(m, struct sdl_hdr *);

		/*
		 * [Copied from net/ip_input.c]
		 *
		 * Check that the amount of data in the buffers is
		 * at least as much as the LLC header tells us.
		 * Trim mbufs if longer than expected.
		 * Drop packets if shorter than we think they are.
		 *
		 * Layout of mbuf chain at this point:
		 *
		 *  +-------------------------------+----+	-\
                 *  |  sockaddr_dl src - sdlhdr_src | 20 |	  \
                 *  +-------------------------------+----+	   |
		 *  |  sockaddr_dl dst - sdlhdr_dst | 20 |	    > sizeof(struct sdl_hdr) == 44
		 *  +-------------------------------+----+	   |
                 *  |  LLC frame len - sdlhdr_len   | 04 |	  /
		 *  +-------------------------------+----+	-/
		 * /
		 * | m_next
		 * \
                 *  +----------------------------+----+	 -\
                 *  |  llc DSAP 		 | 01 |	   \
		 *  +----------------------------+----+	    |
                 *  |  llc SSAP 		 | 01 |	    |
		 *  +----------------------------+----+	     > sdlhdr_len
                 *  |  llc control      	 | 01 |	    |
		 *  +----------------------------+----+	    |
		 *  |  ...                       |    |	   /
		 *      			      	 -/
		 *
		 * Thus the we expect to have exactly 
		 * (sdlhdr->sdlhdr_len+sizeof(struct sdl_hdr)) in the mbuf chain
		 */
		expected_len = sdlhdr->sdlhdr_len + sizeof(struct sdl_hdr);

		if (m->m_pkthdr.len < expected_len) {
			m_freem(m);
			continue;
		}
		if (m->m_pkthdr.len > expected_len) {
			if (m->m_len == m->m_pkthdr.len) {
				m->m_len = expected_len;
				m->m_pkthdr.len = expected_len;
			} else
				m_adj(m, expected_len - m->m_pkthdr.len);
		}

		/*
		 * Get llc header
		 */
		if (m->m_len > sizeof(struct sdl_hdr))
			frame = mtod((struct mbuf *)((struct sdl_hdr*)(m+1)),
			     	     struct llc *);
		else frame = mtod(m->m_next, struct llc *);
		if (frame == (struct llc *) NULL)
			panic("llcintr no llc header");

		/*
		 * Now check for bogus I/S frame, i.e. those with a control
		 * field telling us they're an I/S frame yet their length
		 * is less than the established I/S frame length (DSAP + SSAP +
		 * control + N(R)&P/F = 4) --- we drop those suckers
		 */
		if (((frame->llc_control & 0x03) != 0x03) 
		    && ((expected_len - sizeof(struct sdl_hdr)) < LLC_ISFRAMELEN)) {
			m_freem(m);
			printf("llc: hurz error\n");
			continue;
		}

		/*
		 * Get link control block for the addressed link connection.
		 * If there is none we take care of it later on.
		 */
		cmdrsp = (frame->llc_ssap & 0x01);
		frame->llc_ssap &= ~0x01;
		llrt = rtalloc1((struct sockaddr *)&sdlhdr->sdlhdr_src, 0);
		if (llrt)
			llrt->rt_refcnt--;
#ifdef notyet
		else
			llrt = npaidb_enter(&sdlhdr->sdlhdr_src, 0, 0, 0);
#endif /* notyet */
		else {
Пример #13
0
/*
 * MPLS Label Switch Engine
 */
static int
mpls_lse(struct mbuf *m)
{
	struct sockaddr_mpls dst;
	union mpls_shim tshim, *htag;
	struct rtentry *rt = NULL;
	int error = ENOBUFS;
	uint psize = sizeof(struct sockaddr_mpls);
	bool push_back_alert = false;

	if (m->m_len < sizeof(union mpls_shim) &&
	    (m = m_pullup(m, sizeof(union mpls_shim))) == NULL)
		goto done;

	dst.smpls_len = sizeof(struct sockaddr_mpls);
	dst.smpls_family = AF_MPLS;
	dst.smpls_addr.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);

	/* Check if we're accepting MPLS Frames */
	error = EINVAL;
	if (!mpls_accept)
		goto done;

	/* TTL decrement */
	if ((m = mpls_ttl_dec(m)) == NULL)
		goto done;

	/* RFC 4182 */
	if (mpls_rfc4182 != 0)
		while((dst.smpls_addr.shim.label == MPLS_LABEL_IPV4NULL ||
		    dst.smpls_addr.shim.label == MPLS_LABEL_IPV6NULL) &&
		    __predict_false(dst.smpls_addr.shim.bos == 0))
			TRIM_LABEL;

	/* RFC 3032 Section 2.1 Page 4 */
	if (__predict_false(dst.smpls_addr.shim.label == MPLS_LABEL_RTALERT) &&
	    dst.smpls_addr.shim.bos == 0) {
		TRIM_LABEL;
		push_back_alert = true;
	}

	if (dst.smpls_addr.shim.label <= MPLS_LABEL_RESMAX) {
		/* Don't swap reserved labels */
		switch (dst.smpls_addr.shim.label) {
#ifdef INET
		case MPLS_LABEL_IPV4NULL:
			/* Pop shim and push mbuf to IP stack */
			if (dst.smpls_addr.shim.bos)
				error = mpls_unlabel_inet(m);
			break;
#endif
#ifdef INET6
		case MPLS_LABEL_IPV6NULL:
			/* Pop shim and push mbuf to IPv6 stack */
			if (dst.smpls_addr.shim.bos)
				error = mpls_unlabel_inet6(m);
			break;
#endif
		case MPLS_LABEL_RTALERT:	/* Yeah, I'm all alerted */
		case MPLS_LABEL_IMPLNULL:	/* This is logical only */
		default:			/* Rest are not allowed */
			break;
		}
		goto done;
	}

	/* Check if we should do MPLS forwarding */
	error = EHOSTUNREACH;
	if (!mpls_forwarding)
		goto done;

	/* Get a route to dst */
	dst.smpls_addr.shim.ttl =
	    dst.smpls_addr.shim.bos =
	    dst.smpls_addr.shim.exp = 0;
	dst.smpls_addr.s_addr = htonl(dst.smpls_addr.s_addr);
	if ((rt = rtalloc1((const struct sockaddr*)&dst, 1)) == NULL)
		goto done;

	/* MPLS packet with no MPLS tagged route ? */
	if ((rt->rt_flags & RTF_GATEWAY) == 0 ||
	     rt_gettag(rt) == NULL ||
	     rt_gettag(rt)->sa_family != AF_MPLS)
		goto done;

	tshim.s_addr = MPLS_GETSADDR(rt);

	/* Swap labels */
	if ((m->m_len < sizeof(union mpls_shim)) &&
	    (m = m_pullup(m, sizeof(union mpls_shim))) == 0) {
		error = ENOBUFS;
		goto done;
	}

	/* Replace only the label */
	htag = mtod(m, union mpls_shim *);
	htag->s_addr = ntohl(htag->s_addr);
	htag->shim.label = tshim.shim.label;
	htag->s_addr = htonl(htag->s_addr);

	/* check if there is anything more to prepend */
	htag = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr;
	while (psize <= rt_gettag(rt)->sa_len - sizeof(tshim)) {
		htag++;
		memset(&tshim, 0, sizeof(tshim));
		tshim.s_addr = ntohl(htag->s_addr);
		tshim.shim.bos = tshim.shim.exp = 0;
		tshim.shim.ttl = mpls_defttl;
		if (tshim.shim.label != MPLS_LABEL_IMPLNULL &&
		    ((m = mpls_prepend_shim(m, &tshim)) == NULL))
			return ENOBUFS;
		psize += sizeof(tshim);
	}

	if (__predict_false(push_back_alert == true)) {
		/* re-add the router alert label */
		memset(&tshim, 0, sizeof(tshim));
		tshim.s_addr = MPLS_LABEL_RTALERT;
		tshim.shim.bos = tshim.shim.exp = 0;
		tshim.shim.ttl = mpls_defttl;
		if ((m = mpls_prepend_shim(m, &tshim)) == NULL)
			return ENOBUFS;
	}

	error = mpls_send_frame(m, rt->rt_ifp, rt);

done:
	if (error != 0 && m != NULL)
		m_freem(m);
	if (rt != NULL)
		RTFREE(rt);

	return error;
}
Пример #14
0
/*
 * Ethernet output routine.
 * Encapsulate a packet of type family for the local net.
 * Assumes that ifp is actually pointer to arpcom structure.
 */
int
ether_output(struct ifnet *ifp0, struct mbuf *m0, struct sockaddr *dst,
    struct rtentry *rt0)
{
	u_int16_t etype;
	int s, len, error = 0;
	u_char edst[ETHER_ADDR_LEN];
	u_char *esrc;
	struct mbuf *m = m0;
	struct rtentry *rt;
	struct mbuf *mcopy = NULL;
	struct ether_header *eh;
	struct arpcom *ac = (struct arpcom *)ifp0;
	short mflags;
	struct ifnet *ifp = ifp0;

#ifdef DIAGNOSTIC
	if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) {
		printf("%s: trying to send packet on wrong domain. "
		    "if %d vs. mbuf %d, AF %d\n", ifp->if_xname,
		    ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid),
		    dst->sa_family);
	}
#endif

#if NTRUNK > 0
	/* restrict transmission on trunk members to bpf only */
	if (ifp->if_type == IFT_IEEE8023ADLAG &&
	    (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL))
		senderr(EBUSY);
#endif

#if NCARP > 0
	if (ifp->if_type == IFT_CARP) {
		ifp = ifp->if_carpdev;
		ac = (struct arpcom *)ifp;

		if ((ifp0->if_flags & (IFF_UP|IFF_RUNNING)) !=
		    (IFF_UP|IFF_RUNNING))
			senderr(ENETDOWN);
	}
#endif /* NCARP > 0 */

	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
		senderr(ENETDOWN);
	if ((rt = rt0) != NULL) {
		if ((rt->rt_flags & RTF_UP) == 0) {
			if ((rt0 = rt = rtalloc1(dst, RT_REPORT,
			    m->m_pkthdr.ph_rtableid)) != NULL)
				rt->rt_refcnt--;
			else
				senderr(EHOSTUNREACH);
		}

		if (rt->rt_flags & RTF_GATEWAY) {
			if (rt->rt_gwroute == NULL)
				goto lookup;
			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
				rtfree(rt);
				rt = rt0;
			lookup:
				rt->rt_gwroute = rtalloc1(rt->rt_gateway,
				    RT_REPORT, ifp->if_rdomain);
				if ((rt = rt->rt_gwroute) == NULL)
					senderr(EHOSTUNREACH);
			}
		}
		if (rt->rt_flags & RTF_REJECT)
			if (rt->rt_rmx.rmx_expire == 0 ||
			    time_second < rt->rt_rmx.rmx_expire)
				senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
	}
	esrc = ac->ac_enaddr;
	switch (dst->sa_family) {

#ifdef INET
	case AF_INET:
		if (!arpresolve(ac, rt, m, dst, edst))
			return (0);	/* if not yet resolved */
		/* If broadcasting on a simplex interface, loopback a copy */
		if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) &&
		    !m->m_pkthdr.pf.routed)
			mcopy = m_copy(m, 0, (int)M_COPYALL);
		etype = htons(ETHERTYPE_IP);
		break;
#endif
#ifdef INET6
	case AF_INET6:
		if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst))
			return (0); /* it must be impossible, but... */
		etype = htons(ETHERTYPE_IPV6);
		break;
#endif
#ifdef MPLS
       case AF_MPLS:
		if (rt)
			dst = rt_key(rt);
		else
			senderr(EHOSTUNREACH);

		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
			senderr(ENETUNREACH);

		switch (dst->sa_family) {
			case AF_LINK:
				if (((struct sockaddr_dl *)dst)->sdl_alen <
				    sizeof(edst))
					senderr(EHOSTUNREACH);
				memcpy(edst, LLADDR((struct sockaddr_dl *)dst),
				    sizeof(edst));
				break;
			case AF_INET:
				if (!arpresolve(ac, rt, m, dst, edst))
					return (0); /* if not yet resolved */
				break;
			default:
				senderr(EHOSTUNREACH);
		}
		/* XXX handling for simplex devices in case of M/BCAST ?? */
		if (m->m_flags & (M_BCAST | M_MCAST))
			etype = htons(ETHERTYPE_MPLS_MCAST);
		else
			etype = htons(ETHERTYPE_MPLS);
		break;
#endif /* MPLS */
	case pseudo_AF_HDRCMPLT:
		eh = (struct ether_header *)dst->sa_data;
		esrc = eh->ether_shost;
		/* FALLTHROUGH */

	case AF_UNSPEC:
		eh = (struct ether_header *)dst->sa_data;
		memcpy(edst, eh->ether_dhost, sizeof(edst));
		/* AF_UNSPEC doesn't swap the byte order of the ether_type. */
		etype = eh->ether_type;
		break;

	default:
		printf("%s: can't handle af%d\n", ifp->if_xname,
			dst->sa_family);
		senderr(EAFNOSUPPORT);
	}

	/* XXX Should we feed-back an unencrypted IPsec packet ? */
	if (mcopy)
		(void) looutput(ifp, mcopy, dst, rt);

#if NCARP > 0
	if (ifp0 != ifp && ifp0->if_type == IFT_CARP)
		esrc = carp_get_srclladdr(ifp0, esrc);
#endif

	if (ether_addheader(&m, ifp, etype, esrc, edst) == -1)
		senderr(ENOBUFS);

#if NBRIDGE > 0
	/*
	 * Interfaces that are bridgeports need special handling for output.
	 */
	if (ifp->if_bridgeport) {
		struct m_tag *mtag;

		/*
		 * Check if this packet has already been sent out through
		 * this bridgeport, in which case we simply send it out
		 * without further bridge processing.
		 */
		for (mtag = m_tag_find(m, PACKET_TAG_BRIDGE, NULL); mtag;
		    mtag = m_tag_find(m, PACKET_TAG_BRIDGE, mtag)) {
#ifdef DEBUG
			/* Check that the information is there */
			if (mtag->m_tag_len != sizeof(caddr_t)) {
				error = EINVAL;
				goto bad;
			}
#endif
			if (!memcmp(&ifp->if_bridgeport, mtag + 1,
			    sizeof(caddr_t)))
				break;
		}
		if (mtag == NULL) {
			/* Attach a tag so we can detect loops */
			mtag = m_tag_get(PACKET_TAG_BRIDGE, sizeof(caddr_t),
			    M_NOWAIT);
			if (mtag == NULL) {
				error = ENOBUFS;
				goto bad;
			}
			memcpy(mtag + 1, &ifp->if_bridgeport, sizeof(caddr_t));
			m_tag_prepend(m, mtag);
			error = bridge_output(ifp, m, NULL, NULL);
			return (error);
		}
	}
#endif
	mflags = m->m_flags;
	len = m->m_pkthdr.len;
	s = splnet();
	/*
	 * Queue message on interface, and start output if interface
	 * not yet active.
	 */
	IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
	if (error) {
		/* mbuf is already freed */
		splx(s);
		return (error);
	}
	ifp->if_obytes += len;
#if NCARP > 0
	if (ifp != ifp0)
		ifp0->if_obytes += len;
#endif /* NCARP > 0 */
	if (mflags & M_MCAST)
		ifp->if_omcasts++;
	if_start(ifp);
	splx(s);
	return (error);

bad:
	if (m)
		m_freem(m);
	return (error);
}
Пример #15
0
/*
 * Input a Neighbor Solicitation Message.
 *
 * Based on RFC 2461
 * Based on RFC 2462 (duplicate address detection)
 */
void
nd6_ns_input(struct mbuf *m, int off, int icmp6len)
{
    struct ifnet *ifp = m->m_pkthdr.rcvif;
    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;
    int lladdrlen = 0;
    int anycast = 0, proxy = 0, tentative = 0;
    int router = ip6_forwarding;
    int tlladdr;
    union nd_opts ndopts;
    const struct sockaddr_dl *proxydl = NULL;

    IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
    if (nd_ns == NULL) {
        ICMP6_STATINC(ICMP6_STAT_TOOSHORT);
        return;
    }
    ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
    taddr6 = nd_ns->nd_ns_target;
    if (in6_setscope(&taddr6, ifp, NULL) != 0)
        goto bad;

    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 a solicited node multicast address. */
        /* don't check ifindex portion */
        if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL &&
                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 {
        struct sockaddr_in6 ssin6;

        /*
         * Make sure the source address is from a neighbor's address.
         */
        sockaddr_in6_init(&ssin6, &saddr6, 0, 0, 0);
        if (nd6_is_addr_neighbor(&ssin6, ifp) == 0) {
            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;
    }

    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 multicast			MUST add
     * Otherwise					MAY be omitted
     *
     * In this implementation, we omit the target link-layer address
     * in the "MAY" case.
     */
#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 address for which I'm offering proxy service, or
     * (3) "tentative" address on which DAD is being performed.
     */
    /* (1) and (3) check. */
#if NCARP > 0
    if (ifp->if_carp && ifp->if_type != IFT_CARP)
        ifa = carp_iamatch6(ifp->if_carp, &taddr6);
    else
        ifa = NULL;
    if (!ifa)
        ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
#else
    ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
#endif

    /* (2) check. */
    if (ifa == NULL) {
        struct rtentry *rt;
        struct sockaddr_in6 tsin6;

        sockaddr_in6_init(&tsin6, &taddr6, 0, 0, 0);

        rt = rtalloc1((struct sockaddr *)&tsin6, 0);
        if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
                rt->rt_gateway->sa_family == AF_LINK) {
            /*
             * proxy NDP for single entry
             */
            ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
                    IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
            if (ifa) {
                proxy = 1;
                proxydl = satocsdl(rt->rt_gateway);
                router = 0;	/* XXX */
            }
        }
        if (rt)
            rtfree(rt);
    }
    if (ifa == NULL) {
        /*
         * We've got an NS packet, and we don't have that address
         * 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 && ((ifp->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), ifp->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
         * duplicate 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)) {
        struct in6_addr in6_all;

        in6_all = in6addr_linklocal_allnodes;
        if (in6_setscope(&in6_all, ifp, NULL) != 0)
            goto bad;
        nd6_na_output(ifp, &in6_all, &taddr6,
                      ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
                      (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
                      tlladdr, (const struct sockaddr *)proxydl);
        goto freeit;
    }

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

    nd6_na_output(ifp, &saddr6, &taddr6,
                  ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
                  (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
                  tlladdr, (const 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)));
    ICMP6_STATINC(ICMP6_STAT_BADNS);
    m_freem(m);
}
Пример #16
0
/*
 * prepend shim and deliver
 */
static int
mpls_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct rtentry *rt)
{
	union mpls_shim mh, *pms;
	struct rtentry *rt1;
	int err;
	uint psize = sizeof(struct sockaddr_mpls);

	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
		m_freem(m);
		return ENETDOWN;
	}

	if (rt_gettag(rt) == NULL || rt_gettag(rt)->sa_family != AF_MPLS) {
		m_freem(m);
		return EINVAL;
	}

	bpf_mtap_af(ifp, dst->sa_family, m);

	memset(&mh, 0, sizeof(mh));
	mh.s_addr = MPLS_GETSADDR(rt);
	mh.shim.bos = 1;
	mh.shim.exp = 0;
	mh.shim.ttl = mpls_defttl;

	pms = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr;

	while (psize <= rt_gettag(rt)->sa_len - sizeof(mh)) {
		pms++;
		if (mh.shim.label != MPLS_LABEL_IMPLNULL &&
		    ((m = mpls_prepend_shim(m, &mh)) == NULL))
			return ENOBUFS;
		memset(&mh, 0, sizeof(mh));
		mh.s_addr = ntohl(pms->s_addr);
		mh.shim.bos = mh.shim.exp = 0;
		mh.shim.ttl = mpls_defttl;
		psize += sizeof(mh);
	}

	switch(dst->sa_family) {
#ifdef INET
	case AF_INET:
		m = mpls_label_inet(m, &mh, psize - sizeof(struct sockaddr_mpls));
		break;
#endif
#ifdef INET6
	case AF_INET6:
		m = mpls_label_inet6(m, &mh, psize - sizeof(struct sockaddr_mpls));
		break;
#endif
	default:
		m = mpls_prepend_shim(m, &mh);
		break;
	}

	if (m == NULL) {
		IF_DROP(&ifp->if_snd);
		ifp->if_oerrors++;
		return ENOBUFS;
	}

	ifp->if_opackets++;
	ifp->if_obytes += m->m_pkthdr.len;

	if ((rt1=rtalloc1(rt->rt_gateway, 1)) == NULL) {
		m_freem(m);
		return EHOSTUNREACH;
	}

	err = mpls_send_frame(m, rt1->rt_ifp, rt);
	RTFREE(rt1);
	return err;
}
Пример #17
0
/*
 * Do what we need to do when inserting a route.
 */
static struct radix_node *
in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
	    struct radix_node *treenodes)
{
	struct rtentry *rt = (struct rtentry *)treenodes;
	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
	struct radix_node *ret;

	if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
		rt->rt_flags |= RTF_MULTICAST;

	/*
	 * A little bit of help for both IPv6 output and input:
	 *   For local addresses, we make sure that RTF_LOCAL is set,
	 *   with the thought that this might one day be used to speed up
	 *   ip_input().
	 *
	 * We also mark routes to multicast addresses as such, because
	 * it's easy to do and might be useful (but this is much more
	 * dubious since it's so easy to inspect the address).  (This
	 * is done above.)
	 *
	 * XXX
	 * should elaborate the code.
	 */
	if (rt->rt_flags & RTF_HOST) {
		if (IN6_ARE_ADDR_EQUAL(&satosin6(rt->rt_ifa->ifa_addr)
					->sin6_addr,
				       &sin6->sin6_addr)) {
			rt->rt_flags |= RTF_LOCAL;
		}
	}

	if (!rt->rt_rmx.rmx_mtu && rt->rt_ifp)
		rt->rt_rmx.rmx_mtu = IN6_LINKMTU(rt->rt_ifp);

	ret = rn_addroute(v_arg, n_arg, head, treenodes);
	if (ret == NULL && rt->rt_flags & RTF_HOST) {
		struct rtentry *rt2;
		/*
		 * We are trying to add a host route, but can't.
		 * Find out if it is because of an
		 * ARP entry and delete it if so.
		 */
		rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_CLONING);
		if (rt2) {
			if (rt2->rt_flags & RTF_LLINFO &&
				rt2->rt_flags & RTF_HOST &&
				rt2->rt_gateway &&
				rt2->rt_gateway->sa_family == AF_LINK) {
				rtexpunge(rt2);
				RTFREE_LOCKED(rt2);
				ret = rn_addroute(v_arg, n_arg, head,
					treenodes);
			} else
				RTFREE_LOCKED(rt2);
		}
	} else if (ret == NULL && rt->rt_flags & RTF_CLONING) {
		struct rtentry *rt2;
		/*
		 * We are trying to add a net route, but can't.
		 * The following case should be allowed, so we'll make a
		 * special check for this:
		 *	Two IPv6 addresses with the same prefix is assigned
		 *	to a single interrface.
		 *	# ifconfig if0 inet6 3ffe:0501::1 prefix 64 alias (*1)
		 *	# ifconfig if0 inet6 3ffe:0501::2 prefix 64 alias (*2)
		 *	In this case, (*1) and (*2) want to add the same
		 *	net route entry, 3ffe:0501:: -> if0.
		 *	This case should not raise an error.
		 */
		rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_CLONING);
		if (rt2) {
			if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY))
					== RTF_CLONING
			 && rt2->rt_gateway
			 && rt2->rt_gateway->sa_family == AF_LINK
			 && rt2->rt_ifp == rt->rt_ifp) {
				ret = rt2->rt_nodes;
			}
			RTFREE_LOCKED(rt2);
		}
	}
	return ret;
}
Пример #18
0
STATUS arpResolve
    (
    char                *targetAddr,  /* name or Internet address of target */
    char                *pHwAddr,     /* where to return the H/W address */
    int                 numTries,     /* number of times to try ARPing (-1 means try
                                         forever) */
    int                 numTicks      /* number of ticks between ARPs */
    )
    {
    struct ifnet *	pIf = NULL;
    struct sockaddr_in  sockInetAddr;
    struct rtentry *	pRt;
    unsigned long	addr;
    int			retVal = 0;

    if (pHwAddr == NULL || numTries < -1 || numTries == 0)     /* user messed up */
	{
	errno = S_arpLib_INVALID_ARGUMENT;
        return (ERROR);
	}

    /* the 'targetAddr' can either be the hostname or the actual Internet
     * address.
     */

    if ((addr = (unsigned long) hostGetByName (targetAddr)) == ERROR &&
	(addr = inet_addr (targetAddr)) == ERROR)
	{
	errno = S_arpLib_INVALID_HOST;
	return (ERROR);
	}

    bzero ((caddr_t)&sockInetAddr, sizeof (sockInetAddr));
    sockInetAddr.sin_len = sizeof(struct sockaddr_in);
    sockInetAddr.sin_family = AF_INET;
    sockInetAddr.sin_addr.s_addr = addr; 

    /*
     * Get associated local interface's ifnet. This search also
     * clones an empty ARP entry from the interface route if one
     * does not already exist.
     */

    pRt = rtalloc1 ( (struct sockaddr *)&sockInetAddr, 1);
    if (pRt == NULL)
	{
	errno = S_arpLib_INVALID_HOST;
	return (ERROR);
	}

    pIf = pRt->rt_ifp;
    if (pIf == NULL)
	{
        rtfree (pRt);
	errno = S_arpLib_INVALID_HOST;
	return (ERROR);
	}

    /* return 0xffffffffffff for broadcast Internet address */

    if (in_broadcast (sockInetAddr.sin_addr, pIf))
	{
        bcopy ((char *) etherbroadcastaddr, pHwAddr,
	       sizeof (etherbroadcastaddr));

        rtfree (pRt);

	return (OK);
	}

    /* Try to resolve the Ethernet address by calling arpresolve() which
     * may send out ARP request messages out onto the Ethernet wire.
     */

    while ((numTries == -1 || numTries-- > 0) &&
	   (retVal = arpresolve ((struct arpcom *) pIf, 
				 (struct rtentry *)pRt, 
				 (struct mbuf *) NULL,
				 (struct sockaddr *)&sockInetAddr,
				 (UCHAR *)pHwAddr))
	   == 0)
    	if (numTries)           /* don't delay after last arp */
            taskDelay (numTicks);

    rtfree (pRt);

    if (retVal == 0)		/* unsuccessful resolution */
	{
	errno = S_arpLib_INVALID_HOST;
        return (ERROR);
	}

    return (OK);
    }
Пример #19
0
int
rtrequest1(int req, struct rt_addrinfo *info, u_int8_t prio,
    struct rtentry **ret_nrt, u_int tableid)
{
	int			 s = splsoftnet(); int error = 0;
	struct rtentry		*rt, *crt;
	struct radix_node	*rn;
	struct radix_node_head	*rnh;
	struct ifaddr		*ifa;
	struct sockaddr		*ndst;
	struct sockaddr_rtlabel	*sa_rl, sa_rl2;
#ifdef MPLS
	struct sockaddr_mpls	*sa_mpls;
#endif
#define senderr(x) { error = x ; goto bad; }

	if ((rnh = rt_gettable(info->rti_info[RTAX_DST]->sa_family, tableid)) ==
	    NULL)
		senderr(EAFNOSUPPORT);
	if (info->rti_flags & RTF_HOST)
		info->rti_info[RTAX_NETMASK] = NULL;
	switch (req) {
	case RTM_DELETE:
		if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_NETMASK], rnh)) == NULL)
			senderr(ESRCH);
		rt = (struct rtentry *)rn;
#ifndef SMALL_KERNEL
		/*
		 * if we got multipath routes, we require users to specify
		 * a matching RTAX_GATEWAY.
		 */
		if (rn_mpath_capable(rnh)) {
			rt = rt_mpath_matchgate(rt,
			    info->rti_info[RTAX_GATEWAY], prio);
			rn = (struct radix_node *)rt;
			if (!rt)
				senderr(ESRCH);
		}
#endif
		if ((rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_NETMASK], rnh, rn)) == NULL)
			senderr(ESRCH);
		rt = (struct rtentry *)rn;

		/* clean up any cloned children */
		if ((rt->rt_flags & RTF_CLONING) != 0)
			rtflushclone(rnh, rt);

		if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
			panic ("rtrequest delete");

		if (rt->rt_gwroute) {
			rt = rt->rt_gwroute; RTFREE(rt);
			(rt = (struct rtentry *)rn)->rt_gwroute = NULL;
		}

		if (rt->rt_parent) {
			rt->rt_parent->rt_refcnt--;
			rt->rt_parent = NULL;
		}

#ifndef SMALL_KERNEL
		if (rn_mpath_capable(rnh)) {
			if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST],
			    info->rti_info[RTAX_NETMASK], rnh)) != NULL &&
			    rn_mpath_next(rn, 0) == NULL)
				((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH;
		}
#endif

		rt->rt_flags &= ~RTF_UP;
		if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
			ifa->ifa_rtrequest(RTM_DELETE, rt, info);
		rttrash++;

		if (ret_nrt)
			*ret_nrt = rt;
		else if (rt->rt_refcnt <= 0) {
			rt->rt_refcnt++;
			rtfree(rt);
		}
		break;

	case RTM_RESOLVE:
		if (ret_nrt == NULL || (rt = *ret_nrt) == NULL)
			senderr(EINVAL);
		if ((rt->rt_flags & RTF_CLONING) == 0)
			senderr(EINVAL);
		ifa = rt->rt_ifa;
		info->rti_flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC);
		info->rti_flags |= RTF_CLONED;
		info->rti_info[RTAX_GATEWAY] = rt->rt_gateway;
		if ((info->rti_info[RTAX_NETMASK] = rt->rt_genmask) == NULL)
			info->rti_flags |= RTF_HOST;
		info->rti_info[RTAX_LABEL] =
		    rtlabel_id2sa(rt->rt_labelid, &sa_rl2);
		goto makeroute;

	case RTM_ADD:
		if (info->rti_ifa == 0 && (error = rt_getifa(info, tableid)))
			senderr(error);
		ifa = info->rti_ifa;
makeroute:
		rt = pool_get(&rtentry_pool, PR_NOWAIT | PR_ZERO);
		if (rt == NULL)
			senderr(ENOBUFS);

		rt->rt_flags = info->rti_flags;

		if (prio == 0)
			prio = ifa->ifa_ifp->if_priority + RTP_STATIC;
		rt->rt_priority = prio;	/* init routing priority */
		if ((LINK_STATE_IS_UP(ifa->ifa_ifp->if_link_state) ||
		    ifa->ifa_ifp->if_link_state == LINK_STATE_UNKNOWN) &&
		    ifa->ifa_ifp->if_flags & IFF_UP)
			rt->rt_flags |= RTF_UP;
		else {
			rt->rt_flags &= ~RTF_UP;
			rt->rt_priority |= RTP_DOWN;
		}
		LIST_INIT(&rt->rt_timer);
		if (rt_setgate(rt, info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_GATEWAY], tableid)) {
			pool_put(&rtentry_pool, rt);
			senderr(ENOBUFS);
		}
		ndst = rt_key(rt);
		if (info->rti_info[RTAX_NETMASK] != NULL) {
			rt_maskedcopy(info->rti_info[RTAX_DST], ndst,
			    info->rti_info[RTAX_NETMASK]);
		} else
			Bcopy(info->rti_info[RTAX_DST], ndst,
			    info->rti_info[RTAX_DST]->sa_len);
#ifndef SMALL_KERNEL
		/* do not permit exactly the same dst/mask/gw pair */
		if (rn_mpath_capable(rnh) &&
		    rt_mpath_conflict(rnh, rt, info->rti_info[RTAX_NETMASK],
		    info->rti_flags & RTF_MPATH)) {
			if (rt->rt_gwroute)
				rtfree(rt->rt_gwroute);
			Free(rt_key(rt));
			pool_put(&rtentry_pool, rt);
			senderr(EEXIST);
		}
#endif

		if (info->rti_info[RTAX_LABEL] != NULL) {
			sa_rl = (struct sockaddr_rtlabel *)
			    info->rti_info[RTAX_LABEL];
			rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label);
		}

#ifdef MPLS
		/* We have to allocate additional space for MPLS infos */ 
		if (info->rti_info[RTAX_SRC] != NULL ||
		    info->rti_info[RTAX_DST]->sa_family == AF_MPLS) {
			struct rt_mpls *rt_mpls;

			sa_mpls = (struct sockaddr_mpls *)
			    info->rti_info[RTAX_SRC];

			rt->rt_llinfo = (caddr_t)malloc(sizeof(struct rt_mpls),
			    M_TEMP, M_NOWAIT|M_ZERO);

			if (rt->rt_llinfo == NULL) {
				if (rt->rt_gwroute)
					rtfree(rt->rt_gwroute);
				Free(rt_key(rt));
				pool_put(&rtentry_pool, rt);
				senderr(ENOMEM);
			}

			rt_mpls = (struct rt_mpls *)rt->rt_llinfo;

			if (sa_mpls != NULL)
				rt_mpls->mpls_label = sa_mpls->smpls_label;

			rt_mpls->mpls_operation = info->rti_mpls;

			/* XXX: set experimental bits */

			rt->rt_flags |= RTF_MPLS;
		}
#endif

		ifa->ifa_refcnt++;
		rt->rt_ifa = ifa;
		rt->rt_ifp = ifa->ifa_ifp;
		if (req == RTM_RESOLVE) {
			/*
			 * Copy both metrics and a back pointer to the cloned
			 * route's parent.
			 */
			rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
			rt->rt_priority = (*ret_nrt)->rt_priority;
			rt->rt_parent = *ret_nrt;	 /* Back ptr. to parent. */
			rt->rt_parent->rt_refcnt++;
		}
		rn = rnh->rnh_addaddr((caddr_t)ndst,
		    (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes,
		    rt->rt_priority);
		if (rn == NULL && (crt = rtalloc1(ndst, 0, tableid)) != NULL) {
			/* overwrite cloned route */
			if ((crt->rt_flags & RTF_CLONED) != 0) {
				rtdeletemsg(crt, tableid);
				rn = rnh->rnh_addaddr((caddr_t)ndst,
				    (caddr_t)info->rti_info[RTAX_NETMASK],
				    rnh, rt->rt_nodes, rt->rt_priority);
			}
			RTFREE(crt);
		}
		if (rn == 0) {
			IFAFREE(ifa);
			if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent)
				rtfree(rt->rt_parent);
			if (rt->rt_gwroute)
				rtfree(rt->rt_gwroute);
			Free(rt_key(rt));
			pool_put(&rtentry_pool, rt);
			senderr(EEXIST);
		}

#ifndef SMALL_KERNEL
		if (rn_mpath_capable(rnh) &&
		    (rn = rnh->rnh_lookup(info->rti_info[RTAX_DST],
		    info->rti_info[RTAX_NETMASK], rnh)) != NULL &&
		    (rn = rn_mpath_prio(rn, prio)) != NULL) {
			if (rn_mpath_next(rn, 0) == NULL)
				((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH;
			else
				((struct rtentry *)rn)->rt_flags |= RTF_MPATH;
		}
#endif

		if (ifa->ifa_rtrequest)
			ifa->ifa_rtrequest(req, rt, info);
		if (ret_nrt) {
			*ret_nrt = rt;
			rt->rt_refcnt++;
		}
		if ((rt->rt_flags & RTF_CLONING) != 0) {
			/* clean up any cloned children */
			rtflushclone(rnh, rt);
		}

		if_group_routechange(info->rti_info[RTAX_DST],
			info->rti_info[RTAX_NETMASK]);
		break;
	}
bad:
	splx(s);
	return (error);
}
/*
 * Ethernet output routine.
 * Encapsulate a packet of type family for the local net.
 * Assumes that ifp is actually pointer to ethercom structure.
 */
int
ssh_interceptor_ether_output(struct ifnet *ifp, struct mbuf *m0,
                             struct sockaddr *dst, struct rtentry *rt0)
{
        u_int16_t etype = 0;
        int s, error = 0, hdrcmplt = 0;
        u_char esrc[6], edst[6];
        struct mbuf *m = m0;
        struct rtentry *rt;
        struct mbuf *mcopy = (struct mbuf *)0;
        struct ether_header *eh, ehd;
#ifdef INET
        struct arphdr *ah;
#endif /* INET */
#ifdef NETATALK
        struct at_ifaddr *aa;
#endif /* NETATALK */

        if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
                senderr(ENETDOWN);
        ifp->if_lastchange = time;
        if ((rt = rt0) != NULL) {
                if ((rt->rt_flags & RTF_UP) == 0) {
                        if ((rt0 = rt = rtalloc1(dst, 1)) != NULL) {
                                rt->rt_refcnt--;
                                if (rt->rt_ifp != ifp)
                                        return (*rt->rt_ifp->if_output)
                                                        (ifp, m0, dst, rt);
                        } else
                                senderr(EHOSTUNREACH);
                }
                if ((rt->rt_flags & RTF_GATEWAY) && dst->sa_family != AF_NS) {
                        if (rt->rt_gwroute == 0)
                                goto lookup;
                        if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
                                rtfree(rt); rt = rt0;
                        lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
                                if ((rt = rt->rt_gwroute) == 0)
                                        senderr(EHOSTUNREACH);
                                /* the "G" test below also prevents rt == rt0 */
                                if ((rt->rt_flags & RTF_GATEWAY) ||
                                    (rt->rt_ifp != ifp)) {
                                        rt->rt_refcnt--;
                                        rt0->rt_gwroute = 0;
                                        senderr(EHOSTUNREACH);
                                }
                        }
                }
                if (rt->rt_flags & RTF_REJECT)
                        if (rt->rt_rmx.rmx_expire == 0 ||
                            time.tv_sec < rt->rt_rmx.rmx_expire)
                                senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
        }
        switch (dst->sa_family) {

#ifdef INET
        case AF_INET:
                if (m->m_flags & M_BCAST)
                        bcopy((caddr_t)etherbroadcastaddr, (caddr_t)edst,
                                sizeof(edst));

                else if (m->m_flags & M_MCAST) {
                        ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr,
                            (caddr_t)edst)

                } else if (!arpresolve(ifp, rt, m, dst, edst))
                        return (0);     /* if not yet resolved */
                /* If broadcasting on a simplex interface, loopback a copy */
                if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
                etype = htons(ETHERTYPE_IP);
                break;

        case AF_ARP:
                ah = mtod(m, struct arphdr *);
                if (m->m_flags & M_BCAST)
                        bcopy((caddr_t)etherbroadcastaddr, (caddr_t)edst,
                                sizeof(edst));
                else
                        bcopy((caddr_t)ar_tha(ah),
                                (caddr_t)edst, sizeof(edst));

                ah->ar_hrd = htons(ARPHRD_ETHER);

                switch(ntohs(ah->ar_op)) {
                case ARPOP_REVREQUEST:
                case ARPOP_REVREPLY:
                        etype = htons(ETHERTYPE_REVARP);
                        break;

                case ARPOP_REQUEST:
                case ARPOP_REPLY:
                default:
                        etype = htons(ETHERTYPE_ARP);
                }

                break;
#endif
#ifdef INET6
        case AF_INET6:
#ifdef OLDIP6OUTPUT
                if (!nd6_resolve(ifp, rt, m, dst, (u_char *)edst))
                        return(0);      /* if not yet resolves */
#else
                if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst)){
                        /* this must be impossible, so we bark */
                        printf("nd6_storelladdr failed\n");
                        return(0);
                }
#endif /* OLDIP6OUTPUT */
                etype = htons(ETHERTYPE_IPV6);
                break;
#endif
#ifdef NETATALK
    case AF_APPLETALK:
                if (!aarpresolve(ifp, m, (struct sockaddr_at *)dst, edst)) {
#ifdef NETATALKDEBUG
                        printf("aarpresolv failed\n");
#endif /* NETATALKDEBUG */
                        return (0);
                }
                /*
                 * ifaddr is the first thing in at_ifaddr
                 */
                aa = (struct at_ifaddr *) at_ifawithnet(
                    (struct sockaddr_at *)dst, ifp);
                if (aa == NULL)
                    goto bad;

                /*
                 * In the phase 2 case, we need to prepend an mbuf for the
                 * llc header.  Since we must preserve the value of m,
                 * which is passed to us by value, we m_copy() the first
                 * mbuf, and use it for our llc header.
                 */
                if (aa->aa_flags & AFA_PHASE2) {
                        struct llc llc;

                        M_PREPEND(m, sizeof(struct llc), M_DONTWAIT);
                        llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
                        llc.llc_control = LLC_UI;
                        bcopy(at_org_code, llc.llc_snap_org_code,
                            sizeof(llc.llc_snap_org_code));
                        llc.llc_snap_ether_type = htons(ETHERTYPE_ATALK);
                        bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc));
                } else {
                        etype = htons(ETHERTYPE_ATALK);
                }
                break;
#endif /* NETATALK */
#ifdef NS
        case AF_NS:
                etype = htons(ETHERTYPE_NS);
                bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
                    (caddr_t)edst, sizeof (edst));
                if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst)))
                        return (looutput(ifp, m, dst, rt));
                /* If broadcasting on a simplex interface, loopback a copy */
                if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
                break;
#endif
#ifdef IPX
        case AF_IPX:
                etype = htons(ETHERTYPE_IPX);
                bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host),
                    (caddr_t)edst, sizeof (edst));
                /* If broadcasting on a simplex interface, loopback a copy */
                if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
                        mcopy = m_copy(m, 0, (int)M_COPYALL);
                break;
#endif
#ifdef  ISO
        case AF_ISO: {
                int     snpalen;
                struct  llc *l;
                struct sockaddr_dl *sdl;

                if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway) &&
                    sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) {
                        bcopy(LLADDR(sdl), (caddr_t)edst, sizeof(edst));
                } else {
                        error = iso_snparesolve(ifp, (struct sockaddr_iso *)dst,
                                                (char *)edst, &snpalen);
                        if (error)
                                goto bad; /* Not Resolved */
                }
                /* If broadcasting on a simplex interface, loopback a copy */
                if (*edst & 1)
                        m->m_flags |= (M_BCAST|M_MCAST);
                if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) &&
                    (mcopy = m_copy(m, 0, (int)M_COPYALL))) {
                        M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
                        if (mcopy) {
                                eh = mtod(mcopy, struct ether_header *);
                                bcopy((caddr_t)edst,
                                      (caddr_t)eh->ether_dhost, sizeof (edst));
                                bcopy(LLADDR(ifp->if_sadl),
                                      (caddr_t)eh->ether_shost, sizeof (edst));
                        }
                }
                M_PREPEND(m, 3, M_DONTWAIT);
                if (m == NULL)
                        return (0);
                l = mtod(m, struct llc *);
                l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP;
                l->llc_control = LLC_UI;
#ifdef ARGO_DEBUG
                if (argo_debug[D_ETHER]) {
                        int i;
                        printf("unoutput: sending pkt to: ");
                        for (i=0; i<6; i++)
                                printf("%x ", edst[i] & 0xff);
                        printf("\n");
                }
#endif
                } break;
#endif /* ISO */
#ifdef  LLC
/*      case AF_NSAP: */
        case AF_CCITT: {
                struct sockaddr_dl *sdl =
                        (struct sockaddr_dl *) rt -> rt_gateway;

                if (sdl && sdl->sdl_family == AF_LINK
                    && sdl->sdl_alen > 0) {
                        bcopy(LLADDR(sdl), (char *)edst,
                                sizeof(edst));
                } else goto bad; /* Not a link interface ? Funny ... */
                if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) &&
                    (mcopy = m_copy(m, 0, (int)M_COPYALL))) {
                        M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
                        if (mcopy) {
                                eh = mtod(mcopy, struct ether_header *);
                                bcopy((caddr_t)edst,
                                      (caddr_t)eh->ether_dhost, sizeof (edst));
                                bcopy(LLADDR(ifp->if_sadl),
                                      (caddr_t)eh->ether_shost, sizeof (edst));
                        }
                }
#ifdef LLC_DEBUG
                {
                        int i;
                        struct llc *l = mtod(m, struct llc *);

                        printf("ether_output: sending LLC2 pkt to: ");
                        for (i=0; i<6; i++)
                                printf("%x ", edst[i] & 0xff);
                        printf(" len 0x%x dsap 0x%x ssap 0x%x control 0x%x\n",
                            m->m_pkthdr.len, l->llc_dsap & 0xff, l->llc_ssap &0xff,
                            l->llc_control & 0xff);

                }
#endif /* LLC_DEBUG */
                } break;
Пример #21
0
/*
 * Do what we need to do when inserting a route.
 */
static struct radix_node *
in_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
	    struct radix_node *treenodes)
{
	struct rtentry *rt = (struct rtentry *)treenodes;
	struct sockaddr_in *sin = (struct sockaddr_in *)rt_key(rt);
	struct radix_node *ret;

	/*
	 * For IP, all unicast non-host routes are automatically cloning.
	 */
	if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
		rt->rt_flags |= RTF_MULTICAST;

	if(!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) {
		rt->rt_flags |= RTF_PRCLONING;
	}

	/*
	 * A little bit of help for both IP output and input:
	 *   For host routes, we make sure that RTF_BROADCAST
	 *   is set for anything that looks like a broadcast address.
	 *   This way, we can avoid an expensive call to in_broadcast()
	 *   in ip_output() most of the time (because the route passed
	 *   to ip_output() is almost always a host route).
	 *
	 *   We also do the same for local addresses, with the thought
	 *   that this might one day be used to speed up ip_input().
	 *
	 * We also mark routes to multicast addresses as such, because
	 * it's easy to do and might be useful (but this is much more
	 * dubious since it's so easy to inspect the address).  (This
	 * is done above.)
	 */
	if (rt->rt_flags & RTF_HOST) {
		if (in_broadcast(sin->sin_addr, rt->rt_ifp)) {
			rt->rt_flags |= RTF_BROADCAST;
		} else {
#define satosin(sa) ((struct sockaddr_in *)sa)
			if (satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr
			    == sin->sin_addr.s_addr)
				rt->rt_flags |= RTF_LOCAL;
#undef satosin
		}
	}

	/*
	 * We also specify a send and receive pipe size for every
	 * route added, to help TCP a bit.  TCP doesn't actually
	 * want a true pipe size, which would be prohibitive in memory
	 * costs and is hard to compute anyway; it simply uses these
	 * values to size its buffers.  So, we fill them in with the
	 * same values that TCP would have used anyway, and allow the
	 * installing program or the link layer to override these values
	 * as it sees fit.  This will hopefully allow TCP more
	 * opportunities to save its ssthresh value.
	 */
	if (!rt->rt_rmx.rmx_sendpipe && !(rt->rt_rmx.rmx_locks & RTV_SPIPE))
		rt->rt_rmx.rmx_sendpipe = tcp_sendspace;

	if (!rt->rt_rmx.rmx_recvpipe && !(rt->rt_rmx.rmx_locks & RTV_RPIPE))
		rt->rt_rmx.rmx_recvpipe = tcp_recvspace;

	if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) 
	    && rt->rt_ifp)
		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;

	ret = rn_addroute(v_arg, n_arg, head, treenodes);
	if (ret == NULL && rt->rt_flags & RTF_HOST) {
		struct rtentry *rt2;
		/*
		 * We are trying to add a host route, but can't.
		 * Find out if it is because of an
		 * ARP entry and delete it if so.
		 */
		rt2 = rtalloc1((struct sockaddr *)sin, 0,
				RTF_CLONING | RTF_PRCLONING);
		if (rt2) {
			if (rt2->rt_flags & RTF_LLINFO &&
				rt2->rt_flags & RTF_HOST &&
				rt2->rt_gateway &&
				rt2->rt_gateway->sa_family == AF_LINK) {
				rtrequest(RTM_DELETE,
					  (struct sockaddr *)rt_key(rt2),
					  rt2->rt_gateway,
					  rt_mask(rt2), rt2->rt_flags, 0);
				ret = rn_addroute(v_arg, n_arg, head,
					treenodes);
			}
			RTFREE(rt2);
		}
	}
	return ret;
}
Пример #22
0
/*
 * Do what we need to do when inserting a route.
 */
static struct radix_node *
in_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
	    struct radix_node *treenodes)
{
	struct rtentry *rt = (struct rtentry *)treenodes;
	struct sockaddr_in *sin = (struct sockaddr_in *)rt_key(rt);
	struct radix_node *ret;

	/*
	 * For IP, all unicast non-host routes are automatically cloning.
	 */
	if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
		rt->rt_flags |= RTF_MULTICAST;

	if(!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) {
		rt->rt_flags |= RTF_PRCLONING;
	}

	/*
	 * A little bit of help for both IP output and input:
	 *   For host routes, we make sure that RTF_BROADCAST
	 *   is set for anything that looks like a broadcast address.
	 *   This way, we can avoid an expensive call to in_broadcast()
	 *   in ip_output() most of the time (because the route passed
	 *   to ip_output() is almost always a host route).
	 *
	 *   We also do the same for local addresses, with the thought
	 *   that this might one day be used to speed up ip_input().
	 *
	 * We also mark routes to multicast addresses as such, because
	 * it's easy to do and might be useful (but this is much more
	 * dubious since it's so easy to inspect the address).  (This
	 * is done above.)
	 */
	if (rt->rt_flags & RTF_HOST) {
		if (in_broadcast(sin->sin_addr, rt->rt_ifp)) {
			rt->rt_flags |= RTF_BROADCAST;
		} else {
#define satosin(sa) ((struct sockaddr_in *)sa)
			if (satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr
			    == sin->sin_addr.s_addr)
				rt->rt_flags |= RTF_LOCAL;
#undef satosin
		}
	}

	if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) 
	    && rt->rt_ifp)
		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;

	ret = rn_addroute(v_arg, n_arg, head, treenodes);
	if (ret == NULL && rt->rt_flags & RTF_HOST) {
		struct rtentry *rt2;
		/*
		 * We are trying to add a host route, but can't.
		 * Find out if it is because of an
		 * ARP entry and delete it if so.
		 */
		rt2 = rtalloc1((struct sockaddr *)sin, 0,
				RTF_CLONING | RTF_PRCLONING);
		if (rt2) {
			if (rt2->rt_flags & RTF_LLINFO &&
				rt2->rt_flags & RTF_HOST &&
				rt2->rt_gateway &&
				rt2->rt_gateway->sa_family == AF_LINK) {
				rtrequest(RTM_DELETE,
					  (struct sockaddr *)rt_key(rt2),
					  rt2->rt_gateway,
					  rt_mask(rt2), rt2->rt_flags, 0);
				ret = rn_addroute(v_arg, n_arg, head,
					treenodes);
			}
			RTFREE(rt2);
		}
	}
	return ret;
}
Пример #23
0
/*
 * NOTE: in6_ifdetach() does not support loopback if at this moment.
 * We don't need this function in bsdi, because interfaces are never removed
 * from the ifnet list in bsdi.
 */
void
in6_ifdetach(struct ifnet *ifp)
{
	struct in6_ifaddr *ia, *oia;
	struct ifaddr *ifa, *next;
	struct rtentry *rt;
	short rtflags;
	struct in6_multi_mship *imm;

	/* remove ip6_mrouter stuff */
	ip6_mrouter_detach(ifp);

	/* remove neighbor management table */
	nd6_purge(ifp);

	/* XXX this code is duplicated in in6_purgeif() --dyoung */
	/* nuke any of IPv6 addresses we have */
	if_purgeaddrs(ifp, AF_INET6, in6_purgeaddr);

	/* XXX isn't this code is redundant, given the above? --dyoung */
	/* XXX doesn't this code replicate code in in6_purgeaddr() ? --dyoung */
	/* undo everything done by in6_ifattach(), just in case */
	for (ifa = IFADDR_FIRST(ifp); ifa != NULL; ifa = next) {
		next = IFADDR_NEXT(ifa);

		if (ifa->ifa_addr->sa_family != AF_INET6
		 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
			continue;
		}

		ia = (struct in6_ifaddr *)ifa;

		/*
		 * leave from multicast groups we have joined for the interface
		 */
		while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) {
			LIST_REMOVE(imm, i6mm_chain);
			in6_leavegroup(imm);
		}

		/* remove from the routing table */
		if ((ia->ia_flags & IFA_ROUTE) &&
		    (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) {
			rtflags = rt->rt_flags;
			rtfree(rt);
			rtrequest(RTM_DELETE, (struct sockaddr *)&ia->ia_addr,
			    (struct sockaddr *)&ia->ia_addr,
			    (struct sockaddr *)&ia->ia_prefixmask,
			    rtflags, NULL);
		}

		/* remove from the linked list */
		ifa_remove(ifp, &ia->ia_ifa);

		/* also remove from the IPv6 address chain(itojun&jinmei) */
		oia = ia;
		if (oia == (ia = in6_ifaddr))
			in6_ifaddr = ia->ia_next;
		else {
			while (ia->ia_next && (ia->ia_next != oia))
				ia = ia->ia_next;
			if (ia->ia_next)
				ia->ia_next = oia->ia_next;
			else {
				nd6log((LOG_ERR,
				    "%s: didn't unlink in6ifaddr from list\n",
				    if_name(ifp)));
			}
		}

		ifafree(&oia->ia_ifa);
	}

	/* cleanup multicast address kludge table, if there is any */
	in6_purgemkludge(ifp);

	/*
	 * remove neighbor management table.  we call it twice just to make
	 * sure we nuke everything.  maybe we need just one call.
	 * XXX: since the first call did not release addresses, some prefixes
	 * might remain.  We should call nd6_purge() again to release the
	 * prefixes after removing all addresses above.
	 * (Or can we just delay calling nd6_purge until at this point?)
	 */
	nd6_purge(ifp);
}