Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93a714d6 authored by Madhu Challa's avatar Madhu Challa Committed by David S. Miller
Browse files

multicast: Extend ip address command to enable multicast group join/leave on



Joining multicast group on ethernet level via "ip maddr" command would
not work if we have an Ethernet switch that does igmp snooping since
the switch would not replicate multicast packets on ports that did not
have IGMP reports for the multicast addresses.

Linux vxlan interfaces created via "ip link add vxlan" have the group option
that enables then to do the required join.

By extending ip address command with option "autojoin" we can get similar
functionality for openvswitch vxlan interfaces as well as other tunneling
mechanisms that need to receive multicast traffic. The kernel code is
structured similar to how the vxlan driver does a group join / leave.

example:
ip address add 224.1.1.10/24 dev eth5 autojoin
ip address del 224.1.1.10/24 dev eth5

Signed-off-by: default avatarMadhu Challa <challa@noironetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 46a4dee0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;
+31 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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;
	}
@@ -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);
+13 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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)
@@ -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:
@@ -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 = {
Loading