📄 ipmr.c
字号:
skb2 = skb_realloc_headroom(skb, (encap + 15)&~15); else if (atomic_read(&skb->users) != 1) skb2 = skb_clone(skb, GFP_ATOMIC); else { atomic_inc(&skb->users); skb2 = skb; } if (skb2 == NULL) { ip_rt_put(rt); return; } vif->pkt_out++; vif->bytes_out+=skb->len; dst_release(skb2->dst); skb2->dst = &rt->u.dst; iph = skb2->nh.iph; ip_decrease_ttl(iph); /* FIXME: forward and output firewalls used to be called here. * What do we do with netfilter? -- RR */ if (vif->flags & VIFF_TUNNEL) { ip_encap(skb2, vif->local, vif->remote); /* FIXME: extra output firewall step used to be here. --RR */ ((struct ip_tunnel *)vif->dev->priv)->stat.tx_packets++; ((struct ip_tunnel *)vif->dev->priv)->stat.tx_bytes+=skb2->len; } IPCB(skb2)->flags |= IPSKB_FORWARDED; /* * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally * not only before forwarding, but after forwarding on all output * interfaces. It is clear, if mrouter runs a multicasting * program, it should receive packets not depending to what interface * program is joined. * If we will not make it, the program will have to join on all * interfaces. On the other hand, multihoming host (or router, but * not mrouter) cannot join to more than one interface - it will * result in receiving multiple packets. */ NF_HOOK(PF_INET, NF_IP_FORWARD, skb2, skb->dev, dev, ipmr_forward_finish);}int ipmr_find_vif(struct net_device *dev){ int ct; for (ct=maxvif-1; ct>=0; ct--) { if (vif_table[ct].dev == dev) break; } return ct;}/* "local" means that we should preserve one skb (for local delivery) */int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local){ int psend = -1; int vif, ct; vif = cache->mfc_parent; cache->mfc_un.res.pkt++; cache->mfc_un.res.bytes += skb->len; /* * Wrong interface: drop packet and (maybe) send PIM assert. */ if (vif_table[vif].dev != skb->dev) { int true_vifi; if (((struct rtable*)skb->dst)->key.iif == 0) { /* It is our own packet, looped back. Very complicated situation... The best workaround until routing daemons will be fixed is not to redistribute packet, if it was send through wrong interface. It means, that multicast applications WILL NOT work for (S,G), which have default multicast route pointing to wrong oif. In any case, it is not a good idea to use multicasting applications on router. */ goto dont_forward; } cache->mfc_un.res.wrong_if++; true_vifi = ipmr_find_vif(skb->dev); if (true_vifi >= 0 && mroute_do_assert && /* pimsm uses asserts, when switching from RPT to SPT, so that we cannot check that packet arrived on an oif. It is bad, but otherwise we would need to move pretty large chunk of pimd to kernel. Ough... --ANK */ (mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) && jiffies - cache->mfc_un.res.last_assert > MFC_ASSERT_THRESH) { cache->mfc_un.res.last_assert = jiffies; ipmr_cache_report(skb, true_vifi, IGMPMSG_WRONGVIF); } goto dont_forward; } vif_table[vif].pkt_in++; vif_table[vif].bytes_in+=skb->len; /* * Forward the frame */ for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) { if (skb->nh.iph->ttl > cache->mfc_un.res.ttls[ct]) { if (psend != -1) ipmr_queue_xmit(skb, cache, psend, 0); psend=ct; } } if (psend != -1) ipmr_queue_xmit(skb, cache, psend, !local);dont_forward: if (!local) kfree_skb(skb); return 0;}/* * Multicast packets for forwarding arrive here */int ip_mr_input(struct sk_buff *skb){ struct mfc_cache *cache; int local = ((struct rtable*)skb->dst)->rt_flags&RTCF_LOCAL; /* Packet is looped back after forward, it should not be forwarded second time, but still can be delivered locally. */ if (IPCB(skb)->flags&IPSKB_FORWARDED) goto dont_forward; if (!local) { if (IPCB(skb)->opt.router_alert) { if (ip_call_ra_chain(skb)) return 0; } else if (skb->nh.iph->protocol == IPPROTO_IGMP){ /* IGMPv1 (and broken IGMPv2 implementations sort of Cisco IOS <= 11.2(8)) do not put router alert option to IGMP packets destined to routable groups. It is very bad, because it means that we can forward NO IGMP messages. */ read_lock(&mrt_lock); if (mroute_socket) { raw_rcv(mroute_socket, skb); read_unlock(&mrt_lock); return 0; } read_unlock(&mrt_lock); } } read_lock(&mrt_lock); cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr); /* * No usable cache entry */ if (cache==NULL) { int vif; if (local) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); ip_local_deliver(skb); if (skb2 == NULL) { read_unlock(&mrt_lock); return -ENOBUFS; } skb = skb2; } vif = ipmr_find_vif(skb->dev); if (vif >= 0) { int err = ipmr_cache_unresolved(vif, skb); read_unlock(&mrt_lock); return err; } read_unlock(&mrt_lock); kfree_skb(skb); return -ENODEV; } ip_mr_forward(skb, cache, local); read_unlock(&mrt_lock); if (local) return ip_local_deliver(skb); return 0;dont_forward: if (local) return ip_local_deliver(skb); kfree_skb(skb); return 0;}#ifdef CONFIG_IP_PIMSM_V1/* * Handle IGMP messages of PIMv1 */int pim_rcv_v1(struct sk_buff * skb, unsigned short len){ struct igmphdr *pim = (struct igmphdr*)skb->h.raw; struct iphdr *encap; struct net_device *reg_dev = NULL; if (!mroute_do_pim || len < sizeof(*pim) + sizeof(*encap) || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) { kfree_skb(skb); return -EINVAL; } encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr)); /* Check that: a. packet is really destinted to a multicast group b. packet is not a NULL-REGISTER c. packet is not truncated */ if (!MULTICAST(encap->daddr) || ntohs(encap->tot_len) == 0 || ntohs(encap->tot_len) + sizeof(*pim) > len) { kfree_skb(skb); return -EINVAL; } read_lock(&mrt_lock); if (reg_vif_num >= 0) reg_dev = vif_table[reg_vif_num].dev; if (reg_dev) dev_hold(reg_dev); read_unlock(&mrt_lock); if (reg_dev == NULL) { kfree_skb(skb); return -EINVAL; } skb->mac.raw = skb->nh.raw; skb_pull(skb, (u8*)encap - skb->data); skb->nh.iph = (struct iphdr *)skb->data; skb->dev = reg_dev; memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); skb->protocol = __constant_htons(ETH_P_IP); skb->ip_summed = 0; skb->pkt_type = PACKET_HOST; dst_release(skb->dst); skb->dst = NULL; ((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len; ((struct net_device_stats*)reg_dev->priv)->rx_packets++;#ifdef CONFIG_NETFILTER nf_conntrack_put(skb->nfct); skb->nfct = NULL;#endif netif_rx(skb); dev_put(reg_dev); return 0;}#endif#ifdef CONFIG_IP_PIMSM_V2int pim_rcv(struct sk_buff * skb, unsigned short len){ struct pimreghdr *pim = (struct pimreghdr*)skb->h.raw; struct iphdr *encap; struct net_device *reg_dev = NULL; if (len < sizeof(*pim) + sizeof(*encap) || pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) || (pim->flags&PIM_NULL_REGISTER) || (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && ip_compute_csum((void *)pim, len))) { kfree_skb(skb); return -EINVAL; } /* check if the inner packet is destined to mcast group */ encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr)); if (!MULTICAST(encap->daddr) || ntohs(encap->tot_len) == 0 || ntohs(encap->tot_len) + sizeof(*pim) > len) { kfree_skb(skb); return -EINVAL; } read_lock(&mrt_lock); if (reg_vif_num >= 0) reg_dev = vif_table[reg_vif_num].dev; if (reg_dev) dev_hold(reg_dev); read_unlock(&mrt_lock); if (reg_dev == NULL) { kfree_skb(skb); return -EINVAL; } skb->mac.raw = skb->nh.raw; skb_pull(skb, (u8*)encap - skb->data); skb->nh.iph = (struct iphdr *)skb->data; skb->dev = reg_dev; memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); skb->protocol = __constant_htons(ETH_P_IP); skb->ip_summed = 0; skb->pkt_type = PACKET_HOST; dst_release(skb->dst); ((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len; ((struct net_device_stats*)reg_dev->priv)->rx_packets++; skb->dst = NULL;#ifdef CONFIG_NETFILTER nf_conntrack_put(skb->nfct); skb->nfct = NULL;#endif netif_rx(skb); dev_put(reg_dev); return 0;}#endif#ifdef CONFIG_RTNETLINKstatic intipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm){ int ct; struct rtnexthop *nhp; struct net_device *dev = vif_table[c->mfc_parent].dev; u8 *b = skb->tail; struct rtattr *mp_head; if (dev) RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex); mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0)); for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { if (c->mfc_un.res.ttls[ct] < 255) { if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4)) goto rtattr_failure; nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp))); nhp->rtnh_flags = 0; nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; nhp->rtnh_ifindex = vif_table[ct].dev->ifindex; nhp->rtnh_len = sizeof(*nhp); } } mp_head->rta_type = RTA_MULTIPATH; mp_head->rta_len = skb->tail - (u8*)mp_head; rtm->rtm_type = RTN_MULTICAST; return 1;rtattr_failure: skb_trim(skb, b - skb->data); return -EMSGSIZE;}int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait){ int err; struct mfc_cache *cache; struct rtable *rt = (struct rtable*)skb->dst; read_lock(&mrt_lock); cache = ipmr_cache_find(rt->rt_src, rt->rt_dst); if (cache==NULL) { struct net_device *dev; int vif; if (nowait) { read_unlock(&mrt_lock); return -EAGAIN; } dev = skb->dev; if (dev == NULL || (vif = ipmr_find_vif(dev)) < 0) { read_unlock(&mrt_lock); return -ENODEV; } skb->nh.raw = skb_push(skb, sizeof(struct iphdr)); skb->nh.iph->ihl = sizeof(struct iphdr)>>2; skb->nh.iph->saddr = rt->rt_src; skb->nh.iph->daddr = rt->rt_dst; skb->nh.iph->version = 0; err = ipmr_cache_unresolved(vif, skb); read_unlock(&mrt_lock); return err; } if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY)) cache->mfc_flags |= MFC_NOTIFY; err = ipmr_fill_mroute(skb, cache, rtm); read_unlock(&mrt_lock); return err;}#endif#ifdef CONFIG_PROC_FS /* * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif */ static int ipmr_vif_info(char *buffer, char **start, off_t offset, int length){ struct vif_device *vif; int len=0; off_t pos=0; off_t begin=0; int size; int ct; len += sprintf(buffer, "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); pos=len; read_lock(&mrt_lock); for (ct=0;ct<maxvif;ct++) { char *name = "none"; vif=&vif_table[ct]; if(!VIF_EXISTS(ct)) continue; if (vif->dev) name = vif->dev->name; size = sprintf(buffer+len, "%2d %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", ct, name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out, vif->flags, vif->local, vif->remote); len+=size; pos+=size; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } read_unlock(&mrt_lock); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; if (len<0) len = 0; return len;}static int ipmr_mfc_info(char *buffer, char **start, off_t offset, int length){ struct mfc_cache *mfc; int len=0; off_t pos=0; off_t begin=0; int size; int ct; len += sprintf(buffer, "Group Origin Iif Pkts Bytes Wrong Oifs\n"); pos=len; read_lock(&mrt_lock); for (ct=0;ct<MFC_LINES;ct++) { for(mfc=mfc_cache_array[ct]; mfc; mfc=mfc->next) { int n; /* * Interface forwarding map */ size = sprintf(buffer+len, "%08lX %08lX %-3d %8ld %8ld %8ld", (unsigned long)mfc->mfc_mcastgrp, (unsigned long)mfc->mfc_origin, mfc->mfc_parent, mfc->mfc_un.res.pkt, mfc->mfc_un.res.bytes, mfc->mfc_un.res.wrong_if); for(n=mfc->mfc_un.res.minvif;n<mfc->mfc_un.res.maxvif;n++) { if(VIF_EXISTS(n) && mfc->mfc_un.res.ttls[n] < 255) size += sprintf(buffer+len+size, " %2d:%-3d", n, mfc->mfc_un.res.ttls[n]); } size += sprintf(buffer+len+size, "\n"); len+=size; pos+=size; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) goto done; } } spin_lock_bh(&mfc_unres_lock); for(mfc=mfc_unres_queue; mfc; mfc=mfc->next) { size = sprintf(buffer+len, "%08lX %08lX %-3d %8ld %8ld %8ld\n", (unsigned long)mfc->mfc_mcastgrp, (unsigned long)mfc->mfc_origin, -1, (long)mfc->mfc_un.unres.unresolved.qlen, 0L, 0L); len+=size; pos+=size; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } spin_unlock_bh(&mfc_unres_lock);done: read_unlock(&mrt_lock); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; if (len < 0) { len = 0; } return len;}#endif #ifdef CONFIG_IP_PIMSM_V2struct inet_protocol pim_protocol = { pim_rcv, /* PIM handler */ NULL, /* PIM error control */ NULL, /* next */ IPPROTO_PIM, /* protocol ID */ 0, /* copy */ NULL, /* data */ "PIM" /* name */};#endif/* * Setup for IP multicast routing */ void __init ip_mr_init(void){ printk(KERN_INFO "Linux IP multicast router 0.06 plus PIM-SM\n"); mrt_cachep = kmem_cache_create("ip_mrt_cache", sizeof(struct mfc_cache), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); init_timer(&ipmr_expire_timer); ipmr_expire_timer.function=ipmr_expire_process; register_netdevice_notifier(&ip_mr_notifier);#ifdef CONFIG_PROC_FS proc_net_create("ip_mr_vif",0,ipmr_vif_info); proc_net_create("ip_mr_cache",0,ipmr_mfc_info);#endif }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -