diff options
author | Linus Lüssing <linus.luessing@web.de> | 2014-06-07 12:26:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-11 02:50:47 -0400 |
commit | 2cd4143192e8c60f66cb32c3a30c76d0470a372d (patch) | |
tree | e9690ec1d858835250edaa4cb5943bdd8e04d19e /net/bridge/br_multicast.c | |
parent | 07f8ac4a1e26e8283542cdaf658a6e2a12fd6980 (diff) |
bridge: memorize and export selected IGMP/MLD querier port
Adding bridge support to the batman-adv multicast optimization requires
batman-adv knowing about the existence of bridged-in IGMP/MLD queriers
to be able to reliably serve any multicast listener behind this same
bridge.
Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_multicast.c')
-rw-r--r-- | net/bridge/br_multicast.c | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 772476b7c4b7..cd3cf394c477 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c | |||
@@ -1081,6 +1081,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, | |||
1081 | #endif | 1081 | #endif |
1082 | 1082 | ||
1083 | static bool br_ip4_multicast_select_querier(struct net_bridge *br, | 1083 | static bool br_ip4_multicast_select_querier(struct net_bridge *br, |
1084 | struct net_bridge_port *port, | ||
1084 | __be32 saddr) | 1085 | __be32 saddr) |
1085 | { | 1086 | { |
1086 | if (!timer_pending(&br->ip4_own_query.timer) && | 1087 | if (!timer_pending(&br->ip4_own_query.timer) && |
@@ -1098,11 +1099,15 @@ static bool br_ip4_multicast_select_querier(struct net_bridge *br, | |||
1098 | update: | 1099 | update: |
1099 | br->ip4_querier.addr.u.ip4 = saddr; | 1100 | br->ip4_querier.addr.u.ip4 = saddr; |
1100 | 1101 | ||
1102 | /* update protected by general multicast_lock by caller */ | ||
1103 | rcu_assign_pointer(br->ip4_querier.port, port); | ||
1104 | |||
1101 | return true; | 1105 | return true; |
1102 | } | 1106 | } |
1103 | 1107 | ||
1104 | #if IS_ENABLED(CONFIG_IPV6) | 1108 | #if IS_ENABLED(CONFIG_IPV6) |
1105 | static bool br_ip6_multicast_select_querier(struct net_bridge *br, | 1109 | static bool br_ip6_multicast_select_querier(struct net_bridge *br, |
1110 | struct net_bridge_port *port, | ||
1106 | struct in6_addr *saddr) | 1111 | struct in6_addr *saddr) |
1107 | { | 1112 | { |
1108 | if (!timer_pending(&br->ip6_own_query.timer) && | 1113 | if (!timer_pending(&br->ip6_own_query.timer) && |
@@ -1117,19 +1122,23 @@ static bool br_ip6_multicast_select_querier(struct net_bridge *br, | |||
1117 | update: | 1122 | update: |
1118 | br->ip6_querier.addr.u.ip6 = *saddr; | 1123 | br->ip6_querier.addr.u.ip6 = *saddr; |
1119 | 1124 | ||
1125 | /* update protected by general multicast_lock by caller */ | ||
1126 | rcu_assign_pointer(br->ip6_querier.port, port); | ||
1127 | |||
1120 | return true; | 1128 | return true; |
1121 | } | 1129 | } |
1122 | #endif | 1130 | #endif |
1123 | 1131 | ||
1124 | static bool br_multicast_select_querier(struct net_bridge *br, | 1132 | static bool br_multicast_select_querier(struct net_bridge *br, |
1133 | struct net_bridge_port *port, | ||
1125 | struct br_ip *saddr) | 1134 | struct br_ip *saddr) |
1126 | { | 1135 | { |
1127 | switch (saddr->proto) { | 1136 | switch (saddr->proto) { |
1128 | case htons(ETH_P_IP): | 1137 | case htons(ETH_P_IP): |
1129 | return br_ip4_multicast_select_querier(br, saddr->u.ip4); | 1138 | return br_ip4_multicast_select_querier(br, port, saddr->u.ip4); |
1130 | #if IS_ENABLED(CONFIG_IPV6) | 1139 | #if IS_ENABLED(CONFIG_IPV6) |
1131 | case htons(ETH_P_IPV6): | 1140 | case htons(ETH_P_IPV6): |
1132 | return br_ip6_multicast_select_querier(br, &saddr->u.ip6); | 1141 | return br_ip6_multicast_select_querier(br, port, &saddr->u.ip6); |
1133 | #endif | 1142 | #endif |
1134 | } | 1143 | } |
1135 | 1144 | ||
@@ -1201,7 +1210,7 @@ static void br_multicast_query_received(struct net_bridge *br, | |||
1201 | struct br_ip *saddr, | 1210 | struct br_ip *saddr, |
1202 | unsigned long max_delay) | 1211 | unsigned long max_delay) |
1203 | { | 1212 | { |
1204 | if (!br_multicast_select_querier(br, saddr)) | 1213 | if (!br_multicast_select_querier(br, port, saddr)) |
1205 | return; | 1214 | return; |
1206 | 1215 | ||
1207 | br_multicast_update_query_timer(br, query, max_delay); | 1216 | br_multicast_update_query_timer(br, query, max_delay); |
@@ -1804,12 +1813,14 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, | |||
1804 | } | 1813 | } |
1805 | 1814 | ||
1806 | static void br_multicast_query_expired(struct net_bridge *br, | 1815 | static void br_multicast_query_expired(struct net_bridge *br, |
1807 | struct bridge_mcast_own_query *query) | 1816 | struct bridge_mcast_own_query *query, |
1817 | struct bridge_mcast_querier *querier) | ||
1808 | { | 1818 | { |
1809 | spin_lock(&br->multicast_lock); | 1819 | spin_lock(&br->multicast_lock); |
1810 | if (query->startup_sent < br->multicast_startup_query_count) | 1820 | if (query->startup_sent < br->multicast_startup_query_count) |
1811 | query->startup_sent++; | 1821 | query->startup_sent++; |
1812 | 1822 | ||
1823 | rcu_assign_pointer(querier, NULL); | ||
1813 | br_multicast_send_query(br, NULL, query); | 1824 | br_multicast_send_query(br, NULL, query); |
1814 | spin_unlock(&br->multicast_lock); | 1825 | spin_unlock(&br->multicast_lock); |
1815 | } | 1826 | } |
@@ -1818,7 +1829,7 @@ static void br_ip4_multicast_query_expired(unsigned long data) | |||
1818 | { | 1829 | { |
1819 | struct net_bridge *br = (void *)data; | 1830 | struct net_bridge *br = (void *)data; |
1820 | 1831 | ||
1821 | br_multicast_query_expired(br, &br->ip4_own_query); | 1832 | br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier); |
1822 | } | 1833 | } |
1823 | 1834 | ||
1824 | #if IS_ENABLED(CONFIG_IPV6) | 1835 | #if IS_ENABLED(CONFIG_IPV6) |
@@ -1826,7 +1837,7 @@ static void br_ip6_multicast_query_expired(unsigned long data) | |||
1826 | { | 1837 | { |
1827 | struct net_bridge *br = (void *)data; | 1838 | struct net_bridge *br = (void *)data; |
1828 | 1839 | ||
1829 | br_multicast_query_expired(br, &br->ip6_own_query); | 1840 | br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier); |
1830 | } | 1841 | } |
1831 | #endif | 1842 | #endif |
1832 | 1843 | ||
@@ -1849,8 +1860,10 @@ void br_multicast_init(struct net_bridge *br) | |||
1849 | br->multicast_membership_interval = 260 * HZ; | 1860 | br->multicast_membership_interval = 260 * HZ; |
1850 | 1861 | ||
1851 | br->ip4_other_query.delay_time = 0; | 1862 | br->ip4_other_query.delay_time = 0; |
1863 | br->ip4_querier.port = NULL; | ||
1852 | #if IS_ENABLED(CONFIG_IPV6) | 1864 | #if IS_ENABLED(CONFIG_IPV6) |
1853 | br->ip6_other_query.delay_time = 0; | 1865 | br->ip6_other_query.delay_time = 0; |
1866 | br->ip6_querier.port = NULL; | ||
1854 | #endif | 1867 | #endif |
1855 | 1868 | ||
1856 | spin_lock_init(&br->multicast_lock); | 1869 | spin_lock_init(&br->multicast_lock); |
@@ -2199,3 +2212,50 @@ unlock: | |||
2199 | return count; | 2212 | return count; |
2200 | } | 2213 | } |
2201 | EXPORT_SYMBOL_GPL(br_multicast_list_adjacent); | 2214 | EXPORT_SYMBOL_GPL(br_multicast_list_adjacent); |
2215 | |||
2216 | /** | ||
2217 | * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port | ||
2218 | * @dev: The bridge port adjacent to which to check for a querier | ||
2219 | * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 | ||
2220 | * | ||
2221 | * Checks whether the given interface has a bridge on top and if so returns | ||
2222 | * true if a selected querier is behind one of the other ports of this | ||
2223 | * bridge. Otherwise returns false. | ||
2224 | */ | ||
2225 | bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto) | ||
2226 | { | ||
2227 | struct net_bridge *br; | ||
2228 | struct net_bridge_port *port; | ||
2229 | bool ret = false; | ||
2230 | |||
2231 | rcu_read_lock(); | ||
2232 | if (!br_port_exists(dev)) | ||
2233 | goto unlock; | ||
2234 | |||
2235 | port = br_port_get_rcu(dev); | ||
2236 | if (!port || !port->br) | ||
2237 | goto unlock; | ||
2238 | |||
2239 | br = port->br; | ||
2240 | |||
2241 | switch (proto) { | ||
2242 | case ETH_P_IP: | ||
2243 | if (!timer_pending(&br->ip4_other_query.timer) || | ||
2244 | rcu_dereference(br->ip4_querier.port) == port) | ||
2245 | goto unlock; | ||
2246 | break; | ||
2247 | case ETH_P_IPV6: | ||
2248 | if (!timer_pending(&br->ip6_other_query.timer) || | ||
2249 | rcu_dereference(br->ip6_querier.port) == port) | ||
2250 | goto unlock; | ||
2251 | break; | ||
2252 | default: | ||
2253 | goto unlock; | ||
2254 | } | ||
2255 | |||
2256 | ret = true; | ||
2257 | unlock: | ||
2258 | rcu_read_unlock(); | ||
2259 | return ret; | ||
2260 | } | ||
2261 | EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent); | ||