ip_mroute.c

来自「eCos操作系统源码」· C语言 代码 · 共 2,320 行 · 第 1/4 页

C
2,320
字号
     * 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_LKMint (*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 voidexpire_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 intip_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 intX_legal_vif_num(vif)    int vif;{    if (vif >= 0 && vif < numvifs)       return(1);    else       return(0);}#ifndef MROUTE_LKMint (*legal_vif_num)(int) = X_legal_vif_num;#endif/* * Return the local address used by this vif */static u_longX_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_LKMu_long (*ip_mcast_src)(int) = X_ip_mcast_src;#endifstatic voidphyint_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 voidencap_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_LKMX_ipip_input(m, off)#elseipip_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 voidtbf_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 + =
减小字号Ctrl + -
显示快捷键?