Example #1
0
/*
 * Connect from a socket to a specified address.
 * Both address and port must be specified in argument slpx.
 * If don't have a local address for this socket yet,
 * then pick one.
 */
int Lpx_PCB_connect( struct lpxpcb *lpxp,
                    struct sockaddr *nam,
                    struct proc *td )
{
    struct lpx_ifaddr *ia = NULL;
    register struct sockaddr_lpx *slpx = (struct sockaddr_lpx *)nam;
    register struct lpx_addr *dst;
    register struct route *ro;
    struct ifnet *ifp;
	struct lpx_addr laddr;

    DEBUG_CALL(4, ("Lpx_PCB_connect\n"));

    if (nam == NULL) {
      return(EINVAL);
    }

    DEBUG_CALL(2, ("slpx->sipx_addr.x_net = %x\n", slpx->sipx_addr.x_net));

    if (slpx->slpx_family != AF_LPX)
        return (EAFNOSUPPORT);
    if (slpx->slpx_port == 0 || lpx_nullhost(slpx->sipx_addr))
        return (EADDRNOTAVAIL);
    /*
     * If we haven't bound which network number to use as ours,
     * we will use the number of the outgoing interface.
     * This depends on having done a routing lookup, which
     * we will probably have to do anyway, so we might
     * as well do it now.  On the other hand if we are
     * sending to multiple destinations we may have already
     * done the lookup, so see if we can use the route
     * from before.  In any case, we only
     * chose a port number once, even if sending to multiple
     * destinations.
     */
    ro = &lpxp->lpxp_route;
    dst = &satolpx_addr(ro->ro_dst);
    if (lpxp->lpxp_socket->so_options & SO_DONTROUTE)
        goto flush;
    if (!lpx_neteq(lpxp->lpxp_lastdst, slpx->sipx_addr))
        goto flush;
    if (!lpx_hosteq(lpxp->lpxp_lastdst, slpx->sipx_addr)) {
        if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
            /* can patch route to avoid rtalloc */
            *dst = slpx->sipx_addr;
        } else {
    flush:
        if (ro->ro_rt != NULL)
                RTFREE(ro->ro_rt);
            ro->ro_rt = NULL;
        }
    }/* else cached route is ok; do nothing */
    lpxp->lpxp_lastdst = slpx->sipx_addr;
    if ((lpxp->lpxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
        (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
            /* No route yet, so try to acquire one */
            ro->ro_dst.sa_family = AF_LPX;
            ro->ro_dst.sa_len = sizeof(ro->ro_dst);
            *dst = slpx->sipx_addr;
            dst->x_port = 0;
            rtalloc(ro);
			
			DEBUG_CALL(4, ("Lpx_PCB_connect RO 1\n"));
        }
    if (lpx_neteqnn(lpxp->lpxp_laddr.x_net, lpx_zeronet)) {
        
		DEBUG_CALL(4, ("Lpx_PCB_connect RO 2\n"));
		
		/* 
         * If route is known or can be allocated now,
         * our src addr is taken from the i/f, else punt.
         */

        /*
         * If we found a route, use the address
         * corresponding to the outgoing interface
         */
        ia = NULL;
        if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
            for (ia = lpx_ifaddr; ia != NULL; ia = ia->ia_next)
                if (ia->ia_ifp == ifp)
                    break;
        if (ia == NULL) {
           // u_short fport = slpx->sipx_addr.x_port;
            //slpx->sipx_addr.x_port = 0;
            ia = (struct lpx_ifaddr *)
                ifa_ifwithaddr((struct sockaddr *)&lpxp->lpxp_laddr);
            //slpx->sipx_addr.x_port = fport;

			// BUG BUG BUG!!! 
            if (ia == NULL)
                ia = Lpx_CTL_iaonnetof(&slpx->sipx_addr);
            if (ia == NULL)
                ia = lpx_ifaddr;
            if (ia == NULL)
                return (EADDRNOTAVAIL);
        }
        lpxp->lpxp_laddr.x_net = satolpx_addr(ia->ia_addr).x_net;
    }
    if (lpx_nullhost(lpxp->lpxp_laddr)) {
		
		DEBUG_CALL(4, ("Lpx_PCB_connect RO 3\n"));
		
        /* 
         * If route is known or can be allocated now,
         * our src addr is taken from the i/f, else punt.
         */

        /*
         * If we found a route, use the address
         * corresponding to the outgoing interface
         */
        ia = NULL;
        if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
            for (ia = lpx_ifaddr; ia != NULL; ia = ia->ia_next)
                if (ia->ia_ifp == ifp)
                    break;
        if (ia == NULL) {
            u_short fport = slpx->sipx_addr.x_port;
            slpx->sipx_addr.x_port = 0;
            ia = (struct lpx_ifaddr *)
                ifa_ifwithdstaddr((struct sockaddr *)slpx);
            slpx->sipx_addr.x_port = fport;
			// BUG BUG BUG!!!
			
            if (ia == NULL)
                ia = Lpx_CTL_iaonnetof(&slpx->sipx_addr);
            if (ia == NULL)
                ia = lpx_ifaddr;
            if (ia == NULL)
                return (EADDRNOTAVAIL);
        }
        lpxp->lpxp_laddr.x_host = satolpx_addr(ia->ia_addr).x_host;
    }

	DEBUG_CALL(4, ("Lpx_PCB_connect: 4.\n"));

	laddr.x_port = lpxp->lpxp_lport;

    if (Lpx_PCB_lookup(&slpx->sipx_addr, &laddr, 0))
        return (EADDRINUSE);
    if (lpxp->lpxp_lport == 0)
        Lpx_PCB_bind(lpxp, (struct sockaddr *)NULL, td);

    /* XXX just leave it zero if we can't find a route */

    lpxp->lpxp_faddr = slpx->sipx_addr;
    /* Includes lpxp->lpxp_fport = slpx->slpx_port; */
    return (0);
}
Example #2
0
/*
 * Forward a packet.  If some error occurs return the sender
 * an icmp packet.  Note we can't always generate a meaningful
 * icmp message because icmp doesn't have a large enough repertoire
 * of codes and types.
 *
 * If not forwarding, just drop the packet.  This could be confusing
 * if ipforwarding was zero but some routing protocol was advancing
 * us as a gateway to somewhere.  However, we must let the routing
 * protocol deal with that.
 *
 */
void
ip6_forward(struct mbuf *m, int srcrt)
{
	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
	struct sockaddr_in6 *dst = NULL;
	struct rtentry *rt = NULL;
	struct route_in6 rin6;
	int error, type = 0, code = 0;
	struct mbuf *mcopy = NULL;
	struct ifnet *origifp;	/* maybe unnecessary */
	u_int32_t inzone, outzone;
	struct in6_addr src_in6, dst_in6, odst;
#ifdef IPSEC
	struct secpolicy *sp = NULL;
#endif
#ifdef SCTP
	int sw_csum;
#endif
	struct m_tag *fwd_tag;
	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];

	/*
	 * Do not forward packets to multicast destination (should be handled
	 * by ip6_mforward().
	 * Do not forward packets with unspecified source.  It was discussed
	 * in July 2000, on the ipngwg mailing list.
	 */
	if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
	    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
		IP6STAT_INC(ip6s_cantforward);
		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
		if (V_ip6_log_time + V_ip6_log_interval < time_uptime) {
			V_ip6_log_time = time_uptime;
			log(LOG_DEBUG,
			    "cannot forward "
			    "from %s to %s nxt %d received on %s\n",
			    ip6_sprintf(ip6bufs, &ip6->ip6_src),
			    ip6_sprintf(ip6bufd, &ip6->ip6_dst),
			    ip6->ip6_nxt,
			    if_name(m->m_pkthdr.rcvif));
		}
		m_freem(m);
		return;
	}
#ifdef IPSEC
	/*
	 * Check if this packet has an active SA and needs to be dropped
	 * instead of forwarded.
	 */
	if (ip6_ipsec_fwd(m) != 0) {
		IP6STAT_INC(ip6s_cantforward);
		m_freem(m);
		return;
	}
#endif /* IPSEC */

#ifdef IPSTEALTH
	if (!V_ip6stealth) {
#endif
	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
		icmp6_error(m, ICMP6_TIME_EXCEEDED,
				ICMP6_TIME_EXCEED_TRANSIT, 0);
		return;
	}
	ip6->ip6_hlim -= IPV6_HLIMDEC;

#ifdef IPSTEALTH
	}
#endif

	/*
	 * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
	 * size of IPv6 + ICMPv6 headers) bytes of the packet in case
	 * we need to generate an ICMP6 message to the src.
	 * Thanks to M_EXT, in most cases copy will not occur.
	 *
	 * It is important to save it before IPsec processing as IPsec
	 * processing may modify the mbuf.
	 */
	mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));

#ifdef IPSEC
	/* get a security policy for this packet */
	sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, &error);
	if (sp == NULL) {
		IPSEC6STAT_INC(ips_out_inval);
		IP6STAT_INC(ip6s_cantforward);
		if (mcopy) {
#if 0
			/* XXX: what icmp ? */
#else
			m_freem(mcopy);
#endif
		}
		m_freem(m);
		return;
	}

	error = 0;

	/* check policy */
	switch (sp->policy) {
	case IPSEC_POLICY_DISCARD:
		/*
		 * This packet is just discarded.
		 */
		IPSEC6STAT_INC(ips_out_polvio);
		IP6STAT_INC(ip6s_cantforward);
		KEY_FREESP(&sp);
		if (mcopy) {
#if 0
			/* XXX: what icmp ? */
#else
			m_freem(mcopy);
#endif
		}
		m_freem(m);
		return;

	case IPSEC_POLICY_BYPASS:
	case IPSEC_POLICY_NONE:
		/* no need to do IPsec. */
		KEY_FREESP(&sp);
		goto skip_ipsec;

	case IPSEC_POLICY_IPSEC:
		if (sp->req == NULL) {
			/* XXX should be panic ? */
			printf("ip6_forward: No IPsec request specified.\n");
			IP6STAT_INC(ip6s_cantforward);
			KEY_FREESP(&sp);
			if (mcopy) {
#if 0
				/* XXX: what icmp ? */
#else
				m_freem(mcopy);
#endif
			}
			m_freem(m);
			return;
		}
		/* do IPsec */
		break;

	case IPSEC_POLICY_ENTRUST:
	default:
		/* should be panic ?? */
		printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
		KEY_FREESP(&sp);
		goto skip_ipsec;
	}

    {
	struct ipsecrequest *isr = NULL;

	/*
	 * when the kernel forwards a packet, it is not proper to apply
	 * IPsec transport mode to the packet. This check avoid from this.
	 * at present, if there is even a transport mode SA request in the
	 * security policy, the kernel does not apply IPsec to the packet.
	 * this check is not enough because the following case is valid.
	 *      ipsec esp/tunnel/xxx-xxx/require esp/transport//require;
	 */
	for (isr = sp->req; isr; isr = isr->next) {
		if (isr->saidx.mode == IPSEC_MODE_ANY)
			goto doipsectunnel;
		if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
			goto doipsectunnel;
	}

	/*
	 * if there's no need for tunnel mode IPsec, skip.
	 */
	if (!isr)
		goto skip_ipsec;

    doipsectunnel:
	/*
	 * All the extension headers will become inaccessible
	 * (since they can be encrypted).
	 * Don't panic, we need no more updates to extension headers
	 * on inner IPv6 packet (since they are now encapsulated).
	 *
	 * IPv6 [ESP|AH] IPv6 [extension headers] payload
	 */

	/*
	 * If we need to encapsulate the packet, do it here
	 * ipsec6_proces_packet will send the packet using ip6_output
	 */
	error = ipsec6_process_packet(m, sp->req);
	/* Release SP if an error occurred */
	if (error != 0)
		KEY_FREESP(&sp);
	if (error == EJUSTRETURN) {
		/*
		 * We had a SP with a level of 'use' and no SA. We
		 * will just continue to process the packet without
		 * IPsec processing.
		 */
		error = 0;
		goto skip_ipsec;
	}

	if (error) {
		/* mbuf is already reclaimed in ipsec6_process_packet. */
		switch (error) {
		case EHOSTUNREACH:
		case ENETUNREACH:
		case EMSGSIZE:
		case ENOBUFS:
		case ENOMEM:
			break;
		default:
			printf("ip6_output (ipsec): error code %d\n", error);
			/* FALLTHROUGH */
		case ENOENT:
			/* don't show these error codes to the user */
			break;
		}
		IP6STAT_INC(ip6s_cantforward);
		if (mcopy) {
#if 0
			/* XXX: what icmp ? */
#else
			m_freem(mcopy);
#endif
		}
		return;
	} else {
		/*
		 * In the FAST IPSec case we have already
		 * re-injected the packet and it has been freed
		 * by the ipsec_done() function.  So, just clean
		 * up after ourselves.
		 */
		m = NULL;
		goto freecopy;
	}
    }
skip_ipsec:
#endif
again:
	bzero(&rin6, sizeof(struct route_in6));
	dst = (struct sockaddr_in6 *)&rin6.ro_dst;
	dst->sin6_len = sizeof(struct sockaddr_in6);
	dst->sin6_family = AF_INET6;
	dst->sin6_addr = ip6->ip6_dst;
again2:
	rin6.ro_rt = in6_rtalloc1((struct sockaddr *)dst, 0, 0, M_GETFIB(m));
	rt = rin6.ro_rt;
	if (rin6.ro_rt != NULL)
		RT_UNLOCK(rin6.ro_rt);
	else {
		IP6STAT_INC(ip6s_noroute);
		in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
		if (mcopy) {
			icmp6_error(mcopy, ICMP6_DST_UNREACH,
			ICMP6_DST_UNREACH_NOROUTE, 0);
		}
		goto bad;
	}

	/*
	 * Source scope check: if a packet can't be delivered to its
	 * destination for the reason that the destination is beyond the scope
	 * of the source address, discard the packet and return an icmp6
	 * destination unreachable error with Code 2 (beyond scope of source
	 * address).  We use a local copy of ip6_src, since in6_setscope()
	 * will possibly modify its first argument.
	 * [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1]
	 */
	src_in6 = ip6->ip6_src;
	if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
		/* XXX: this should not happen */
		IP6STAT_INC(ip6s_cantforward);
		IP6STAT_INC(ip6s_badscope);
		goto bad;
	}
	if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
		IP6STAT_INC(ip6s_cantforward);
		IP6STAT_INC(ip6s_badscope);
		goto bad;
	}
	if (inzone != outzone) {
		IP6STAT_INC(ip6s_cantforward);
		IP6STAT_INC(ip6s_badscope);
		in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);

		if (V_ip6_log_time + V_ip6_log_interval < time_uptime) {
			V_ip6_log_time = time_uptime;
			log(LOG_DEBUG,
			    "cannot forward "
			    "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
			    ip6_sprintf(ip6bufs, &ip6->ip6_src),
			    ip6_sprintf(ip6bufd, &ip6->ip6_dst),
			    ip6->ip6_nxt,
			    if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp));
		}
		if (mcopy)
			icmp6_error(mcopy, ICMP6_DST_UNREACH,
				    ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
		goto bad;
	}

	/*
	 * Destination scope check: if a packet is going to break the scope
	 * zone of packet's destination address, discard it.  This case should
	 * usually be prevented by appropriately-configured routing table, but
	 * we need an explicit check because we may mistakenly forward the
	 * packet to a different zone by (e.g.) a default route.
	 */
	dst_in6 = ip6->ip6_dst;
	if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 ||
	    in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 ||
	    inzone != outzone) {
		IP6STAT_INC(ip6s_cantforward);
		IP6STAT_INC(ip6s_badscope);
		goto bad;
	}

	if (rt->rt_flags & RTF_GATEWAY)
		dst = (struct sockaddr_in6 *)rt->rt_gateway;

	/*
	 * If we are to forward the packet using the same interface
	 * as one we got the packet from, perhaps we should send a redirect
	 * to sender to shortcut a hop.
	 * Only send redirect if source is sending directly to us,
	 * and if packet was not source routed (or has any options).
	 * Also, don't send redirect if forwarding using a route
	 * modified by a redirect.
	 */
	if (V_ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) {
		if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) {
			/*
			 * If the incoming interface is equal to the outgoing
			 * one, and the link attached to the interface is
			 * point-to-point, then it will be highly probable
			 * that a routing loop occurs. Thus, we immediately
			 * drop the packet and send an ICMPv6 error message.
			 *
			 * type/code is based on suggestion by Rich Draves.
			 * not sure if it is the best pick.
			 */
			icmp6_error(mcopy, ICMP6_DST_UNREACH,
				    ICMP6_DST_UNREACH_ADDR, 0);
			goto bad;
		}
		type = ND_REDIRECT;
	}

	/*
	 * Fake scoped addresses. Note that even link-local source or
	 * destinaion can appear, if the originating node just sends the
	 * packet to us (without address resolution for the destination).
	 * Since both icmp6_error and icmp6_redirect_output fill the embedded
	 * link identifiers, we can do this stuff after making a copy for
	 * returning an error.
	 */
	if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
		/*
		 * See corresponding comments in ip6_output.
		 * XXX: but is it possible that ip6_forward() sends a packet
		 *      to a loopback interface? I don't think so, and thus
		 *      I bark here. ([email protected])
		 * XXX: it is common to route invalid packets to loopback.
		 *	also, the codepath will be visited on use of ::1 in
		 *	rthdr. (itojun)
		 */
#if 1
		if (0)
#else
		if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)
#endif
		{
			printf("ip6_forward: outgoing interface is loopback. "
			       "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
			       ip6_sprintf(ip6bufs, &ip6->ip6_src),
			       ip6_sprintf(ip6bufd, &ip6->ip6_dst),
			       ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),
			       if_name(rt->rt_ifp));
		}

		/* we can just use rcvif in forwarding. */
		origifp = m->m_pkthdr.rcvif;
	}
	else
		origifp = rt->rt_ifp;
	/*
	 * clear embedded scope identifiers if necessary.
	 * in6_clearscope will touch the addresses only when necessary.
	 */
	in6_clearscope(&ip6->ip6_src);
	in6_clearscope(&ip6->ip6_dst);

	/* Jump over all PFIL processing if hooks are not active. */
	if (!PFIL_HOOKED(&V_inet6_pfil_hook))
		goto pass;

	odst = ip6->ip6_dst;
	/* Run through list of hooks for output packets. */
	error = pfil_run_hooks(&V_inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT, NULL);
	if (error != 0 || m == NULL)
		goto freecopy;		/* consumed by filter */
	ip6 = mtod(m, struct ip6_hdr *);

	/* See if destination IP address was changed by packet filter. */
	if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) {
		m->m_flags |= M_SKIP_FIREWALL;
		/* If destination is now ourself drop to ip6_input(). */
		if (in6_localip(&ip6->ip6_dst))
			m->m_flags |= M_FASTFWD_OURS;
		else {
			RTFREE(rt);
			goto again;	/* Redo the routing table lookup. */
		}
	}

	/* See if local, if yes, send it to netisr. */
	if (m->m_flags & M_FASTFWD_OURS) {
		if (m->m_pkthdr.rcvif == NULL)
			m->m_pkthdr.rcvif = V_loif;
		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
			m->m_pkthdr.csum_flags |=
			    CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
			m->m_pkthdr.csum_data = 0xffff;
		}
#ifdef SCTP
		if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
			m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
#endif
		error = netisr_queue(NETISR_IPV6, m);
		goto out;
	}
	/* Or forward to some other address? */
	if ((m->m_flags & M_IP6_NEXTHOP) &&
	    (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
		dst = (struct sockaddr_in6 *)&rin6.ro_dst;
		bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6));
		m->m_flags |= M_SKIP_FIREWALL;
		m->m_flags &= ~M_IP6_NEXTHOP;
		m_tag_delete(m, fwd_tag);
		RTFREE(rt);
		goto again2;
	}

pass:
	/* See if the size was changed by the packet filter. */
	if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
		in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
		if (mcopy) {
			u_long mtu;
#ifdef IPSEC
			size_t ipsechdrsiz;
#endif /* IPSEC */

			mtu = IN6_LINKMTU(rt->rt_ifp);
#ifdef IPSEC
			/*
			 * When we do IPsec tunnel ingress, we need to play
			 * with the link value (decrement IPsec header size
			 * from mtu value).  The code is much simpler than v4
			 * case, as we have the outgoing interface for
			 * encapsulated packet as "rt->rt_ifp".
			 */
			ipsechdrsiz = ipsec_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND,
			    NULL);
			if (ipsechdrsiz < mtu)
				mtu -= ipsechdrsiz;
			/*
			 * if mtu becomes less than minimum MTU,
			 * tell minimum MTU (and I'll need to fragment it).
			 */
			if (mtu < IPV6_MMTU)
				mtu = IPV6_MMTU;
#endif /* IPSEC */
			icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
		}
		goto bad;
	}

	error = nd6_output_ifp(rt->rt_ifp, origifp, m, dst, NULL);
	if (error) {
		in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
		IP6STAT_INC(ip6s_cantforward);
	} else {
		IP6STAT_INC(ip6s_forward);
		in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
		if (type)
			IP6STAT_INC(ip6s_redirectsent);
		else {
			if (mcopy)
				goto freecopy;
		}
	}

	if (mcopy == NULL)
		goto out;
	switch (error) {
	case 0:
		if (type == ND_REDIRECT) {
			icmp6_redirect_output(mcopy, rt);
			goto out;
		}
		goto freecopy;

	case EMSGSIZE:
		/* xxx MTU is constant in PPP? */
		goto freecopy;

	case ENOBUFS:
		/* Tell source to slow down like source quench in IP? */
		goto freecopy;

	case ENETUNREACH:	/* shouldn't happen, checked above */
	case EHOSTUNREACH:
	case ENETDOWN:
	case EHOSTDOWN:
	default:
		type = ICMP6_DST_UNREACH;
		code = ICMP6_DST_UNREACH_ADDR;
		break;
	}
	icmp6_error(mcopy, type, code, 0);
	goto out;

 freecopy:
	m_freem(mcopy);
	goto out;
bad:
	m_freem(m);
out:
	if (rt != NULL)
		RTFREE(rt);
}
Example #3
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;
}
Example #4
0
void
ip6_forward(struct mbuf *m, int srcrt)
{
	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
	struct sockaddr_in6 *dst;
	struct rtentry *rt;
	int error = 0, type = 0, code = 0;
	struct mbuf *mcopy = NULL;
	struct ifnet *origifp;	/* maybe unnecessary */
#ifdef IPSEC
	u_int8_t sproto = 0;
	struct m_tag *mtag;
	union sockaddr_union sdst;
	struct tdb_ident *tdbi;
	u_int32_t sspi;
	struct tdb *tdb;
	int s;
#if NPF > 0
	struct ifnet *encif;
#endif
#endif /* IPSEC */
	u_int rtableid = 0;

	/*
	 * Do not forward packets to multicast destination (should be handled
	 * by ip6_mforward().
	 * Do not forward packets with unspecified source.  It was discussed
	 * in July 2000, on ipngwg mailing list.
	 */
	if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
	    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
		ip6stat.ip6s_cantforward++;
		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
		if (ip6_log_time + ip6_log_interval < time_second) {
			ip6_log_time = time_second;
			log(LOG_DEBUG,
			    "cannot forward "
			    "from %s to %s nxt %d received on %s\n",
			    ip6_sprintf(&ip6->ip6_src),
			    ip6_sprintf(&ip6->ip6_dst),
			    ip6->ip6_nxt,
			    m->m_pkthdr.rcvif->if_xname);
		}
		m_freem(m);
		return;
	}

	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
		icmp6_error(m, ICMP6_TIME_EXCEEDED,
				ICMP6_TIME_EXCEED_TRANSIT, 0);
		return;
	}
	ip6->ip6_hlim -= IPV6_HLIMDEC;

#if NPF > 0
reroute:
#endif

#ifdef IPSEC
	if (!ipsec_in_use)
		goto done_spd;

	s = splnet();

	/*
	 * Check if there was an outgoing SA bound to the flow
	 * from a transport protocol.
	 */

	/* Do we have any pending SAs to apply ? */
	mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL);
	if (mtag != NULL) {
#ifdef DIAGNOSTIC
		if (mtag->m_tag_len != sizeof (struct tdb_ident))
			panic("ip6_forward: tag of length %d (should be %d",
			    mtag->m_tag_len, sizeof (struct tdb_ident));
#endif
		tdbi = (struct tdb_ident *)(mtag + 1);
		tdb = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
		    tdbi->proto);
		if (tdb == NULL)
			error = -EINVAL;
		m_tag_delete(m, mtag);
	} else
		tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr),
		    &error, IPSP_DIRECTION_OUT, NULL, NULL);

	if (tdb == NULL) {
	        splx(s);

		if (error == 0) {
		        /*
			 * No IPsec processing required, we'll just send the
			 * packet out.
			 */
		        sproto = 0;

			/* Fall through to routing/multicast handling */
		} else {
		        /*
			 * -EINVAL is used to indicate that the packet should
			 * be silently dropped, typically because we've asked
			 * key management for an SA.
			 */
		        if (error == -EINVAL) /* Should silently drop packet */
				error = 0;

			goto freecopy;
		}
	} else {
		/* Loop detection */
		for (mtag = m_tag_first(m); mtag != NULL;
		    mtag = m_tag_next(m, mtag)) {
			if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE &&
			    mtag->m_tag_id !=
			    PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED)
				continue;
			tdbi = (struct tdb_ident *)(mtag + 1);
			if (tdbi->spi == tdb->tdb_spi &&
			    tdbi->proto == tdb->tdb_sproto &&
			    tdbi->rdomain == tdb->tdb_rdomain &&
			    !bcmp(&tdbi->dst, &tdb->tdb_dst,
			    sizeof(union sockaddr_union))) {
				splx(s);
				sproto = 0; /* mark as no-IPsec-needed */
				goto done_spd;
			}
		}

	        /* We need to do IPsec */
	        bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst));
		sspi = tdb->tdb_spi;
		sproto = tdb->tdb_sproto;
	        splx(s);
	}

	/* Fall through to the routing/multicast handling code */
 done_spd:
#endif /* IPSEC */

#if NPF > 0
	rtableid = m->m_pkthdr.rdomain;
#endif

	/*
	 * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
	 * size of IPv6 + ICMPv6 headers) bytes of the packet in case
	 * we need to generate an ICMP6 message to the src.
	 * Thanks to M_EXT, in most cases copy will not occur.
	 *
	 * It is important to save it before IPsec processing as IPsec
	 * processing may modify the mbuf.
	 */
	mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));

	dst = &ip6_forward_rt.ro_dst;
	if (!srcrt) {
		/*
		 * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
		 */
		if (ip6_forward_rt.ro_rt == 0 ||
		    (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 ||
		    ip6_forward_rt.ro_tableid != rtableid) {
			if (ip6_forward_rt.ro_rt) {
				RTFREE(ip6_forward_rt.ro_rt);
				ip6_forward_rt.ro_rt = 0;
			}
			/* this probably fails but give it a try again */
			ip6_forward_rt.ro_tableid = rtableid;
			rtalloc_mpath((struct route *)&ip6_forward_rt,
			    &ip6->ip6_src.s6_addr32[0]);
		}

		if (ip6_forward_rt.ro_rt == 0) {
			ip6stat.ip6s_noroute++;
			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
			if (mcopy) {
				icmp6_error(mcopy, ICMP6_DST_UNREACH,
					    ICMP6_DST_UNREACH_NOROUTE, 0);
			}
			m_freem(m);
			return;
		}
	} else if (ip6_forward_rt.ro_rt == 0 ||
	   (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 ||
	   !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) ||
	   ip6_forward_rt.ro_tableid != rtableid) {
		if (ip6_forward_rt.ro_rt) {
			RTFREE(ip6_forward_rt.ro_rt);
			ip6_forward_rt.ro_rt = 0;
		}
		bzero(dst, sizeof(*dst));
		dst->sin6_len = sizeof(struct sockaddr_in6);
		dst->sin6_family = AF_INET6;
		dst->sin6_addr = ip6->ip6_dst;
		ip6_forward_rt.ro_tableid = rtableid;

		rtalloc_mpath((struct route *)&ip6_forward_rt,
		    &ip6->ip6_src.s6_addr32[0]);

		if (ip6_forward_rt.ro_rt == 0) {
			ip6stat.ip6s_noroute++;
			/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
			if (mcopy) {
				icmp6_error(mcopy, ICMP6_DST_UNREACH,
					    ICMP6_DST_UNREACH_NOROUTE, 0);
			}
			m_freem(m);
			return;
		}
	}
	rt = ip6_forward_rt.ro_rt;

	/*
	 * Scope check: if a packet can't be delivered to its destination
	 * for the reason that the destination is beyond the scope of the
	 * source address, discard the packet and return an icmp6 destination
	 * unreachable error with Code 2 (beyond scope of source address).
	 * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1]
	 */
	if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) !=
	    in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) {
		ip6stat.ip6s_cantforward++;
		ip6stat.ip6s_badscope++;
		in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);

		if (ip6_log_time + ip6_log_interval < time_second) {
			ip6_log_time = time_second;
			log(LOG_DEBUG,
			    "cannot forward "
			    "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
			    ip6_sprintf(&ip6->ip6_src),
			    ip6_sprintf(&ip6->ip6_dst),
			    ip6->ip6_nxt,
			    m->m_pkthdr.rcvif->if_xname, rt->rt_ifp->if_xname);
		}
		if (mcopy)
			icmp6_error(mcopy, ICMP6_DST_UNREACH,
				    ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
		m_freem(m);
		goto freert;
	}

#ifdef IPSEC
	/*
	 * Check if the packet needs encapsulation.
	 * ipsp_process_packet will never come back to here.
	 * XXX ipsp_process_packet() calls ip6_output(), and there'll be no
	 * PMTU notification.  is it okay?
	 */
	if (sproto != 0) {
		s = splnet();

		tdb = gettdb(rtable_l2(m->m_pkthdr.rdomain),
		    sspi, &sdst, sproto);
		if (tdb == NULL) {
			splx(s);
			error = EHOSTUNREACH;
			m_freem(m);
			goto senderr;	/*XXX*/
		}

#if NPF > 0
		if ((encif = enc_getif(tdb->tdb_rdomain,
		    tdb->tdb_tap)) == NULL ||
		    pf_test6(PF_FWD, encif, &m, NULL) != PF_PASS) {
			splx(s);
			error = EHOSTUNREACH;
			m_freem(m);
			goto senderr;
		}
		if (m == NULL) {
			splx(s);
			goto senderr;
		}
		ip6 = mtod(m, struct ip6_hdr *);
		/*
		 * PF_TAG_REROUTE handling or not...
		 * Packet is entering IPsec so the routing is
		 * already overruled by the IPsec policy.
		 * Until now the change was not reconsidered.
		 * What's the behaviour?
		 */
#endif

		m->m_flags &= ~(M_BCAST | M_MCAST);	/* just in case */

		/* Callee frees mbuf */
		error = ipsp_process_packet(m, tdb, AF_INET6, 0);
		splx(s);
		m_freem(mcopy);
		goto freert;
	}
Example #5
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);
    else
        ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);

    /* (2) check. */
    if (ifa == NULL) {
        struct route_in6 ro;
        int need_proxy;

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

        /* Always use the default FIB. */
#ifdef RADIX_MPATH
        rtalloc_mpath_fib((struct route *)&ro, RTF_ANNOUNCE,
                          RT_DEFAULT_FIB);
#else
        in6_rtalloc(&ro, RT_DEFAULT_FIB);
#endif
        need_proxy = (ro.ro_rt &&
                      (ro.ro_rt->rt_flags & RTF_ANNOUNCE) != 0 &&
                      ro.ro_rt->rt_gateway->sa_family == AF_LINK);
        if (ro.ro_rt != NULL) {
            if (need_proxy)
                proxydl = *SDL(ro.ro_rt->rt_gateway);
            RTFREE(ro.ro_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_fib(ifp, &in6_all, &taddr6,
                          ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
                          rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL,
                          M_GETFIB(m));
        goto freeit;
    }

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

    nd6_na_output_fib(ifp, &saddr6, &taddr6,
                      ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
                      rflag | ND_NA_FLAG_SOLICITED, tlladdr,
                      proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m));
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);
}
Example #6
0
int
ipx_outputfl(struct mbuf *m0, struct route *ro, int flags)
{
	struct ipx *ipx = mtod(m0, struct ipx *);
	struct ifnet *ifp = NULL;
	int error = 0;
	struct sockaddr_ipx *dst;
	struct route ipxroute;

	/*
	 * Route packet.
	 */
	if (ro == NULL) {
		ro = &ipxroute;
		bzero((caddr_t)ro, sizeof(*ro));
	}
	dst = (struct sockaddr_ipx *)&ro->ro_dst;
	if (ro->ro_rt == NULL) {
		dst->sipx_family = AF_IPX;
		dst->sipx_len = sizeof(*dst);
		dst->sipx_addr = ipx->ipx_dna;
		dst->sipx_addr.x_port = 0;
		/*
		 * If routing to interface only,
		 * short circuit routing lookup.
		 */
		if (flags & IPX_ROUTETOIF) {
			struct ipx_ifaddr *ia = ipx_iaonnetof(&ipx->ipx_dna);

			if (ia == NULL) {
				ipxstat.ipxs_noroute++;
				error = ENETUNREACH;
				goto bad;
			}
			ifp = ia->ia_ifp;
			goto gotif;
		}
		rtalloc(ro);
	} else if ((ro->ro_rt->rt_flags & RTF_UP) == 0) {
		/*
		 * The old route has gone away; try for a new one.
		 */
		rtfree(ro->ro_rt);
		ro->ro_rt = NULL;
		rtalloc(ro);
	}
	if (ro->ro_rt == NULL || (ifp = ro->ro_rt->rt_ifp) == NULL) {
		ipxstat.ipxs_noroute++;
		error = ENETUNREACH;
		goto bad;
	}
	ro->ro_rt->rt_use++;
	if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST))
		dst = (struct sockaddr_ipx *)ro->ro_rt->rt_gateway;
gotif:
	/*
	 * Look for multicast addresses and
	 * and verify user is allowed to send
	 * such a packet.
	 */
	if (dst->sipx_addr.x_host.c_host[0]&1) {
		if ((ifp->if_flags & (IFF_BROADCAST | IFF_LOOPBACK)) == 0) {
			error = EADDRNOTAVAIL;
			goto bad;
		}
		if ((flags & IPX_ALLOWBROADCAST) == 0) {
			error = EACCES;
			goto bad;
		}
		m0->m_flags |= M_BCAST;
	}

	if (htons(ipx->ipx_len) <= ifp->if_mtu) {
		ipxstat.ipxs_localout++;
		if (ipx_copy_output) {
			ipx_watch_output(m0, ifp);
		}
		error = ifp->if_output(ifp, m0, (struct sockaddr *)dst,
				       ro->ro_rt);
		goto done;
	} else {
		ipxstat.ipxs_mtutoosmall++;
		error = EMSGSIZE;
	}
bad:
	if (ipx_copy_output) {
		ipx_watch_output(m0, ifp);
	}
	m_freem(m0);
done:
	if (ro == &ipxroute && (flags & IPX_ROUTETOIF) == 0 &&
	    ro->ro_rt != NULL) {
		RTFREE(ro->ro_rt);
		ro->ro_rt = NULL;
	}
	return (error);
}
Example #7
0
static void
tcp6_connect(netmsg_t msg)
{
	struct tcpcb *tp;
	struct socket *so = msg->connect.base.nm_so;
	struct sockaddr *nam = msg->connect.nm_nam;
	struct thread *td = msg->connect.nm_td;
	struct inpcb *inp;
	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
	struct in6_addr *addr6;
#ifdef SMP
	lwkt_port_t port;
#endif
	int error;

	COMMON_START(so, inp, 0);

	/*
	 * Reconnect our pcb if we have to
	 */
	if (msg->connect.nm_reconnect & NMSG_RECONNECT_RECONNECT) {
		msg->connect.nm_reconnect &= ~NMSG_RECONNECT_RECONNECT;
		in_pcblink(so->so_pcb, &tcbinfo[mycpu->gd_cpuid]);
	}

	/*
	 * Bind if we have to
	 */
	if (inp->inp_lport == 0) {
		error = in6_pcbbind(inp, NULL, td);
		if (error)
			goto out;
	}

	/*
	 * Cannot simply call in_pcbconnect, because there might be an
	 * earlier incarnation of this same connection still in
	 * TIME_WAIT state, creating an ADDRINUSE error.
	 */
	error = in6_pcbladdr(inp, nam, &addr6, td);
	if (error)
		goto out;

#ifdef SMP
	port = tcp6_addrport();	/* XXX hack for now, always cpu0 */

	if (port != &curthread->td_msgport) {
		struct route *ro = &inp->inp_route;

		/*
		 * in_pcbladdr() may have allocated a route entry for us
		 * on the current CPU, but we need a route entry on the
		 * inpcb's owner CPU, so free it here.
		 */
		if (ro->ro_rt != NULL)
			RTFREE(ro->ro_rt);
		bzero(ro, sizeof(*ro));

		in_pcbunlink(so->so_pcb, &tcbinfo[mycpu->gd_cpuid]);
		sosetport(so, port);
		msg->connect.nm_reconnect |= NMSG_RECONNECT_RECONNECT;
		msg->connect.base.nm_dispatch = tcp6_connect;

		lwkt_forwardmsg(port, &msg->connect.base.lmsg);
		/* msg invalid now */
		return;
	}
#endif
	error = tcp6_connect_oncpu(tp, msg->connect.nm_flags,
				   &msg->connect.nm_m, sin6, addr6);
	/* nm_m may still be intact */
out:
	if (error && (msg->connect.nm_reconnect & NMSG_RECONNECT_FALLBACK)) {
		tcp_connect(msg);
		/* msg invalid now */
	} else {
		if (msg->connect.nm_m) {
			m_freem(msg->connect.nm_m);
			msg->connect.nm_m = NULL;
		}
		if (msg->connect.nm_reconnect & NMSG_RECONNECT_NAMALLOC) {
			kfree(msg->connect.nm_nam, M_LWKTMSG);
			msg->connect.nm_nam = NULL;
		}
		lwkt_replymsg(&msg->connect.base.lmsg, error);
		/* msg invalid now */
	}
}
Example #8
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);
}
Example #9
0
/*
 * The output routine. Takes a packet and encapsulates it in the protocol
 * given by sc->g_proto. See also RFC 1701 and RFC 2004
 */
static int
gre_output_serialized(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
		      struct rtentry *rt)
{
	int error = 0;
	struct gre_softc *sc = ifp->if_softc;
	struct greip *gh;
	struct ip *ip;
	u_short etype = 0;
	struct mobile_h mob_h;
	struct route *ro;
	struct sockaddr_in *ro_dst;

	ASSERT_NETISR_NCPUS(mycpuid);

	/*
	 * gre may cause infinite recursion calls when misconfigured.
	 * We'll prevent this by introducing upper limit.
	 */
	if (++(sc->called) > max_gre_nesting) {
		kprintf("%s: gre_output: recursively called too many "
		       "times(%d)\n", if_name(&sc->sc_if), sc->called);
		m_freem(m);
		error = EIO;    /* is there better errno? */
		goto end;
	}

	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == 0 ||
	    sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) {
		m_freem(m);
		error = ENETDOWN;
		goto end;
	}

	ro = &sc->route_pcpu[mycpuid];
	ro_dst = (struct sockaddr_in *)&ro->ro_dst;
	if (ro->ro_rt != NULL &&
	    ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
	     ro_dst->sin_addr.s_addr != sc->g_dst.s_addr)) {
		RTFREE(ro->ro_rt);
		ro->ro_rt = NULL;
	}
	if (ro->ro_rt == NULL) {
		error = gre_compute_route(sc, ro);
		if (error) {
			m_freem(m);
			goto end;
		}
	}

	gh = NULL;
	ip = NULL;

	if (ifp->if_bpf) {
		bpf_gettoken();
		if (ifp->if_bpf) {
			uint32_t af = dst->sa_family;

			bpf_ptap(ifp->if_bpf, m, &af, sizeof(af));
		}
		bpf_reltoken();
	}

	m->m_flags &= ~(M_BCAST|M_MCAST);

	if (sc->g_proto == IPPROTO_MOBILE) {
		if (dst->sa_family == AF_INET) {
			struct mbuf *m0;
			int msiz;

			ip = mtod(m, struct ip *);

			/*
			 * RFC2004 specifies that fragmented datagrams shouldn't
			 * be encapsulated.
			 */
			if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
				m_freem(m);
				error = EINVAL;    /* is there better errno? */
				goto end;
			}
			memset(&mob_h, 0, MOB_H_SIZ_L);
			mob_h.proto = (ip->ip_p) << 8;
			mob_h.odst = ip->ip_dst.s_addr;
			ip->ip_dst.s_addr = sc->g_dst.s_addr;

			/*
			 * If the packet comes from our host, we only change
			 * the destination address in the IP header.
			 * Else we also need to save and change the source
			 */
			if (in_hosteq(ip->ip_src, sc->g_src)) {
				msiz = MOB_H_SIZ_S;
			} else {
				mob_h.proto |= MOB_H_SBIT;
				mob_h.osrc = ip->ip_src.s_addr;
				ip->ip_src.s_addr = sc->g_src.s_addr;
				msiz = MOB_H_SIZ_L;
			}
			mob_h.proto = htons(mob_h.proto);
			mob_h.hcrc = gre_in_cksum((u_short *)&mob_h, msiz);

			if ((m->m_data - msiz) < m->m_pktdat) {
				/* need new mbuf */
				MGETHDR(m0, M_NOWAIT, MT_HEADER);
				if (m0 == NULL) {
					m_freem(m);
					error = ENOBUFS;
					goto end;
				}
				m0->m_next = m;
				m->m_data += sizeof(struct ip);
				m->m_len -= sizeof(struct ip);
				m0->m_pkthdr.len = m->m_pkthdr.len + msiz;
				m0->m_len = msiz + sizeof(struct ip);
				m0->m_data += max_linkhdr;
				memcpy(mtod(m0, caddr_t), (caddr_t)ip,
				       sizeof(struct ip));
				m = m0;
			} else {  /* we have some space left in the old one */
				m->m_data -= msiz;
				m->m_len += msiz;
				m->m_pkthdr.len += msiz;
				bcopy(ip, mtod(m, caddr_t),
					sizeof(struct ip));
			}
			ip = mtod(m, struct ip *);
			memcpy((caddr_t)(ip + 1), &mob_h, (unsigned)msiz);
			ip->ip_len = ntohs(ip->ip_len) + msiz;
		} else {  /* AF_INET */
			m_freem(m);
			error = EINVAL;
			goto end;
		}
	} else if (sc->g_proto == IPPROTO_GRE) {
Example #10
0
/*
 * Common subroutine to open a TCP connection to remote host specified
 * by struct sockaddr_in in mbuf *nam.  Call in_pcbbind to assign a local
 * port number if needed.  Call in_pcbladdr to do the routing and to choose
 * a local host address (interface).
 * Initialize connection parameters and enter SYN-SENT state.
 */
static void
tcp_connect(netmsg_t msg)
{
	struct socket *so = msg->connect.base.nm_so;
	struct sockaddr *nam = msg->connect.nm_nam;
	struct thread *td = msg->connect.nm_td;
	struct sockaddr_in *sin = (struct sockaddr_in *)nam;
	struct sockaddr_in *if_sin;
	struct inpcb *inp;
	struct tcpcb *tp;
	int error, calc_laddr = 1;
#ifdef SMP
	lwkt_port_t port;
#endif

	COMMON_START(so, inp, 0);

	/*
	 * Reconnect our pcb if we have to
	 */
	if (msg->connect.nm_reconnect & NMSG_RECONNECT_RECONNECT) {
		msg->connect.nm_reconnect &= ~NMSG_RECONNECT_RECONNECT;
		in_pcblink(so->so_pcb, &tcbinfo[mycpu->gd_cpuid]);
	}

	/*
	 * Bind if we have to
	 */
	if (inp->inp_lport == 0) {
		if (tcp_lport_extension) {
			KKASSERT(inp->inp_laddr.s_addr == INADDR_ANY);

			error = in_pcbladdr(inp, nam, &if_sin, td);
			if (error)
				goto out;
			inp->inp_laddr.s_addr = if_sin->sin_addr.s_addr;

			error = in_pcbconn_bind(inp, nam, td);
			if (error)
				goto out;

			calc_laddr = 0;
		} else {
			error = in_pcbbind(inp, NULL, td);
			if (error)
				goto out;
		}
	}

	if (calc_laddr) {
		/*
		 * Calculate the correct protocol processing thread.  The
		 * connect operation must run there.  Set the forwarding
		 * port before we forward the message or it will get bounced
		 * right back to us.
		 */
		error = in_pcbladdr(inp, nam, &if_sin, td);
		if (error)
			goto out;
	}
	KKASSERT(inp->inp_socket == so);

#ifdef SMP
	port = tcp_addrport(sin->sin_addr.s_addr, sin->sin_port,
			    (inp->inp_laddr.s_addr ?
			     inp->inp_laddr.s_addr : if_sin->sin_addr.s_addr),
			    inp->inp_lport);

	if (port != &curthread->td_msgport) {
		struct route *ro = &inp->inp_route;

		/*
		 * in_pcbladdr() may have allocated a route entry for us
		 * on the current CPU, but we need a route entry on the
		 * inpcb's owner CPU, so free it here.
		 */
		if (ro->ro_rt != NULL)
			RTFREE(ro->ro_rt);
		bzero(ro, sizeof(*ro));

		/*
		 * We are moving the protocol processing port the socket
		 * is on, we have to unlink here and re-link on the
		 * target cpu.
		 */
		in_pcbunlink(so->so_pcb, &tcbinfo[mycpu->gd_cpuid]);
		sosetport(so, port);
		msg->connect.nm_reconnect |= NMSG_RECONNECT_RECONNECT;
		msg->connect.base.nm_dispatch = tcp_connect;

		lwkt_forwardmsg(port, &msg->connect.base.lmsg);
		/* msg invalid now */
		return;
	}
#else
	KKASSERT(so->so_port == &curthread->td_msgport);
#endif
	error = tcp_connect_oncpu(tp, msg->connect.nm_flags,
				  msg->connect.nm_m, sin, if_sin);
	msg->connect.nm_m = NULL;
out:
	if (msg->connect.nm_m) {
		m_freem(msg->connect.nm_m);
		msg->connect.nm_m = NULL;
	}
	if (msg->connect.nm_reconnect & NMSG_RECONNECT_NAMALLOC) {
		kfree(msg->connect.nm_nam, M_LWKTMSG);
		msg->connect.nm_nam = NULL;
	}
	lwkt_replymsg(&msg->connect.base.lmsg, error);
	/* msg invalid now */
}
Example #11
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;
}
Example #12
0
int
in_pcbladdr(struct inpcb *inp, struct mbuf *nam, struct sockaddr_in **plocal_sin)
{
	struct in_ifaddr *ia;
	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);

	if (nam->m_len != sizeof (*sin))
		return (EINVAL);
	if (sin->sin_family != AF_INET)
		return (EAFNOSUPPORT);
	if (sin->sin_port == 0)
		return (EADDRNOTAVAIL);
	if (in_ifaddr) {
		/*
		 * If the destination address is INADDR_ANY,
		 * use the primary local address.
		 * If the supplied address is INADDR_BROADCAST,
		 * and the primary interface supports broadcast,
		 * choose the broadcast address for that interface.
		 */
#define	satosin(sa)	((struct sockaddr_in *)(sa))
#define sintosa(sin)	((struct sockaddr *)(sin))
#define ifatoia(ifa)	((struct in_ifaddr *)(ifa))
		if (sin->sin_addr.s_addr == INADDR_ANY)
		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
	}
	if (inp->inp_laddr.s_addr == INADDR_ANY) {
		register struct route *ro;

		ia = (struct in_ifaddr *)0;
		/*
		 * If route is known or can be allocated now,
		 * our src addr is taken from the i/f, else punt.
		 */
		ro = &inp->inp_route;
		if (ro->ro_rt &&
		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
			sin->sin_addr.s_addr ||
		    inp->inp_socket->so_options & SO_DONTROUTE)) {
			RTFREE(ro->ro_rt);
			ro->ro_rt = (struct rtentry *)0;
		}
		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
		    (ro->ro_rt == (struct rtentry *)0 ||
		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
			/* No route yet, so try to acquire one */
			ro->ro_dst.sa_family = AF_INET;
			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
				sin->sin_addr;
			rtalloc(ro);
		}
		/*
		 * If we found a route, use the address
		 * corresponding to the outgoing interface
		 * unless it is the loopback (in case a route
		 * to our address on another net goes to loopback).
		 */
		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
			ia = ifatoia(ro->ro_rt->rt_ifa);
		if (ia == 0) {
			u_short fport = sin->sin_port;

			sin->sin_port = 0;
			ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
			if (ia == 0)
				ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
			sin->sin_port = fport;
			if (ia == 0)
				ia = in_ifaddr;
			if (ia == 0)
				return (EADDRNOTAVAIL);
		}
		/*
		 * If the destination address is multicast and an outgoing
		 * interface has been set as a multicast option, use the
		 * address of that interface as our source address.
		 */
		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
		    inp->inp_moptions != NULL) {
			struct ip_moptions *imo;
			struct ifnet *ifp;

			imo = inp->inp_moptions;
			if (imo->imo_multicast_ifp != NULL) {
				ifp = imo->imo_multicast_ifp;
				for (ia = in_ifaddr; ia; ia = ia->ia_next)
					if (ia->ia_ifp == ifp)
						break;
				if (ia == 0)
					return (EADDRNOTAVAIL);
			}
		}
	/*
	 * Don't do pcblookup call here; return interface in plocal_sin
	 * and exit to caller, that will do the lookup.
	 */
		*plocal_sin = &ia->ia_addr;

	}
	return(0);
}
Example #13
0
static int
at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct proc *p)
{
    struct sockaddr_at	*sat = (struct sockaddr_at *)addr;
    struct route	*ro;
    struct at_ifaddr	*aa = 0;
    struct ifnet	*ifp;
    u_short		hintnet = 0, net;

    if (sat->sat_family != AF_APPLETALK) {
	return(EAFNOSUPPORT);
    }

    /*
     * Under phase 2, network 0 means "the network".  We take "the
     * network" to mean the network the control block is bound to.
     * If the control block is not bound, there is an error.
     */
    if ( sat->sat_addr.s_net == ATADDR_ANYNET
		&& sat->sat_addr.s_node != ATADDR_ANYNODE ) {
	if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
	    return( EADDRNOTAVAIL );
	}
	hintnet = ddp->ddp_lsat.sat_addr.s_net;
    }

    ro = &ddp->ddp_route;
    /*
     * If we've got an old route for this pcb, check that it is valid.
     * If we've changed our address, we may have an old "good looking"
     * route here.  Attempt to detect it.
     */
    if ( ro->ro_rt ) {
	if ( hintnet ) {
	    net = hintnet;
	} else {
	    net = sat->sat_addr.s_net;
	}
	aa = 0;
	if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
	    for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
		if ( aa->aa_ifp == ifp &&
			ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
			ntohs( net ) <= ntohs( aa->aa_lastnet )) {
		    break;
		}
	    }
	}
	if ( aa == NULL || ( satosat( &ro->ro_dst )->sat_addr.s_net !=
		( hintnet ? hintnet : sat->sat_addr.s_net ) ||
		satosat( &ro->ro_dst )->sat_addr.s_node !=
		sat->sat_addr.s_node )) {
	    RTFREE( ro->ro_rt );
	    ro->ro_rt = (struct rtentry *)0;
	}
    }

    /*
     * If we've got no route for this interface, try to find one.
     */
    if ( ro->ro_rt == (struct rtentry *)0 ||
	 ro->ro_rt->rt_ifp == (struct ifnet *)0 ) {
	ro->ro_dst.sa_len = sizeof( struct sockaddr_at );
	ro->ro_dst.sa_family = AF_APPLETALK;
	if ( hintnet ) {
	    satosat( &ro->ro_dst )->sat_addr.s_net = hintnet;
	} else {
	    satosat( &ro->ro_dst )->sat_addr.s_net = sat->sat_addr.s_net;
	}
	satosat( &ro->ro_dst )->sat_addr.s_node = sat->sat_addr.s_node;
	rtalloc( ro );
    }

    /*
     * Make sure any route that we have has a valid interface.
     */
    aa = 0;
    if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
	for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
	    if ( aa->aa_ifp == ifp ) {
		break;
	    }
	}
    }
    if ( aa == 0 ) {
	return( ENETUNREACH );
    }

    ddp->ddp_fsat = *sat;
    if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
	return(at_pcbsetaddr(ddp, (struct sockaddr *)0, p));
    }
    return( 0 );
}
Example #14
0
/*------------------------------------------------------------------------
 * rttimer - update ttls and delete expired routes
 *------------------------------------------------------------------------
 */
void
rttimer(unsigned int delta)
{
	struct	route	*prt, *prev;
	Bool		ripnotify;
	int		i;

	if (!Route.ri_valid)
		return;
	wait(Route.ri_mutex);
	
	ripnotify = FALSE;
	for (i=0; i<RT_TSIZE; ++i) {
		if (rttable[i] == 0)
			continue;
		for (prev = NULL, prt = rttable[i]; prt != NULL;) {
			if (prt->rt_ttl != RT_INF)
				prt->rt_ttl -= delta;
			if (prt->rt_ttl <= 0) {
#ifdef	RIP
				if (dorip && prt->rt_metric < RTM_INF) {
					prt->rt_metric = RTM_INF;
					prt->rt_ttl = RIPZTIME;
					ripnotify = TRUE;
					continue;
				}
#endif	/* RIP */
				if (prev) {
					prev->rt_next = prt->rt_next;
					RTFREE(prt);
					prt = prev->rt_next;
				} else {
					rttable[i] = prt->rt_next;
					RTFREE(prt);
					prt = rttable[i];
				}
				continue;
			}
			prev = prt;
			prt = prt->rt_next;
		}
	}
	prt = Route.ri_default;
	if (prt && (prt->rt_ttl<RT_INF) && (prt->rt_ttl -= delta) <= 0) {
#ifdef	RIP
		if (dorip && prt->rt_metric < RTM_INF) {
			prt->rt_metric = RTM_INF;
			prt->rt_ttl = RIPZTIME;
		} else
#endif	/* RIP */
		{
			RTFREE(Route.ri_default);
			Route.ri_default = 0;
		}
	}
	signal(Route.ri_mutex);
#ifdef	RIP
	if (dorip && ripnotify)
		send(rippid, 0);	/* send anything but TIMEOUT	*/
#endif	/* RIP */
	return;
}
Example #15
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;

	/*
	 * For IPv6, all unicast non-host routes are automatically cloning.
	 */
	if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
		rt->rt_flags |= RTF_MULTICAST;

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

	/*
	 * 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
#ifdef RTF_PRCLONING
				| RTF_PRCLONING
#endif
				);
		if (rt2) {
			if (rt2->rt_flags & RTF_LLINFO &&
				rt2->rt_flags & RTF_HOST &&
				rt2->rt_gateway &&
				rt2->rt_gateway->sa_family == AF_LINK) {
#ifdef __FreeBSD__
				rtexpunge(rt2);
				RTFREE_LOCKED(rt2);
#else
				rtrequest(RTM_DELETE,
					  (struct sockaddr *)rt_key(rt2),
					  rt2->rt_gateway,
					  rt_mask(rt2), rt2->rt_flags, 0);
#endif
				ret = rn_addroute(v_arg, n_arg, head,
					treenodes);
			} else
#ifdef __FreeBSD__
				RTFREE_LOCKED(rt2);
#else
				RTFREE(rt2);
#endif
		}
	} 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
#ifdef RTF_PRCLONING
				| RTF_PRCLONING
#endif
			      );
		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;
			}
#ifdef __FreeBSD__
			RTFREE_LOCKED(rt2);
#else
			RTFREE(rt2);
#endif
		}
	}
	return ret;
}