📄 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 *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 + -