OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From: Felix Fietkau <nbd@nbd.name> |
2 | Subject: [PATCH] bridge: multicast to unicast |
||
3 | |||
4 | Implement optinal multicast->unicast conversion for igmp snooping |
||
5 | --- |
||
6 | --- a/net/bridge/br_multicast.c |
||
7 | +++ b/net/bridge/br_multicast.c |
||
8 | @@ -635,7 +635,8 @@ struct net_bridge_port_group *br_multica |
||
9 | struct net_bridge_port *port, |
||
10 | struct br_ip *group, |
||
11 | struct net_bridge_port_group __rcu *next, |
||
12 | - unsigned char state) |
||
13 | + unsigned char state, |
||
14 | + const unsigned char *src) |
||
15 | { |
||
16 | struct net_bridge_port_group *p; |
||
17 | |||
18 | @@ -650,12 +651,33 @@ struct net_bridge_port_group *br_multica |
||
19 | hlist_add_head(&p->mglist, &port->mglist); |
||
20 | setup_timer(&p->timer, br_multicast_port_group_expired, |
||
21 | (unsigned long)p); |
||
22 | + if ((port->flags & BR_MULTICAST_TO_UCAST) && src) { |
||
23 | + memcpy(p->eth_addr, src, ETH_ALEN); |
||
24 | + p->unicast = true; |
||
25 | + } |
||
26 | return p; |
||
27 | } |
||
28 | |||
29 | +static bool br_port_group_equal(struct net_bridge_port_group *p, |
||
30 | + struct net_bridge_port *port, |
||
31 | + const unsigned char *src) |
||
32 | +{ |
||
33 | + if (p->port != port) |
||
34 | + return false; |
||
35 | + |
||
36 | + if (!p->unicast) |
||
37 | + return true; |
||
38 | + |
||
39 | + if (!src) |
||
40 | + return false; |
||
41 | + |
||
42 | + return ether_addr_equal(src, p->eth_addr); |
||
43 | +} |
||
44 | + |
||
45 | static int br_multicast_add_group(struct net_bridge *br, |
||
46 | struct net_bridge_port *port, |
||
47 | - struct br_ip *group) |
||
48 | + struct br_ip *group, |
||
49 | + const unsigned char *src) |
||
50 | { |
||
51 | struct net_bridge_mdb_entry *mp; |
||
52 | struct net_bridge_port_group *p; |
||
53 | @@ -682,13 +704,13 @@ static int br_multicast_add_group(struct |
||
54 | for (pp = &mp->ports; |
||
55 | (p = mlock_dereference(*pp, br)) != NULL; |
||
56 | pp = &p->next) { |
||
57 | - if (p->port == port) |
||
58 | + if (br_port_group_equal(p, port, src)) |
||
59 | goto found; |
||
60 | if ((unsigned long)p->port < (unsigned long)port) |
||
61 | break; |
||
62 | } |
||
63 | |||
64 | - p = br_multicast_new_port_group(port, group, *pp, MDB_TEMPORARY); |
||
65 | + p = br_multicast_new_port_group(port, group, *pp, MDB_TEMPORARY, src); |
||
66 | if (unlikely(!p)) |
||
67 | goto err; |
||
68 | rcu_assign_pointer(*pp, p); |
||
69 | @@ -707,7 +729,7 @@ err: |
||
70 | static int br_ip4_multicast_add_group(struct net_bridge *br, |
||
71 | struct net_bridge_port *port, |
||
72 | __be32 group, |
||
73 | - __u16 vid) |
||
74 | + __u16 vid, const unsigned char *src) |
||
75 | { |
||
76 | struct br_ip br_group; |
||
77 | |||
78 | @@ -718,14 +740,14 @@ static int br_ip4_multicast_add_group(st |
||
79 | br_group.proto = htons(ETH_P_IP); |
||
80 | br_group.vid = vid; |
||
81 | |||
82 | - return br_multicast_add_group(br, port, &br_group); |
||
83 | + return br_multicast_add_group(br, port, &br_group, src); |
||
84 | } |
||
85 | |||
86 | #if IS_ENABLED(CONFIG_IPV6) |
||
87 | static int br_ip6_multicast_add_group(struct net_bridge *br, |
||
88 | struct net_bridge_port *port, |
||
89 | const struct in6_addr *group, |
||
90 | - __u16 vid) |
||
91 | + __u16 vid, const unsigned char *src) |
||
92 | { |
||
93 | struct br_ip br_group; |
||
94 | |||
95 | @@ -736,7 +758,7 @@ static int br_ip6_multicast_add_group(st |
||
96 | br_group.proto = htons(ETH_P_IPV6); |
||
97 | br_group.vid = vid; |
||
98 | |||
99 | - return br_multicast_add_group(br, port, &br_group); |
||
100 | + return br_multicast_add_group(br, port, &br_group, src); |
||
101 | } |
||
102 | #endif |
||
103 | |||
104 | @@ -965,6 +987,7 @@ static int br_ip4_multicast_igmp3_report |
||
105 | struct sk_buff *skb, |
||
106 | u16 vid) |
||
107 | { |
||
108 | + const unsigned char *src; |
||
109 | struct igmpv3_report *ih; |
||
110 | struct igmpv3_grec *grec; |
||
111 | int i; |
||
112 | @@ -1008,7 +1031,8 @@ static int br_ip4_multicast_igmp3_report |
||
113 | continue; |
||
114 | } |
||
115 | |||
116 | - err = br_ip4_multicast_add_group(br, port, group, vid); |
||
117 | + src = eth_hdr(skb)->h_source; |
||
118 | + err = br_ip4_multicast_add_group(br, port, group, vid, src); |
||
119 | if (err) |
||
120 | break; |
||
121 | } |
||
122 | @@ -1022,6 +1046,7 @@ static int br_ip6_multicast_mld2_report( |
||
123 | struct sk_buff *skb, |
||
124 | u16 vid) |
||
125 | { |
||
126 | + const unsigned char *src; |
||
127 | struct icmp6hdr *icmp6h; |
||
128 | struct mld2_grec *grec; |
||
129 | int i; |
||
130 | @@ -1069,8 +1094,9 @@ static int br_ip6_multicast_mld2_report( |
||
131 | continue; |
||
132 | } |
||
133 | |||
134 | + src = eth_hdr(skb)->h_source; |
||
135 | err = br_ip6_multicast_add_group(br, port, &grec->grec_mca, |
||
136 | - vid); |
||
137 | + vid, src); |
||
138 | if (err) |
||
139 | break; |
||
140 | } |
||
141 | @@ -1406,7 +1432,8 @@ br_multicast_leave_group(struct net_brid |
||
142 | struct net_bridge_port *port, |
||
143 | struct br_ip *group, |
||
144 | struct bridge_mcast_other_query *other_query, |
||
145 | - struct bridge_mcast_own_query *own_query) |
||
146 | + struct bridge_mcast_own_query *own_query, |
||
147 | + const unsigned char *src) |
||
148 | { |
||
149 | struct net_bridge_mdb_htable *mdb; |
||
150 | struct net_bridge_mdb_entry *mp; |
||
151 | @@ -1456,7 +1483,7 @@ br_multicast_leave_group(struct net_brid |
||
152 | for (pp = &mp->ports; |
||
153 | (p = mlock_dereference(*pp, br)) != NULL; |
||
154 | pp = &p->next) { |
||
155 | - if (p->port != port) |
||
156 | + if (!br_port_group_equal(p, port, src)) |
||
157 | continue; |
||
158 | |||
159 | rcu_assign_pointer(*pp, p->next); |
||
160 | @@ -1490,7 +1517,7 @@ br_multicast_leave_group(struct net_brid |
||
161 | for (p = mlock_dereference(mp->ports, br); |
||
162 | p != NULL; |
||
163 | p = mlock_dereference(p->next, br)) { |
||
164 | - if (p->port != port) |
||
165 | + if (!br_port_group_equal(p, port, src)) |
||
166 | continue; |
||
167 | |||
168 | if (!hlist_unhashed(&p->mglist) && |
||
169 | @@ -1508,8 +1535,8 @@ out: |
||
170 | |||
171 | static void br_ip4_multicast_leave_group(struct net_bridge *br, |
||
172 | struct net_bridge_port *port, |
||
173 | - __be32 group, |
||
174 | - __u16 vid) |
||
175 | + __be32 group, __u16 vid, |
||
176 | + const unsigned char *src) |
||
177 | { |
||
178 | struct br_ip br_group; |
||
179 | struct bridge_mcast_own_query *own_query; |
||
180 | @@ -1524,14 +1551,14 @@ static void br_ip4_multicast_leave_group |
||
181 | br_group.vid = vid; |
||
182 | |||
183 | br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query, |
||
184 | - own_query); |
||
185 | + own_query, src); |
||
186 | } |
||
187 | |||
188 | #if IS_ENABLED(CONFIG_IPV6) |
||
189 | static void br_ip6_multicast_leave_group(struct net_bridge *br, |
||
190 | struct net_bridge_port *port, |
||
191 | const struct in6_addr *group, |
||
192 | - __u16 vid) |
||
193 | + __u16 vid, const unsigned char *src) |
||
194 | { |
||
195 | struct br_ip br_group; |
||
196 | struct bridge_mcast_own_query *own_query; |
||
197 | @@ -1546,7 +1573,7 @@ static void br_ip6_multicast_leave_group |
||
198 | br_group.vid = vid; |
||
199 | |||
200 | br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query, |
||
201 | - own_query); |
||
202 | + own_query, src); |
||
203 | } |
||
204 | #endif |
||
205 | |||
206 | @@ -1555,6 +1582,7 @@ static int br_multicast_ipv4_rcv(struct |
||
207 | struct sk_buff *skb, |
||
208 | u16 vid) |
||
209 | { |
||
210 | + const unsigned char *src; |
||
211 | struct sk_buff *skb2 = skb; |
||
212 | const struct iphdr *iph; |
||
213 | struct igmphdr *ih; |
||
214 | @@ -1628,7 +1656,8 @@ static int br_multicast_ipv4_rcv(struct |
||
215 | case IGMP_HOST_MEMBERSHIP_REPORT: |
||
216 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
||
217 | BR_INPUT_SKB_CB(skb)->mrouters_only = 1; |
||
218 | - err = br_ip4_multicast_add_group(br, port, ih->group, vid); |
||
219 | + src = eth_hdr(skb)->h_source; |
||
220 | + err = br_ip4_multicast_add_group(br, port, ih->group, vid, src); |
||
221 | break; |
||
222 | case IGMPV3_HOST_MEMBERSHIP_REPORT: |
||
223 | err = br_ip4_multicast_igmp3_report(br, port, skb2, vid); |
||
224 | @@ -1637,7 +1666,8 @@ static int br_multicast_ipv4_rcv(struct |
||
225 | err = br_ip4_multicast_query(br, port, skb2, vid); |
||
226 | break; |
||
227 | case IGMP_HOST_LEAVE_MESSAGE: |
||
228 | - br_ip4_multicast_leave_group(br, port, ih->group, vid); |
||
229 | + src = eth_hdr(skb)->h_source; |
||
230 | + br_ip4_multicast_leave_group(br, port, ih->group, vid, src); |
||
231 | break; |
||
232 | } |
||
233 | |||
234 | @@ -1655,6 +1685,7 @@ static int br_multicast_ipv6_rcv(struct |
||
235 | struct sk_buff *skb, |
||
236 | u16 vid) |
||
237 | { |
||
238 | + const unsigned char *src; |
||
239 | struct sk_buff *skb2; |
||
240 | const struct ipv6hdr *ip6h; |
||
241 | u8 icmp6_type; |
||
242 | @@ -1764,7 +1795,9 @@ static int br_multicast_ipv6_rcv(struct |
||
243 | } |
||
244 | mld = (struct mld_msg *)skb_transport_header(skb2); |
||
245 | BR_INPUT_SKB_CB(skb)->mrouters_only = 1; |
||
246 | - err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid); |
||
247 | + src = eth_hdr(skb)->h_source; |
||
248 | + err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid, |
||
249 | + src); |
||
250 | break; |
||
251 | } |
||
252 | case ICMPV6_MLD2_REPORT: |
||
253 | @@ -1781,7 +1814,8 @@ static int br_multicast_ipv6_rcv(struct |
||
254 | goto out; |
||
255 | } |
||
256 | mld = (struct mld_msg *)skb_transport_header(skb2); |
||
257 | - br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid); |
||
258 | + src = eth_hdr(skb)->h_source; |
||
259 | + br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src); |
||
260 | } |
||
261 | } |
||
262 | |||
263 | --- a/net/bridge/br_private.h |
||
264 | +++ b/net/bridge/br_private.h |
||
265 | @@ -112,6 +112,9 @@ struct net_bridge_port_group { |
||
266 | struct timer_list timer; |
||
267 | struct br_ip addr; |
||
268 | unsigned char state; |
||
269 | + |
||
270 | + unsigned char eth_addr[ETH_ALEN]; |
||
271 | + bool unicast; |
||
272 | }; |
||
273 | |||
274 | struct net_bridge_mdb_entry |
||
275 | @@ -173,6 +176,7 @@ struct net_bridge_port |
||
276 | #define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING) |
||
277 | #define BR_PROMISC 0x00000080 |
||
278 | #define BR_ISOLATE_MODE 0x00000100 |
||
279 | +#define BR_MULTICAST_TO_UCAST 0x00000200 |
||
280 | |||
281 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
||
282 | struct bridge_mcast_own_query ip4_own_query; |
||
283 | @@ -485,7 +489,8 @@ void br_multicast_free_pg(struct rcu_hea |
||
284 | struct net_bridge_port_group * |
||
285 | br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, |
||
286 | struct net_bridge_port_group __rcu *next, |
||
287 | - unsigned char state); |
||
288 | + unsigned char state, |
||
289 | + const unsigned char *src); |
||
290 | void br_mdb_init(void); |
||
291 | void br_mdb_uninit(void); |
||
292 | void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, |
||
293 | --- a/net/bridge/br_mdb.c |
||
294 | +++ b/net/bridge/br_mdb.c |
||
295 | @@ -342,7 +342,7 @@ static int br_mdb_add_group(struct net_b |
||
296 | break; |
||
297 | } |
||
298 | |||
299 | - p = br_multicast_new_port_group(port, group, *pp, state); |
||
300 | + p = br_multicast_new_port_group(port, group, *pp, state, NULL); |
||
301 | if (unlikely(!p)) |
||
302 | return -ENOMEM; |
||
303 | rcu_assign_pointer(*pp, p); |
||
304 | --- a/net/bridge/br_forward.c |
||
305 | +++ b/net/bridge/br_forward.c |
||
306 | @@ -168,6 +168,34 @@ out: |
||
307 | return p; |
||
308 | } |
||
309 | |||
310 | +static struct net_bridge_port *maybe_deliver_addr( |
||
311 | + struct net_bridge_port *prev, struct net_bridge_port *p, |
||
312 | + struct sk_buff *skb, const unsigned char *addr, |
||
313 | + void (*__packet_hook)(const struct net_bridge_port *p, |
||
314 | + struct sk_buff *skb)) |
||
315 | +{ |
||
316 | + struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; |
||
317 | + const unsigned char *src = eth_hdr(skb)->h_source; |
||
318 | + |
||
319 | + if (!should_deliver(p, skb)) |
||
320 | + return prev; |
||
321 | + |
||
322 | + /* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */ |
||
323 | + if (skb->dev == p->dev && ether_addr_equal(src, addr)) |
||
324 | + return prev; |
||
325 | + |
||
326 | + skb = skb_copy(skb, GFP_ATOMIC); |
||
327 | + if (!skb) { |
||
328 | + dev->stats.tx_dropped++; |
||
329 | + return prev; |
||
330 | + } |
||
331 | + |
||
332 | + memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN); |
||
333 | + __packet_hook(p, skb); |
||
334 | + |
||
335 | + return prev; |
||
336 | +} |
||
337 | + |
||
338 | /* called under bridge lock */ |
||
339 | static void br_flood(struct net_bridge *br, struct sk_buff *skb, |
||
340 | struct sk_buff *skb0, |
||
341 | @@ -232,6 +260,7 @@ static void br_multicast_flood(struct ne |
||
342 | struct net_bridge_port *prev = NULL; |
||
343 | struct net_bridge_port_group *p; |
||
344 | struct hlist_node *rp; |
||
345 | + const unsigned char *addr; |
||
346 | |||
347 | rp = rcu_dereference(hlist_first_rcu(&br->router_list)); |
||
348 | p = mdst ? rcu_dereference(mdst->ports) : NULL; |
||
349 | @@ -242,10 +271,19 @@ static void br_multicast_flood(struct ne |
||
350 | rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : |
||
351 | NULL; |
||
352 | |||
353 | - port = (unsigned long)lport > (unsigned long)rport ? |
||
354 | - lport : rport; |
||
355 | - |
||
356 | - prev = maybe_deliver(prev, port, skb, __packet_hook); |
||
357 | + if ((unsigned long)lport > (unsigned long)rport) { |
||
358 | + port = lport; |
||
359 | + addr = p->unicast ? p->eth_addr : NULL; |
||
360 | + } else { |
||
361 | + port = rport; |
||
362 | + addr = NULL; |
||
363 | + } |
||
364 | + |
||
365 | + if (addr) |
||
366 | + prev = maybe_deliver_addr(prev, port, skb, addr, |
||
367 | + __packet_hook); |
||
368 | + else |
||
369 | + prev = maybe_deliver(prev, port, skb, __packet_hook); |
||
370 | if (IS_ERR(prev)) |
||
371 | goto out; |
||
372 | |||
373 | --- a/net/bridge/br_sysfs_if.c |
||
374 | +++ b/net/bridge/br_sysfs_if.c |
||
375 | @@ -187,6 +187,7 @@ static BRPORT_ATTR(multicast_router, S_I |
||
376 | store_multicast_router); |
||
377 | |||
378 | BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); |
||
379 | +BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UCAST); |
||
380 | #endif |
||
381 | |||
382 | static const struct brport_attribute *brport_attrs[] = { |
||
383 | @@ -213,6 +214,7 @@ static const struct brport_attribute *br |
||
384 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
||
385 | &brport_attr_multicast_router, |
||
386 | &brport_attr_multicast_fast_leave, |
||
387 | + &brport_attr_multicast_to_unicast, |
||
388 | #endif |
||
389 | &brport_attr_isolated, |
||
390 | NULL |