コード例 #1
0
ファイル: etherLib.c プロジェクト: andy345/vxworks5
STATUS etherAddrResolve
(
    struct ifnet        *pIf,           /* interface on which to send ARP req */
    char                *targetAddr,    /* name or Internet address of target */
    char                *eHdr,          /* where to return the Ethernet addr */
    int                 numTries,       /* number of times to try ARPing */
    int                 numTicks        /* number of ticks between ARPing */
)
{
    struct sockaddr_in  sockInetAddr;
    unsigned long	addr;
    int			retVal = 0;

    if (eHdr == NULL)		/* user messed up */
        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)
        return (ERROR);

    sockInetAddr.sin_len = sizeof(struct sockaddr_in);
    sockInetAddr.sin_family = AF_INET;
    sockInetAddr.sin_addr.s_addr = addr;

    /* return 0xffffffffffff for broadcast Internet address */

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

        return (OK);
    }

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

    while ((numTries == -1 || numTries-- > 0) &&
            (retVal = arpresolve ((struct arpcom *) pIf,
                                  (struct rtentry *)NULL,
                                  (struct mbuf *) NULL,
                                  (struct sockaddr *)&sockInetAddr,
                                  (UCHAR *)eHdr))
            == 0)
        taskDelay (numTicks);

    if (retVal == 0)		/* unsuccessful resolution */
        return (ERROR);

    return (OK);
}
コード例 #2
0
ファイル: in_rmx.c プロジェクト: jp629/freebsd
/*
 * 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);

	RADIX_NODE_HEAD_WLOCK_ASSERT(head);
	/*
	 * 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).
	 */
	if (rt->rt_flags & RTF_HOST) {
		if (in_broadcast(sin->sin_addr, rt->rt_ifp)) {
			rt->rt_flags |= RTF_BROADCAST;
		} else if (satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr ==
		    sin->sin_addr.s_addr) {
			rt->rt_flags |= RTF_LOCAL;
		}
	}
	if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
		rt->rt_flags |= RTF_MULTICAST;

	if (rt->rt_ifp != NULL) {

		/*
		 * Check route MTU:
		 * inherit interface MTU if not set or
		 * check if MTU is too large.
		 */
		if (rt->rt_mtu == 0) {
			rt->rt_mtu = rt->rt_ifp->if_mtu;
		} else if (rt->rt_mtu > rt->rt_ifp->if_mtu)
			rt->rt_mtu = rt->rt_ifp->if_mtu;
	}

	return (rn_addroute(v_arg, n_arg, head, treenodes));
}
コード例 #3
0
ファイル: udp_usrreq.c プロジェクト: iHaD/DragonFlyBSD
int
udp_input(struct mbuf **mp, int *offp, int proto)
{
	struct sockaddr_in udp_in = { sizeof udp_in, AF_INET };
	int iphlen;
	struct ip *ip;
	struct udphdr *uh;
	struct inpcb *inp;
	struct mbuf *m;
	struct mbuf *opts = NULL;
	int len, off;
	struct ip save_ip;
	struct inpcbinfo *pcbinfo = &udbinfo[mycpuid];

	off = *offp;
	m = *mp;
	*mp = NULL;

	iphlen = off;
	udp_stat.udps_ipackets++;

	/*
	 * Strip IP options, if any; should skip this,
	 * make available to user, and use on returned packets,
	 * but we don't yet have a way to check the checksum
	 * with options still present.
	 */
	if (iphlen > sizeof(struct ip)) {
		ip_stripoptions(m);
		iphlen = sizeof(struct ip);
	}

	/*
	 * IP and UDP headers are together in first mbuf.
	 * Already checked and pulled up in ip_demux().
	 */
	KASSERT(m->m_len >= iphlen + sizeof(struct udphdr),
	    ("UDP header not in one mbuf"));

	ip = mtod(m, struct ip *);
	uh = (struct udphdr *)((caddr_t)ip + iphlen);

	/* destination port of 0 is illegal, based on RFC768. */
	if (uh->uh_dport == 0)
		goto bad;

	/*
	 * Make mbuf data length reflect UDP length.
	 * If not enough data to reflect UDP length, drop.
	 */
	len = ntohs((u_short)uh->uh_ulen);
	if (ip->ip_len != len) {
		if (len > ip->ip_len || len < sizeof(struct udphdr)) {
			udp_stat.udps_badlen++;
			goto bad;
		}
		m_adj(m, len - ip->ip_len);
		/* ip->ip_len = len; */
	}
	/*
	 * Save a copy of the IP header in case we want restore it
	 * for sending an ICMP error message in response.
	 */
	save_ip = *ip;

	/*
	 * Checksum extended UDP header and data.
	 */
	if (uh->uh_sum) {
		if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
			if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
				uh->uh_sum = m->m_pkthdr.csum_data;
			else
				uh->uh_sum = in_pseudo(ip->ip_src.s_addr,
				    ip->ip_dst.s_addr, htonl((u_short)len +
				    m->m_pkthdr.csum_data + IPPROTO_UDP));
			uh->uh_sum ^= 0xffff;
		} else {
			char b[9];

			bcopy(((struct ipovly *)ip)->ih_x1, b, 9);
			bzero(((struct ipovly *)ip)->ih_x1, 9);
			((struct ipovly *)ip)->ih_len = uh->uh_ulen;
			uh->uh_sum = in_cksum(m, len + sizeof(struct ip));
			bcopy(b, ((struct ipovly *)ip)->ih_x1, 9);
		}
		if (uh->uh_sum) {
			udp_stat.udps_badsum++;
			m_freem(m);
			return(IPPROTO_DONE);
		}
	} else
		udp_stat.udps_nosum++;

	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
	    in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
	    	struct inpcbhead *connhead;
		struct inpcontainer *ic, *ic_marker;
		struct inpcontainerhead *ichead;
		struct udp_mcast_arg arg;
		struct inpcb *last;
		int error;

		/*
		 * Deliver a multicast or broadcast datagram to *all* sockets
		 * for which the local and remote addresses and ports match
		 * those of the incoming datagram.  This allows more than
		 * one process to receive multi/broadcasts on the same port.
		 * (This really ought to be done for unicast datagrams as
		 * well, but that would cause problems with existing
		 * applications that open both address-specific sockets and
		 * a wildcard socket listening to the same port -- they would
		 * end up receiving duplicates of every unicast datagram.
		 * Those applications open the multiple sockets to overcome an
		 * inadequacy of the UDP socket interface, but for backwards
		 * compatibility we avoid the problem here rather than
		 * fixing the interface.  Maybe 4.5BSD will remedy this?)
		 */

		/*
		 * Construct sockaddr format source address.
		 */
		udp_in.sin_port = uh->uh_sport;
		udp_in.sin_addr = ip->ip_src;
		arg.udp_in = &udp_in;
		/*
		 * Locate pcb(s) for datagram.
		 * (Algorithm copied from raw_intr().)
		 */
		last = NULL;
		arg.iphlen = iphlen;

		connhead = &pcbinfo->hashbase[
		    INP_PCBCONNHASH(ip->ip_src.s_addr, uh->uh_sport,
		    ip->ip_dst.s_addr, uh->uh_dport, pcbinfo->hashmask)];
		LIST_FOREACH(inp, connhead, inp_hash) {
#ifdef INET6
			if (!INP_ISIPV4(inp))
				continue;
#endif
			if (!in_hosteq(inp->inp_faddr, ip->ip_src) ||
			    !in_hosteq(inp->inp_laddr, ip->ip_dst) ||
			    inp->inp_fport != uh->uh_sport ||
			    inp->inp_lport != uh->uh_dport)
				continue;

			arg.inp = inp;
			arg.last = last;
			arg.ip = ip;
			arg.m = m;

			error = udp_mcast_input(&arg);
			if (error == ERESTART)
				continue;
			last = arg.last;

			if (error == EJUSTRETURN)
				goto done;
		}

		ichead = &pcbinfo->wildcardhashbase[
		    INP_PCBWILDCARDHASH(uh->uh_dport,
		    pcbinfo->wildcardhashmask)];
		ic_marker = in_pcbcontainer_marker(mycpuid);

		GET_PCBINFO_TOKEN(pcbinfo);
		LIST_INSERT_HEAD(ichead, ic_marker, ic_list);
		while ((ic = LIST_NEXT(ic_marker, ic_list)) != NULL) {
			LIST_REMOVE(ic_marker, ic_list);
			LIST_INSERT_AFTER(ic, ic_marker, ic_list);

			inp = ic->ic_inp;
			if (inp->inp_flags & INP_PLACEMARKER)
				continue;
#ifdef INET6
			if (!INP_ISIPV4(inp))
				continue;
#endif
			if (inp->inp_lport != uh->uh_dport)
				continue;
			if (inp->inp_laddr.s_addr != INADDR_ANY &&
			    inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
				continue;

			arg.inp = inp;
			arg.last = last;
			arg.ip = ip;
			arg.m = m;

			error = udp_mcast_input(&arg);
			if (error == ERESTART)
				continue;
			last = arg.last;

			if (error == EJUSTRETURN)
				break;
		}
		LIST_REMOVE(ic_marker, ic_list);
		REL_PCBINFO_TOKEN(pcbinfo);
done:
		if (last == NULL) {
			/*
			 * No matching pcb found; discard datagram.
			 * (No need to send an ICMP Port Unreachable
			 * for a broadcast or multicast datgram.)
			 */
			udp_stat.udps_noportbcast++;
			goto bad;
		}
#ifdef IPSEC
		/* check AH/ESP integrity. */
		if (ipsec4_in_reject_so(m, last->inp_socket)) {
			ipsecstat.in_polvio++;
			goto bad;
		}
#endif /*IPSEC*/
#ifdef FAST_IPSEC
		/* check AH/ESP integrity. */
		if (ipsec4_in_reject(m, last))
			goto bad;
#endif /*FAST_IPSEC*/
		udp_append(last, ip, m, iphlen + sizeof(struct udphdr),
		    &udp_in);
		return(IPPROTO_DONE);
	}
	/*
	 * Locate pcb for datagram.
	 */
	inp = in_pcblookup_pkthash(pcbinfo, ip->ip_src, uh->uh_sport,
	    ip->ip_dst, uh->uh_dport, TRUE, m->m_pkthdr.rcvif,
	    udp_reuseport_ext ? m : NULL);
	if (inp == NULL) {
		if (log_in_vain) {
			char buf[sizeof "aaa.bbb.ccc.ddd"];

			strcpy(buf, inet_ntoa(ip->ip_dst));
			log(LOG_INFO,
			    "Connection attempt to UDP %s:%d from %s:%d\n",
			    buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src),
			    ntohs(uh->uh_sport));
		}
		udp_stat.udps_noport++;
		if (m->m_flags & (M_BCAST | M_MCAST)) {
			udp_stat.udps_noportbcast++;
			goto bad;
		}
		if (blackhole)
			goto bad;
#ifdef ICMP_BANDLIM
		if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0)
			goto bad;
#endif
		*ip = save_ip;
		ip->ip_len += iphlen;
		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
		return(IPPROTO_DONE);
	}
	KASSERT(INP_ISIPV4(inp), ("not inet inpcb"));
#ifdef IPSEC
	if (ipsec4_in_reject_so(m, inp->inp_socket)) {
		ipsecstat.in_polvio++;
		goto bad;
	}
#endif /*IPSEC*/
#ifdef FAST_IPSEC
	if (ipsec4_in_reject(m, inp))
		goto bad;
#endif /*FAST_IPSEC*/
	/*
	 * Check the minimum TTL for socket.
	 */
	if (ip->ip_ttl < inp->inp_ip_minttl)
		goto bad;

	/*
	 * Construct sockaddr format source address.
	 * Stuff source address and datagram in user buffer.
	 */
	udp_in.sin_port = uh->uh_sport;
	udp_in.sin_addr = ip->ip_src;
	if ((inp->inp_flags & INP_CONTROLOPTS) ||
	    (inp->inp_socket->so_options & SO_TIMESTAMP))
		ip_savecontrol(inp, &opts, ip, m);
	m_adj(m, iphlen + sizeof(struct udphdr));

	lwkt_gettoken(&inp->inp_socket->so_rcv.ssb_token);
	if (ssb_appendaddr(&inp->inp_socket->so_rcv,
	    (struct sockaddr *)&udp_in, m, opts) == 0) {
		lwkt_reltoken(&inp->inp_socket->so_rcv.ssb_token);
		udp_stat.udps_fullsock++;
		goto bad;
	}
	lwkt_reltoken(&inp->inp_socket->so_rcv.ssb_token);
	sorwakeup(inp->inp_socket);
	return(IPPROTO_DONE);
bad:
	m_freem(m);
	if (opts)
		m_freem(opts);
	return(IPPROTO_DONE);
}
コード例 #4
0
ファイル: udp_usrreq.c プロジェクト: ChristosKa/freebsd
void
udp_input(struct mbuf *m, int off)
{
	int iphlen = off;
	struct ip *ip;
	struct udphdr *uh;
	struct ifnet *ifp;
	struct inpcb *inp;
	uint16_t len, ip_len;
	struct inpcbinfo *pcbinfo;
	struct ip save_ip;
	struct sockaddr_in udp_in;
	struct m_tag *fwd_tag;
	int cscov_partial;
	uint8_t pr;

	ifp = m->m_pkthdr.rcvif;
	UDPSTAT_INC(udps_ipackets);

	/*
	 * Strip IP options, if any; should skip this, make available to
	 * user, and use on returned packets, but we don't yet have a way to
	 * check the checksum with options still present.
	 */
	if (iphlen > sizeof (struct ip)) {
		ip_stripoptions(m);
		iphlen = sizeof(struct ip);
	}

	/*
	 * Get IP and UDP header together in first mbuf.
	 */
	ip = mtod(m, struct ip *);
	if (m->m_len < iphlen + sizeof(struct udphdr)) {
		if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == NULL) {
			UDPSTAT_INC(udps_hdrops);
			return;
		}
		ip = mtod(m, struct ip *);
	}
	uh = (struct udphdr *)((caddr_t)ip + iphlen);
	pr = ip->ip_p;
	cscov_partial = (pr == IPPROTO_UDPLITE) ? 1 : 0;

	/*
	 * Destination port of 0 is illegal, based on RFC768.
	 */
	if (uh->uh_dport == 0)
		goto badunlocked;

	/*
	 * Construct sockaddr format source address.  Stuff source address
	 * and datagram in user buffer.
	 */
	bzero(&udp_in, sizeof(udp_in));
	udp_in.sin_len = sizeof(udp_in);
	udp_in.sin_family = AF_INET;
	udp_in.sin_port = uh->uh_sport;
	udp_in.sin_addr = ip->ip_src;

	/*
	 * Make mbuf data length reflect UDP length.  If not enough data to
	 * reflect UDP length, drop.
	 */
	len = ntohs((u_short)uh->uh_ulen);
	ip_len = ntohs(ip->ip_len) - iphlen;
	if (pr == IPPROTO_UDPLITE && len == 0) {
		/* Zero means checksum over the complete packet. */
		len = ip_len;
		cscov_partial = 0;
	}
	if (ip_len != len) {
		if (len > ip_len || len < sizeof(struct udphdr)) {
			UDPSTAT_INC(udps_badlen);
			goto badunlocked;
		}
		if (pr == IPPROTO_UDP)
			m_adj(m, len - ip_len);
	}

	/*
	 * Save a copy of the IP header in case we want restore it for
	 * sending an ICMP error message in response.
	 */
	if (!V_udp_blackhole)
		save_ip = *ip;
	else
		memset(&save_ip, 0, sizeof(save_ip));

	/*
	 * Checksum extended UDP header and data.
	 */
	if (uh->uh_sum) {
		u_short uh_sum;

		if ((m->m_pkthdr.csum_flags & CSUM_DATA_VALID) &&
		    !cscov_partial) {
			if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
				uh_sum = m->m_pkthdr.csum_data;
			else
				uh_sum = in_pseudo(ip->ip_src.s_addr,
				    ip->ip_dst.s_addr, htonl((u_short)len +
				    m->m_pkthdr.csum_data + pr));
			uh_sum ^= 0xffff;
		} else {
			char b[9];

			bcopy(((struct ipovly *)ip)->ih_x1, b, 9);
			bzero(((struct ipovly *)ip)->ih_x1, 9);
			((struct ipovly *)ip)->ih_len = (pr == IPPROTO_UDP) ?
			    uh->uh_ulen : htons(ip_len);
			uh_sum = in_cksum(m, len + sizeof (struct ip));
			bcopy(b, ((struct ipovly *)ip)->ih_x1, 9);
		}
		if (uh_sum) {
			UDPSTAT_INC(udps_badsum);
			m_freem(m);
			return;
		}
	} else
		UDPSTAT_INC(udps_nosum);

	pcbinfo = get_inpcbinfo(pr);
	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
	    in_broadcast(ip->ip_dst, ifp)) {
		struct inpcb *last;
		struct inpcbhead *pcblist;
		struct ip_moptions *imo;

		INP_INFO_RLOCK(pcbinfo);
		pcblist = get_pcblist(pr);
		last = NULL;
		LIST_FOREACH(inp, pcblist, inp_list) {
			if (inp->inp_lport != uh->uh_dport)
				continue;
#ifdef INET6
			if ((inp->inp_vflag & INP_IPV4) == 0)
				continue;
#endif
			if (inp->inp_laddr.s_addr != INADDR_ANY &&
			    inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
				continue;
			if (inp->inp_faddr.s_addr != INADDR_ANY &&
			    inp->inp_faddr.s_addr != ip->ip_src.s_addr)
				continue;
			if (inp->inp_fport != 0 &&
			    inp->inp_fport != uh->uh_sport)
				continue;

			INP_RLOCK(inp);

			/*
			 * XXXRW: Because we weren't holding either the inpcb
			 * or the hash lock when we checked for a match
			 * before, we should probably recheck now that the
			 * inpcb lock is held.
			 */

			/*
			 * Handle socket delivery policy for any-source
			 * and source-specific multicast. [RFC3678]
			 */
			imo = inp->inp_moptions;
			if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
				struct sockaddr_in	 group;
				int			 blocked;
				if (imo == NULL) {
					INP_RUNLOCK(inp);
					continue;
				}
				bzero(&group, sizeof(struct sockaddr_in));
				group.sin_len = sizeof(struct sockaddr_in);
				group.sin_family = AF_INET;
				group.sin_addr = ip->ip_dst;

				blocked = imo_multi_filter(imo, ifp,
					(struct sockaddr *)&group,
					(struct sockaddr *)&udp_in);
				if (blocked != MCAST_PASS) {
					if (blocked == MCAST_NOTGMEMBER)
						IPSTAT_INC(ips_notmember);
					if (blocked == MCAST_NOTSMEMBER ||
					    blocked == MCAST_MUTED)
						UDPSTAT_INC(udps_filtermcast);
					INP_RUNLOCK(inp);
					continue;
				}
			}
			if (last != NULL) {
				struct mbuf *n;

				n = m_copy(m, 0, M_COPYALL);
				udp_append(last, ip, n, iphlen, &udp_in);
				INP_RUNLOCK(last);
			}
			last = inp;
			/*
			 * Don't look for additional matches if this one does
			 * not have either the SO_REUSEPORT or SO_REUSEADDR
			 * socket options set.  This heuristic avoids
			 * searching through all pcbs in the common case of a
			 * non-shared port.  It assumes that an application
			 * will never clear these options after setting them.
			 */
			if ((last->inp_socket->so_options &
			    (SO_REUSEPORT|SO_REUSEADDR)) == 0)
				break;
		}

		if (last == NULL) {
			/*
			 * No matching pcb found; discard datagram.  (No need
			 * to send an ICMP Port Unreachable for a broadcast
			 * or multicast datgram.)
			 */
			UDPSTAT_INC(udps_noportbcast);
			if (inp)
				INP_RUNLOCK(inp);
			INP_INFO_RUNLOCK(pcbinfo);
			goto badunlocked;
		}
		udp_append(last, ip, m, iphlen, &udp_in);
		INP_RUNLOCK(last);
		INP_INFO_RUNLOCK(pcbinfo);
		return;
	}

	/*
	 * Locate pcb for datagram.
	 */

	/*
	 * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
	 */
	if ((m->m_flags & M_IP_NEXTHOP) &&
	    (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
		struct sockaddr_in *next_hop;

		next_hop = (struct sockaddr_in *)(fwd_tag + 1);

		/*
		 * Transparently forwarded. Pretend to be the destination.
		 * Already got one like this?
		 */
		inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport,
		    ip->ip_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, ifp, m);
		if (!inp) {
			/*
			 * It's new.  Try to find the ambushing socket.
			 * Because we've rewritten the destination address,
			 * any hardware-generated hash is ignored.
			 */
			inp = in_pcblookup(pcbinfo, ip->ip_src,
			    uh->uh_sport, next_hop->sin_addr,
			    next_hop->sin_port ? htons(next_hop->sin_port) :
			    uh->uh_dport, INPLOOKUP_WILDCARD |
			    INPLOOKUP_RLOCKPCB, ifp);
		}
		/* Remove the tag from the packet. We don't need it anymore. */
		m_tag_delete(m, fwd_tag);
		m->m_flags &= ~M_IP_NEXTHOP;
	} else
		inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport,
		    ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD |
		    INPLOOKUP_RLOCKPCB, ifp, m);
	if (inp == NULL) {
		if (udp_log_in_vain) {
			char buf[4*sizeof "123"];

			strcpy(buf, inet_ntoa(ip->ip_dst));
			log(LOG_INFO,
			    "Connection attempt to UDP %s:%d from %s:%d\n",
			    buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src),
			    ntohs(uh->uh_sport));
		}
		UDPSTAT_INC(udps_noport);
		if (m->m_flags & (M_BCAST | M_MCAST)) {
			UDPSTAT_INC(udps_noportbcast);
			goto badunlocked;
		}
		if (V_udp_blackhole)
			goto badunlocked;
		if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0)
			goto badunlocked;
		*ip = save_ip;
		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
		return;
	}

	/*
	 * Check the minimum TTL for socket.
	 */
	INP_RLOCK_ASSERT(inp);
	if (inp->inp_ip_minttl && inp->inp_ip_minttl > ip->ip_ttl) {
		INP_RUNLOCK(inp);
		m_freem(m);
		return;
	}
	if (cscov_partial) {
		struct udpcb *up;

		up = intoudpcb(inp);
		if (up->u_rxcslen > len) {
			INP_RUNLOCK(inp);
			m_freem(m);
			return;
		}
	}

	UDP_PROBE(receive, NULL, inp, ip, inp, uh);
	udp_append(inp, ip, m, iphlen, &udp_in);
	INP_RUNLOCK(inp);
	return;

badunlocked:
	m_freem(m);
}
コード例 #5
0
ファイル: icmp.c プロジェクト: bobmittmann/thinkos
int icmp_error(struct iphdr * __ip, int __type, 
			   int __code, struct ifnet * __if) 
{
//	uint8_t icmp_buf[sizeof(struct icmp) + IP_MAXOPTLEN + 8];
	struct icmp * icp;
	int len;

	/* TODO: 
	if (type != ICMP_REDIRECT)
		icmpstat.icps_error++; */

	/* Don't send error if not the first fragment 
	   Ref.: TCP/IP Illustrated Volume 2, pg. 325 */
	icp = (struct icmp *)__ip->opt;

	if (__ip->ip_off & ~(IP_MF | IP_DF)) {
		DCC_LOG1(LOG_WARNING, "not first fragment!; ip_off = %04x", 
				 __ip->ip_off);
		return -1;
	}

	len = MIN((__ip->hlen << 2) + 8 , ntohs(__ip->tot_len));
	memcpy((void *)&(icp->icmp_ip), __ip, len);

	/*
	if ((ip->proto == IPPROTO_ICMP) && (type != ICMP_REDIRECT) &&
	    (totlen >= iplen + ICMP_MINLEN) ) {
	 TODO: 
		icmpstat.icps_oldicmp++; 
		return -1;
	} */

	/* Don't send error in response to a multicast or broadcast packet */
	if (in_broadcast(__ip->daddr, __if) || IN_MULTICAST(__ip->daddr)) {
		DCC_LOG(LOG_INFO, "broadcast/multicast!");
		return -1;
	}

	if ((unsigned int)__type > ICMP_MAXTYPE) {
		DCC_LOG(LOG_PANIC, "icmp_error");
		return -1;
	}

	/* TODO: 
		icmpstat.icps_outhist[type]++; */

	icp->icmp_type = __type;
	icp->icmp_void = 0;
	if (__type == ICMP_PARAMETERPROB)  {
		icp->icmp_pptr = __code;
		__code = 0;
	} else {
		if ((__type == ICMP_DEST_UNREACH) && 
		    (__code == ICMP_FRAG_NEEDED) && (__if)) {
			icp->icmp_nextmtu = htons(__if->if_mtu);
		}
	}
	icp->icmp_code = __code;

	return icmp_send(__ip, icp, len);
}
コード例 #6
0
ファイル: in_rmx.c プロジェクト: daisu-mi/aod
/*
 * 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;
}
コード例 #7
0
ファイル: in_rmx.c プロジェクト: epicsdeb/rtems
/*
 * 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;
}
コード例 #8
0
ファイル: spans_arp.c プロジェクト: AhmadTux/DragonFlyBSD
/*
 * Process a SPANS ARP input packet
 * 
 * Arguments:
 *	clp	pointer to interface CLS control block
 *	m	pointer to input packet buffer chain
 *
 * Returns:
 *	none
 *
 */
void
spansarp_input(struct spanscls *clp, KBuffer *m)
{
	struct spans		*spp = clp->cls_spans;
	struct spanscls_hdr	*chp;
	struct spansarp_hdr	*ahp;
	struct spansarp		*sap;
	struct ip_nif		*inp = clp->cls_ipnif;
	struct in_addr	in_me, in_src, in_targ;
	int		err;

	/*
	 * Make sure IP interface has been activated
	 */
	if (inp == NULL)
		goto free;

	/*
	 * Get the packet together
	 */
	if (KB_LEN(m) < ARP_PACKET_LEN) {
		KB_PULLUP(m, ARP_PACKET_LEN, m);
		if (m == NULL)
			return;
	}
	KB_DATASTART(m, chp, struct spanscls_hdr *);
	ahp = (struct spansarp_hdr *)(chp + 1);

	KM_COPY(ahp->ah_spa, &in_src, sizeof(struct in_addr));
	KM_COPY(ahp->ah_tpa, &in_targ, sizeof(struct in_addr));
	KM_COPY(&(IA_SIN(inp->inf_addr)->sin_addr), &in_me,
		sizeof(struct in_addr));

	/*
	 * Initial packet verification
	 */
	if ((ahp->ah_hrd != htons(ARP_SPANS)) ||
	    (ahp->ah_pro != htons(ETHERTYPE_IP)))
		goto free;

	/*
	 * Validate source addresses
	 * 	can't be from hardware broadcast
	 *	can't be from me
	 */
	if (!spans_addr_cmp(&ahp->ah_sha, &spans_bcastaddr))
		goto free;
	if (!spans_addr_cmp(&ahp->ah_sha, spp->sp_addr.address))
		goto free;
	if (in_src.s_addr == in_me.s_addr) {
		log(LOG_ERR, 
			"duplicate IP address sent from spans address %s\n",
			spans_addr_print(&ahp->ah_sha));
		in_targ = in_me;
		goto chkop;
	}

	/*
	 * If source IP address is from unspecified or broadcast addresses,
	 * don't bother updating arp table, but answer possible requests
	 */
	if (in_broadcast(in_src, &inp->inf_nif->nif_if))
		goto chkop;

	/*
	 * Update arp table with source address info
	 */
	crit_enter();
	SPANSARP_LOOKUP(in_src.s_addr, sap);
	if (sap) {
		/*
		 * Found an entry for the source, but don't
		 * update permanent entries
		 */
		if (sap->sa_origin != SAO_PERM) {

			/*
			 * Update the entry
			 */
			sap->sa_dstatm.address_format = T_ATM_SPANS_ADDR;
			sap->sa_dstatm.address_length = sizeof(spans_addr);
			spans_addr_copy(&ahp->ah_sha, sap->sa_dstatm.address);
			sap->sa_cls = clp;
			sap->sa_reftime = 0;
			if ((sap->sa_flags & SAF_VALID) == 0) {
				/*
				 * Newly valid entry, notify waiting users
				 */
				struct ipvcc	*ivp, *inext;

				sap->sa_flags |= SAF_VALID;
				for (ivp = sap->sa_ivp; ivp; ivp = inext) {
					inext = ivp->iv_arpnext;

					ivp->iv_arpent = (struct arpmap *)sap;
					(*inp->inf_arpnotify)(ivp, MAP_VALID);
				}

				/*
				 * Remove ourselves from the retry chain
				 */
				UNLINK(sap, struct spansarp,
					spansarp_retry_head, sa_rnext);
			}
		}

	} else if (in_targ.s_addr == in_me.s_addr) {
		/*
		 * Source unknown and we're the target - add new entry
		 */
		sap = (struct spansarp *)atm_allocate(&spansarp_pool);
		if (sap) {
			sap->sa_dstip.s_addr = in_src.s_addr;
			sap->sa_dstatm.address_format = T_ATM_SPANS_ADDR;
			sap->sa_dstatm.address_length = sizeof(spans_addr);
			spans_addr_copy(&ahp->ah_sha, sap->sa_dstatm.address);
			sap->sa_dstatmsub.address_format = T_ATM_ABSENT;
			sap->sa_dstatmsub.address_length = 0;
			sap->sa_cls = clp;
			sap->sa_flags = SAF_VALID;
			sap->sa_origin = SAO_LOOKUP;
			SPANSARP_ADD(sap);
		}
	}
	crit_exit();

chkop:
	/*
	 * If this is a request for our address, send a reply 
	 */
	if (ntohs(ahp->ah_op) != ARP_REQUEST)
		goto free;
	if (in_targ.s_addr != in_me.s_addr)
		goto free;

	spans_addr_copy(&chp->ch_src, &chp->ch_dst);
	spans_addr_copy(spp->sp_addr.address, &chp->ch_src);
	ahp->ah_op = htons(ARP_REPLY);
	spans_addr_copy(&ahp->ah_sha, &ahp->ah_tha);
	spans_addr_copy(spp->sp_addr.address, &ahp->ah_sha);
	KM_COPY(ahp->ah_spa, ahp->ah_tpa, sizeof(struct in_addr));
	KM_COPY(&in_me, ahp->ah_spa, sizeof(struct in_addr));

	err = atm_cm_cpcs_data(clp->cls_conn, m);
	if (err)
		goto free;
	return;

free:
	KB_FREEALL(m);
}
コード例 #9
0
ファイル: arpLib.c プロジェクト: andy345/vxworks5
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);
    }
コード例 #10
0
ファイル: in_arp.c プロジェクト: SbIm/xnu-env
/*
 * Parallel to llc_rtrequest.
 */
static void
arp_rtrequest(
	int req,
	struct rtentry *rt,
	__unused struct sockaddr *sa)
{
	struct sockaddr *gate = rt->rt_gateway;
	struct llinfo_arp *la = rt->rt_llinfo;
	static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK, 0, 0, 0, 0, 0, {0}};
	struct timeval timenow;

	if (!arpinit_done) {
		panic("%s: ARP has not been initialized", __func__);
		/* NOTREACHED */
	}
	lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED);
	RT_LOCK_ASSERT_HELD(rt);

	if (rt->rt_flags & RTF_GATEWAY)
		return;
	getmicrotime(&timenow);
	switch (req) {

	case RTM_ADD:
		/*
		 * XXX: If this is a manually added route to interface
		 * such as older version of routed or gated might provide,
		 * restore cloning bit.
		 */
		if ((rt->rt_flags & RTF_HOST) == 0 &&
		    SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
			rt->rt_flags |= RTF_CLONING;
		if (rt->rt_flags & RTF_CLONING) {
			/*
			 * Case 1: This route should come from a route to iface.
			 */
			if (rt_setgate(rt, rt_key(rt),
			    (struct sockaddr *)&null_sdl) == 0) {
				gate = rt->rt_gateway;
				SDL(gate)->sdl_type = rt->rt_ifp->if_type;
				SDL(gate)->sdl_index = rt->rt_ifp->if_index;
				/*
				 * In case we're called before 1.0 sec.
				 * has elapsed.
				 */
				rt->rt_expire = MAX(timenow.tv_sec, 1);
			}
			break;
		}
		/* Announce a new entry if requested. */
		if (rt->rt_flags & RTF_ANNOUNCE) {
			RT_UNLOCK(rt);
			dlil_send_arp(rt->rt_ifp, ARPOP_REQUEST,
			    SDL(gate), rt_key(rt), NULL, rt_key(rt));
			RT_LOCK(rt);
		}
		/*FALLTHROUGH*/
	case RTM_RESOLVE:
		if (gate->sa_family != AF_LINK ||
		    gate->sa_len < sizeof(null_sdl)) {
		        if (log_arp_warnings)
				log(LOG_DEBUG, "arp_rtrequest: bad gateway value\n");
			break;
		}
		SDL(gate)->sdl_type = rt->rt_ifp->if_type;
		SDL(gate)->sdl_index = rt->rt_ifp->if_index;
		if (la != 0)
			break; /* This happens on a route change */
		/*
		 * Case 2:  This route may come from cloning, or a manual route
		 * add with a LL address.
		 */
		rt->rt_llinfo = la = arp_llinfo_alloc();
		if (la == NULL) {
			if (log_arp_warnings)
				log(LOG_DEBUG, "%s: malloc failed\n", __func__);
			break;
		}
		rt->rt_llinfo_free = arp_llinfo_free;

		arp_inuse++, arp_allocated++;
		Bzero(la, sizeof(*la));
		la->la_rt = rt;
		rt->rt_flags |= RTF_LLINFO;
		LIST_INSERT_HEAD(&llinfo_arp, la, la_le);

		/*
		 * This keeps the multicast addresses from showing up
		 * in `arp -a' listings as unresolved.  It's not actually
		 * functional.  Then the same for broadcast.
		 */
		if (IN_MULTICAST(ntohl(SIN(rt_key(rt))->sin_addr.s_addr))) {
			RT_UNLOCK(rt);
			dlil_resolve_multi(rt->rt_ifp, rt_key(rt), gate,
			    sizeof(struct sockaddr_dl));
			RT_LOCK(rt);
			rt->rt_expire = 0;
		}
		else if (in_broadcast(SIN(rt_key(rt))->sin_addr, rt->rt_ifp)) {
			struct sockaddr_dl	*gate_ll = SDL(gate);
			size_t	broadcast_len;
			ifnet_llbroadcast_copy_bytes(rt->rt_ifp,
			    LLADDR(gate_ll), sizeof(gate_ll->sdl_data),
			    &broadcast_len);
			gate_ll->sdl_alen = broadcast_len;
			gate_ll->sdl_family = AF_LINK;
			gate_ll->sdl_len = sizeof(struct sockaddr_dl);
			/* In case we're called before 1.0 sec. has elapsed */
			rt->rt_expire = MAX(timenow.tv_sec, 1);
		}

		if (SIN(rt_key(rt))->sin_addr.s_addr ==
		    (IA_SIN(rt->rt_ifa))->sin_addr.s_addr) {
		    /*
		     * This test used to be
		     *	if (loif.if_flags & IFF_UP)
		     * It allowed local traffic to be forced
		     * through the hardware by configuring the loopback down.
		     * However, it causes problems during network configuration
		     * for boards that can't receive packets they send.
		     * It is now necessary to clear "useloopback" and remove
		     * the route to force traffic out to the hardware.
		     */
			rt->rt_expire = 0;
			ifnet_lladdr_copy_bytes(rt->rt_ifp, LLADDR(SDL(gate)), SDL(gate)->sdl_alen = 6);
			if (useloopback) {
#if IFNET_ROUTE_REFCNT
				/* Adjust route ref count for the interfaces */
				if (rt->rt_if_ref_fn != NULL &&
				    rt->rt_ifp != lo_ifp) {
					rt->rt_if_ref_fn(lo_ifp, 1);
					rt->rt_if_ref_fn(rt->rt_ifp, -1);
				}
#endif /* IFNET_ROUTE_REFCNT */
				rt->rt_ifp = lo_ifp;
			}

		}
		break;

	case RTM_DELETE:
		if (la == 0)
			break;
		arp_inuse--;
		/*
		 * Unchain it but defer the actual freeing until the route
		 * itself is to be freed.  rt->rt_llinfo still points to
		 * llinfo_arp, and likewise, la->la_rt still points to this
		 * route entry, except that RTF_LLINFO is now cleared.
		 */
		LIST_REMOVE(la, la_le);
		la->la_le.le_next = NULL;
		la->la_le.le_prev = NULL;
		rt->rt_flags &= ~RTF_LLINFO;
		if (la->la_hold != NULL)
			m_freem(la->la_hold);
		la->la_hold = NULL;
	}
}