⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip_mroute.c

📁 嵌入式操作系统ECOS的网络开发包
💻 C
📖 第 1 页 / 共 4 页
字号:
     * Don't forward a packet with time-to-live of zero or one,
     * or a packet destined to a local-only group.
     */
    if (ip->ip_ttl <= 1 ||
	ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP)
	return 0;

    /*
     * Determine forwarding vifs from the forwarding cache table
     */
    s = splnet();
    MFCFIND(ip->ip_src.s_addr, ip->ip_dst.s_addr, rt);

    /* Entry exists, so forward if necessary */
    if (rt != NULL) {
	splx(s);
	return (ip_mdq(m, ifp, rt, -1));
    } else {
	/*
	 * If we don't have a route for packet's origin,
	 * Make a copy of the packet &
	 * send message to routing daemon
	 */

	register struct mbuf *mb0;
	register struct rtdetq *rte;
	register u_long hash;
	int hlen = ip->ip_hl << 2;
#ifdef UPCALL_TIMING
	struct timeval tp;

	GET_TIME(tp);
#endif

	mrtstat.mrts_no_route++;
	if (mrtdebug & (DEBUG_FORWARD | DEBUG_MFC))
	    log(LOG_DEBUG, "ip_mforward: no rte s %lx g %lx\n",
		(u_long)ntohl(ip->ip_src.s_addr),
		(u_long)ntohl(ip->ip_dst.s_addr));

	/*
	 * Allocate mbufs early so that we don't do extra work if we are
	 * just going to fail anyway.  Make sure to pullup the header so
	 * that other people can't step on it.
	 */
	rte = (struct rtdetq *)malloc((sizeof *rte), M_MRTABLE, M_NOWAIT);
	if (rte == NULL) {
	    splx(s);
	    return ENOBUFS;
	}
	mb0 = m_copy(m, 0, M_COPYALL);
	if (mb0 && (M_HASCL(mb0) || mb0->m_len < hlen))
	    mb0 = m_pullup(mb0, hlen);
	if (mb0 == NULL) {
	    free(rte, M_MRTABLE);
	    splx(s);
	    return ENOBUFS;
	}

	/* is there an upcall waiting for this packet? */
	hash = MFCHASH(ip->ip_src.s_addr, ip->ip_dst.s_addr);
	for (rt = mfctable[hash]; rt; rt = rt->mfc_next) {
	    if ((ip->ip_src.s_addr == rt->mfc_origin.s_addr) &&
		(ip->ip_dst.s_addr == rt->mfc_mcastgrp.s_addr) &&
		(rt->mfc_stall != NULL))
		break;
	}

	if (rt == NULL) {
	    int i;
	    struct igmpmsg *im;

	    /* no upcall, so make a new entry */
	    rt = (struct mfc *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT);
	    if (rt == NULL) {
		free(rte, M_MRTABLE);
		m_freem(mb0);
		splx(s);
		return ENOBUFS;
	    }
	    /* Make a copy of the header to send to the user level process */
	    mm = m_copy(mb0, 0, hlen);
	    if (mm == NULL) {
		free(rte, M_MRTABLE);
		m_freem(mb0);
		free(rt, M_MRTABLE);
		splx(s);
		return ENOBUFS;
	    }

	    /* 
	     * Send message to routing daemon to install 
	     * a route into the kernel table
	     */
	    k_igmpsrc.sin_addr = ip->ip_src;
	    
	    im = mtod(mm, struct igmpmsg *);
	    im->im_msgtype	= IGMPMSG_NOCACHE;
	    im->im_mbz		= 0;

	    mrtstat.mrts_upcalls++;

	    if (socket_send(ip_mrouter, mm, &k_igmpsrc) < 0) {
		log(LOG_WARNING, "ip_mforward: ip_mrouter socket queue full\n");
		++mrtstat.mrts_upq_sockfull;
		free(rte, M_MRTABLE);
		m_freem(mb0);
		free(rt, M_MRTABLE);
		splx(s);
		return ENOBUFS;
	    }

	    /* insert new entry at head of hash chain */
	    rt->mfc_origin.s_addr     = ip->ip_src.s_addr;
	    rt->mfc_mcastgrp.s_addr   = ip->ip_dst.s_addr;
	    rt->mfc_expire	      = UPCALL_EXPIRE;
	    nexpire[hash]++;
	    for (i = 0; i < numvifs; i++)
		rt->mfc_ttls[i] = 0;
	    rt->mfc_parent = -1;

	    /* link into table */
	    rt->mfc_next   = mfctable[hash];
	    mfctable[hash] = rt;
	    rt->mfc_stall = rte;

	} else {
	    /* determine if q has overflowed */
	    int npkts = 0;
	    struct rtdetq **p;

	    for (p = &rt->mfc_stall; *p != NULL; p = &(*p)->next)
		npkts++;

	    if (npkts > MAX_UPQ) {
		mrtstat.mrts_upq_ovflw++;
		free(rte, M_MRTABLE);
		m_freem(mb0);
		splx(s);
		return 0;
	    }

	    /* Add this entry to the end of the queue */
	    *p = rte;
	}

	rte->m 			= mb0;
	rte->ifp 		= ifp;
#ifdef UPCALL_TIMING
	rte->t			= tp;
#endif
	rte->next		= NULL;

	splx(s);

	return 0;
    }		
}

#ifndef MROUTE_LKM
int (*ip_mforward)(struct ip *, struct ifnet *, struct mbuf *,
		   struct ip_moptions *) = X_ip_mforward;
#endif

/*
 * Clean up the cache entry if upcall is not serviced
 */
static void
expire_upcalls(void *unused)
{
    struct rtdetq *rte;
    struct mfc *mfc, **nptr;
    int i;
    int s;

    s = splnet();
    for (i = 0; i < MFCTBLSIZ; i++) {
	if (nexpire[i] == 0)
	    continue;
	nptr = &mfctable[i];
	for (mfc = *nptr; mfc != NULL; mfc = *nptr) {
	    /*
	     * Skip real cache entries
	     * Make sure it wasn't marked to not expire (shouldn't happen)
	     * If it expires now
	     */
	    if (mfc->mfc_stall != NULL &&
	        mfc->mfc_expire != 0 &&
		--mfc->mfc_expire == 0) {
		if (mrtdebug & DEBUG_EXPIRE)
		    log(LOG_DEBUG, "expire_upcalls: expiring (%lx %lx)\n",
			(u_long)ntohl(mfc->mfc_origin.s_addr),
			(u_long)ntohl(mfc->mfc_mcastgrp.s_addr));
		/*
		 * drop all the packets
		 * free the mbuf with the pkt, if, timing info
		 */
		for (rte = mfc->mfc_stall; rte; ) {
		    struct rtdetq *n = rte->next;

		    m_freem(rte->m);
		    free(rte, M_MRTABLE);
		    rte = n;
		}
		++mrtstat.mrts_cache_cleanups;
		nexpire[i]--;

		*nptr = mfc->mfc_next;
		free(mfc, M_MRTABLE);
	    } else {
		nptr = &mfc->mfc_next;
	    }
	}
    }
    splx(s);
    expire_upcalls_ch = timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT);
}

/*
 * Packet forwarding routine once entry in the cache is made
 */
static int
ip_mdq(m, ifp, rt, xmt_vif)
    register struct mbuf *m;
    register struct ifnet *ifp;
    register struct mfc *rt;
    register vifi_t xmt_vif;
{
    register struct ip  *ip = mtod(m, struct ip *);
    register vifi_t vifi;
    register struct vif *vifp;
    register int plen = ip->ip_len;

/*
 * Macro to send packet on vif.  Since RSVP packets don't get counted on
 * input, they shouldn't get counted on output, so statistics keeping is
 * seperate.
 */
#define MC_SEND(ip,vifp,m) {                             \
                if ((vifp)->v_flags & VIFF_TUNNEL)  	 \
                    encap_send((ip), (vifp), (m));       \
                else                                     \
                    phyint_send((ip), (vifp), (m));      \
}

    /*
     * If xmt_vif is not -1, send on only the requested vif.
     *
     * (since vifi_t is u_short, -1 becomes MAXUSHORT, which > numvifs.)
     */
    if (xmt_vif < numvifs) {
	MC_SEND(ip, viftable + xmt_vif, m);
	return 1;
    }

    /*
     * Don't forward if it didn't arrive from the parent vif for its origin.
     */
    vifi = rt->mfc_parent;
    if ((vifi >= numvifs) || (viftable[vifi].v_ifp != ifp)) {
	/* came in the wrong interface */
	if (mrtdebug & DEBUG_FORWARD)
	    log(LOG_DEBUG, "wrong if: ifp %p vifi %d vififp %p\n",
		(void *)ifp, vifi, (void *)viftable[vifi].v_ifp); 
	++mrtstat.mrts_wrong_if;
	++rt->mfc_wrong_if;
	/*
	 * If we are doing PIM assert processing, and we are forwarding
	 * packets on this interface, and it is a broadcast medium
	 * interface (and not a tunnel), send a message to the routing daemon.
	 */
	if (pim_assert && rt->mfc_ttls[vifi] &&
		(ifp->if_flags & IFF_BROADCAST) &&
		!(viftable[vifi].v_flags & VIFF_TUNNEL)) {
	    struct sockaddr_in k_igmpsrc;
	    struct mbuf *mm;
	    struct igmpmsg *im;
	    int hlen = ip->ip_hl << 2;
	    struct timeval now;
	    register u_long delta;

	    GET_TIME(now);

	    TV_DELTA(rt->mfc_last_assert, now, delta);

	    if (delta > ASSERT_MSG_TIME) {
		mm = m_copy(m, 0, hlen);
		if (mm && (M_HASCL(mm) || mm->m_len < hlen))
		    mm = m_pullup(mm, hlen);
		if (mm == NULL) {
		    return ENOBUFS;
		}

		rt->mfc_last_assert = now;

		im = mtod(mm, struct igmpmsg *);
		im->im_msgtype	= IGMPMSG_WRONGVIF;
		im->im_mbz		= 0;
		im->im_vif		= vifi;

		k_igmpsrc.sin_addr = im->im_src;

		socket_send(ip_mrouter, mm, &k_igmpsrc);
	    }
	}
	return 0;
    }

    /* If I sourced this packet, it counts as output, else it was input. */
    if (ip->ip_src.s_addr == viftable[vifi].v_lcl_addr.s_addr) {
	viftable[vifi].v_pkt_out++;
	viftable[vifi].v_bytes_out += plen;
    } else {
	viftable[vifi].v_pkt_in++;
	viftable[vifi].v_bytes_in += plen;
    }
    rt->mfc_pkt_cnt++;
    rt->mfc_byte_cnt += plen;

    /*
     * For each vif, decide if a copy of the packet should be forwarded.
     * Forward if:
     *		- the ttl exceeds the vif's threshold
     *		- there are group members downstream on interface
     */
    for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++)
	if ((rt->mfc_ttls[vifi] > 0) &&
	    (ip->ip_ttl > rt->mfc_ttls[vifi])) {
	    vifp->v_pkt_out++;
	    vifp->v_bytes_out += plen;
	    MC_SEND(ip, vifp, m);
	}

    return 0;
}

/*
 * check if a vif number is legal/ok. This is used by ip_output, to export
 * numvifs there, 
 */
static int
X_legal_vif_num(vif)
    int vif;
{
    if (vif >= 0 && vif < numvifs)
       return(1);
    else
       return(0);
}

#ifndef MROUTE_LKM
int (*legal_vif_num)(int) = X_legal_vif_num;
#endif

/*
 * Return the local address used by this vif
 */
static u_long
X_ip_mcast_src(vifi)
    int vifi;
{
    if (vifi >= 0 && vifi < numvifs)
	return viftable[vifi].v_lcl_addr.s_addr;
    else
	return INADDR_ANY;
}

#ifndef MROUTE_LKM
u_long (*ip_mcast_src)(int) = X_ip_mcast_src;
#endif

static void
phyint_send(ip, vifp, m)
    struct ip *ip;
    struct vif *vifp;
    struct mbuf *m;
{
    register struct mbuf *mb_copy;
    register int hlen = ip->ip_hl << 2;

    /*
     * Make a new reference to the packet; make sure that
     * the IP header is actually copied, not just referenced,
     * so that ip_output() only scribbles on the copy.
     */
    mb_copy = m_copy(m, 0, M_COPYALL);
    if (mb_copy && (M_HASCL(mb_copy) || mb_copy->m_len < hlen))
	mb_copy = m_pullup(mb_copy, hlen);
    if (mb_copy == NULL)
	return;

    if (vifp->v_rate_limit == 0)
	tbf_send_packet(vifp, mb_copy);
    else
	tbf_control(vifp, mb_copy, mtod(mb_copy, struct ip *), ip->ip_len);
}

static void
encap_send(ip, vifp, m)
    register struct ip *ip;
    register struct vif *vifp;
    register struct mbuf *m;
{
    register struct mbuf *mb_copy;
    register struct ip *ip_copy;
    register int i, len = ip->ip_len;

    /*
     * copy the old packet & pullup its IP header into the
     * new mbuf so we can modify it.  Try to fill the new
     * mbuf since if we don't the ethernet driver will.
     */
    MGETHDR(mb_copy, M_DONTWAIT, MT_HEADER);
    if (mb_copy == NULL)
	return;
    mb_copy->m_data += max_linkhdr;
    mb_copy->m_len = sizeof(multicast_encap_iphdr);

    if ((mb_copy->m_next = m_copy(m, 0, M_COPYALL)) == NULL) {
	m_freem(mb_copy);
	return;
    }
    i = MHLEN - M_LEADINGSPACE(mb_copy);
    if (i > len)
	i = len;
    mb_copy = m_pullup(mb_copy, i);
    if (mb_copy == NULL)
	return;
    mb_copy->m_pkthdr.len = len + sizeof(multicast_encap_iphdr);

    /*
     * fill in the encapsulating IP header.
     */
    ip_copy = mtod(mb_copy, struct ip *);
    *ip_copy = multicast_encap_iphdr;
#ifdef RANDOM_IP_ID
    ip_copy->ip_id = ip_randomid();
#else
    ip_copy->ip_id = htons(ip_id++);
#endif
    ip_copy->ip_len += len;
    ip_copy->ip_src = vifp->v_lcl_addr;
    ip_copy->ip_dst = vifp->v_rmt_addr;

    /*
     * turn the encapsulated IP header back into a valid one.
     */
    ip = (struct ip *)((caddr_t)ip_copy + sizeof(multicast_encap_iphdr));
    --ip->ip_ttl;
    HTONS(ip->ip_len);
    HTONS(ip->ip_off);
    ip->ip_sum = 0;
    mb_copy->m_data += sizeof(multicast_encap_iphdr);
    ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2);
    mb_copy->m_data -= sizeof(multicast_encap_iphdr);

    if (vifp->v_rate_limit == 0)
	tbf_send_packet(vifp, mb_copy);
    else
	tbf_control(vifp, mb_copy, ip, ip_copy->ip_len);
}

/*
 * De-encapsulate a packet and feed it back through ip input (this
 * routine is called whenever IP gets a packet with proto type
 * ENCAP_PROTO and a local destination address).
 */
void
#ifdef MROUTE_LKM
X_ipip_input(m, off)
#else
ipip_input(m, off)
#endif
	register struct mbuf *m;
	int off;
{
    struct ifnet *ifp = m->m_pkthdr.rcvif;
    register struct ip *ip = mtod(m, struct ip *);
    register int hlen = ip->ip_hl << 2;
    register int s;
    register struct ifqueue *ifq;
    register struct vif *vifp;

    if (!have_encap_tunnel) {
	    rip_input(m, off);
	    return;
    }
    /*
     * dump the packet if it's not to a multicast destination or if
     * we don't have an encapsulating tunnel with the source.
     * Note:  This code assumes that the remote site IP address
     * uniquely identifies the tunnel (i.e., that this site has
     * at most one tunnel with the remote site).
     */
    if (! IN_MULTICAST(ntohl(((struct ip *)((char *)ip + hlen))->ip_dst.s_addr))) {
	++mrtstat.mrts_bad_tunnel;
	m_freem(m);
	return;
    }
    if (ip->ip_src.s_addr != last_encap_src) {
	register struct vif *vife;
	
	vifp = viftable;
	vife = vifp + numvifs;
	last_encap_src = ip->ip_src.s_addr;
	last_encap_vif = 0;
	for ( ; vifp < vife; ++vifp)
	    if (vifp->v_rmt_addr.s_addr == ip->ip_src.s_addr) {
		if ((vifp->v_flags & (VIFF_TUNNEL|VIFF_SRCRT))
		    == VIFF_TUNNEL)
		    last_encap_vif = vifp;
		break;
	    }
    }
    if ((vifp = last_encap_vif) == 0) {
	last_encap_src = 0;
	mrtstat.mrts_cant_tunnel++; /*XXX*/
	m_freem(m);
	if (mrtdebug)
	  log(LOG_DEBUG, "ip_mforward: no tunnel with %lx\n",
		(u_long)ntohl(ip->ip_src.s_addr));
	return;
    }
    ifp = vifp->v_ifp;

    if (hlen > IP_HDR_LEN)
      ip_stripoptions(m, (struct mbuf *) 0);
    m->m_data += IP_HDR_LEN;
    m->m_len -= IP_HDR_LEN;
    m->m_pkthdr.len -= IP_HDR_LEN;
    m->m_pkthdr.rcvif = ifp;

    ifq = &ipintrq;
    s = splimp();
    if (IF_QFULL(ifq)) {
	IF_DROP(ifq);
	m_freem(m);
    } else {
	IF_ENQUEUE(ifq, m);
	/*
	 * normally we would need a "schednetisr(NETISR_IP)"
	 * here but we were called by ip_input and it is going
	 * to loop back & try to dequeue the packet we just
	 * queued as soon as we return so we avoid the
	 * unnecessary software interrrupt.
	 */
    }
    splx(s);
}

/*
 * Token bucket filter module
 */

static void
tbf_control(vifp, m, ip, p_len)
	register struct vif *vifp;
	register struct mbuf *m;
	register struct ip *ip;
	register u_long p_len;
{
    register struct tbf *t = vifp->v_tbf;

    if (p_len > MAX_BKT_SIZE) {
	/* drop if packet is too large */
	mrtstat.mrts_pkt2large++;
	m_freem(m);
	return;
    }

    tbf_update_tokens(vifp);

    /* if there are enough tokens, 
     * and the queue is empty,
     * send this packet out
     */

    if (t->tbf_q_len == 0) {
	/* queue empty, send packet if enough tokens */
	if (p_len <= t->tbf_n_tok) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -