Skip to content

Commit

Permalink
Merge branch 'ip_level_multicast_join_leave'
Browse files Browse the repository at this point in the history
Madhu Challa says:

====================
Multicast group join/leave at ip level

This series enables configuring multicast group join/leave at ip level
by extending the "ip address" command.

It adds a new control socket mc_autojoin_sock and ifa_flag IFA_F_MCAUTOJOIN
to invoke the corresponding igmp group join/leave api.

Since the igmp group join/leave api takes the rtnl_lock the code had to
be refactored by adding a shim layer prefixed by __ that can be invoked
by code that already has the rtnl_lock. This way we avoid proliferation of
work queues.

The first patch in this series does the refactoring for igmp v6.
Its based on igmp v4 changes that were added by Eric Dumazet.

The second patch in this series does the group join/leave based on the
setting of the IFA_F_MCAUTOJOIN flag.

v5:
- addressed comments from Daniel Borkmann.
 - removed blank line in patch 1/2
 - removed unused variable, const arg in patch 2/2
v4:
- addressed comments from Yoshifuji Hideaki.
 - Remove WARN_ON not needed because we return a value from v2.
- addressed comments from Daniel Borkmann.
 - rename sock to mc_autojoin_sk
 - ip_mc_config() pass ifa so it needs one less argument.
 - igmp_net_{init|destroy}() use inet_ctl_sock_{create|destroy}
 - inet_rtm_newaddr() change scope of ret.
 - igmp_net_init() no need to initialize sock to NULL.
v3:
- addressed comments from David Miller.
 - fixed indentation and local variable order.
v2:
- addressed comments from Eric Dumazet.
 - removed workqueue and call __ip_mc_{join|leave}_group or
   __ipv6_sock_mc_{join|drop}
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Feb 27, 2015
2 parents 723b8e4 + 93a714d commit 7705f73
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 16 deletions.
8 changes: 8 additions & 0 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -940,4 +940,12 @@ int ipv6_sysctl_register(void);
void ipv6_sysctl_unregister(void);
#endif

int ipv6_sock_mc_join(struct sock *sk, int ifindex,
const struct in6_addr *addr);
int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
const struct in6_addr *addr);
int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
const struct in6_addr *addr);
int __ipv6_sock_mc_drop(struct sock *sk, int ifindex,
const struct in6_addr *addr);
#endif /* _NET_IPV6_H */
1 change: 1 addition & 0 deletions include/net/netns/ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct netns_ipv4 {
struct sock *fibnl;

struct sock * __percpu *icmp_sk;
struct sock *mc_autojoin_sk;

struct inet_peer_base *peers;
struct tcpm_hash_bucket *tcp_metrics_hash;
Expand Down
1 change: 1 addition & 0 deletions include/net/netns/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct netns_ipv6 {
struct sock *ndisc_sk;
struct sock *tcp_sk;
struct sock *igmp_sk;
struct sock *mc_autojoin_sk;
#ifdef CONFIG_IPV6_MROUTE
#ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
struct mr6_table *mrt6;
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/if_addr.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ enum {
#define IFA_F_PERMANENT 0x80
#define IFA_F_MANAGETEMPADDR 0x100
#define IFA_F_NOPREFIXROUTE 0x200
#define IFA_F_MCAUTOJOIN 0x400

struct ifa_cacheinfo {
__u32 ifa_prefered;
Expand Down
31 changes: 31 additions & 0 deletions net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,26 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
return NULL;
}

static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
{
struct ip_mreqn mreq = {
.imr_multiaddr.s_addr = ifa->ifa_address,
.imr_ifindex = ifa->ifa_dev->dev->ifindex,
};
int ret;

ASSERT_RTNL();

lock_sock(sk);
if (join)
ret = __ip_mc_join_group(sk, &mreq);
else
ret = __ip_mc_leave_group(sk, &mreq);
release_sock(sk);

return ret;
}

static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
Expand Down Expand Up @@ -584,6 +604,8 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
!inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
continue;

if (ipv4_is_multicast(ifa->ifa_address))
ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
return 0;
}
Expand Down Expand Up @@ -838,6 +860,15 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
* userspace already relies on not having to provide this.
*/
set_ifa_lifetime(ifa, valid_lft, prefered_lft);
if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
true, ifa);

if (ret < 0) {
inet_free_ifa(ifa);
return ret;
}
}
return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
} else {
inet_free_ifa(ifa);
Expand Down
13 changes: 13 additions & 0 deletions net/ipv4/igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
#include <net/route.h>
#include <net/sock.h>
#include <net/checksum.h>
#include <net/inet_common.h>
#include <linux/netfilter_ipv4.h>
#ifdef CONFIG_IP_MROUTE
#include <linux/mroute.h>
Expand Down Expand Up @@ -2740,6 +2741,7 @@ static const struct file_operations igmp_mcf_seq_fops = {
static int __net_init igmp_net_init(struct net *net)
{
struct proc_dir_entry *pde;
int err;

pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops);
if (!pde)
Expand All @@ -2748,8 +2750,18 @@ static int __net_init igmp_net_init(struct net *net)
&igmp_mcf_seq_fops);
if (!pde)
goto out_mcfilter;
err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET,
SOCK_DGRAM, 0, net);
if (err < 0) {
pr_err("Failed to initialize the IGMP autojoin socket (err %d)\n",
err);
goto out_sock;
}

return 0;

out_sock:
remove_proc_entry("mcfilter", net->proc_net);
out_mcfilter:
remove_proc_entry("igmp", net->proc_net);
out_igmp:
Expand All @@ -2760,6 +2772,7 @@ static void __net_exit igmp_net_exit(struct net *net)
{
remove_proc_entry("mcfilter", net->proc_net);
remove_proc_entry("igmp", net->proc_net);
inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk);
}

static struct pernet_operations igmp_net_ops = {
Expand Down
38 changes: 35 additions & 3 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2464,6 +2464,23 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
return err;
}

static int ipv6_mc_config(struct sock *sk, bool join,
const struct in6_addr *addr, int ifindex)
{
int ret;

ASSERT_RTNL();

lock_sock(sk);
if (join)
ret = __ipv6_sock_mc_join(sk, ifindex, addr);
else
ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
release_sock(sk);

return ret;
}

/*
* Manual configuration of address on an interface
*/
Expand All @@ -2476,10 +2493,10 @@ static int inet6_addr_add(struct net *net, int ifindex,
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct net_device *dev;
unsigned long timeout;
clock_t expires;
int scope;
u32 flags;
clock_t expires;
unsigned long timeout;

ASSERT_RTNL();

Expand All @@ -2501,6 +2518,14 @@ static int inet6_addr_add(struct net *net, int ifindex,
if (IS_ERR(idev))
return PTR_ERR(idev);

if (ifa_flags & IFA_F_MCAUTOJOIN) {
int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk,
true, pfx, ifindex);

if (ret < 0)
return ret;
}

scope = ipv6_addr_scope(pfx);

timeout = addrconf_timeout_fixup(valid_lft, HZ);
Expand Down Expand Up @@ -2542,6 +2567,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
in6_ifa_put(ifp);
addrconf_verify_rtnl();
return 0;
} else if (ifa_flags & IFA_F_MCAUTOJOIN) {
ipv6_mc_config(net->ipv6.mc_autojoin_sk,
false, pfx, ifindex);
}

return PTR_ERR(ifp);
Expand Down Expand Up @@ -2578,6 +2606,10 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
jiffies);
ipv6_del_addr(ifp);
addrconf_verify_rtnl();
if (ipv6_addr_is_multicast(pfx)) {
ipv6_mc_config(net->ipv6.mc_autojoin_sk,
false, pfx, dev->ifindex);
}
return 0;
}
}
Expand Down Expand Up @@ -3945,7 +3977,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)

/* We ignore other flags so far. */
ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
IFA_F_NOPREFIXROUTE;
IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN;

ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
if (ifa == NULL) {
Expand Down
60 changes: 47 additions & 13 deletions net/ipv6/mcast.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,16 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
return iv > 0 ? iv : 1;
}

int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
struct net_device *dev = NULL;
struct ipv6_mc_socklist *mc_lst;
struct ipv6_pinfo *np = inet6_sk(sk);
struct net *net = sock_net(sk);
int err;

ASSERT_RTNL();

if (!ipv6_addr_is_multicast(addr))
return -EINVAL;

Expand All @@ -161,7 +163,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
mc_lst->next = NULL;
mc_lst->addr = *addr;

rtnl_lock();
if (ifindex == 0) {
struct rt6_info *rt;
rt = rt6_lookup(net, addr, NULL, 0, 0);
Expand All @@ -173,7 +174,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
dev = __dev_get_by_index(net, ifindex);

if (dev == NULL) {
rtnl_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return -ENODEV;
}
Expand All @@ -190,33 +190,44 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
err = ipv6_dev_mc_inc(dev, addr);

if (err) {
rtnl_unlock();
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return err;
}

mc_lst->next = np->ipv6_mc_list;
rcu_assign_pointer(np->ipv6_mc_list, mc_lst);

return 0;
}
EXPORT_SYMBOL(__ipv6_sock_mc_join);

int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
int ret;

rtnl_lock();
ret = __ipv6_sock_mc_join(sk, ifindex, addr);
rtnl_unlock();

return 0;
return ret;
}
EXPORT_SYMBOL(ipv6_sock_mc_join);

/*
* socket leave on multicast group
*/
int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst;
struct ipv6_mc_socklist __rcu **lnk;
struct net *net = sock_net(sk);

ASSERT_RTNL();

if (!ipv6_addr_is_multicast(addr))
return -EINVAL;

rtnl_lock();
for (lnk = &np->ipv6_mc_list;
(mc_lst = rtnl_dereference(*lnk)) != NULL;
lnk = &mc_lst->next) {
Expand All @@ -235,17 +246,28 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
} else
(void) ip6_mc_leave_src(sk, mc_lst, NULL);
rtnl_unlock();

atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu);
return 0;
}
}
rtnl_unlock();

return -EADDRNOTAVAIL;
}
EXPORT_SYMBOL(__ipv6_sock_mc_drop);

int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
int ret;

rtnl_lock();
ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
rtnl_unlock();

return ret;
}
EXPORT_SYMBOL(ipv6_sock_mc_drop);

/* called with rcu_read_lock() */
static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
Expand Down Expand Up @@ -2907,20 +2929,32 @@ static int __net_init igmp6_net_init(struct net *net)

inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;

err = inet_ctl_sock_create(&net->ipv6.mc_autojoin_sk, PF_INET6,
SOCK_RAW, IPPROTO_ICMPV6, net);
if (err < 0) {
pr_err("Failed to initialize the IGMP6 autojoin socket (err %d)\n",
err);
goto out_sock_create;
}

err = igmp6_proc_init(net);
if (err)
goto out_sock_create;
out:
return err;
goto out_sock_create_autojoin;

return 0;

out_sock_create_autojoin:
inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk);
out_sock_create:
inet_ctl_sock_destroy(net->ipv6.igmp_sk);
goto out;
out:
return err;
}

static void __net_exit igmp6_net_exit(struct net *net)
{
inet_ctl_sock_destroy(net->ipv6.igmp_sk);
inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk);
igmp6_proc_exit(net);
}

Expand Down

0 comments on commit 7705f73

Please sign in to comment.