📄 mcast.c
字号:
/* * Multicast support for IPv6 * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * * $Id: mcast.c,v 1.38 2001/08/15 07:36:31 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *//* Changes: * * yoshfuji : fix format of router-alert option */#define __NO_VERSION__#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/string.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/sched.h>#include <linux/net.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/route.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/if_inet6.h>#include <net/ndisc.h>#include <net/addrconf.h>#include <net/ip6_route.h>#include <net/checksum.h>/* Set to 3 to get tracing... */#define MCAST_DEBUG 2#if MCAST_DEBUG >= 3#define MDBG(x) printk x#else#define MDBG(x)#endif/* Big mc list lock for all the sockets */static rwlock_t ipv6_sk_mc_lock = RW_LOCK_UNLOCKED;static struct socket *igmp6_socket;static void igmp6_join_group(struct ifmcaddr6 *ma);static void igmp6_leave_group(struct ifmcaddr6 *ma);void igmp6_timer_handler(unsigned long data);#define IGMP6_UNSOLICITED_IVAL (10*HZ)/* * socket join on multicast group */int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr){ struct net_device *dev = NULL; struct ipv6_mc_socklist *mc_lst; struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; int err; if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST)) return -EINVAL; mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL); if (mc_lst == NULL) return -ENOMEM; mc_lst->next = NULL; memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr)); if (ifindex == 0) { struct rt6_info *rt; rt = rt6_lookup(addr, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); dst_release(&rt->u.dst); } } else dev = dev_get_by_index(ifindex); if (dev == NULL) { sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); return -ENODEV; } mc_lst->ifindex = dev->ifindex; /* * now add/increase the group membership on the device */ err = ipv6_dev_mc_inc(dev, addr); if (err) { sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); dev_put(dev); return err; } write_lock_bh(&ipv6_sk_mc_lock); mc_lst->next = np->ipv6_mc_list; np->ipv6_mc_list = mc_lst; write_unlock_bh(&ipv6_sk_mc_lock); dev_put(dev); return 0;}/* * socket leave on multicast group */int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr){ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct ipv6_mc_socklist *mc_lst, **lnk; write_lock_bh(&ipv6_sk_mc_lock); for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) { if (mc_lst->ifindex == ifindex && ipv6_addr_cmp(&mc_lst->addr, addr) == 0) { struct net_device *dev; *lnk = mc_lst->next; write_unlock_bh(&ipv6_sk_mc_lock); if ((dev = dev_get_by_index(ifindex)) != NULL) { ipv6_dev_mc_dec(dev, &mc_lst->addr); dev_put(dev); } sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); return 0; } } write_unlock_bh(&ipv6_sk_mc_lock); return -ENOENT;}void ipv6_sock_mc_close(struct sock *sk){ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct ipv6_mc_socklist *mc_lst; write_lock_bh(&ipv6_sk_mc_lock); while ((mc_lst = np->ipv6_mc_list) != NULL) { struct net_device *dev; np->ipv6_mc_list = mc_lst->next; write_unlock_bh(&ipv6_sk_mc_lock); dev = dev_get_by_index(mc_lst->ifindex); if (dev) { ipv6_dev_mc_dec(dev, &mc_lst->addr); dev_put(dev); } sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); write_lock_bh(&ipv6_sk_mc_lock); } write_unlock_bh(&ipv6_sk_mc_lock);}int inet6_mc_check(struct sock *sk, struct in6_addr *addr){ struct ipv6_mc_socklist *mc; read_lock(&ipv6_sk_mc_lock); for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) { if (ipv6_addr_cmp(&mc->addr, addr) == 0) { read_unlock(&ipv6_sk_mc_lock); return 1; } } read_unlock(&ipv6_sk_mc_lock); return 0;}static void ma_put(struct ifmcaddr6 *mc){ if (atomic_dec_and_test(&mc->mca_refcnt)) { in6_dev_put(mc->idev); kfree(mc); }}static int igmp6_group_added(struct ifmcaddr6 *mc){ struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; spin_lock_bh(&mc->mca_lock); if (!(mc->mca_flags&MAF_LOADED)) { mc->mca_flags |= MAF_LOADED; if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) dev_mc_add(dev, buf, dev->addr_len, 0); } spin_unlock_bh(&mc->mca_lock); if (dev->flags&IFF_UP) igmp6_join_group(mc); return 0;}static int igmp6_group_dropped(struct ifmcaddr6 *mc){ struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; spin_lock_bh(&mc->mca_lock); if (mc->mca_flags&MAF_LOADED) { mc->mca_flags &= ~MAF_LOADED; if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) dev_mc_delete(dev, buf, dev->addr_len, 0); } spin_unlock_bh(&mc->mca_lock); if (dev->flags&IFF_UP) igmp6_leave_group(mc); return 0;}/* * device multicast group inc (add if not found) */int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr){ struct ifmcaddr6 *mc; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev == NULL) return -EINVAL; write_lock_bh(&idev->lock); if (idev->dead) { write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENODEV; } for (mc = idev->mc_list; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { mc->mca_users++; write_unlock_bh(&idev->lock); in6_dev_put(idev); return 0; } } /* * not found: create a new one. */ mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC); if (mc == NULL) { write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENOMEM; } memset(mc, 0, sizeof(struct ifmcaddr6)); mc->mca_timer.function = igmp6_timer_handler; mc->mca_timer.data = (unsigned long) mc; memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr)); mc->idev = idev; mc->mca_users = 1; atomic_set(&mc->mca_refcnt, 2); mc->mca_lock = SPIN_LOCK_UNLOCKED; mc->next = idev->mc_list; idev->mc_list = mc; write_unlock_bh(&idev->lock); igmp6_group_added(mc); ma_put(mc); return 0;}/* * device multicast group del */int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr){ struct inet6_dev *idev; struct ifmcaddr6 *ma, **map; idev = in6_dev_get(dev); if (idev == NULL) return -ENODEV; write_lock_bh(&idev->lock); for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) { if (--ma->mca_users == 0) { *map = ma->next; write_unlock_bh(&idev->lock); igmp6_group_dropped(ma); ma_put(ma); in6_dev_put(idev); return 0; } write_unlock_bh(&idev->lock); in6_dev_put(idev); return 0; } } write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENOENT;}/* * check if the interface/address pair is valid */int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *addr){ struct inet6_dev *idev; struct ifmcaddr6 *mc; idev = in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (mc = idev->mc_list; mc; mc=mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { read_unlock_bh(&idev->lock); in6_dev_put(idev); return 1; } } read_unlock_bh(&idev->lock); in6_dev_put(idev); } return 0;}/* * IGMP handling (alias multicast ICMPv6 messages) */static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime){ unsigned long delay = resptime; /* Do not start timer for addresses with link/host scope */ if (ipv6_addr_type(&ma->mca_addr)&(IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)) return; spin_lock(&ma->mca_lock); if (del_timer(&ma->mca_timer)) { atomic_dec(&ma->mca_refcnt); delay = ma->mca_timer.expires - jiffies; } if (delay >= resptime) { if (resptime) delay = net_random() % resptime; else delay = 1; } ma->mca_timer.expires = jiffies + delay; if (!mod_timer(&ma->mca_timer, jiffies + delay)) atomic_inc(&ma->mca_refcnt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -