📄 ip_mroute.c
字号:
} /* * 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 *mb_rt; register struct mbuf *mb_ntry; register struct mbuf *mb0; register struct rtdetq *rte; register struct mbuf *rte_m; register u_long hash; register int npkts; 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 %x g %x\n", ntohl(ip->ip_src.s_addr), 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. */ MGET(mb_ntry, M_DONTWAIT, MT_DATA); if (mb_ntry == 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) { m_free(mb_ntry); 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 (mb_rt = mfctable[hash]; mb_rt; mb_rt = mb_rt->m_next) { rt = mtod(mb_rt, struct mfc *); if ((ip->ip_src.s_addr == rt->mfc_origin.s_addr) && (ip->ip_dst.s_addr == rt->mfc_mcastgrp.s_addr) && (mb_rt->m_act != NULL)) break; } if (mb_rt == NULL) { int i; struct igmpmsg *im; /* no upcall, so make a new entry */ MGET(mb_rt, M_DONTWAIT, MT_MRTABLE); if (mb_rt == NULL) { m_free(mb_ntry); 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) { m_free(mb_ntry); m_freem(mb0); m_free(mb_rt); 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; m_free(mb_ntry); m_freem(mb0); m_free(mb_rt); splx(s); return ENOBUFS; } rt = mtod(mb_rt, struct mfc *); /* 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 */ mb_rt->m_next = mfctable[hash]; mfctable[hash] = mb_rt; mb_rt->m_act = NULL; rte_m = mb_rt; } else { /* determine if q has overflowed */ for (rte_m = mb_rt, npkts = 0; rte_m->m_act; rte_m = rte_m->m_act) npkts++; if (npkts > MAX_UPQ) { mrtstat.mrts_upq_ovflw++; m_free(mb_ntry); m_freem(mb0); splx(s); return 0; } } mb_ntry->m_act = NULL; rte = mtod(mb_ntry, struct rtdetq *); rte->m = mb0; rte->ifp = ifp;#ifdef UPCALL_TIMING rte->t = tp;#endif /* Add this entry to the end of the queue */ rte_m->m_act = mb_ntry; 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 mbuf *mb_rt, *m, **nptr; struct rtdetq *rte; struct mfc *mfc; int i; int s; s = splnet(); for (i = 0; i < MFCTBLSIZ; i++) { if (nexpire[i] == 0) continue; nptr = &mfctable[i]; for (mb_rt = *nptr; mb_rt != NULL; mb_rt = *nptr) { mfc = mtod(mb_rt, struct mfc *); /* * Skip real cache entries * Make sure it wasn't marked to not expire (shouldn't happen) * If it expires now */ if (mb_rt->m_act != NULL && mfc->mfc_expire != 0 && --mfc->mfc_expire == 0) { if (mrtdebug & DEBUG_EXPIRE) log(LOG_DEBUG, "expire_upcalls: expiring (%x %x)\n", ntohl(mfc->mfc_origin.s_addr), ntohl(mfc->mfc_mcastgrp.s_addr)); /* * drop all the packets * free the mbuf with the pkt, if, timing info */ while (mb_rt->m_act) { m = mb_rt->m_act; mb_rt->m_act = m->m_act; rte = mtod(m, struct rtdetq *); m_freem(rte->m); m_free(m); } ++mrtstat.mrts_cache_cleanups; nexpire[i]--; MFREE(mb_rt, *nptr); } else { nptr = &mb_rt->m_next; } } } splx(s); 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 = ntohs(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 %x vifi %d vififp %x\n", ifp, vifi, 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 it's 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; ip_copy->ip_id = htons(ip_id++); 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, iphlen)#elseipip_input(m, iphlen)#endif register struct mbuf *m; int iphlen;{ 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, iphlen); 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 %x\n", 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -