OpenWrt – Blame information for rev 3
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From: Felix Fietkau <nbd@nbd.name> |
2 | Date: Thu, 19 Jan 2017 03:45:10 +0100 |
||
3 | Subject: [PATCH] bridge: multicast to unicast |
||
4 | MIME-Version: 1.0 |
||
5 | Content-Type: text/plain; charset=UTF-8 |
||
6 | Content-Transfer-Encoding: 8bit |
||
7 | |||
8 | Implements an optional, per bridge port flag and feature to deliver |
||
9 | multicast packets to any host on the according port via unicast |
||
10 | individually. This is done by copying the packet per host and |
||
11 | changing the multicast destination MAC to a unicast one accordingly. |
||
12 | |||
13 | multicast-to-unicast works on top of the multicast snooping feature of |
||
14 | the bridge. Which means unicast copies are only delivered to hosts which |
||
15 | are interested in it and signalized this via IGMP/MLD reports |
||
16 | previously. |
||
17 | |||
18 | This feature is intended for interface types which have a more reliable |
||
19 | and/or efficient way to deliver unicast packets than broadcast ones |
||
20 | (e.g. wifi). |
||
21 | |||
22 | However, it should only be enabled on interfaces where no IGMPv2/MLDv1 |
||
23 | report suppression takes place. This feature is disabled by default. |
||
24 | |||
25 | The initial patch and idea is from Felix Fietkau. |
||
26 | |||
27 | Signed-off-by: Felix Fietkau <nbd@nbd.name> |
||
28 | [linus.luessing@c0d3.blue: various bug + style fixes, commit message] |
||
29 | Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> |
||
30 | --- |
||
31 | |||
32 | --- a/include/linux/if_bridge.h |
||
33 | +++ b/include/linux/if_bridge.h |
||
34 | @@ -46,6 +46,7 @@ struct br_ip_list { |
||
35 | #define BR_LEARNING_SYNC BIT(9) |
||
36 | #define BR_PROXYARP_WIFI BIT(10) |
||
37 | #define BR_MCAST_FLOOD BIT(11) |
||
38 | +#define BR_MULTICAST_TO_UNICAST BIT(12) |
||
39 | |||
40 | #define BR_DEFAULT_AGEING_TIME (300 * HZ) |
||
41 | |||
42 | --- a/include/uapi/linux/if_link.h |
||
43 | +++ b/include/uapi/linux/if_link.h |
||
44 | @@ -319,6 +319,7 @@ enum { |
||
45 | IFLA_BRPORT_MULTICAST_ROUTER, |
||
46 | IFLA_BRPORT_PAD, |
||
47 | IFLA_BRPORT_MCAST_FLOOD, |
||
48 | + IFLA_BRPORT_MCAST_TO_UCAST, |
||
49 | __IFLA_BRPORT_MAX |
||
50 | }; |
||
51 | #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) |
||
52 | --- a/net/bridge/br_forward.c |
||
53 | +++ b/net/bridge/br_forward.c |
||
3 | office | 54 | @@ -174,6 +174,29 @@ out: |
1 | office | 55 | return p; |
56 | } |
||
57 | |||
58 | +static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb, |
||
59 | + const unsigned char *addr, bool local_orig) |
||
60 | +{ |
||
61 | + struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; |
||
62 | + const unsigned char *src = eth_hdr(skb)->h_source; |
||
63 | + |
||
64 | + if (!should_deliver(p, skb)) |
||
65 | + return; |
||
66 | + |
||
67 | + /* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */ |
||
68 | + if (skb->dev == p->dev && ether_addr_equal(src, addr)) |
||
69 | + return; |
||
70 | + |
||
71 | + skb = skb_copy(skb, GFP_ATOMIC); |
||
72 | + if (!skb) { |
||
73 | + dev->stats.tx_dropped++; |
||
74 | + return; |
||
75 | + } |
||
76 | + |
||
77 | + memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN); |
||
78 | + __br_forward(p, skb, local_orig); |
||
79 | +} |
||
80 | + |
||
81 | /* called under rcu_read_lock */ |
||
82 | void br_flood(struct net_bridge *br, struct sk_buff *skb, |
||
83 | enum br_pkt_type pkt_type, bool local_rcv, bool local_orig) |
||
3 | office | 84 | @@ -242,10 +265,20 @@ void br_multicast_flood(struct net_bridg |
1 | office | 85 | rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : |
86 | NULL; |
||
87 | |||
88 | - port = (unsigned long)lport > (unsigned long)rport ? |
||
89 | - lport : rport; |
||
90 | + if ((unsigned long)lport > (unsigned long)rport) { |
||
91 | + port = lport; |
||
92 | + |
||
93 | + if (p->flags & MDB_PG_FLAGS_MCAST_TO_UCAST) { |
||
94 | + maybe_deliver_addr(lport, skb, p->eth_addr, |
||
95 | + local_orig); |
||
96 | + goto delivered; |
||
97 | + } |
||
98 | + } else { |
||
99 | + port = rport; |
||
100 | + } |
||
101 | |||
102 | prev = maybe_deliver(prev, port, skb, local_orig); |
||
103 | +delivered: |
||
104 | if (IS_ERR(prev)) |
||
105 | goto out; |
||
106 | if (prev == port) |
||
107 | --- a/net/bridge/br_mdb.c |
||
108 | +++ b/net/bridge/br_mdb.c |
||
109 | @@ -532,7 +532,7 @@ static int br_mdb_add_group(struct net_b |
||
110 | break; |
||
111 | } |
||
112 | |||
113 | - p = br_multicast_new_port_group(port, group, *pp, state); |
||
114 | + p = br_multicast_new_port_group(port, group, *pp, state, NULL); |
||
115 | if (unlikely(!p)) |
||
116 | return -ENOMEM; |
||
117 | rcu_assign_pointer(*pp, p); |
||
118 | --- a/net/bridge/br_multicast.c |
||
119 | +++ b/net/bridge/br_multicast.c |
||
120 | @@ -42,12 +42,14 @@ static void br_multicast_add_router(stru |
||
121 | static void br_ip4_multicast_leave_group(struct net_bridge *br, |
||
122 | struct net_bridge_port *port, |
||
123 | __be32 group, |
||
124 | - __u16 vid); |
||
125 | + __u16 vid, |
||
126 | + const unsigned char *src); |
||
127 | + |
||
128 | #if IS_ENABLED(CONFIG_IPV6) |
||
129 | static void br_ip6_multicast_leave_group(struct net_bridge *br, |
||
130 | struct net_bridge_port *port, |
||
131 | const struct in6_addr *group, |
||
132 | - __u16 vid); |
||
133 | + __u16 vid, const unsigned char *src); |
||
134 | #endif |
||
135 | unsigned int br_mdb_rehash_seq; |
||
136 | |||
137 | @@ -658,7 +660,8 @@ struct net_bridge_port_group *br_multica |
||
138 | struct net_bridge_port *port, |
||
139 | struct br_ip *group, |
||
140 | struct net_bridge_port_group __rcu *next, |
||
141 | - unsigned char flags) |
||
142 | + unsigned char flags, |
||
143 | + const unsigned char *src) |
||
144 | { |
||
145 | struct net_bridge_port_group *p; |
||
146 | |||
147 | @@ -673,12 +676,39 @@ struct net_bridge_port_group *br_multica |
||
148 | hlist_add_head(&p->mglist, &port->mglist); |
||
149 | setup_timer(&p->timer, br_multicast_port_group_expired, |
||
150 | (unsigned long)p); |
||
151 | + |
||
152 | + if ((port->flags & BR_MULTICAST_TO_UNICAST) && src) { |
||
153 | + memcpy(p->eth_addr, src, ETH_ALEN); |
||
154 | + p->flags |= MDB_PG_FLAGS_MCAST_TO_UCAST; |
||
155 | + } |
||
156 | + |
||
157 | return p; |
||
158 | } |
||
159 | |||
160 | +static bool br_port_group_equal(struct net_bridge_port_group *p, |
||
161 | + struct net_bridge_port *port, |
||
162 | + const unsigned char *src) |
||
163 | +{ |
||
164 | + if (p->port != port) |
||
165 | + return false; |
||
166 | + |
||
167 | + if (!(p->flags & MDB_PG_FLAGS_MCAST_TO_UCAST) != |
||
168 | + !(port->flags & BR_MULTICAST_TO_UNICAST)) |
||
169 | + return false; |
||
170 | + |
||
171 | + if (!(p->flags & MDB_PG_FLAGS_MCAST_TO_UCAST)) |
||
172 | + return true; |
||
173 | + |
||
174 | + if (!src) |
||
175 | + return false; |
||
176 | + |
||
177 | + return ether_addr_equal(src, p->eth_addr); |
||
178 | +} |
||
179 | + |
||
180 | static int br_multicast_add_group(struct net_bridge *br, |
||
181 | struct net_bridge_port *port, |
||
182 | - struct br_ip *group) |
||
183 | + struct br_ip *group, |
||
184 | + const unsigned char *src) |
||
185 | { |
||
186 | struct net_bridge_mdb_entry *mp; |
||
187 | struct net_bridge_port_group *p; |
||
188 | @@ -705,13 +735,13 @@ static int br_multicast_add_group(struct |
||
189 | for (pp = &mp->ports; |
||
190 | (p = mlock_dereference(*pp, br)) != NULL; |
||
191 | pp = &p->next) { |
||
192 | - if (p->port == port) |
||
193 | + if (br_port_group_equal(p, port, src)) |
||
194 | goto found; |
||
195 | if ((unsigned long)p->port < (unsigned long)port) |
||
196 | break; |
||
197 | } |
||
198 | |||
199 | - p = br_multicast_new_port_group(port, group, *pp, 0); |
||
200 | + p = br_multicast_new_port_group(port, group, *pp, 0, src); |
||
201 | if (unlikely(!p)) |
||
202 | goto err; |
||
203 | rcu_assign_pointer(*pp, p); |
||
204 | @@ -730,7 +760,8 @@ err: |
||
205 | static int br_ip4_multicast_add_group(struct net_bridge *br, |
||
206 | struct net_bridge_port *port, |
||
207 | __be32 group, |
||
208 | - __u16 vid) |
||
209 | + __u16 vid, |
||
210 | + const unsigned char *src) |
||
211 | { |
||
212 | struct br_ip br_group; |
||
213 | |||
214 | @@ -741,14 +772,15 @@ static int br_ip4_multicast_add_group(st |
||
215 | br_group.proto = htons(ETH_P_IP); |
||
216 | br_group.vid = vid; |
||
217 | |||
218 | - return br_multicast_add_group(br, port, &br_group); |
||
219 | + return br_multicast_add_group(br, port, &br_group, src); |
||
220 | } |
||
221 | |||
222 | #if IS_ENABLED(CONFIG_IPV6) |
||
223 | static int br_ip6_multicast_add_group(struct net_bridge *br, |
||
224 | struct net_bridge_port *port, |
||
225 | const struct in6_addr *group, |
||
226 | - __u16 vid) |
||
227 | + __u16 vid, |
||
228 | + const unsigned char *src) |
||
229 | { |
||
230 | struct br_ip br_group; |
||
231 | |||
232 | @@ -759,7 +791,7 @@ static int br_ip6_multicast_add_group(st |
||
233 | br_group.proto = htons(ETH_P_IPV6); |
||
234 | br_group.vid = vid; |
||
235 | |||
236 | - return br_multicast_add_group(br, port, &br_group); |
||
237 | + return br_multicast_add_group(br, port, &br_group, src); |
||
238 | } |
||
239 | #endif |
||
240 | |||
241 | @@ -1028,6 +1060,7 @@ static int br_ip4_multicast_igmp3_report |
||
242 | struct sk_buff *skb, |
||
243 | u16 vid) |
||
244 | { |
||
245 | + const unsigned char *src; |
||
246 | struct igmpv3_report *ih; |
||
247 | struct igmpv3_grec *grec; |
||
248 | int i; |
||
249 | @@ -1068,12 +1101,14 @@ static int br_ip4_multicast_igmp3_report |
||
250 | continue; |
||
251 | } |
||
252 | |||
253 | + src = eth_hdr(skb)->h_source; |
||
254 | if ((type == IGMPV3_CHANGE_TO_INCLUDE || |
||
255 | type == IGMPV3_MODE_IS_INCLUDE) && |
||
256 | ntohs(grec->grec_nsrcs) == 0) { |
||
257 | - br_ip4_multicast_leave_group(br, port, group, vid); |
||
258 | + br_ip4_multicast_leave_group(br, port, group, vid, src); |
||
259 | } else { |
||
260 | - err = br_ip4_multicast_add_group(br, port, group, vid); |
||
261 | + err = br_ip4_multicast_add_group(br, port, group, vid, |
||
262 | + src); |
||
263 | if (err) |
||
264 | break; |
||
265 | } |
||
266 | @@ -1088,6 +1123,7 @@ static int br_ip6_multicast_mld2_report( |
||
267 | struct sk_buff *skb, |
||
268 | u16 vid) |
||
269 | { |
||
270 | + const unsigned char *src = eth_hdr(skb)->h_source; |
||
271 | struct icmp6hdr *icmp6h; |
||
272 | struct mld2_grec *grec; |
||
273 | int i; |
||
274 | @@ -1139,10 +1175,11 @@ static int br_ip6_multicast_mld2_report( |
||
275 | grec->grec_type == MLD2_MODE_IS_INCLUDE) && |
||
276 | ntohs(*nsrcs) == 0) { |
||
277 | br_ip6_multicast_leave_group(br, port, &grec->grec_mca, |
||
278 | - vid); |
||
279 | + vid, src); |
||
280 | } else { |
||
281 | err = br_ip6_multicast_add_group(br, port, |
||
282 | - &grec->grec_mca, vid); |
||
283 | + &grec->grec_mca, vid, |
||
284 | + src); |
||
285 | if (err) |
||
286 | break; |
||
287 | } |
||
3 | office | 288 | @@ -1458,7 +1495,8 @@ br_multicast_leave_group(struct net_brid |
1 | office | 289 | struct net_bridge_port *port, |
290 | struct br_ip *group, |
||
291 | struct bridge_mcast_other_query *other_query, |
||
292 | - struct bridge_mcast_own_query *own_query) |
||
293 | + struct bridge_mcast_own_query *own_query, |
||
294 | + const unsigned char *src) |
||
295 | { |
||
296 | struct net_bridge_mdb_htable *mdb; |
||
297 | struct net_bridge_mdb_entry *mp; |
||
3 | office | 298 | @@ -1482,7 +1520,7 @@ br_multicast_leave_group(struct net_brid |
1 | office | 299 | for (pp = &mp->ports; |
300 | (p = mlock_dereference(*pp, br)) != NULL; |
||
301 | pp = &p->next) { |
||
302 | - if (p->port != port) |
||
303 | + if (!br_port_group_equal(p, port, src)) |
||
304 | continue; |
||
305 | |||
306 | rcu_assign_pointer(*pp, p->next); |
||
3 | office | 307 | @@ -1513,7 +1551,7 @@ br_multicast_leave_group(struct net_brid |
1 | office | 308 | for (p = mlock_dereference(mp->ports, br); |
309 | p != NULL; |
||
310 | p = mlock_dereference(p->next, br)) { |
||
311 | - if (p->port != port) |
||
312 | + if (!br_port_group_equal(p, port, src)) |
||
313 | continue; |
||
314 | |||
315 | if (!hlist_unhashed(&p->mglist) && |
||
3 | office | 316 | @@ -1564,7 +1602,8 @@ out: |
1 | office | 317 | static void br_ip4_multicast_leave_group(struct net_bridge *br, |
318 | struct net_bridge_port *port, |
||
319 | __be32 group, |
||
320 | - __u16 vid) |
||
321 | + __u16 vid, |
||
322 | + const unsigned char *src) |
||
323 | { |
||
324 | struct br_ip br_group; |
||
325 | struct bridge_mcast_own_query *own_query; |
||
3 | office | 326 | @@ -1579,14 +1618,15 @@ static void br_ip4_multicast_leave_group |
1 | office | 327 | br_group.vid = vid; |
328 | |||
329 | br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query, |
||
330 | - own_query); |
||
331 | + own_query, src); |
||
332 | } |
||
333 | |||
334 | #if IS_ENABLED(CONFIG_IPV6) |
||
335 | static void br_ip6_multicast_leave_group(struct net_bridge *br, |
||
336 | struct net_bridge_port *port, |
||
337 | const struct in6_addr *group, |
||
338 | - __u16 vid) |
||
339 | + __u16 vid, |
||
340 | + const unsigned char *src) |
||
341 | { |
||
342 | struct br_ip br_group; |
||
343 | struct bridge_mcast_own_query *own_query; |
||
3 | office | 344 | @@ -1601,7 +1641,7 @@ static void br_ip6_multicast_leave_group |
1 | office | 345 | br_group.vid = vid; |
346 | |||
347 | br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query, |
||
348 | - own_query); |
||
349 | + own_query, src); |
||
350 | } |
||
351 | #endif |
||
352 | |||
3 | office | 353 | @@ -1644,6 +1684,7 @@ static int br_multicast_ipv4_rcv(struct |
1 | office | 354 | u16 vid) |
355 | { |
||
356 | struct sk_buff *skb_trimmed = NULL; |
||
357 | + const unsigned char *src; |
||
358 | struct igmphdr *ih; |
||
359 | int err; |
||
360 | |||
3 | office | 361 | @@ -1659,13 +1700,14 @@ static int br_multicast_ipv4_rcv(struct |
1 | office | 362 | } |
363 | |||
364 | ih = igmp_hdr(skb); |
||
365 | + src = eth_hdr(skb)->h_source; |
||
366 | BR_INPUT_SKB_CB(skb)->igmp = ih->type; |
||
367 | |||
368 | switch (ih->type) { |
||
369 | case IGMP_HOST_MEMBERSHIP_REPORT: |
||
370 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
||
371 | BR_INPUT_SKB_CB(skb)->mrouters_only = 1; |
||
372 | - err = br_ip4_multicast_add_group(br, port, ih->group, vid); |
||
373 | + err = br_ip4_multicast_add_group(br, port, ih->group, vid, src); |
||
374 | break; |
||
375 | case IGMPV3_HOST_MEMBERSHIP_REPORT: |
||
376 | err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid); |
||
3 | office | 377 | @@ -1674,7 +1716,7 @@ static int br_multicast_ipv4_rcv(struct |
1 | office | 378 | err = br_ip4_multicast_query(br, port, skb_trimmed, vid); |
379 | break; |
||
380 | case IGMP_HOST_LEAVE_MESSAGE: |
||
381 | - br_ip4_multicast_leave_group(br, port, ih->group, vid); |
||
382 | + br_ip4_multicast_leave_group(br, port, ih->group, vid, src); |
||
383 | break; |
||
384 | } |
||
385 | |||
3 | office | 386 | @@ -1694,6 +1736,7 @@ static int br_multicast_ipv6_rcv(struct |
1 | office | 387 | u16 vid) |
388 | { |
||
389 | struct sk_buff *skb_trimmed = NULL; |
||
390 | + const unsigned char *src; |
||
391 | struct mld_msg *mld; |
||
392 | int err; |
||
393 | |||
3 | office | 394 | @@ -1713,8 +1756,10 @@ static int br_multicast_ipv6_rcv(struct |
1 | office | 395 | |
396 | switch (mld->mld_type) { |
||
397 | case ICMPV6_MGM_REPORT: |
||
398 | + src = eth_hdr(skb)->h_source; |
||
399 | BR_INPUT_SKB_CB(skb)->mrouters_only = 1; |
||
400 | - err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid); |
||
401 | + err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid, |
||
402 | + src); |
||
403 | break; |
||
404 | case ICMPV6_MLD2_REPORT: |
||
405 | err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid); |
||
3 | office | 406 | @@ -1723,7 +1768,8 @@ static int br_multicast_ipv6_rcv(struct |
1 | office | 407 | err = br_ip6_multicast_query(br, port, skb_trimmed, vid); |
408 | break; |
||
409 | case ICMPV6_MGM_REDUCTION: |
||
410 | - br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid); |
||
411 | + src = eth_hdr(skb)->h_source; |
||
412 | + br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src); |
||
413 | break; |
||
414 | } |
||
415 | |||
416 | --- a/net/bridge/br_netlink.c |
||
417 | +++ b/net/bridge/br_netlink.c |
||
418 | @@ -123,6 +123,7 @@ static inline size_t br_port_info_size(v |
||
419 | + nla_total_size(1) /* IFLA_BRPORT_GUARD */ |
||
420 | + nla_total_size(1) /* IFLA_BRPORT_PROTECT */ |
||
421 | + nla_total_size(1) /* IFLA_BRPORT_FAST_LEAVE */ |
||
422 | + + nla_total_size(1) /* IFLA_BRPORT_MCAST_TO_UCAST */ |
||
423 | + nla_total_size(1) /* IFLA_BRPORT_LEARNING */ |
||
424 | + nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */ |
||
425 | + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */ |
||
426 | @@ -173,6 +174,8 @@ static int br_port_fill_attrs(struct sk_ |
||
427 | !!(p->flags & BR_ROOT_BLOCK)) || |
||
428 | nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, |
||
429 | !!(p->flags & BR_MULTICAST_FAST_LEAVE)) || |
||
430 | + nla_put_u8(skb, IFLA_BRPORT_MCAST_TO_UCAST, |
||
431 | + !!(p->flags & BR_MULTICAST_TO_UNICAST)) || |
||
432 | nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) || |
||
433 | nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, |
||
434 | !!(p->flags & BR_FLOOD)) || |
||
435 | @@ -586,6 +589,7 @@ static const struct nla_policy br_port_p |
||
436 | [IFLA_BRPORT_PROXYARP] = { .type = NLA_U8 }, |
||
437 | [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 }, |
||
438 | [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 }, |
||
439 | + [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 }, |
||
440 | }; |
||
441 | |||
442 | /* Change the state of the port and notify spanning tree */ |
||
443 | @@ -636,6 +640,7 @@ static int br_setport(struct net_bridge_ |
||
444 | br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); |
||
445 | br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); |
||
446 | br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD); |
||
447 | + br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST); |
||
448 | br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); |
||
449 | br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); |
||
450 | |||
451 | --- a/net/bridge/br_private.h |
||
452 | +++ b/net/bridge/br_private.h |
||
453 | @@ -166,8 +166,9 @@ struct net_bridge_fdb_entry |
||
454 | struct rcu_head rcu; |
||
455 | }; |
||
456 | |||
457 | -#define MDB_PG_FLAGS_PERMANENT BIT(0) |
||
458 | -#define MDB_PG_FLAGS_OFFLOAD BIT(1) |
||
459 | +#define MDB_PG_FLAGS_PERMANENT BIT(0) |
||
460 | +#define MDB_PG_FLAGS_OFFLOAD BIT(1) |
||
461 | +#define MDB_PG_FLAGS_MCAST_TO_UCAST BIT(2) |
||
462 | |||
463 | struct net_bridge_port_group { |
||
464 | struct net_bridge_port *port; |
||
465 | @@ -177,6 +178,7 @@ struct net_bridge_port_group { |
||
466 | struct timer_list timer; |
||
467 | struct br_ip addr; |
||
468 | unsigned char flags; |
||
469 | + unsigned char eth_addr[ETH_ALEN]; |
||
470 | }; |
||
471 | |||
472 | struct net_bridge_mdb_entry |
||
473 | @@ -591,7 +593,7 @@ void br_multicast_free_pg(struct rcu_hea |
||
474 | struct net_bridge_port_group * |
||
475 | br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, |
||
476 | struct net_bridge_port_group __rcu *next, |
||
477 | - unsigned char flags); |
||
478 | + unsigned char flags, const unsigned char *src); |
||
479 | void br_mdb_init(void); |
||
480 | void br_mdb_uninit(void); |
||
481 | void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, |
||
482 | --- a/net/bridge/br_sysfs_if.c |
||
483 | +++ b/net/bridge/br_sysfs_if.c |
||
484 | @@ -188,6 +188,7 @@ static BRPORT_ATTR(multicast_router, S_I |
||
485 | store_multicast_router); |
||
486 | |||
487 | BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); |
||
488 | +BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST); |
||
489 | #endif |
||
490 | |||
491 | static const struct brport_attribute *brport_attrs[] = { |
||
492 | @@ -214,6 +215,7 @@ static const struct brport_attribute *br |
||
493 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
||
494 | &brport_attr_multicast_router, |
||
495 | &brport_attr_multicast_fast_leave, |
||
496 | + &brport_attr_multicast_to_unicast, |
||
497 | #endif |
||
498 | &brport_attr_proxyarp, |
||
499 | &brport_attr_proxyarp_wifi, |