| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1 | /* | 
 | 2 |  * Bridge multicast support. | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au> | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify it | 
 | 7 |  * under the terms of the GNU General Public License as published by the Free | 
 | 8 |  * Software Foundation; either version 2 of the License, or (at your option) | 
 | 9 |  * any later version. | 
 | 10 |  * | 
 | 11 |  */ | 
 | 12 |  | 
 | 13 | #include <linux/err.h> | 
 | 14 | #include <linux/if_ether.h> | 
 | 15 | #include <linux/igmp.h> | 
 | 16 | #include <linux/jhash.h> | 
 | 17 | #include <linux/kernel.h> | 
| Herbert Xu | b195167 | 2010-02-27 19:41:51 +0000 | [diff] [blame] | 18 | #include <linux/log2.h> | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 19 | #include <linux/netdevice.h> | 
 | 20 | #include <linux/netfilter_bridge.h> | 
 | 21 | #include <linux/random.h> | 
 | 22 | #include <linux/rculist.h> | 
 | 23 | #include <linux/skbuff.h> | 
 | 24 | #include <linux/slab.h> | 
 | 25 | #include <linux/timer.h> | 
 | 26 | #include <net/ip.h> | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 27 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 28 | #include <net/ipv6.h> | 
 | 29 | #include <net/mld.h> | 
 | 30 | #include <net/addrconf.h> | 
| David S. Miller | d4c4f07 | 2010-04-27 10:16:54 -0700 | [diff] [blame] | 31 | #include <net/ip6_checksum.h> | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 32 | #endif | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 33 |  | 
 | 34 | #include "br_private.h" | 
 | 35 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 36 | #define mlock_dereference(X, br) \ | 
 | 37 | 	rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) | 
 | 38 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 39 | #if IS_ENABLED(CONFIG_IPV6) | 
| Linus Lüssing | e4de9f9 | 2011-02-15 13:19:21 +0000 | [diff] [blame] | 40 | static inline int ipv6_is_transient_multicast(const struct in6_addr *addr) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 41 | { | 
| Linus Lüssing | e4de9f9 | 2011-02-15 13:19:21 +0000 | [diff] [blame] | 42 | 	if (ipv6_addr_is_multicast(addr) && IPV6_ADDR_MC_FLAG_TRANSIENT(addr)) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 43 | 		return 1; | 
 | 44 | 	return 0; | 
 | 45 | } | 
 | 46 | #endif | 
 | 47 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 48 | static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) | 
 | 49 | { | 
 | 50 | 	if (a->proto != b->proto) | 
 | 51 | 		return 0; | 
 | 52 | 	switch (a->proto) { | 
 | 53 | 	case htons(ETH_P_IP): | 
 | 54 | 		return a->u.ip4 == b->u.ip4; | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 55 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 56 | 	case htons(ETH_P_IPV6): | 
 | 57 | 		return ipv6_addr_equal(&a->u.ip6, &b->u.ip6); | 
 | 58 | #endif | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 59 | 	} | 
 | 60 | 	return 0; | 
 | 61 | } | 
 | 62 |  | 
 | 63 | static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 64 | { | 
| Eric Dumazet | 0eae88f | 2010-04-20 19:06:52 -0700 | [diff] [blame] | 65 | 	return jhash_1word(mdb->secret, (__force u32)ip) & (mdb->max - 1); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 66 | } | 
 | 67 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 68 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 69 | static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb, | 
 | 70 | 				const struct in6_addr *ip) | 
 | 71 | { | 
 | 72 | 	return jhash2((__force u32 *)ip->s6_addr32, 4, mdb->secret) & (mdb->max - 1); | 
 | 73 | } | 
 | 74 | #endif | 
 | 75 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 76 | static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb, | 
 | 77 | 			     struct br_ip *ip) | 
 | 78 | { | 
 | 79 | 	switch (ip->proto) { | 
 | 80 | 	case htons(ETH_P_IP): | 
 | 81 | 		return __br_ip4_hash(mdb, ip->u.ip4); | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 82 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 83 | 	case htons(ETH_P_IPV6): | 
 | 84 | 		return __br_ip6_hash(mdb, &ip->u.ip6); | 
 | 85 | #endif | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 86 | 	} | 
 | 87 | 	return 0; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 88 | } | 
 | 89 |  | 
 | 90 | static struct net_bridge_mdb_entry *__br_mdb_ip_get( | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 91 | 	struct net_bridge_mdb_htable *mdb, struct br_ip *dst, int hash) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 92 | { | 
 | 93 | 	struct net_bridge_mdb_entry *mp; | 
 | 94 | 	struct hlist_node *p; | 
 | 95 |  | 
| Herbert Xu | 49f5fcf | 2010-03-05 21:07:39 +0000 | [diff] [blame] | 96 | 	hlist_for_each_entry_rcu(mp, p, &mdb->mhash[hash], hlist[mdb->ver]) { | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 97 | 		if (br_ip_equal(&mp->addr, dst)) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 98 | 			return mp; | 
 | 99 | 	} | 
 | 100 |  | 
 | 101 | 	return NULL; | 
 | 102 | } | 
 | 103 |  | 
| Herbert Xu | 7f285fa | 2010-07-05 14:50:08 +0000 | [diff] [blame] | 104 | static struct net_bridge_mdb_entry *br_mdb_ip_get( | 
 | 105 | 	struct net_bridge_mdb_htable *mdb, struct br_ip *dst) | 
 | 106 | { | 
 | 107 | 	if (!mdb) | 
 | 108 | 		return NULL; | 
 | 109 |  | 
 | 110 | 	return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst)); | 
 | 111 | } | 
 | 112 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 113 | static struct net_bridge_mdb_entry *br_mdb_ip4_get( | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 114 | 	struct net_bridge_mdb_htable *mdb, __be32 dst) | 
 | 115 | { | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 116 | 	struct br_ip br_dst; | 
| Herbert Xu | 0821ec5 | 2010-03-15 20:38:25 -0700 | [diff] [blame] | 117 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 118 | 	br_dst.u.ip4 = dst; | 
 | 119 | 	br_dst.proto = htons(ETH_P_IP); | 
 | 120 |  | 
| Herbert Xu | 7f285fa | 2010-07-05 14:50:08 +0000 | [diff] [blame] | 121 | 	return br_mdb_ip_get(mdb, &br_dst); | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 122 | } | 
 | 123 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 124 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 125 | static struct net_bridge_mdb_entry *br_mdb_ip6_get( | 
 | 126 | 	struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst) | 
 | 127 | { | 
 | 128 | 	struct br_ip br_dst; | 
 | 129 |  | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 130 | 	br_dst.u.ip6 = *dst; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 131 | 	br_dst.proto = htons(ETH_P_IPV6); | 
 | 132 |  | 
| Herbert Xu | 7f285fa | 2010-07-05 14:50:08 +0000 | [diff] [blame] | 133 | 	return br_mdb_ip_get(mdb, &br_dst); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 134 | } | 
 | 135 | #endif | 
 | 136 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 137 | struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, | 
 | 138 | 					struct sk_buff *skb) | 
 | 139 | { | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 140 | 	struct net_bridge_mdb_htable *mdb = rcu_dereference(br->mdb); | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 141 | 	struct br_ip ip; | 
 | 142 |  | 
| Herbert Xu | 7f285fa | 2010-07-05 14:50:08 +0000 | [diff] [blame] | 143 | 	if (br->multicast_disabled) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 144 | 		return NULL; | 
 | 145 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 146 | 	if (BR_INPUT_SKB_CB(skb)->igmp) | 
 | 147 | 		return NULL; | 
 | 148 |  | 
 | 149 | 	ip.proto = skb->protocol; | 
 | 150 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 151 | 	switch (skb->protocol) { | 
 | 152 | 	case htons(ETH_P_IP): | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 153 | 		ip.u.ip4 = ip_hdr(skb)->daddr; | 
 | 154 | 		break; | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 155 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 156 | 	case htons(ETH_P_IPV6): | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 157 | 		ip.u.ip6 = ipv6_hdr(skb)->daddr; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 158 | 		break; | 
 | 159 | #endif | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 160 | 	default: | 
 | 161 | 		return NULL; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 162 | 	} | 
 | 163 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 164 | 	return br_mdb_ip_get(mdb, &ip); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 165 | } | 
 | 166 |  | 
 | 167 | static void br_mdb_free(struct rcu_head *head) | 
 | 168 | { | 
 | 169 | 	struct net_bridge_mdb_htable *mdb = | 
 | 170 | 		container_of(head, struct net_bridge_mdb_htable, rcu); | 
 | 171 | 	struct net_bridge_mdb_htable *old = mdb->old; | 
 | 172 |  | 
 | 173 | 	mdb->old = NULL; | 
 | 174 | 	kfree(old->mhash); | 
 | 175 | 	kfree(old); | 
 | 176 | } | 
 | 177 |  | 
 | 178 | static int br_mdb_copy(struct net_bridge_mdb_htable *new, | 
 | 179 | 		       struct net_bridge_mdb_htable *old, | 
 | 180 | 		       int elasticity) | 
 | 181 | { | 
 | 182 | 	struct net_bridge_mdb_entry *mp; | 
 | 183 | 	struct hlist_node *p; | 
 | 184 | 	int maxlen; | 
 | 185 | 	int len; | 
 | 186 | 	int i; | 
 | 187 |  | 
 | 188 | 	for (i = 0; i < old->max; i++) | 
 | 189 | 		hlist_for_each_entry(mp, p, &old->mhash[i], hlist[old->ver]) | 
 | 190 | 			hlist_add_head(&mp->hlist[new->ver], | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 191 | 				       &new->mhash[br_ip_hash(new, &mp->addr)]); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 192 |  | 
 | 193 | 	if (!elasticity) | 
 | 194 | 		return 0; | 
 | 195 |  | 
 | 196 | 	maxlen = 0; | 
 | 197 | 	for (i = 0; i < new->max; i++) { | 
 | 198 | 		len = 0; | 
 | 199 | 		hlist_for_each_entry(mp, p, &new->mhash[i], hlist[new->ver]) | 
 | 200 | 			len++; | 
 | 201 | 		if (len > maxlen) | 
 | 202 | 			maxlen = len; | 
 | 203 | 	} | 
 | 204 |  | 
 | 205 | 	return maxlen > elasticity ? -EINVAL : 0; | 
 | 206 | } | 
 | 207 |  | 
 | 208 | static void br_multicast_free_pg(struct rcu_head *head) | 
 | 209 | { | 
 | 210 | 	struct net_bridge_port_group *p = | 
 | 211 | 		container_of(head, struct net_bridge_port_group, rcu); | 
 | 212 |  | 
 | 213 | 	kfree(p); | 
 | 214 | } | 
 | 215 |  | 
 | 216 | static void br_multicast_free_group(struct rcu_head *head) | 
 | 217 | { | 
 | 218 | 	struct net_bridge_mdb_entry *mp = | 
 | 219 | 		container_of(head, struct net_bridge_mdb_entry, rcu); | 
 | 220 |  | 
 | 221 | 	kfree(mp); | 
 | 222 | } | 
 | 223 |  | 
 | 224 | static void br_multicast_group_expired(unsigned long data) | 
 | 225 | { | 
 | 226 | 	struct net_bridge_mdb_entry *mp = (void *)data; | 
 | 227 | 	struct net_bridge *br = mp->br; | 
 | 228 | 	struct net_bridge_mdb_htable *mdb; | 
 | 229 |  | 
 | 230 | 	spin_lock(&br->multicast_lock); | 
 | 231 | 	if (!netif_running(br->dev) || timer_pending(&mp->timer)) | 
 | 232 | 		goto out; | 
 | 233 |  | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 234 | 	mp->mglist = false; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 235 |  | 
 | 236 | 	if (mp->ports) | 
 | 237 | 		goto out; | 
 | 238 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 239 | 	mdb = mlock_dereference(br->mdb, br); | 
 | 240 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 241 | 	hlist_del_rcu(&mp->hlist[mdb->ver]); | 
 | 242 | 	mdb->size--; | 
 | 243 |  | 
 | 244 | 	del_timer(&mp->query_timer); | 
 | 245 | 	call_rcu_bh(&mp->rcu, br_multicast_free_group); | 
 | 246 |  | 
 | 247 | out: | 
 | 248 | 	spin_unlock(&br->multicast_lock); | 
 | 249 | } | 
 | 250 |  | 
 | 251 | static void br_multicast_del_pg(struct net_bridge *br, | 
 | 252 | 				struct net_bridge_port_group *pg) | 
 | 253 | { | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 254 | 	struct net_bridge_mdb_htable *mdb; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 255 | 	struct net_bridge_mdb_entry *mp; | 
 | 256 | 	struct net_bridge_port_group *p; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 257 | 	struct net_bridge_port_group __rcu **pp; | 
 | 258 |  | 
 | 259 | 	mdb = mlock_dereference(br->mdb, br); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 260 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 261 | 	mp = br_mdb_ip_get(mdb, &pg->addr); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 262 | 	if (WARN_ON(!mp)) | 
 | 263 | 		return; | 
 | 264 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 265 | 	for (pp = &mp->ports; | 
 | 266 | 	     (p = mlock_dereference(*pp, br)) != NULL; | 
 | 267 | 	     pp = &p->next) { | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 268 | 		if (p != pg) | 
 | 269 | 			continue; | 
 | 270 |  | 
| stephen hemminger | 83f6a74 | 2010-04-27 15:01:06 +0000 | [diff] [blame] | 271 | 		rcu_assign_pointer(*pp, p->next); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 272 | 		hlist_del_init(&p->mglist); | 
 | 273 | 		del_timer(&p->timer); | 
 | 274 | 		del_timer(&p->query_timer); | 
 | 275 | 		call_rcu_bh(&p->rcu, br_multicast_free_pg); | 
 | 276 |  | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 277 | 		if (!mp->ports && !mp->mglist && | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 278 | 		    netif_running(br->dev)) | 
 | 279 | 			mod_timer(&mp->timer, jiffies); | 
 | 280 |  | 
 | 281 | 		return; | 
 | 282 | 	} | 
 | 283 |  | 
 | 284 | 	WARN_ON(1); | 
 | 285 | } | 
 | 286 |  | 
 | 287 | static void br_multicast_port_group_expired(unsigned long data) | 
 | 288 | { | 
 | 289 | 	struct net_bridge_port_group *pg = (void *)data; | 
 | 290 | 	struct net_bridge *br = pg->port->br; | 
 | 291 |  | 
 | 292 | 	spin_lock(&br->multicast_lock); | 
 | 293 | 	if (!netif_running(br->dev) || timer_pending(&pg->timer) || | 
 | 294 | 	    hlist_unhashed(&pg->mglist)) | 
 | 295 | 		goto out; | 
 | 296 |  | 
 | 297 | 	br_multicast_del_pg(br, pg); | 
 | 298 |  | 
 | 299 | out: | 
 | 300 | 	spin_unlock(&br->multicast_lock); | 
 | 301 | } | 
 | 302 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 303 | static int br_mdb_rehash(struct net_bridge_mdb_htable __rcu **mdbp, int max, | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 304 | 			 int elasticity) | 
 | 305 | { | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 306 | 	struct net_bridge_mdb_htable *old = rcu_dereference_protected(*mdbp, 1); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 307 | 	struct net_bridge_mdb_htable *mdb; | 
 | 308 | 	int err; | 
 | 309 |  | 
 | 310 | 	mdb = kmalloc(sizeof(*mdb), GFP_ATOMIC); | 
 | 311 | 	if (!mdb) | 
 | 312 | 		return -ENOMEM; | 
 | 313 |  | 
 | 314 | 	mdb->max = max; | 
 | 315 | 	mdb->old = old; | 
 | 316 |  | 
 | 317 | 	mdb->mhash = kzalloc(max * sizeof(*mdb->mhash), GFP_ATOMIC); | 
 | 318 | 	if (!mdb->mhash) { | 
 | 319 | 		kfree(mdb); | 
 | 320 | 		return -ENOMEM; | 
 | 321 | 	} | 
 | 322 |  | 
 | 323 | 	mdb->size = old ? old->size : 0; | 
 | 324 | 	mdb->ver = old ? old->ver ^ 1 : 0; | 
 | 325 |  | 
 | 326 | 	if (!old || elasticity) | 
 | 327 | 		get_random_bytes(&mdb->secret, sizeof(mdb->secret)); | 
 | 328 | 	else | 
 | 329 | 		mdb->secret = old->secret; | 
 | 330 |  | 
 | 331 | 	if (!old) | 
 | 332 | 		goto out; | 
 | 333 |  | 
 | 334 | 	err = br_mdb_copy(mdb, old, elasticity); | 
 | 335 | 	if (err) { | 
 | 336 | 		kfree(mdb->mhash); | 
 | 337 | 		kfree(mdb); | 
 | 338 | 		return err; | 
 | 339 | 	} | 
 | 340 |  | 
 | 341 | 	call_rcu_bh(&mdb->rcu, br_mdb_free); | 
 | 342 |  | 
 | 343 | out: | 
 | 344 | 	rcu_assign_pointer(*mdbp, mdb); | 
 | 345 |  | 
 | 346 | 	return 0; | 
 | 347 | } | 
 | 348 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 349 | static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, | 
 | 350 | 						    __be32 group) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 351 | { | 
 | 352 | 	struct sk_buff *skb; | 
 | 353 | 	struct igmphdr *ih; | 
 | 354 | 	struct ethhdr *eth; | 
 | 355 | 	struct iphdr *iph; | 
 | 356 |  | 
 | 357 | 	skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*iph) + | 
 | 358 | 						 sizeof(*ih) + 4); | 
 | 359 | 	if (!skb) | 
 | 360 | 		goto out; | 
 | 361 |  | 
 | 362 | 	skb->protocol = htons(ETH_P_IP); | 
 | 363 |  | 
 | 364 | 	skb_reset_mac_header(skb); | 
 | 365 | 	eth = eth_hdr(skb); | 
 | 366 |  | 
 | 367 | 	memcpy(eth->h_source, br->dev->dev_addr, 6); | 
 | 368 | 	eth->h_dest[0] = 1; | 
 | 369 | 	eth->h_dest[1] = 0; | 
 | 370 | 	eth->h_dest[2] = 0x5e; | 
 | 371 | 	eth->h_dest[3] = 0; | 
 | 372 | 	eth->h_dest[4] = 0; | 
 | 373 | 	eth->h_dest[5] = 1; | 
 | 374 | 	eth->h_proto = htons(ETH_P_IP); | 
 | 375 | 	skb_put(skb, sizeof(*eth)); | 
 | 376 |  | 
 | 377 | 	skb_set_network_header(skb, skb->len); | 
 | 378 | 	iph = ip_hdr(skb); | 
 | 379 |  | 
 | 380 | 	iph->version = 4; | 
 | 381 | 	iph->ihl = 6; | 
 | 382 | 	iph->tos = 0xc0; | 
 | 383 | 	iph->tot_len = htons(sizeof(*iph) + sizeof(*ih) + 4); | 
 | 384 | 	iph->id = 0; | 
 | 385 | 	iph->frag_off = htons(IP_DF); | 
 | 386 | 	iph->ttl = 1; | 
 | 387 | 	iph->protocol = IPPROTO_IGMP; | 
 | 388 | 	iph->saddr = 0; | 
 | 389 | 	iph->daddr = htonl(INADDR_ALLHOSTS_GROUP); | 
 | 390 | 	((u8 *)&iph[1])[0] = IPOPT_RA; | 
 | 391 | 	((u8 *)&iph[1])[1] = 4; | 
 | 392 | 	((u8 *)&iph[1])[2] = 0; | 
 | 393 | 	((u8 *)&iph[1])[3] = 0; | 
 | 394 | 	ip_send_check(iph); | 
 | 395 | 	skb_put(skb, 24); | 
 | 396 |  | 
 | 397 | 	skb_set_transport_header(skb, skb->len); | 
 | 398 | 	ih = igmp_hdr(skb); | 
 | 399 | 	ih->type = IGMP_HOST_MEMBERSHIP_QUERY; | 
 | 400 | 	ih->code = (group ? br->multicast_last_member_interval : | 
 | 401 | 			    br->multicast_query_response_interval) / | 
 | 402 | 		   (HZ / IGMP_TIMER_SCALE); | 
 | 403 | 	ih->group = group; | 
 | 404 | 	ih->csum = 0; | 
 | 405 | 	ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); | 
 | 406 | 	skb_put(skb, sizeof(*ih)); | 
 | 407 |  | 
 | 408 | 	__skb_pull(skb, sizeof(*eth)); | 
 | 409 |  | 
 | 410 | out: | 
 | 411 | 	return skb; | 
 | 412 | } | 
 | 413 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 414 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 415 | static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, | 
| Eric Dumazet | b71d1d4 | 2011-04-22 04:53:02 +0000 | [diff] [blame] | 416 | 						    const struct in6_addr *group) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 417 | { | 
 | 418 | 	struct sk_buff *skb; | 
 | 419 | 	struct ipv6hdr *ip6h; | 
 | 420 | 	struct mld_msg *mldq; | 
 | 421 | 	struct ethhdr *eth; | 
 | 422 | 	u8 *hopopt; | 
 | 423 | 	unsigned long interval; | 
 | 424 |  | 
 | 425 | 	skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*ip6h) + | 
 | 426 | 						 8 + sizeof(*mldq)); | 
 | 427 | 	if (!skb) | 
 | 428 | 		goto out; | 
 | 429 |  | 
 | 430 | 	skb->protocol = htons(ETH_P_IPV6); | 
 | 431 |  | 
 | 432 | 	/* Ethernet header */ | 
 | 433 | 	skb_reset_mac_header(skb); | 
 | 434 | 	eth = eth_hdr(skb); | 
 | 435 |  | 
 | 436 | 	memcpy(eth->h_source, br->dev->dev_addr, 6); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 437 | 	eth->h_proto = htons(ETH_P_IPV6); | 
 | 438 | 	skb_put(skb, sizeof(*eth)); | 
 | 439 |  | 
 | 440 | 	/* IPv6 header + HbH option */ | 
 | 441 | 	skb_set_network_header(skb, skb->len); | 
 | 442 | 	ip6h = ipv6_hdr(skb); | 
 | 443 |  | 
 | 444 | 	*(__force __be32 *)ip6h = htonl(0x60000000); | 
| David Stevens | 76d6615 | 2010-12-14 08:42:16 +0000 | [diff] [blame] | 445 | 	ip6h->payload_len = htons(8 + sizeof(*mldq)); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 446 | 	ip6h->nexthdr = IPPROTO_HOPOPTS; | 
 | 447 | 	ip6h->hop_limit = 1; | 
| Linus Lüssing | a7bff75 | 2011-03-22 11:40:32 +0000 | [diff] [blame] | 448 | 	ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); | 
| Linus Lüssing | fe29ec4 | 2011-02-17 08:17:52 +0000 | [diff] [blame] | 449 | 	ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0, | 
 | 450 | 			   &ip6h->saddr); | 
| Linus Lüssing | 36cff5a | 2011-02-17 08:17:51 +0000 | [diff] [blame] | 451 | 	ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 452 |  | 
 | 453 | 	hopopt = (u8 *)(ip6h + 1); | 
 | 454 | 	hopopt[0] = IPPROTO_ICMPV6;		/* next hdr */ | 
 | 455 | 	hopopt[1] = 0;				/* length of HbH */ | 
 | 456 | 	hopopt[2] = IPV6_TLV_ROUTERALERT;	/* Router Alert */ | 
 | 457 | 	hopopt[3] = 2;				/* Length of RA Option */ | 
 | 458 | 	hopopt[4] = 0;				/* Type = 0x0000 (MLD) */ | 
 | 459 | 	hopopt[5] = 0; | 
 | 460 | 	hopopt[6] = IPV6_TLV_PAD0;		/* Pad0 */ | 
 | 461 | 	hopopt[7] = IPV6_TLV_PAD0;		/* Pad0 */ | 
 | 462 |  | 
 | 463 | 	skb_put(skb, sizeof(*ip6h) + 8); | 
 | 464 |  | 
 | 465 | 	/* ICMPv6 */ | 
 | 466 | 	skb_set_transport_header(skb, skb->len); | 
 | 467 | 	mldq = (struct mld_msg *) icmp6_hdr(skb); | 
 | 468 |  | 
 | 469 | 	interval = ipv6_addr_any(group) ? br->multicast_last_member_interval : | 
 | 470 | 					  br->multicast_query_response_interval; | 
 | 471 |  | 
 | 472 | 	mldq->mld_type = ICMPV6_MGM_QUERY; | 
 | 473 | 	mldq->mld_code = 0; | 
 | 474 | 	mldq->mld_cksum = 0; | 
 | 475 | 	mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); | 
 | 476 | 	mldq->mld_reserved = 0; | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 477 | 	mldq->mld_mca = *group; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 478 |  | 
 | 479 | 	/* checksum */ | 
 | 480 | 	mldq->mld_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, | 
 | 481 | 					  sizeof(*mldq), IPPROTO_ICMPV6, | 
 | 482 | 					  csum_partial(mldq, | 
 | 483 | 						       sizeof(*mldq), 0)); | 
 | 484 | 	skb_put(skb, sizeof(*mldq)); | 
 | 485 |  | 
 | 486 | 	__skb_pull(skb, sizeof(*eth)); | 
 | 487 |  | 
 | 488 | out: | 
 | 489 | 	return skb; | 
 | 490 | } | 
 | 491 | #endif | 
 | 492 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 493 | static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, | 
 | 494 | 						struct br_ip *addr) | 
 | 495 | { | 
 | 496 | 	switch (addr->proto) { | 
 | 497 | 	case htons(ETH_P_IP): | 
 | 498 | 		return br_ip4_multicast_alloc_query(br, addr->u.ip4); | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 499 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 500 | 	case htons(ETH_P_IPV6): | 
 | 501 | 		return br_ip6_multicast_alloc_query(br, &addr->u.ip6); | 
 | 502 | #endif | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 503 | 	} | 
 | 504 | 	return NULL; | 
 | 505 | } | 
 | 506 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 507 | static void br_multicast_send_group_query(struct net_bridge_mdb_entry *mp) | 
 | 508 | { | 
 | 509 | 	struct net_bridge *br = mp->br; | 
 | 510 | 	struct sk_buff *skb; | 
 | 511 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 512 | 	skb = br_multicast_alloc_query(br, &mp->addr); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 513 | 	if (!skb) | 
 | 514 | 		goto timer; | 
 | 515 |  | 
 | 516 | 	netif_rx(skb); | 
 | 517 |  | 
 | 518 | timer: | 
 | 519 | 	if (++mp->queries_sent < br->multicast_last_member_count) | 
 | 520 | 		mod_timer(&mp->query_timer, | 
 | 521 | 			  jiffies + br->multicast_last_member_interval); | 
 | 522 | } | 
 | 523 |  | 
 | 524 | static void br_multicast_group_query_expired(unsigned long data) | 
 | 525 | { | 
 | 526 | 	struct net_bridge_mdb_entry *mp = (void *)data; | 
 | 527 | 	struct net_bridge *br = mp->br; | 
 | 528 |  | 
 | 529 | 	spin_lock(&br->multicast_lock); | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 530 | 	if (!netif_running(br->dev) || !mp->mglist || | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 531 | 	    mp->queries_sent >= br->multicast_last_member_count) | 
 | 532 | 		goto out; | 
 | 533 |  | 
 | 534 | 	br_multicast_send_group_query(mp); | 
 | 535 |  | 
 | 536 | out: | 
 | 537 | 	spin_unlock(&br->multicast_lock); | 
 | 538 | } | 
 | 539 |  | 
 | 540 | static void br_multicast_send_port_group_query(struct net_bridge_port_group *pg) | 
 | 541 | { | 
 | 542 | 	struct net_bridge_port *port = pg->port; | 
 | 543 | 	struct net_bridge *br = port->br; | 
 | 544 | 	struct sk_buff *skb; | 
 | 545 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 546 | 	skb = br_multicast_alloc_query(br, &pg->addr); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 547 | 	if (!skb) | 
 | 548 | 		goto timer; | 
 | 549 |  | 
 | 550 | 	br_deliver(port, skb); | 
 | 551 |  | 
 | 552 | timer: | 
 | 553 | 	if (++pg->queries_sent < br->multicast_last_member_count) | 
 | 554 | 		mod_timer(&pg->query_timer, | 
 | 555 | 			  jiffies + br->multicast_last_member_interval); | 
 | 556 | } | 
 | 557 |  | 
 | 558 | static void br_multicast_port_group_query_expired(unsigned long data) | 
 | 559 | { | 
 | 560 | 	struct net_bridge_port_group *pg = (void *)data; | 
 | 561 | 	struct net_bridge_port *port = pg->port; | 
 | 562 | 	struct net_bridge *br = port->br; | 
 | 563 |  | 
 | 564 | 	spin_lock(&br->multicast_lock); | 
 | 565 | 	if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) || | 
 | 566 | 	    pg->queries_sent >= br->multicast_last_member_count) | 
 | 567 | 		goto out; | 
 | 568 |  | 
 | 569 | 	br_multicast_send_port_group_query(pg); | 
 | 570 |  | 
 | 571 | out: | 
 | 572 | 	spin_unlock(&br->multicast_lock); | 
 | 573 | } | 
 | 574 |  | 
 | 575 | static struct net_bridge_mdb_entry *br_multicast_get_group( | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 576 | 	struct net_bridge *br, struct net_bridge_port *port, | 
 | 577 | 	struct br_ip *group, int hash) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 578 | { | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 579 | 	struct net_bridge_mdb_htable *mdb; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 580 | 	struct net_bridge_mdb_entry *mp; | 
 | 581 | 	struct hlist_node *p; | 
 | 582 | 	unsigned count = 0; | 
 | 583 | 	unsigned max; | 
 | 584 | 	int elasticity; | 
 | 585 | 	int err; | 
 | 586 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 587 | 	mdb = rcu_dereference_protected(br->mdb, 1); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 588 | 	hlist_for_each_entry(mp, p, &mdb->mhash[hash], hlist[mdb->ver]) { | 
 | 589 | 		count++; | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 590 | 		if (unlikely(br_ip_equal(group, &mp->addr))) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 591 | 			return mp; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 592 | 	} | 
 | 593 |  | 
 | 594 | 	elasticity = 0; | 
 | 595 | 	max = mdb->max; | 
 | 596 |  | 
 | 597 | 	if (unlikely(count > br->hash_elasticity && count)) { | 
 | 598 | 		if (net_ratelimit()) | 
| stephen hemminger | 28a16c9 | 2010-05-10 09:31:09 +0000 | [diff] [blame] | 599 | 			br_info(br, "Multicast hash table " | 
 | 600 | 				"chain limit reached: %s\n", | 
 | 601 | 				port ? port->dev->name : br->dev->name); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 602 |  | 
 | 603 | 		elasticity = br->hash_elasticity; | 
 | 604 | 	} | 
 | 605 |  | 
 | 606 | 	if (mdb->size >= max) { | 
 | 607 | 		max *= 2; | 
 | 608 | 		if (unlikely(max >= br->hash_max)) { | 
| stephen hemminger | 28a16c9 | 2010-05-10 09:31:09 +0000 | [diff] [blame] | 609 | 			br_warn(br, "Multicast hash table maximum " | 
 | 610 | 				"reached, disabling snooping: %s, %d\n", | 
 | 611 | 				port ? port->dev->name : br->dev->name, max); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 612 | 			err = -E2BIG; | 
 | 613 | disable: | 
 | 614 | 			br->multicast_disabled = 1; | 
 | 615 | 			goto err; | 
 | 616 | 		} | 
 | 617 | 	} | 
 | 618 |  | 
 | 619 | 	if (max > mdb->max || elasticity) { | 
 | 620 | 		if (mdb->old) { | 
 | 621 | 			if (net_ratelimit()) | 
| stephen hemminger | 28a16c9 | 2010-05-10 09:31:09 +0000 | [diff] [blame] | 622 | 				br_info(br, "Multicast hash table " | 
 | 623 | 					"on fire: %s\n", | 
 | 624 | 					port ? port->dev->name : br->dev->name); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 625 | 			err = -EEXIST; | 
 | 626 | 			goto err; | 
 | 627 | 		} | 
 | 628 |  | 
 | 629 | 		err = br_mdb_rehash(&br->mdb, max, elasticity); | 
 | 630 | 		if (err) { | 
| stephen hemminger | 28a16c9 | 2010-05-10 09:31:09 +0000 | [diff] [blame] | 631 | 			br_warn(br, "Cannot rehash multicast " | 
 | 632 | 				"hash table, disabling snooping: %s, %d, %d\n", | 
 | 633 | 				port ? port->dev->name : br->dev->name, | 
 | 634 | 				mdb->size, err); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 635 | 			goto disable; | 
 | 636 | 		} | 
 | 637 |  | 
 | 638 | 		err = -EAGAIN; | 
 | 639 | 		goto err; | 
 | 640 | 	} | 
 | 641 |  | 
 | 642 | 	return NULL; | 
 | 643 |  | 
 | 644 | err: | 
 | 645 | 	mp = ERR_PTR(err); | 
 | 646 | 	return mp; | 
 | 647 | } | 
 | 648 |  | 
 | 649 | static struct net_bridge_mdb_entry *br_multicast_new_group( | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 650 | 	struct net_bridge *br, struct net_bridge_port *port, | 
 | 651 | 	struct br_ip *group) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 652 | { | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 653 | 	struct net_bridge_mdb_htable *mdb; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 654 | 	struct net_bridge_mdb_entry *mp; | 
 | 655 | 	int hash; | 
| Tobias Klauser | 4c0833b | 2010-12-10 03:18:04 +0000 | [diff] [blame] | 656 | 	int err; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 657 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 658 | 	mdb = rcu_dereference_protected(br->mdb, 1); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 659 | 	if (!mdb) { | 
| Tobias Klauser | 4c0833b | 2010-12-10 03:18:04 +0000 | [diff] [blame] | 660 | 		err = br_mdb_rehash(&br->mdb, BR_HASH_SIZE, 0); | 
 | 661 | 		if (err) | 
 | 662 | 			return ERR_PTR(err); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 663 | 		goto rehash; | 
 | 664 | 	} | 
 | 665 |  | 
 | 666 | 	hash = br_ip_hash(mdb, group); | 
 | 667 | 	mp = br_multicast_get_group(br, port, group, hash); | 
 | 668 | 	switch (PTR_ERR(mp)) { | 
 | 669 | 	case 0: | 
 | 670 | 		break; | 
 | 671 |  | 
 | 672 | 	case -EAGAIN: | 
 | 673 | rehash: | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 674 | 		mdb = rcu_dereference_protected(br->mdb, 1); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 675 | 		hash = br_ip_hash(mdb, group); | 
 | 676 | 		break; | 
 | 677 |  | 
 | 678 | 	default: | 
 | 679 | 		goto out; | 
 | 680 | 	} | 
 | 681 |  | 
 | 682 | 	mp = kzalloc(sizeof(*mp), GFP_ATOMIC); | 
 | 683 | 	if (unlikely(!mp)) | 
| Tobias Klauser | 4c0833b | 2010-12-10 03:18:04 +0000 | [diff] [blame] | 684 | 		return ERR_PTR(-ENOMEM); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 685 |  | 
 | 686 | 	mp->br = br; | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 687 | 	mp->addr = *group; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 688 | 	setup_timer(&mp->timer, br_multicast_group_expired, | 
 | 689 | 		    (unsigned long)mp); | 
 | 690 | 	setup_timer(&mp->query_timer, br_multicast_group_query_expired, | 
 | 691 | 		    (unsigned long)mp); | 
 | 692 |  | 
 | 693 | 	hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]); | 
 | 694 | 	mdb->size++; | 
 | 695 |  | 
 | 696 | out: | 
 | 697 | 	return mp; | 
 | 698 | } | 
 | 699 |  | 
 | 700 | static int br_multicast_add_group(struct net_bridge *br, | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 701 | 				  struct net_bridge_port *port, | 
 | 702 | 				  struct br_ip *group) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 703 | { | 
 | 704 | 	struct net_bridge_mdb_entry *mp; | 
 | 705 | 	struct net_bridge_port_group *p; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 706 | 	struct net_bridge_port_group __rcu **pp; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 707 | 	unsigned long now = jiffies; | 
 | 708 | 	int err; | 
 | 709 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 710 | 	spin_lock(&br->multicast_lock); | 
 | 711 | 	if (!netif_running(br->dev) || | 
 | 712 | 	    (port && port->state == BR_STATE_DISABLED)) | 
 | 713 | 		goto out; | 
 | 714 |  | 
 | 715 | 	mp = br_multicast_new_group(br, port, group); | 
 | 716 | 	err = PTR_ERR(mp); | 
| Tobias Klauser | 4c0833b | 2010-12-10 03:18:04 +0000 | [diff] [blame] | 717 | 	if (IS_ERR(mp)) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 718 | 		goto err; | 
 | 719 |  | 
 | 720 | 	if (!port) { | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 721 | 		mp->mglist = true; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 722 | 		mod_timer(&mp->timer, now + br->multicast_membership_interval); | 
 | 723 | 		goto out; | 
 | 724 | 	} | 
 | 725 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 726 | 	for (pp = &mp->ports; | 
 | 727 | 	     (p = mlock_dereference(*pp, br)) != NULL; | 
 | 728 | 	     pp = &p->next) { | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 729 | 		if (p->port == port) | 
 | 730 | 			goto found; | 
 | 731 | 		if ((unsigned long)p->port < (unsigned long)port) | 
 | 732 | 			break; | 
 | 733 | 	} | 
 | 734 |  | 
 | 735 | 	p = kzalloc(sizeof(*p), GFP_ATOMIC); | 
 | 736 | 	err = -ENOMEM; | 
 | 737 | 	if (unlikely(!p)) | 
 | 738 | 		goto err; | 
 | 739 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 740 | 	p->addr = *group; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 741 | 	p->port = port; | 
 | 742 | 	p->next = *pp; | 
 | 743 | 	hlist_add_head(&p->mglist, &port->mglist); | 
 | 744 | 	setup_timer(&p->timer, br_multicast_port_group_expired, | 
 | 745 | 		    (unsigned long)p); | 
 | 746 | 	setup_timer(&p->query_timer, br_multicast_port_group_query_expired, | 
 | 747 | 		    (unsigned long)p); | 
 | 748 |  | 
 | 749 | 	rcu_assign_pointer(*pp, p); | 
 | 750 |  | 
 | 751 | found: | 
 | 752 | 	mod_timer(&p->timer, now + br->multicast_membership_interval); | 
 | 753 | out: | 
 | 754 | 	err = 0; | 
 | 755 |  | 
 | 756 | err: | 
 | 757 | 	spin_unlock(&br->multicast_lock); | 
 | 758 | 	return err; | 
 | 759 | } | 
 | 760 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 761 | static int br_ip4_multicast_add_group(struct net_bridge *br, | 
 | 762 | 				      struct net_bridge_port *port, | 
 | 763 | 				      __be32 group) | 
 | 764 | { | 
 | 765 | 	struct br_ip br_group; | 
 | 766 |  | 
 | 767 | 	if (ipv4_is_local_multicast(group)) | 
 | 768 | 		return 0; | 
 | 769 |  | 
 | 770 | 	br_group.u.ip4 = group; | 
 | 771 | 	br_group.proto = htons(ETH_P_IP); | 
 | 772 |  | 
 | 773 | 	return br_multicast_add_group(br, port, &br_group); | 
 | 774 | } | 
 | 775 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 776 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 777 | static int br_ip6_multicast_add_group(struct net_bridge *br, | 
 | 778 | 				      struct net_bridge_port *port, | 
 | 779 | 				      const struct in6_addr *group) | 
 | 780 | { | 
 | 781 | 	struct br_ip br_group; | 
 | 782 |  | 
| Linus Lüssing | e4de9f9 | 2011-02-15 13:19:21 +0000 | [diff] [blame] | 783 | 	if (!ipv6_is_transient_multicast(group)) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 784 | 		return 0; | 
 | 785 |  | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 786 | 	br_group.u.ip6 = *group; | 
| Linus Lüssing | 9cc6e0c | 2011-02-15 13:19:17 +0000 | [diff] [blame] | 787 | 	br_group.proto = htons(ETH_P_IPV6); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 788 |  | 
 | 789 | 	return br_multicast_add_group(br, port, &br_group); | 
 | 790 | } | 
 | 791 | #endif | 
 | 792 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 793 | static void br_multicast_router_expired(unsigned long data) | 
 | 794 | { | 
 | 795 | 	struct net_bridge_port *port = (void *)data; | 
 | 796 | 	struct net_bridge *br = port->br; | 
 | 797 |  | 
 | 798 | 	spin_lock(&br->multicast_lock); | 
 | 799 | 	if (port->multicast_router != 1 || | 
 | 800 | 	    timer_pending(&port->multicast_router_timer) || | 
 | 801 | 	    hlist_unhashed(&port->rlist)) | 
 | 802 | 		goto out; | 
 | 803 |  | 
 | 804 | 	hlist_del_init_rcu(&port->rlist); | 
 | 805 |  | 
 | 806 | out: | 
 | 807 | 	spin_unlock(&br->multicast_lock); | 
 | 808 | } | 
 | 809 |  | 
 | 810 | static void br_multicast_local_router_expired(unsigned long data) | 
 | 811 | { | 
 | 812 | } | 
 | 813 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 814 | static void __br_multicast_send_query(struct net_bridge *br, | 
 | 815 | 				      struct net_bridge_port *port, | 
 | 816 | 				      struct br_ip *ip) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 817 | { | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 818 | 	struct sk_buff *skb; | 
 | 819 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 820 | 	skb = br_multicast_alloc_query(br, ip); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 821 | 	if (!skb) | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 822 | 		return; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 823 |  | 
 | 824 | 	if (port) { | 
 | 825 | 		__skb_push(skb, sizeof(struct ethhdr)); | 
 | 826 | 		skb->dev = port->dev; | 
| Jan Engelhardt | 713aefa | 2010-03-23 04:07:21 +0100 | [diff] [blame] | 827 | 		NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 828 | 			dev_queue_xmit); | 
 | 829 | 	} else | 
 | 830 | 		netif_rx(skb); | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 831 | } | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 832 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 833 | static void br_multicast_send_query(struct net_bridge *br, | 
 | 834 | 				    struct net_bridge_port *port, u32 sent) | 
 | 835 | { | 
 | 836 | 	unsigned long time; | 
 | 837 | 	struct br_ip br_group; | 
 | 838 |  | 
 | 839 | 	if (!netif_running(br->dev) || br->multicast_disabled || | 
 | 840 | 	    timer_pending(&br->multicast_querier_timer)) | 
 | 841 | 		return; | 
 | 842 |  | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 843 | 	memset(&br_group.u, 0, sizeof(br_group.u)); | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 844 |  | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 845 | 	br_group.proto = htons(ETH_P_IP); | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 846 | 	__br_multicast_send_query(br, port, &br_group); | 
 | 847 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 848 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 849 | 	br_group.proto = htons(ETH_P_IPV6); | 
 | 850 | 	__br_multicast_send_query(br, port, &br_group); | 
 | 851 | #endif | 
 | 852 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 853 | 	time = jiffies; | 
 | 854 | 	time += sent < br->multicast_startup_query_count ? | 
 | 855 | 		br->multicast_startup_query_interval : | 
 | 856 | 		br->multicast_query_interval; | 
 | 857 | 	mod_timer(port ? &port->multicast_query_timer : | 
 | 858 | 			 &br->multicast_query_timer, time); | 
 | 859 | } | 
 | 860 |  | 
 | 861 | static void br_multicast_port_query_expired(unsigned long data) | 
 | 862 | { | 
 | 863 | 	struct net_bridge_port *port = (void *)data; | 
 | 864 | 	struct net_bridge *br = port->br; | 
 | 865 |  | 
 | 866 | 	spin_lock(&br->multicast_lock); | 
| Dan Carpenter | 02a780c | 2010-03-06 01:14:09 +0000 | [diff] [blame] | 867 | 	if (port->state == BR_STATE_DISABLED || | 
 | 868 | 	    port->state == BR_STATE_BLOCKING) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 869 | 		goto out; | 
 | 870 |  | 
 | 871 | 	if (port->multicast_startup_queries_sent < | 
 | 872 | 	    br->multicast_startup_query_count) | 
 | 873 | 		port->multicast_startup_queries_sent++; | 
 | 874 |  | 
 | 875 | 	br_multicast_send_query(port->br, port, | 
 | 876 | 				port->multicast_startup_queries_sent); | 
 | 877 |  | 
 | 878 | out: | 
 | 879 | 	spin_unlock(&br->multicast_lock); | 
 | 880 | } | 
 | 881 |  | 
 | 882 | void br_multicast_add_port(struct net_bridge_port *port) | 
 | 883 | { | 
 | 884 | 	port->multicast_router = 1; | 
 | 885 |  | 
 | 886 | 	setup_timer(&port->multicast_router_timer, br_multicast_router_expired, | 
 | 887 | 		    (unsigned long)port); | 
 | 888 | 	setup_timer(&port->multicast_query_timer, | 
 | 889 | 		    br_multicast_port_query_expired, (unsigned long)port); | 
 | 890 | } | 
 | 891 |  | 
 | 892 | void br_multicast_del_port(struct net_bridge_port *port) | 
 | 893 | { | 
 | 894 | 	del_timer_sync(&port->multicast_router_timer); | 
 | 895 | } | 
 | 896 |  | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 897 | static void __br_multicast_enable_port(struct net_bridge_port *port) | 
 | 898 | { | 
 | 899 | 	port->multicast_startup_queries_sent = 0; | 
 | 900 |  | 
 | 901 | 	if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 || | 
 | 902 | 	    del_timer(&port->multicast_query_timer)) | 
 | 903 | 		mod_timer(&port->multicast_query_timer, jiffies); | 
 | 904 | } | 
 | 905 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 906 | void br_multicast_enable_port(struct net_bridge_port *port) | 
 | 907 | { | 
 | 908 | 	struct net_bridge *br = port->br; | 
 | 909 |  | 
 | 910 | 	spin_lock(&br->multicast_lock); | 
 | 911 | 	if (br->multicast_disabled || !netif_running(br->dev)) | 
 | 912 | 		goto out; | 
 | 913 |  | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 914 | 	__br_multicast_enable_port(port); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 915 |  | 
 | 916 | out: | 
 | 917 | 	spin_unlock(&br->multicast_lock); | 
 | 918 | } | 
 | 919 |  | 
 | 920 | void br_multicast_disable_port(struct net_bridge_port *port) | 
 | 921 | { | 
 | 922 | 	struct net_bridge *br = port->br; | 
 | 923 | 	struct net_bridge_port_group *pg; | 
 | 924 | 	struct hlist_node *p, *n; | 
 | 925 |  | 
 | 926 | 	spin_lock(&br->multicast_lock); | 
 | 927 | 	hlist_for_each_entry_safe(pg, p, n, &port->mglist, mglist) | 
 | 928 | 		br_multicast_del_pg(br, pg); | 
 | 929 |  | 
 | 930 | 	if (!hlist_unhashed(&port->rlist)) | 
 | 931 | 		hlist_del_init_rcu(&port->rlist); | 
 | 932 | 	del_timer(&port->multicast_router_timer); | 
 | 933 | 	del_timer(&port->multicast_query_timer); | 
 | 934 | 	spin_unlock(&br->multicast_lock); | 
 | 935 | } | 
 | 936 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 937 | static int br_ip4_multicast_igmp3_report(struct net_bridge *br, | 
 | 938 | 					 struct net_bridge_port *port, | 
 | 939 | 					 struct sk_buff *skb) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 940 | { | 
 | 941 | 	struct igmpv3_report *ih; | 
 | 942 | 	struct igmpv3_grec *grec; | 
 | 943 | 	int i; | 
 | 944 | 	int len; | 
 | 945 | 	int num; | 
 | 946 | 	int type; | 
 | 947 | 	int err = 0; | 
 | 948 | 	__be32 group; | 
 | 949 |  | 
 | 950 | 	if (!pskb_may_pull(skb, sizeof(*ih))) | 
 | 951 | 		return -EINVAL; | 
 | 952 |  | 
 | 953 | 	ih = igmpv3_report_hdr(skb); | 
 | 954 | 	num = ntohs(ih->ngrec); | 
 | 955 | 	len = sizeof(*ih); | 
 | 956 |  | 
 | 957 | 	for (i = 0; i < num; i++) { | 
 | 958 | 		len += sizeof(*grec); | 
 | 959 | 		if (!pskb_may_pull(skb, len)) | 
 | 960 | 			return -EINVAL; | 
 | 961 |  | 
| Herbert Xu | fd218cf | 2010-04-07 21:20:47 -0700 | [diff] [blame] | 962 | 		grec = (void *)(skb->data + len - sizeof(*grec)); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 963 | 		group = grec->grec_mca; | 
 | 964 | 		type = grec->grec_type; | 
 | 965 |  | 
| Eric Dumazet | 8eabf95 | 2010-04-20 03:20:05 +0000 | [diff] [blame] | 966 | 		len += ntohs(grec->grec_nsrcs) * 4; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 967 | 		if (!pskb_may_pull(skb, len)) | 
 | 968 | 			return -EINVAL; | 
 | 969 |  | 
 | 970 | 		/* We treat this as an IGMPv2 report for now. */ | 
 | 971 | 		switch (type) { | 
 | 972 | 		case IGMPV3_MODE_IS_INCLUDE: | 
 | 973 | 		case IGMPV3_MODE_IS_EXCLUDE: | 
 | 974 | 		case IGMPV3_CHANGE_TO_INCLUDE: | 
 | 975 | 		case IGMPV3_CHANGE_TO_EXCLUDE: | 
 | 976 | 		case IGMPV3_ALLOW_NEW_SOURCES: | 
 | 977 | 		case IGMPV3_BLOCK_OLD_SOURCES: | 
 | 978 | 			break; | 
 | 979 |  | 
 | 980 | 		default: | 
 | 981 | 			continue; | 
 | 982 | 		} | 
 | 983 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 984 | 		err = br_ip4_multicast_add_group(br, port, group); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 985 | 		if (err) | 
 | 986 | 			break; | 
 | 987 | 	} | 
 | 988 |  | 
 | 989 | 	return err; | 
 | 990 | } | 
 | 991 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 992 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 993 | static int br_ip6_multicast_mld2_report(struct net_bridge *br, | 
 | 994 | 					struct net_bridge_port *port, | 
 | 995 | 					struct sk_buff *skb) | 
 | 996 | { | 
 | 997 | 	struct icmp6hdr *icmp6h; | 
 | 998 | 	struct mld2_grec *grec; | 
 | 999 | 	int i; | 
 | 1000 | 	int len; | 
 | 1001 | 	int num; | 
 | 1002 | 	int err = 0; | 
 | 1003 |  | 
 | 1004 | 	if (!pskb_may_pull(skb, sizeof(*icmp6h))) | 
 | 1005 | 		return -EINVAL; | 
 | 1006 |  | 
 | 1007 | 	icmp6h = icmp6_hdr(skb); | 
 | 1008 | 	num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); | 
 | 1009 | 	len = sizeof(*icmp6h); | 
 | 1010 |  | 
 | 1011 | 	for (i = 0; i < num; i++) { | 
 | 1012 | 		__be16 *nsrcs, _nsrcs; | 
 | 1013 |  | 
 | 1014 | 		nsrcs = skb_header_pointer(skb, | 
 | 1015 | 					   len + offsetof(struct mld2_grec, | 
| Linus Lüssing | 649e984 | 2011-02-15 13:19:18 +0000 | [diff] [blame] | 1016 | 							  grec_nsrcs), | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1017 | 					   sizeof(_nsrcs), &_nsrcs); | 
 | 1018 | 		if (!nsrcs) | 
 | 1019 | 			return -EINVAL; | 
 | 1020 |  | 
 | 1021 | 		if (!pskb_may_pull(skb, | 
 | 1022 | 				   len + sizeof(*grec) + | 
| Linus Lüssing | d41db9f | 2011-02-15 13:19:19 +0000 | [diff] [blame] | 1023 | 				   sizeof(struct in6_addr) * ntohs(*nsrcs))) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1024 | 			return -EINVAL; | 
 | 1025 |  | 
 | 1026 | 		grec = (struct mld2_grec *)(skb->data + len); | 
| Linus Lüssing | d41db9f | 2011-02-15 13:19:19 +0000 | [diff] [blame] | 1027 | 		len += sizeof(*grec) + | 
 | 1028 | 		       sizeof(struct in6_addr) * ntohs(*nsrcs); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1029 |  | 
 | 1030 | 		/* We treat these as MLDv1 reports for now. */ | 
 | 1031 | 		switch (grec->grec_type) { | 
 | 1032 | 		case MLD2_MODE_IS_INCLUDE: | 
 | 1033 | 		case MLD2_MODE_IS_EXCLUDE: | 
 | 1034 | 		case MLD2_CHANGE_TO_INCLUDE: | 
 | 1035 | 		case MLD2_CHANGE_TO_EXCLUDE: | 
 | 1036 | 		case MLD2_ALLOW_NEW_SOURCES: | 
 | 1037 | 		case MLD2_BLOCK_OLD_SOURCES: | 
 | 1038 | 			break; | 
 | 1039 |  | 
 | 1040 | 		default: | 
 | 1041 | 			continue; | 
 | 1042 | 		} | 
 | 1043 |  | 
 | 1044 | 		err = br_ip6_multicast_add_group(br, port, &grec->grec_mca); | 
 | 1045 | 		if (!err) | 
 | 1046 | 			break; | 
 | 1047 | 	} | 
 | 1048 |  | 
 | 1049 | 	return err; | 
 | 1050 | } | 
 | 1051 | #endif | 
 | 1052 |  | 
| stephen hemminger | 7e80c12 | 2010-04-27 15:01:04 +0000 | [diff] [blame] | 1053 | /* | 
 | 1054 |  * Add port to rotuer_list | 
 | 1055 |  *  list is maintained ordered by pointer value | 
 | 1056 |  *  and locked by br->multicast_lock and RCU | 
 | 1057 |  */ | 
| Herbert Xu | 0909e11 | 2010-02-27 19:41:49 +0000 | [diff] [blame] | 1058 | static void br_multicast_add_router(struct net_bridge *br, | 
 | 1059 | 				    struct net_bridge_port *port) | 
 | 1060 | { | 
| stephen hemminger | dcdca2c | 2010-04-27 07:13:11 +0000 | [diff] [blame] | 1061 | 	struct net_bridge_port *p; | 
| stephen hemminger | 7e80c12 | 2010-04-27 15:01:04 +0000 | [diff] [blame] | 1062 | 	struct hlist_node *n, *slot = NULL; | 
| Herbert Xu | 0909e11 | 2010-02-27 19:41:49 +0000 | [diff] [blame] | 1063 |  | 
| David S. Miller | 709b932 | 2010-04-27 16:49:58 -0700 | [diff] [blame] | 1064 | 	hlist_for_each_entry(p, n, &br->router_list, rlist) { | 
| stephen hemminger | 7e80c12 | 2010-04-27 15:01:04 +0000 | [diff] [blame] | 1065 | 		if ((unsigned long) port >= (unsigned long) p) | 
 | 1066 | 			break; | 
 | 1067 | 		slot = n; | 
| stephen hemminger | dcdca2c | 2010-04-27 07:13:11 +0000 | [diff] [blame] | 1068 | 	} | 
| Herbert Xu | 0909e11 | 2010-02-27 19:41:49 +0000 | [diff] [blame] | 1069 |  | 
| stephen hemminger | 7e80c12 | 2010-04-27 15:01:04 +0000 | [diff] [blame] | 1070 | 	if (slot) | 
 | 1071 | 		hlist_add_after_rcu(slot, &port->rlist); | 
| stephen hemminger | dcdca2c | 2010-04-27 07:13:11 +0000 | [diff] [blame] | 1072 | 	else | 
 | 1073 | 		hlist_add_head_rcu(&port->rlist, &br->router_list); | 
| Herbert Xu | 0909e11 | 2010-02-27 19:41:49 +0000 | [diff] [blame] | 1074 | } | 
 | 1075 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1076 | static void br_multicast_mark_router(struct net_bridge *br, | 
 | 1077 | 				     struct net_bridge_port *port) | 
 | 1078 | { | 
 | 1079 | 	unsigned long now = jiffies; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1080 |  | 
 | 1081 | 	if (!port) { | 
 | 1082 | 		if (br->multicast_router == 1) | 
 | 1083 | 			mod_timer(&br->multicast_router_timer, | 
 | 1084 | 				  now + br->multicast_querier_interval); | 
 | 1085 | 		return; | 
 | 1086 | 	} | 
 | 1087 |  | 
 | 1088 | 	if (port->multicast_router != 1) | 
 | 1089 | 		return; | 
 | 1090 |  | 
 | 1091 | 	if (!hlist_unhashed(&port->rlist)) | 
 | 1092 | 		goto timer; | 
 | 1093 |  | 
| Herbert Xu | 0909e11 | 2010-02-27 19:41:49 +0000 | [diff] [blame] | 1094 | 	br_multicast_add_router(br, port); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1095 |  | 
 | 1096 | timer: | 
 | 1097 | 	mod_timer(&port->multicast_router_timer, | 
 | 1098 | 		  now + br->multicast_querier_interval); | 
 | 1099 | } | 
 | 1100 |  | 
 | 1101 | static void br_multicast_query_received(struct net_bridge *br, | 
 | 1102 | 					struct net_bridge_port *port, | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1103 | 					int saddr) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1104 | { | 
 | 1105 | 	if (saddr) | 
 | 1106 | 		mod_timer(&br->multicast_querier_timer, | 
 | 1107 | 			  jiffies + br->multicast_querier_interval); | 
 | 1108 | 	else if (timer_pending(&br->multicast_querier_timer)) | 
 | 1109 | 		return; | 
 | 1110 |  | 
 | 1111 | 	br_multicast_mark_router(br, port); | 
 | 1112 | } | 
 | 1113 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1114 | static int br_ip4_multicast_query(struct net_bridge *br, | 
 | 1115 | 				  struct net_bridge_port *port, | 
 | 1116 | 				  struct sk_buff *skb) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1117 | { | 
| Eric Dumazet | b71d1d4 | 2011-04-22 04:53:02 +0000 | [diff] [blame] | 1118 | 	const struct iphdr *iph = ip_hdr(skb); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1119 | 	struct igmphdr *ih = igmp_hdr(skb); | 
 | 1120 | 	struct net_bridge_mdb_entry *mp; | 
 | 1121 | 	struct igmpv3_query *ih3; | 
 | 1122 | 	struct net_bridge_port_group *p; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1123 | 	struct net_bridge_port_group __rcu **pp; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1124 | 	unsigned long max_delay; | 
 | 1125 | 	unsigned long now = jiffies; | 
 | 1126 | 	__be32 group; | 
| YOSHIFUJI Hideaki | bec68ff | 2010-03-13 12:27:21 -0800 | [diff] [blame] | 1127 | 	int err = 0; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1128 |  | 
 | 1129 | 	spin_lock(&br->multicast_lock); | 
 | 1130 | 	if (!netif_running(br->dev) || | 
 | 1131 | 	    (port && port->state == BR_STATE_DISABLED)) | 
 | 1132 | 		goto out; | 
 | 1133 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1134 | 	br_multicast_query_received(br, port, !!iph->saddr); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1135 |  | 
 | 1136 | 	group = ih->group; | 
 | 1137 |  | 
 | 1138 | 	if (skb->len == sizeof(*ih)) { | 
 | 1139 | 		max_delay = ih->code * (HZ / IGMP_TIMER_SCALE); | 
 | 1140 |  | 
 | 1141 | 		if (!max_delay) { | 
 | 1142 | 			max_delay = 10 * HZ; | 
 | 1143 | 			group = 0; | 
 | 1144 | 		} | 
 | 1145 | 	} else { | 
| YOSHIFUJI Hideaki | bec68ff | 2010-03-13 12:27:21 -0800 | [diff] [blame] | 1146 | 		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) { | 
 | 1147 | 			err = -EINVAL; | 
 | 1148 | 			goto out; | 
 | 1149 | 		} | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1150 |  | 
 | 1151 | 		ih3 = igmpv3_query_hdr(skb); | 
 | 1152 | 		if (ih3->nsrcs) | 
| YOSHIFUJI Hideaki | bec68ff | 2010-03-13 12:27:21 -0800 | [diff] [blame] | 1153 | 			goto out; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1154 |  | 
| YOSHIFUJI Hideaki / 吉藤英明 | 0ba8c9e | 2010-03-15 19:27:00 +0000 | [diff] [blame] | 1155 | 		max_delay = ih3->code ? | 
 | 1156 | 			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1157 | 	} | 
 | 1158 |  | 
 | 1159 | 	if (!group) | 
 | 1160 | 		goto out; | 
 | 1161 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1162 | 	mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1163 | 	if (!mp) | 
 | 1164 | 		goto out; | 
 | 1165 |  | 
 | 1166 | 	max_delay *= br->multicast_last_member_count; | 
 | 1167 |  | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 1168 | 	if (mp->mglist && | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1169 | 	    (timer_pending(&mp->timer) ? | 
 | 1170 | 	     time_after(mp->timer.expires, now + max_delay) : | 
 | 1171 | 	     try_to_del_timer_sync(&mp->timer) >= 0)) | 
 | 1172 | 		mod_timer(&mp->timer, now + max_delay); | 
 | 1173 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1174 | 	for (pp = &mp->ports; | 
 | 1175 | 	     (p = mlock_dereference(*pp, br)) != NULL; | 
 | 1176 | 	     pp = &p->next) { | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1177 | 		if (timer_pending(&p->timer) ? | 
 | 1178 | 		    time_after(p->timer.expires, now + max_delay) : | 
 | 1179 | 		    try_to_del_timer_sync(&p->timer) >= 0) | 
| Herbert Xu | 24f9cdc | 2011-02-11 12:42:07 +0000 | [diff] [blame] | 1180 | 			mod_timer(&p->timer, now + max_delay); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1181 | 	} | 
 | 1182 |  | 
 | 1183 | out: | 
 | 1184 | 	spin_unlock(&br->multicast_lock); | 
| YOSHIFUJI Hideaki | bec68ff | 2010-03-13 12:27:21 -0800 | [diff] [blame] | 1185 | 	return err; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1186 | } | 
 | 1187 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 1188 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1189 | static int br_ip6_multicast_query(struct net_bridge *br, | 
 | 1190 | 				  struct net_bridge_port *port, | 
 | 1191 | 				  struct sk_buff *skb) | 
 | 1192 | { | 
| Eric Dumazet | b71d1d4 | 2011-04-22 04:53:02 +0000 | [diff] [blame] | 1193 | 	const struct ipv6hdr *ip6h = ipv6_hdr(skb); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1194 | 	struct mld_msg *mld = (struct mld_msg *) icmp6_hdr(skb); | 
 | 1195 | 	struct net_bridge_mdb_entry *mp; | 
 | 1196 | 	struct mld2_query *mld2q; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1197 | 	struct net_bridge_port_group *p; | 
 | 1198 | 	struct net_bridge_port_group __rcu **pp; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1199 | 	unsigned long max_delay; | 
 | 1200 | 	unsigned long now = jiffies; | 
| Eric Dumazet | b71d1d4 | 2011-04-22 04:53:02 +0000 | [diff] [blame] | 1201 | 	const struct in6_addr *group = NULL; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1202 | 	int err = 0; | 
 | 1203 |  | 
 | 1204 | 	spin_lock(&br->multicast_lock); | 
 | 1205 | 	if (!netif_running(br->dev) || | 
 | 1206 | 	    (port && port->state == BR_STATE_DISABLED)) | 
 | 1207 | 		goto out; | 
 | 1208 |  | 
 | 1209 | 	br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr)); | 
 | 1210 |  | 
 | 1211 | 	if (skb->len == sizeof(*mld)) { | 
 | 1212 | 		if (!pskb_may_pull(skb, sizeof(*mld))) { | 
 | 1213 | 			err = -EINVAL; | 
 | 1214 | 			goto out; | 
 | 1215 | 		} | 
 | 1216 | 		mld = (struct mld_msg *) icmp6_hdr(skb); | 
 | 1217 | 		max_delay = msecs_to_jiffies(htons(mld->mld_maxdelay)); | 
 | 1218 | 		if (max_delay) | 
 | 1219 | 			group = &mld->mld_mca; | 
 | 1220 | 	} else if (skb->len >= sizeof(*mld2q)) { | 
 | 1221 | 		if (!pskb_may_pull(skb, sizeof(*mld2q))) { | 
 | 1222 | 			err = -EINVAL; | 
 | 1223 | 			goto out; | 
 | 1224 | 		} | 
 | 1225 | 		mld2q = (struct mld2_query *)icmp6_hdr(skb); | 
 | 1226 | 		if (!mld2q->mld2q_nsrcs) | 
 | 1227 | 			group = &mld2q->mld2q_mca; | 
 | 1228 | 		max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(mld2q->mld2q_mrc) : 1; | 
 | 1229 | 	} | 
 | 1230 |  | 
 | 1231 | 	if (!group) | 
 | 1232 | 		goto out; | 
 | 1233 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1234 | 	mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1235 | 	if (!mp) | 
 | 1236 | 		goto out; | 
 | 1237 |  | 
 | 1238 | 	max_delay *= br->multicast_last_member_count; | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 1239 | 	if (mp->mglist && | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1240 | 	    (timer_pending(&mp->timer) ? | 
 | 1241 | 	     time_after(mp->timer.expires, now + max_delay) : | 
 | 1242 | 	     try_to_del_timer_sync(&mp->timer) >= 0)) | 
 | 1243 | 		mod_timer(&mp->timer, now + max_delay); | 
 | 1244 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1245 | 	for (pp = &mp->ports; | 
 | 1246 | 	     (p = mlock_dereference(*pp, br)) != NULL; | 
 | 1247 | 	     pp = &p->next) { | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1248 | 		if (timer_pending(&p->timer) ? | 
 | 1249 | 		    time_after(p->timer.expires, now + max_delay) : | 
 | 1250 | 		    try_to_del_timer_sync(&p->timer) >= 0) | 
| Herbert Xu | 24f9cdc | 2011-02-11 12:42:07 +0000 | [diff] [blame] | 1251 | 			mod_timer(&p->timer, now + max_delay); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1252 | 	} | 
 | 1253 |  | 
 | 1254 | out: | 
 | 1255 | 	spin_unlock(&br->multicast_lock); | 
 | 1256 | 	return err; | 
 | 1257 | } | 
 | 1258 | #endif | 
 | 1259 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1260 | static void br_multicast_leave_group(struct net_bridge *br, | 
 | 1261 | 				     struct net_bridge_port *port, | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1262 | 				     struct br_ip *group) | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1263 | { | 
 | 1264 | 	struct net_bridge_mdb_htable *mdb; | 
 | 1265 | 	struct net_bridge_mdb_entry *mp; | 
 | 1266 | 	struct net_bridge_port_group *p; | 
 | 1267 | 	unsigned long now; | 
 | 1268 | 	unsigned long time; | 
 | 1269 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1270 | 	spin_lock(&br->multicast_lock); | 
 | 1271 | 	if (!netif_running(br->dev) || | 
 | 1272 | 	    (port && port->state == BR_STATE_DISABLED) || | 
 | 1273 | 	    timer_pending(&br->multicast_querier_timer)) | 
 | 1274 | 		goto out; | 
 | 1275 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1276 | 	mdb = mlock_dereference(br->mdb, br); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1277 | 	mp = br_mdb_ip_get(mdb, group); | 
 | 1278 | 	if (!mp) | 
 | 1279 | 		goto out; | 
 | 1280 |  | 
 | 1281 | 	now = jiffies; | 
 | 1282 | 	time = now + br->multicast_last_member_count * | 
 | 1283 | 		     br->multicast_last_member_interval; | 
 | 1284 |  | 
 | 1285 | 	if (!port) { | 
| Herbert Xu | 8a87017 | 2011-02-12 01:05:42 -0800 | [diff] [blame] | 1286 | 		if (mp->mglist && | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1287 | 		    (timer_pending(&mp->timer) ? | 
 | 1288 | 		     time_after(mp->timer.expires, time) : | 
 | 1289 | 		     try_to_del_timer_sync(&mp->timer) >= 0)) { | 
 | 1290 | 			mod_timer(&mp->timer, time); | 
 | 1291 |  | 
 | 1292 | 			mp->queries_sent = 0; | 
 | 1293 | 			mod_timer(&mp->query_timer, now); | 
 | 1294 | 		} | 
 | 1295 |  | 
 | 1296 | 		goto out; | 
 | 1297 | 	} | 
 | 1298 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1299 | 	for (p = mlock_dereference(mp->ports, br); | 
 | 1300 | 	     p != NULL; | 
 | 1301 | 	     p = mlock_dereference(p->next, br)) { | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1302 | 		if (p->port != port) | 
 | 1303 | 			continue; | 
 | 1304 |  | 
 | 1305 | 		if (!hlist_unhashed(&p->mglist) && | 
 | 1306 | 		    (timer_pending(&p->timer) ? | 
 | 1307 | 		     time_after(p->timer.expires, time) : | 
 | 1308 | 		     try_to_del_timer_sync(&p->timer) >= 0)) { | 
 | 1309 | 			mod_timer(&p->timer, time); | 
 | 1310 |  | 
 | 1311 | 			p->queries_sent = 0; | 
 | 1312 | 			mod_timer(&p->query_timer, now); | 
 | 1313 | 		} | 
 | 1314 |  | 
 | 1315 | 		break; | 
 | 1316 | 	} | 
 | 1317 |  | 
 | 1318 | out: | 
 | 1319 | 	spin_unlock(&br->multicast_lock); | 
 | 1320 | } | 
 | 1321 |  | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1322 | static void br_ip4_multicast_leave_group(struct net_bridge *br, | 
 | 1323 | 					 struct net_bridge_port *port, | 
 | 1324 | 					 __be32 group) | 
 | 1325 | { | 
 | 1326 | 	struct br_ip br_group; | 
 | 1327 |  | 
 | 1328 | 	if (ipv4_is_local_multicast(group)) | 
 | 1329 | 		return; | 
 | 1330 |  | 
 | 1331 | 	br_group.u.ip4 = group; | 
 | 1332 | 	br_group.proto = htons(ETH_P_IP); | 
 | 1333 |  | 
 | 1334 | 	br_multicast_leave_group(br, port, &br_group); | 
 | 1335 | } | 
 | 1336 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 1337 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1338 | static void br_ip6_multicast_leave_group(struct net_bridge *br, | 
 | 1339 | 					 struct net_bridge_port *port, | 
 | 1340 | 					 const struct in6_addr *group) | 
 | 1341 | { | 
 | 1342 | 	struct br_ip br_group; | 
 | 1343 |  | 
| Linus Lüssing | e4de9f9 | 2011-02-15 13:19:21 +0000 | [diff] [blame] | 1344 | 	if (!ipv6_is_transient_multicast(group)) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1345 | 		return; | 
 | 1346 |  | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 1347 | 	br_group.u.ip6 = *group; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1348 | 	br_group.proto = htons(ETH_P_IPV6); | 
 | 1349 |  | 
 | 1350 | 	br_multicast_leave_group(br, port, &br_group); | 
 | 1351 | } | 
 | 1352 | #endif | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1353 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1354 | static int br_multicast_ipv4_rcv(struct net_bridge *br, | 
 | 1355 | 				 struct net_bridge_port *port, | 
 | 1356 | 				 struct sk_buff *skb) | 
 | 1357 | { | 
 | 1358 | 	struct sk_buff *skb2 = skb; | 
| Eric Dumazet | b71d1d4 | 2011-04-22 04:53:02 +0000 | [diff] [blame] | 1359 | 	const struct iphdr *iph; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1360 | 	struct igmphdr *ih; | 
 | 1361 | 	unsigned len; | 
 | 1362 | 	unsigned offset; | 
 | 1363 | 	int err; | 
 | 1364 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1365 | 	/* We treat OOM as packet loss for now. */ | 
 | 1366 | 	if (!pskb_may_pull(skb, sizeof(*iph))) | 
 | 1367 | 		return -EINVAL; | 
 | 1368 |  | 
 | 1369 | 	iph = ip_hdr(skb); | 
 | 1370 |  | 
 | 1371 | 	if (iph->ihl < 5 || iph->version != 4) | 
 | 1372 | 		return -EINVAL; | 
 | 1373 |  | 
 | 1374 | 	if (!pskb_may_pull(skb, ip_hdrlen(skb))) | 
 | 1375 | 		return -EINVAL; | 
 | 1376 |  | 
 | 1377 | 	iph = ip_hdr(skb); | 
 | 1378 |  | 
 | 1379 | 	if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) | 
 | 1380 | 		return -EINVAL; | 
 | 1381 |  | 
| Herbert Xu | bd4265f | 2011-06-23 02:39:12 +0000 | [diff] [blame] | 1382 | 	if (iph->protocol != IPPROTO_IGMP) { | 
 | 1383 | 		if ((iph->daddr & IGMP_LOCAL_GROUP_MASK) != IGMP_LOCAL_GROUP) | 
 | 1384 | 			BR_INPUT_SKB_CB(skb)->mrouters_only = 1; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1385 | 		return 0; | 
| Herbert Xu | bd4265f | 2011-06-23 02:39:12 +0000 | [diff] [blame] | 1386 | 	} | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1387 |  | 
 | 1388 | 	len = ntohs(iph->tot_len); | 
 | 1389 | 	if (skb->len < len || len < ip_hdrlen(skb)) | 
 | 1390 | 		return -EINVAL; | 
 | 1391 |  | 
 | 1392 | 	if (skb->len > len) { | 
 | 1393 | 		skb2 = skb_clone(skb, GFP_ATOMIC); | 
 | 1394 | 		if (!skb2) | 
 | 1395 | 			return -ENOMEM; | 
 | 1396 |  | 
 | 1397 | 		err = pskb_trim_rcsum(skb2, len); | 
 | 1398 | 		if (err) | 
| YOSHIFUJI Hideaki / 吉藤英明 | 8440853 | 2010-03-15 19:26:56 +0000 | [diff] [blame] | 1399 | 			goto err_out; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1400 | 	} | 
 | 1401 |  | 
 | 1402 | 	len -= ip_hdrlen(skb2); | 
 | 1403 | 	offset = skb_network_offset(skb2) + ip_hdrlen(skb2); | 
 | 1404 | 	__skb_pull(skb2, offset); | 
 | 1405 | 	skb_reset_transport_header(skb2); | 
 | 1406 |  | 
 | 1407 | 	err = -EINVAL; | 
 | 1408 | 	if (!pskb_may_pull(skb2, sizeof(*ih))) | 
 | 1409 | 		goto out; | 
 | 1410 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1411 | 	switch (skb2->ip_summed) { | 
 | 1412 | 	case CHECKSUM_COMPLETE: | 
 | 1413 | 		if (!csum_fold(skb2->csum)) | 
 | 1414 | 			break; | 
 | 1415 | 		/* fall through */ | 
 | 1416 | 	case CHECKSUM_NONE: | 
 | 1417 | 		skb2->csum = 0; | 
 | 1418 | 		if (skb_checksum_complete(skb2)) | 
| YOSHIFUJI Hideaki / 吉藤英明 | 8440853 | 2010-03-15 19:26:56 +0000 | [diff] [blame] | 1419 | 			goto out; | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1420 | 	} | 
 | 1421 |  | 
 | 1422 | 	err = 0; | 
 | 1423 |  | 
 | 1424 | 	BR_INPUT_SKB_CB(skb)->igmp = 1; | 
 | 1425 | 	ih = igmp_hdr(skb2); | 
 | 1426 |  | 
 | 1427 | 	switch (ih->type) { | 
 | 1428 | 	case IGMP_HOST_MEMBERSHIP_REPORT: | 
 | 1429 | 	case IGMPV2_HOST_MEMBERSHIP_REPORT: | 
| Fernando Luis Vázquez Cao | 62b2bcb | 2011-06-13 15:04:43 +0000 | [diff] [blame] | 1430 | 		BR_INPUT_SKB_CB(skb)->mrouters_only = 1; | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1431 | 		err = br_ip4_multicast_add_group(br, port, ih->group); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1432 | 		break; | 
 | 1433 | 	case IGMPV3_HOST_MEMBERSHIP_REPORT: | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1434 | 		err = br_ip4_multicast_igmp3_report(br, port, skb2); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1435 | 		break; | 
 | 1436 | 	case IGMP_HOST_MEMBERSHIP_QUERY: | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1437 | 		err = br_ip4_multicast_query(br, port, skb2); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1438 | 		break; | 
 | 1439 | 	case IGMP_HOST_LEAVE_MESSAGE: | 
| YOSHIFUJI Hideaki | 8ef2a9a | 2010-04-18 12:42:07 +0900 | [diff] [blame] | 1440 | 		br_ip4_multicast_leave_group(br, port, ih->group); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1441 | 		break; | 
 | 1442 | 	} | 
 | 1443 |  | 
 | 1444 | out: | 
 | 1445 | 	__skb_push(skb2, offset); | 
| YOSHIFUJI Hideaki / 吉藤英明 | 8440853 | 2010-03-15 19:26:56 +0000 | [diff] [blame] | 1446 | err_out: | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1447 | 	if (skb2 != skb) | 
 | 1448 | 		kfree_skb(skb2); | 
 | 1449 | 	return err; | 
 | 1450 | } | 
 | 1451 |  | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 1452 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1453 | static int br_multicast_ipv6_rcv(struct net_bridge *br, | 
 | 1454 | 				 struct net_bridge_port *port, | 
 | 1455 | 				 struct sk_buff *skb) | 
 | 1456 | { | 
| Tomas Winkler | 9d89081 | 2011-01-03 11:26:08 -0800 | [diff] [blame] | 1457 | 	struct sk_buff *skb2; | 
| Eric Dumazet | b71d1d4 | 2011-04-22 04:53:02 +0000 | [diff] [blame] | 1458 | 	const struct ipv6hdr *ip6h; | 
| Eric Dumazet | 22df133 | 2011-08-23 19:57:05 +0000 | [diff] [blame] | 1459 | 	u8 icmp6_type; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1460 | 	u8 nexthdr; | 
| Jesse Gross | 75f2811 | 2011-11-30 17:05:51 -0800 | [diff] [blame] | 1461 | 	__be16 frag_off; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1462 | 	unsigned len; | 
| Kulikov Vasiliy | bb7a0bd | 2010-07-15 08:47:33 +0000 | [diff] [blame] | 1463 | 	int offset; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1464 | 	int err; | 
 | 1465 |  | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1466 | 	if (!pskb_may_pull(skb, sizeof(*ip6h))) | 
 | 1467 | 		return -EINVAL; | 
 | 1468 |  | 
 | 1469 | 	ip6h = ipv6_hdr(skb); | 
 | 1470 |  | 
 | 1471 | 	/* | 
 | 1472 | 	 * We're interested in MLD messages only. | 
 | 1473 | 	 *  - Version is 6 | 
 | 1474 | 	 *  - MLD has always Router Alert hop-by-hop option | 
 | 1475 | 	 *  - But we do not support jumbrograms. | 
 | 1476 | 	 */ | 
 | 1477 | 	if (ip6h->version != 6 || | 
 | 1478 | 	    ip6h->nexthdr != IPPROTO_HOPOPTS || | 
 | 1479 | 	    ip6h->payload_len == 0) | 
 | 1480 | 		return 0; | 
 | 1481 |  | 
| Linus Lüssing | ff9a57a | 2011-03-26 20:27:24 +0000 | [diff] [blame] | 1482 | 	len = ntohs(ip6h->payload_len) + sizeof(*ip6h); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1483 | 	if (skb->len < len) | 
 | 1484 | 		return -EINVAL; | 
 | 1485 |  | 
 | 1486 | 	nexthdr = ip6h->nexthdr; | 
| Jesse Gross | 75f2811 | 2011-11-30 17:05:51 -0800 | [diff] [blame] | 1487 | 	offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, &frag_off); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1488 |  | 
 | 1489 | 	if (offset < 0 || nexthdr != IPPROTO_ICMPV6) | 
 | 1490 | 		return 0; | 
 | 1491 |  | 
 | 1492 | 	/* Okay, we found ICMPv6 header */ | 
 | 1493 | 	skb2 = skb_clone(skb, GFP_ATOMIC); | 
 | 1494 | 	if (!skb2) | 
 | 1495 | 		return -ENOMEM; | 
 | 1496 |  | 
| Tomas Winkler | 9d89081 | 2011-01-03 11:26:08 -0800 | [diff] [blame] | 1497 | 	err = -EINVAL; | 
 | 1498 | 	if (!pskb_may_pull(skb2, offset + sizeof(struct icmp6hdr))) | 
 | 1499 | 		goto out; | 
 | 1500 |  | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1501 | 	len -= offset - skb_network_offset(skb2); | 
 | 1502 |  | 
 | 1503 | 	__skb_pull(skb2, offset); | 
 | 1504 | 	skb_reset_transport_header(skb2); | 
| stephen hemminger | fa2da8c | 2011-11-15 08:09:14 +0000 | [diff] [blame] | 1505 | 	skb_postpull_rcsum(skb2, skb_network_header(skb2), | 
 | 1506 | 			   skb_network_header_len(skb2)); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1507 |  | 
| Eric Dumazet | 22df133 | 2011-08-23 19:57:05 +0000 | [diff] [blame] | 1508 | 	icmp6_type = icmp6_hdr(skb2)->icmp6_type; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1509 |  | 
| Eric Dumazet | 22df133 | 2011-08-23 19:57:05 +0000 | [diff] [blame] | 1510 | 	switch (icmp6_type) { | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1511 | 	case ICMPV6_MGM_QUERY: | 
 | 1512 | 	case ICMPV6_MGM_REPORT: | 
 | 1513 | 	case ICMPV6_MGM_REDUCTION: | 
 | 1514 | 	case ICMPV6_MLD2_REPORT: | 
 | 1515 | 		break; | 
 | 1516 | 	default: | 
 | 1517 | 		err = 0; | 
 | 1518 | 		goto out; | 
 | 1519 | 	} | 
 | 1520 |  | 
 | 1521 | 	/* Okay, we found MLD message. Check further. */ | 
 | 1522 | 	if (skb2->len > len) { | 
 | 1523 | 		err = pskb_trim_rcsum(skb2, len); | 
 | 1524 | 		if (err) | 
 | 1525 | 			goto out; | 
| Yan, Zheng | 4b275d7 | 2011-08-23 22:54:33 +0000 | [diff] [blame] | 1526 | 		err = -EINVAL; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1527 | 	} | 
 | 1528 |  | 
| Yan, Zheng | 4b275d7 | 2011-08-23 22:54:33 +0000 | [diff] [blame] | 1529 | 	ip6h = ipv6_hdr(skb2); | 
 | 1530 |  | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1531 | 	switch (skb2->ip_summed) { | 
 | 1532 | 	case CHECKSUM_COMPLETE: | 
| Yan, Zheng | 4b275d7 | 2011-08-23 22:54:33 +0000 | [diff] [blame] | 1533 | 		if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb2->len, | 
 | 1534 | 					IPPROTO_ICMPV6, skb2->csum)) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1535 | 			break; | 
 | 1536 | 		/*FALLTHROUGH*/ | 
 | 1537 | 	case CHECKSUM_NONE: | 
| Yan, Zheng | 4b275d7 | 2011-08-23 22:54:33 +0000 | [diff] [blame] | 1538 | 		skb2->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr, | 
 | 1539 | 							&ip6h->daddr, | 
 | 1540 | 							skb2->len, | 
 | 1541 | 							IPPROTO_ICMPV6, 0)); | 
 | 1542 | 		if (__skb_checksum_complete(skb2)) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1543 | 			goto out; | 
 | 1544 | 	} | 
 | 1545 |  | 
 | 1546 | 	err = 0; | 
 | 1547 |  | 
 | 1548 | 	BR_INPUT_SKB_CB(skb)->igmp = 1; | 
 | 1549 |  | 
| Eric Dumazet | 22df133 | 2011-08-23 19:57:05 +0000 | [diff] [blame] | 1550 | 	switch (icmp6_type) { | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1551 | 	case ICMPV6_MGM_REPORT: | 
 | 1552 | 	    { | 
| Tomas Winkler | 9d89081 | 2011-01-03 11:26:08 -0800 | [diff] [blame] | 1553 | 		struct mld_msg *mld; | 
 | 1554 | 		if (!pskb_may_pull(skb2, sizeof(*mld))) { | 
 | 1555 | 			err = -EINVAL; | 
 | 1556 | 			goto out; | 
 | 1557 | 		} | 
 | 1558 | 		mld = (struct mld_msg *)skb_transport_header(skb2); | 
| Fernando Luis Vázquez Cao | fc2af6c | 2011-06-13 15:06:58 +0000 | [diff] [blame] | 1559 | 		BR_INPUT_SKB_CB(skb)->mrouters_only = 1; | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1560 | 		err = br_ip6_multicast_add_group(br, port, &mld->mld_mca); | 
 | 1561 | 		break; | 
 | 1562 | 	    } | 
 | 1563 | 	case ICMPV6_MLD2_REPORT: | 
 | 1564 | 		err = br_ip6_multicast_mld2_report(br, port, skb2); | 
 | 1565 | 		break; | 
 | 1566 | 	case ICMPV6_MGM_QUERY: | 
 | 1567 | 		err = br_ip6_multicast_query(br, port, skb2); | 
 | 1568 | 		break; | 
 | 1569 | 	case ICMPV6_MGM_REDUCTION: | 
 | 1570 | 	    { | 
| Tomas Winkler | 9d89081 | 2011-01-03 11:26:08 -0800 | [diff] [blame] | 1571 | 		struct mld_msg *mld; | 
 | 1572 | 		if (!pskb_may_pull(skb2, sizeof(*mld))) { | 
 | 1573 | 			err = -EINVAL; | 
 | 1574 | 			goto out; | 
 | 1575 | 		} | 
 | 1576 | 		mld = (struct mld_msg *)skb_transport_header(skb2); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1577 | 		br_ip6_multicast_leave_group(br, port, &mld->mld_mca); | 
 | 1578 | 	    } | 
 | 1579 | 	} | 
 | 1580 |  | 
 | 1581 | out: | 
| Tomas Winkler | 9d89081 | 2011-01-03 11:26:08 -0800 | [diff] [blame] | 1582 | 	kfree_skb(skb2); | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1583 | 	return err; | 
 | 1584 | } | 
 | 1585 | #endif | 
 | 1586 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1587 | int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, | 
 | 1588 | 		     struct sk_buff *skb) | 
 | 1589 | { | 
| YOSHIFUJI Hideaki / 吉藤英明 | 1fafc7a | 2010-04-25 08:06:40 +0000 | [diff] [blame] | 1590 | 	BR_INPUT_SKB_CB(skb)->igmp = 0; | 
 | 1591 | 	BR_INPUT_SKB_CB(skb)->mrouters_only = 0; | 
 | 1592 |  | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1593 | 	if (br->multicast_disabled) | 
 | 1594 | 		return 0; | 
 | 1595 |  | 
 | 1596 | 	switch (skb->protocol) { | 
 | 1597 | 	case htons(ETH_P_IP): | 
 | 1598 | 		return br_multicast_ipv4_rcv(br, port, skb); | 
| Eric Dumazet | dfd56b8 | 2011-12-10 09:48:31 +0000 | [diff] [blame] | 1599 | #if IS_ENABLED(CONFIG_IPV6) | 
| YOSHIFUJI Hideaki | 08b202b | 2010-04-23 01:54:22 +0900 | [diff] [blame] | 1600 | 	case htons(ETH_P_IPV6): | 
 | 1601 | 		return br_multicast_ipv6_rcv(br, port, skb); | 
 | 1602 | #endif | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1603 | 	} | 
 | 1604 |  | 
 | 1605 | 	return 0; | 
 | 1606 | } | 
 | 1607 |  | 
 | 1608 | static void br_multicast_query_expired(unsigned long data) | 
 | 1609 | { | 
 | 1610 | 	struct net_bridge *br = (void *)data; | 
 | 1611 |  | 
 | 1612 | 	spin_lock(&br->multicast_lock); | 
 | 1613 | 	if (br->multicast_startup_queries_sent < | 
 | 1614 | 	    br->multicast_startup_query_count) | 
 | 1615 | 		br->multicast_startup_queries_sent++; | 
 | 1616 |  | 
 | 1617 | 	br_multicast_send_query(br, NULL, br->multicast_startup_queries_sent); | 
 | 1618 |  | 
 | 1619 | 	spin_unlock(&br->multicast_lock); | 
 | 1620 | } | 
 | 1621 |  | 
 | 1622 | void br_multicast_init(struct net_bridge *br) | 
 | 1623 | { | 
 | 1624 | 	br->hash_elasticity = 4; | 
 | 1625 | 	br->hash_max = 512; | 
 | 1626 |  | 
 | 1627 | 	br->multicast_router = 1; | 
 | 1628 | 	br->multicast_last_member_count = 2; | 
 | 1629 | 	br->multicast_startup_query_count = 2; | 
 | 1630 |  | 
 | 1631 | 	br->multicast_last_member_interval = HZ; | 
 | 1632 | 	br->multicast_query_response_interval = 10 * HZ; | 
 | 1633 | 	br->multicast_startup_query_interval = 125 * HZ / 4; | 
 | 1634 | 	br->multicast_query_interval = 125 * HZ; | 
 | 1635 | 	br->multicast_querier_interval = 255 * HZ; | 
 | 1636 | 	br->multicast_membership_interval = 260 * HZ; | 
 | 1637 |  | 
 | 1638 | 	spin_lock_init(&br->multicast_lock); | 
 | 1639 | 	setup_timer(&br->multicast_router_timer, | 
 | 1640 | 		    br_multicast_local_router_expired, 0); | 
 | 1641 | 	setup_timer(&br->multicast_querier_timer, | 
 | 1642 | 		    br_multicast_local_router_expired, 0); | 
 | 1643 | 	setup_timer(&br->multicast_query_timer, br_multicast_query_expired, | 
 | 1644 | 		    (unsigned long)br); | 
 | 1645 | } | 
 | 1646 |  | 
 | 1647 | void br_multicast_open(struct net_bridge *br) | 
 | 1648 | { | 
 | 1649 | 	br->multicast_startup_queries_sent = 0; | 
 | 1650 |  | 
 | 1651 | 	if (br->multicast_disabled) | 
 | 1652 | 		return; | 
 | 1653 |  | 
 | 1654 | 	mod_timer(&br->multicast_query_timer, jiffies); | 
 | 1655 | } | 
 | 1656 |  | 
 | 1657 | void br_multicast_stop(struct net_bridge *br) | 
 | 1658 | { | 
 | 1659 | 	struct net_bridge_mdb_htable *mdb; | 
 | 1660 | 	struct net_bridge_mdb_entry *mp; | 
 | 1661 | 	struct hlist_node *p, *n; | 
 | 1662 | 	u32 ver; | 
 | 1663 | 	int i; | 
 | 1664 |  | 
 | 1665 | 	del_timer_sync(&br->multicast_router_timer); | 
 | 1666 | 	del_timer_sync(&br->multicast_querier_timer); | 
 | 1667 | 	del_timer_sync(&br->multicast_query_timer); | 
 | 1668 |  | 
 | 1669 | 	spin_lock_bh(&br->multicast_lock); | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1670 | 	mdb = mlock_dereference(br->mdb, br); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1671 | 	if (!mdb) | 
 | 1672 | 		goto out; | 
 | 1673 |  | 
 | 1674 | 	br->mdb = NULL; | 
 | 1675 |  | 
 | 1676 | 	ver = mdb->ver; | 
 | 1677 | 	for (i = 0; i < mdb->max; i++) { | 
 | 1678 | 		hlist_for_each_entry_safe(mp, p, n, &mdb->mhash[i], | 
 | 1679 | 					  hlist[ver]) { | 
 | 1680 | 			del_timer(&mp->timer); | 
 | 1681 | 			del_timer(&mp->query_timer); | 
 | 1682 | 			call_rcu_bh(&mp->rcu, br_multicast_free_group); | 
 | 1683 | 		} | 
 | 1684 | 	} | 
 | 1685 |  | 
 | 1686 | 	if (mdb->old) { | 
 | 1687 | 		spin_unlock_bh(&br->multicast_lock); | 
| Herbert Xu | 10cc2b5 | 2010-03-05 21:03:35 +0000 | [diff] [blame] | 1688 | 		rcu_barrier_bh(); | 
| Herbert Xu | eb1d164 | 2010-02-27 19:41:45 +0000 | [diff] [blame] | 1689 | 		spin_lock_bh(&br->multicast_lock); | 
 | 1690 | 		WARN_ON(mdb->old); | 
 | 1691 | 	} | 
 | 1692 |  | 
 | 1693 | 	mdb->old = mdb; | 
 | 1694 | 	call_rcu_bh(&mdb->rcu, br_mdb_free); | 
 | 1695 |  | 
 | 1696 | out: | 
 | 1697 | 	spin_unlock_bh(&br->multicast_lock); | 
 | 1698 | } | 
| Herbert Xu | 0909e11 | 2010-02-27 19:41:49 +0000 | [diff] [blame] | 1699 |  | 
 | 1700 | int br_multicast_set_router(struct net_bridge *br, unsigned long val) | 
 | 1701 | { | 
 | 1702 | 	int err = -ENOENT; | 
 | 1703 |  | 
 | 1704 | 	spin_lock_bh(&br->multicast_lock); | 
 | 1705 | 	if (!netif_running(br->dev)) | 
 | 1706 | 		goto unlock; | 
 | 1707 |  | 
 | 1708 | 	switch (val) { | 
 | 1709 | 	case 0: | 
 | 1710 | 	case 2: | 
 | 1711 | 		del_timer(&br->multicast_router_timer); | 
 | 1712 | 		/* fall through */ | 
 | 1713 | 	case 1: | 
 | 1714 | 		br->multicast_router = val; | 
 | 1715 | 		err = 0; | 
 | 1716 | 		break; | 
 | 1717 |  | 
 | 1718 | 	default: | 
 | 1719 | 		err = -EINVAL; | 
 | 1720 | 		break; | 
 | 1721 | 	} | 
 | 1722 |  | 
 | 1723 | unlock: | 
 | 1724 | 	spin_unlock_bh(&br->multicast_lock); | 
 | 1725 |  | 
 | 1726 | 	return err; | 
 | 1727 | } | 
 | 1728 |  | 
 | 1729 | int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val) | 
 | 1730 | { | 
 | 1731 | 	struct net_bridge *br = p->br; | 
 | 1732 | 	int err = -ENOENT; | 
 | 1733 |  | 
 | 1734 | 	spin_lock(&br->multicast_lock); | 
 | 1735 | 	if (!netif_running(br->dev) || p->state == BR_STATE_DISABLED) | 
 | 1736 | 		goto unlock; | 
 | 1737 |  | 
 | 1738 | 	switch (val) { | 
 | 1739 | 	case 0: | 
 | 1740 | 	case 1: | 
 | 1741 | 	case 2: | 
 | 1742 | 		p->multicast_router = val; | 
 | 1743 | 		err = 0; | 
 | 1744 |  | 
 | 1745 | 		if (val < 2 && !hlist_unhashed(&p->rlist)) | 
 | 1746 | 			hlist_del_init_rcu(&p->rlist); | 
 | 1747 |  | 
 | 1748 | 		if (val == 1) | 
 | 1749 | 			break; | 
 | 1750 |  | 
 | 1751 | 		del_timer(&p->multicast_router_timer); | 
 | 1752 |  | 
 | 1753 | 		if (val == 0) | 
 | 1754 | 			break; | 
 | 1755 |  | 
 | 1756 | 		br_multicast_add_router(br, p); | 
 | 1757 | 		break; | 
 | 1758 |  | 
 | 1759 | 	default: | 
 | 1760 | 		err = -EINVAL; | 
 | 1761 | 		break; | 
 | 1762 | 	} | 
 | 1763 |  | 
 | 1764 | unlock: | 
 | 1765 | 	spin_unlock(&br->multicast_lock); | 
 | 1766 |  | 
 | 1767 | 	return err; | 
 | 1768 | } | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 1769 |  | 
 | 1770 | int br_multicast_toggle(struct net_bridge *br, unsigned long val) | 
 | 1771 | { | 
 | 1772 | 	struct net_bridge_port *port; | 
| Herbert Xu | 3a7fda0 | 2010-07-29 00:45:30 +0000 | [diff] [blame] | 1773 | 	int err = 0; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1774 | 	struct net_bridge_mdb_htable *mdb; | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 1775 |  | 
| Andrey Vagin | ef5e0d8 | 2011-11-10 05:48:03 +0000 | [diff] [blame] | 1776 | 	spin_lock_bh(&br->multicast_lock); | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 1777 | 	if (br->multicast_disabled == !val) | 
 | 1778 | 		goto unlock; | 
 | 1779 |  | 
 | 1780 | 	br->multicast_disabled = !val; | 
 | 1781 | 	if (br->multicast_disabled) | 
 | 1782 | 		goto unlock; | 
 | 1783 |  | 
| Herbert Xu | 3a7fda0 | 2010-07-29 00:45:30 +0000 | [diff] [blame] | 1784 | 	if (!netif_running(br->dev)) | 
 | 1785 | 		goto unlock; | 
 | 1786 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1787 | 	mdb = mlock_dereference(br->mdb, br); | 
 | 1788 | 	if (mdb) { | 
 | 1789 | 		if (mdb->old) { | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 1790 | 			err = -EEXIST; | 
 | 1791 | rollback: | 
 | 1792 | 			br->multicast_disabled = !!val; | 
 | 1793 | 			goto unlock; | 
 | 1794 | 		} | 
 | 1795 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1796 | 		err = br_mdb_rehash(&br->mdb, mdb->max, | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 1797 | 				    br->hash_elasticity); | 
 | 1798 | 		if (err) | 
 | 1799 | 			goto rollback; | 
 | 1800 | 	} | 
 | 1801 |  | 
 | 1802 | 	br_multicast_open(br); | 
 | 1803 | 	list_for_each_entry(port, &br->port_list, list) { | 
 | 1804 | 		if (port->state == BR_STATE_DISABLED || | 
 | 1805 | 		    port->state == BR_STATE_BLOCKING) | 
 | 1806 | 			continue; | 
 | 1807 |  | 
 | 1808 | 		__br_multicast_enable_port(port); | 
 | 1809 | 	} | 
 | 1810 |  | 
 | 1811 | unlock: | 
| Andrey Vagin | ef5e0d8 | 2011-11-10 05:48:03 +0000 | [diff] [blame] | 1812 | 	spin_unlock_bh(&br->multicast_lock); | 
| Herbert Xu | 561f110 | 2010-02-27 19:41:50 +0000 | [diff] [blame] | 1813 |  | 
 | 1814 | 	return err; | 
 | 1815 | } | 
| Herbert Xu | b195167 | 2010-02-27 19:41:51 +0000 | [diff] [blame] | 1816 |  | 
 | 1817 | int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val) | 
 | 1818 | { | 
 | 1819 | 	int err = -ENOENT; | 
 | 1820 | 	u32 old; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1821 | 	struct net_bridge_mdb_htable *mdb; | 
| Herbert Xu | b195167 | 2010-02-27 19:41:51 +0000 | [diff] [blame] | 1822 |  | 
 | 1823 | 	spin_lock(&br->multicast_lock); | 
 | 1824 | 	if (!netif_running(br->dev)) | 
 | 1825 | 		goto unlock; | 
 | 1826 |  | 
 | 1827 | 	err = -EINVAL; | 
 | 1828 | 	if (!is_power_of_2(val)) | 
 | 1829 | 		goto unlock; | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1830 |  | 
 | 1831 | 	mdb = mlock_dereference(br->mdb, br); | 
 | 1832 | 	if (mdb && val < mdb->size) | 
| Herbert Xu | b195167 | 2010-02-27 19:41:51 +0000 | [diff] [blame] | 1833 | 		goto unlock; | 
 | 1834 |  | 
 | 1835 | 	err = 0; | 
 | 1836 |  | 
 | 1837 | 	old = br->hash_max; | 
 | 1838 | 	br->hash_max = val; | 
 | 1839 |  | 
| Eric Dumazet | e805168 | 2010-11-15 06:38:10 +0000 | [diff] [blame] | 1840 | 	if (mdb) { | 
 | 1841 | 		if (mdb->old) { | 
| Herbert Xu | b195167 | 2010-02-27 19:41:51 +0000 | [diff] [blame] | 1842 | 			err = -EEXIST; | 
 | 1843 | rollback: | 
 | 1844 | 			br->hash_max = old; | 
 | 1845 | 			goto unlock; | 
 | 1846 | 		} | 
 | 1847 |  | 
 | 1848 | 		err = br_mdb_rehash(&br->mdb, br->hash_max, | 
 | 1849 | 				    br->hash_elasticity); | 
 | 1850 | 		if (err) | 
 | 1851 | 			goto rollback; | 
 | 1852 | 	} | 
 | 1853 |  | 
 | 1854 | unlock: | 
 | 1855 | 	spin_unlock(&br->multicast_lock); | 
 | 1856 |  | 
 | 1857 | 	return err; | 
 | 1858 | } |