📄 ipmr.c
字号:
static 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)->fl.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) && time_after(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 (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) ipmr_queue_xmit(skb2, cache, psend); } psend=ct; } } if (psend != -1) { if (local) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) ipmr_queue_xmit(skb2, cache, psend); } else { ipmr_queue_xmit(skb, cache, psend); return 0; } }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 (ip_hdr(skb)->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) { nf_reset(skb); raw_rcv(mroute_socket, skb); read_unlock(&mrt_lock); return 0; } read_unlock(&mrt_lock); } } read_lock(&mrt_lock); cache = ipmr_cache_find(ip_hdr(skb)->saddr, ip_hdr(skb)->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){ struct igmphdr *pim; struct iphdr *encap; struct net_device *reg_dev = NULL; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; pim = igmp_hdr(skb); if (!mroute_do_pim || skb->len < sizeof(*pim) + sizeof(*encap) || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; encap = (struct iphdr *)(skb_transport_header(skb) + 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) || encap->tot_len == 0 || ntohs(encap->tot_len) + sizeof(*pim) > skb->len) goto drop; 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) goto drop; skb->mac_header = skb->network_header; skb_pull(skb, (u8*)encap - skb->data); skb_reset_network_header(skb); skb->dev = reg_dev; skb->protocol = htons(ETH_P_IP); skb->ip_summed = 0; skb->pkt_type = PACKET_HOST; dst_release(skb->dst); skb->dst = NULL; ((struct net_device_stats*)netdev_priv(reg_dev))->rx_bytes += skb->len; ((struct net_device_stats*)netdev_priv(reg_dev))->rx_packets++; nf_reset(skb); netif_rx(skb); dev_put(reg_dev); return 0; drop: kfree_skb(skb); return 0;}#endif#ifdef CONFIG_IP_PIMSM_V2static int pim_rcv(struct sk_buff * skb){ struct pimreghdr *pim; struct iphdr *encap; struct net_device *reg_dev = NULL; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; pim = (struct pimreghdr *)skb_transport_header(skb); if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) || (pim->flags&PIM_NULL_REGISTER) || (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && csum_fold(skb_checksum(skb, 0, skb->len, 0)))) goto drop; /* check if the inner packet is destined to mcast group */ encap = (struct iphdr *)(skb_transport_header(skb) + sizeof(struct pimreghdr)); if (!MULTICAST(encap->daddr) || encap->tot_len == 0 || ntohs(encap->tot_len) + sizeof(*pim) > skb->len) goto drop; 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) goto drop; skb->mac_header = skb->network_header; skb_pull(skb, (u8*)encap - skb->data); skb_reset_network_header(skb); skb->dev = reg_dev; skb->protocol = htons(ETH_P_IP); skb->ip_summed = 0; skb->pkt_type = PACKET_HOST; dst_release(skb->dst); ((struct net_device_stats*)netdev_priv(reg_dev))->rx_bytes += skb->len; ((struct net_device_stats*)netdev_priv(reg_dev))->rx_packets++; skb->dst = NULL; nf_reset(skb); netif_rx(skb); dev_put(reg_dev); return 0; drop: kfree_skb(skb); return 0;}#endifstatic 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_pointer(skb); 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_pointer(skb) - (u8 *)mp_head; rtm->rtm_type = RTN_MULTICAST; return 1;rtattr_failure: nlmsg_trim(skb, b); 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 sk_buff *skb2; struct iphdr *iph; 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; } skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) { read_unlock(&mrt_lock); return -ENOMEM; } skb_push(skb2, sizeof(struct iphdr)); skb_reset_network_header(skb2); iph = ip_hdr(skb2); iph->ihl = sizeof(struct iphdr) >> 2; iph->saddr = rt->rt_src; iph->daddr = rt->rt_dst; iph->version = 0; err = ipmr_cache_unresolved(vif, skb2); 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;}#ifdef CONFIG_PROC_FS/* * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif */struct ipmr_vif_iter { int ct;};static struct vif_device *ipmr_vif_seq_idx(struct ipmr_vif_iter *iter, loff_t pos){ for (iter->ct = 0; iter->ct < maxvif; ++iter->ct) { if (!VIF_EXISTS(iter->ct)) continue; if (pos-- == 0) return &vif_table[iter->ct]; } return NULL;}static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos){ read_lock(&mrt_lock); return *pos ? ipmr_vif_seq_idx(seq->private, *pos - 1) : SEQ_START_TOKEN;}static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos){ struct ipmr_vif_iter *iter = seq->private; ++*pos; if (v == SEQ_START_TOKEN) return ipmr_vif_seq_idx(iter, 0); while (++iter->ct < maxvif) { if (!VIF_EXISTS(iter->ct)) continue; return &vif_table[iter->ct]; } return NULL;}static void ipmr_vif_seq_stop(struct seq_file *seq, void *v){ read_unlock(&mrt_lock);}static int ipmr_vif_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) { seq_puts(seq, "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); } else { const struct vif_device *vif = v; const char *name = vif->dev ? vif->dev->name : "none"; seq_printf(seq, "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", vif - vif_table, name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out, vif->flags, vif->local, vif->remote); } return 0;}static const struct seq_operations ipmr_vif_seq_ops = { .start = ipmr_vif_seq_start, .next = ipmr_vif_seq_next, .stop = ipmr_vif_seq_stop, .show = ipmr_vif_seq_show,};static int ipmr_vif_open(struct inode *inode, struct file *file){ return seq_open_private(file, &ipmr_vif_seq_ops, sizeof(struct ipmr_vif_iter));}static const struct file_operations ipmr_vif_fops = { .owner = THIS_MODULE, .open = ipmr_vif_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private,};struct ipmr_mfc_iter { struct mfc_cache **cache; int ct;};static struct mfc_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos){ struct mfc_cache *mfc; it->cache = mfc_cache_array; read_lock(&mrt_lock); for (it->ct = 0; it->ct < MFC_LINES; it->ct++) for (mfc = mfc_cache_array[it->ct]; mfc; mfc = mfc->next) if (pos-- == 0) return mfc; read_unlock(&mrt_lock); it->cache = &mfc_unres_queue; spin_lock_bh(&mfc_unres_lock); for (mfc = mfc_unres_queue; mfc; mfc = mfc->next) if (pos-- == 0) return mfc; spin_unlock_bh(&mfc_unres_lock); it->cache = NULL; return NULL;}static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos){ struct ipmr_mfc_iter *it = seq->private; it->cache = NULL; it->ct = 0; return *pos ? ipmr_mfc_seq_idx(seq->private, *pos - 1) : SEQ_START_TOKEN;}static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos){ struct mfc_cache *mfc = v; struct ipmr_mfc_iter *it = seq->private; ++*pos; if (v == SEQ_START_TOKEN) return ipmr_mfc_seq_idx(seq->private, 0); if (mfc->next) return mfc->next; if (it->cache == &mfc_unres_queue) goto end_of_list; BUG_ON(it->cache != mfc_cache_array); while (++it->ct < MFC_LINES) { mfc = mfc_cache_array[it->ct]; if (mfc) return mfc; } /* exhausted cache_array, show unresolved */ read_unlock(&mrt_lock); it->cache = &mfc_unres_queue; it->ct = 0; spin_lock_bh(&mfc_unres_lock); mfc = mfc_unres_queue; if (mfc) return mfc; end_of_list: spin_unlock_bh(&mfc_unres_lock); it->cache = NULL; return NULL;}static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v){ struct ipmr_mfc_iter *it = seq->private; if (it->cache == &mfc_unres_queue) spin_unlock_bh(&mfc_unres_lock); else if (it->cache == mfc_cache_array) read_unlock(&mrt_lock);}static int ipmr_mfc_seq_show(struct seq_file *seq, void *v){ int n; if (v == SEQ_START_TOKEN) { seq_puts(seq, "Group Origin Iif Pkts Bytes Wrong Oifs\n"); } else { const struct mfc_cache *mfc = v; const struct ipmr_mfc_iter *it = seq->private; seq_printf(seq, "%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); if (it->cache != &mfc_unres_queue) { 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) seq_printf(seq, " %2d:%-3d", n, mfc->mfc_un.res.ttls[n]); } } seq_putc(seq, '\n'); } return 0;}static const struct seq_operations ipmr_mfc_seq_ops = { .start = ipmr_mfc_seq_start, .next = ipmr_mfc_seq_next, .stop = ipmr_mfc_seq_stop, .show = ipmr_mfc_seq_show,};static int ipmr_mfc_open(struct inode *inode, struct file *file){ return seq_open_private(file, &ipmr_mfc_seq_ops, sizeof(struct ipmr_mfc_iter));}static const struct file_operations ipmr_mfc_fops = { .owner = THIS_MODULE, .open = ipmr_mfc_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private,};#endif#ifdef CONFIG_IP_PIMSM_V2static struct net_protocol pim_protocol = { .handler = pim_rcv,};#endif/* * Setup for IP multicast routing */void __init ip_mr_init(void){ mrt_cachep = kmem_cache_create("ip_mrt_cache", sizeof(struct mfc_cache), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, 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_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops); proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops);#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -