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

Commit aa2ae3e7 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller
Browse files

bridge: mcast: add MLDv2 querier support



This patch adds basic support for MLDv2 queries, the default is MLDv1
as before. A new multicast option - multicast_mld_version, adds the
ability to change it between 1 and 2 via netlink and sysfs.
The MLD option is disabled if CONFIG_IPV6 is disabled.

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5e923585
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -276,6 +276,7 @@ enum {
	IFLA_BR_VLAN_STATS_ENABLED,
	IFLA_BR_MCAST_STATS_ENABLED,
	IFLA_BR_MCAST_IGMP_VERSION,
	IFLA_BR_MCAST_MLD_VERSION,
	__IFLA_BR_MAX,
};

+68 −21
Original line number Diff line number Diff line
@@ -459,15 +459,20 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
						    const struct in6_addr *grp,
						    u8 *igmp_type)
{
	struct sk_buff *skb;
	struct mld2_query *mld2q;
	unsigned long interval;
	struct ipv6hdr *ip6h;
	struct mld_msg *mldq;
	size_t mld_hdr_size;
	struct sk_buff *skb;
	struct ethhdr *eth;
	u8 *hopopt;
	unsigned long interval;

	mld_hdr_size = sizeof(*mldq);
	if (br->multicast_mld_version == 2)
		mld_hdr_size = sizeof(*mld2q);
	skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*ip6h) +
						 8 + sizeof(*mldq));
						 8 + mld_hdr_size);
	if (!skb)
		goto out;

@@ -486,7 +491,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
	ip6h = ipv6_hdr(skb);

	*(__force __be32 *)ip6h = htonl(0x60000000);
	ip6h->payload_len = htons(8 + sizeof(*mldq));
	ip6h->payload_len = htons(8 + mld_hdr_size);
	ip6h->nexthdr = IPPROTO_HOPOPTS;
	ip6h->hop_limit = 1;
	ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1));
@@ -514,26 +519,47 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,

	/* ICMPv6 */
	skb_set_transport_header(skb, skb->len);
	mldq = (struct mld_msg *) icmp6_hdr(skb);

	interval = ipv6_addr_any(grp) ?
			br->multicast_query_response_interval :
			br->multicast_last_member_interval;

	*igmp_type = ICMPV6_MGM_QUERY;
	switch (br->multicast_mld_version) {
	case 1:
		mldq = (struct mld_msg *)icmp6_hdr(skb);
		mldq->mld_type = ICMPV6_MGM_QUERY;
		mldq->mld_code = 0;
		mldq->mld_cksum = 0;
		mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval));
		mldq->mld_reserved = 0;
		mldq->mld_mca = *grp;

	/* checksum */
		mldq->mld_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
						  sizeof(*mldq), IPPROTO_ICMPV6,
						  csum_partial(mldq,
						       sizeof(*mldq), 0));
	skb_put(skb, sizeof(*mldq));
							       sizeof(*mldq),
							       0));
		break;
	case 2:
		mld2q = (struct mld2_query *)icmp6_hdr(skb);
		mld2q->mld2q_mrc = ntohs((u16)jiffies_to_msecs(interval));
		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
		mld2q->mld2q_code = 0;
		mld2q->mld2q_cksum = 0;
		mld2q->mld2q_resv1 = 0;
		mld2q->mld2q_resv2 = 0;
		mld2q->mld2q_suppress = 0;
		mld2q->mld2q_qrv = 2;
		mld2q->mld2q_nsrcs = 0;
		mld2q->mld2q_qqic = br->multicast_query_interval / HZ;
		mld2q->mld2q_mca = *grp;
		mld2q->mld2q_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
						     sizeof(*mld2q),
						     IPPROTO_ICMPV6,
						     csum_partial(mld2q,
								  sizeof(*mld2q),
								  0));
		break;
	}
	skb_put(skb, mld_hdr_size);

	__skb_pull(skb, sizeof(*eth));

@@ -1842,7 +1868,6 @@ void br_multicast_init(struct net_bridge *br)
	br->hash_elasticity = 4;
	br->hash_max = 512;

	br->multicast_igmp_version = 2;
	br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
	br->multicast_querier = 0;
	br->multicast_query_use_ifaddr = 0;
@@ -1858,7 +1883,9 @@ void br_multicast_init(struct net_bridge *br)

	br->ip4_other_query.delay_time = 0;
	br->ip4_querier.port = NULL;
	br->multicast_igmp_version = 2;
#if IS_ENABLED(CONFIG_IPV6)
	br->multicast_mld_version = 1;
	br->ip6_other_query.delay_time = 0;
	br->ip6_querier.port = NULL;
#endif
@@ -2177,6 +2204,26 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
	return 0;
}

#if IS_ENABLED(CONFIG_IPV6)
int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val)
{
	/* Currently we support version 1 and 2 */
	switch (val) {
	case 1:
	case 2:
		break;
	default:
		return -EINVAL;
	}

	spin_lock_bh(&br->multicast_lock);
	br->multicast_mld_version = val;
	spin_unlock_bh(&br->multicast_lock);

	return 0;
}
#endif

/**
 * br_multicast_list_adjacent - Returns snooped multicast addresses
 * @dev:	The bridge port adjacent to which to retrieve addresses
+18 −1
Original line number Diff line number Diff line
@@ -859,6 +859,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
	[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
	[IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 },
	[IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
	[IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
};

static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -1079,6 +1080,17 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
		if (err)
			return err;
	}

#if IS_ENABLED(CONFIG_IPV6)
	if (data[IFLA_BR_MCAST_MLD_VERSION]) {
		__u8 mld_version;

		mld_version = nla_get_u8(data[IFLA_BR_MCAST_MLD_VERSION]);
		err = br_multicast_set_mld_version(br, mld_version);
		if (err)
			return err;
	}
#endif
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
	if (data[IFLA_BR_NF_CALL_IPTABLES]) {
@@ -1146,6 +1158,7 @@ static size_t br_get_size(const struct net_device *brdev)
	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */
	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MCAST_IGMP_VERSION */
	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MCAST_MLD_VERSION */
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IPTABLES */
@@ -1225,7 +1238,11 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
	    nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION,
		       br->multicast_igmp_version))
		return -EMSGSIZE;

#if IS_ENABLED(CONFIG_IPV6)
	if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION,
		       br->multicast_mld_version))
		return -EMSGSIZE;
#endif
	clockval = jiffies_to_clock_t(br->multicast_last_member_interval);
	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval,
			      IFLA_BR_PAD))
+4 −0
Original line number Diff line number Diff line
@@ -355,6 +355,7 @@ struct net_bridge
	struct bridge_mcast_other_query	ip6_other_query;
	struct bridge_mcast_own_query	ip6_own_query;
	struct bridge_mcast_querier	ip6_querier;
	u8				multicast_mld_version;
#endif /* IS_ENABLED(CONFIG_IPV6) */
#endif

@@ -585,6 +586,9 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val);
int br_multicast_set_querier(struct net_bridge *br, unsigned long val);
int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val);
int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val);
#if IS_ENABLED(CONFIG_IPV6)
int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val);
#endif
struct net_bridge_mdb_entry *
br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst);
struct net_bridge_mdb_entry *
+22 −0
Original line number Diff line number Diff line
@@ -659,6 +659,25 @@ static ssize_t multicast_stats_enabled_store(struct device *d,
	return store_bridge_parm(d, buf, len, set_stats_enabled);
}
static DEVICE_ATTR_RW(multicast_stats_enabled);

#if IS_ENABLED(CONFIG_IPV6)
static ssize_t multicast_mld_version_show(struct device *d,
					  struct device_attribute *attr,
					  char *buf)
{
	struct net_bridge *br = to_bridge(d);

	return sprintf(buf, "%u\n", br->multicast_mld_version);
}

static ssize_t multicast_mld_version_store(struct device *d,
					   struct device_attribute *attr,
					   const char *buf, size_t len)
{
	return store_bridge_parm(d, buf, len, br_multicast_set_mld_version);
}
static DEVICE_ATTR_RW(multicast_mld_version);
#endif
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
static ssize_t nf_call_iptables_show(
@@ -827,6 +846,9 @@ static struct attribute *bridge_attrs[] = {
	&dev_attr_multicast_startup_query_interval.attr,
	&dev_attr_multicast_stats_enabled.attr,
	&dev_attr_multicast_igmp_version.attr,
#if IS_ENABLED(CONFIG_IPV6)
	&dev_attr_multicast_mld_version.attr,
#endif
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
	&dev_attr_nf_call_iptables.attr,