]>
Commit | Line | Data |
---|---|---|
7db7d9f3 | 1 | // SPDX-License-Identifier: GPL-2.0 |
cfa55c6d | 2 | /* Copyright (C) B.A.T.M.A.N. contributors: |
c6c8fea2 SE |
3 | * |
4 | * Marek Lindner, Simon Wunderlich | |
c6c8fea2 SE |
5 | */ |
6 | ||
c6c8fea2 | 7 | #include "hard-interface.h" |
1e2c2a4f | 8 | #include "main.h" |
c6c8fea2 | 9 | |
7a659d56 | 10 | #include <linux/atomic.h> |
1e2c2a4f | 11 | #include <linux/byteorder/generic.h> |
eb7da4f1 | 12 | #include <linux/container_of.h> |
b1cb8a71 | 13 | #include <linux/errno.h> |
b92b94ac | 14 | #include <linux/gfp.h> |
fcafa5e7 | 15 | #include <linux/if.h> |
c6c8fea2 | 16 | #include <linux/if_arp.h> |
af5d4f77 | 17 | #include <linux/if_ether.h> |
7a659d56 | 18 | #include <linux/kref.h> |
e1928752 | 19 | #include <linux/limits.h> |
1e2c2a4f | 20 | #include <linux/list.h> |
fcd193e1 | 21 | #include <linux/minmax.h> |
40e220b4 | 22 | #include <linux/mutex.h> |
1e2c2a4f SE |
23 | #include <linux/netdevice.h> |
24 | #include <linux/printk.h> | |
25 | #include <linux/rculist.h> | |
26 | #include <linux/rtnetlink.h> | |
27 | #include <linux/slab.h> | |
cef63419 | 28 | #include <linux/spinlock.h> |
275019d2 AL |
29 | #include <net/net_namespace.h> |
30 | #include <net/rtnetlink.h> | |
fec149f5 | 31 | #include <uapi/linux/batadv_packet.h> |
1e2c2a4f | 32 | |
a2d08166 | 33 | #include "bat_v.h" |
1e2c2a4f | 34 | #include "bridge_loop_avoidance.h" |
1e2c2a4f SE |
35 | #include "distributed-arp-table.h" |
36 | #include "gateway_client.h" | |
ba412080 | 37 | #include "log.h" |
1e2c2a4f | 38 | #include "originator.h" |
1e2c2a4f SE |
39 | #include "send.h" |
40 | #include "soft-interface.h" | |
1e2c2a4f | 41 | #include "translation-table.h" |
c6c8fea2 | 42 | |
140ed8e8 | 43 | /** |
7e9a8c2c | 44 | * batadv_hardif_release() - release hard interface from lists and queue for |
140ed8e8 | 45 | * free after rcu grace period |
7a659d56 | 46 | * @ref: kref pointer of the hard interface |
140ed8e8 | 47 | */ |
7a659d56 | 48 | void batadv_hardif_release(struct kref *ref) |
c6c8fea2 | 49 | { |
7a659d56 SE |
50 | struct batadv_hard_iface *hard_iface; |
51 | ||
52 | hard_iface = container_of(ref, struct batadv_hard_iface, refcount); | |
e6c10f43 | 53 | dev_put(hard_iface->net_dev); |
140ed8e8 SE |
54 | |
55 | kfree_rcu(hard_iface, rcu); | |
c6c8fea2 SE |
56 | } |
57 | ||
ff15c27c SE |
58 | /** |
59 | * batadv_hardif_get_by_netdev() - Get hard interface object of a net_device | |
60 | * @net_dev: net_device to search for | |
61 | * | |
62 | * Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors | |
63 | */ | |
56303d34 SE |
64 | struct batadv_hard_iface * |
65 | batadv_hardif_get_by_netdev(const struct net_device *net_dev) | |
c6c8fea2 | 66 | { |
56303d34 | 67 | struct batadv_hard_iface *hard_iface; |
c6c8fea2 SE |
68 | |
69 | rcu_read_lock(); | |
3193e8fd | 70 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
e6c10f43 | 71 | if (hard_iface->net_dev == net_dev && |
7a659d56 | 72 | kref_get_unless_zero(&hard_iface->refcount)) |
c6c8fea2 SE |
73 | goto out; |
74 | } | |
75 | ||
e6c10f43 | 76 | hard_iface = NULL; |
c6c8fea2 SE |
77 | |
78 | out: | |
c6c8fea2 | 79 | rcu_read_unlock(); |
e6c10f43 | 80 | return hard_iface; |
c6c8fea2 SE |
81 | } |
82 | ||
275019d2 | 83 | /** |
7e9a8c2c | 84 | * batadv_getlink_net() - return link net namespace (of use fallback) |
275019d2 AL |
85 | * @netdev: net_device to check |
86 | * @fallback_net: return in case get_link_net is not available for @netdev | |
87 | * | |
88 | * Return: result of rtnl_link_ops->get_link_net or @fallback_net | |
89 | */ | |
88ffc7d0 SE |
90 | static struct net *batadv_getlink_net(const struct net_device *netdev, |
91 | struct net *fallback_net) | |
275019d2 AL |
92 | { |
93 | if (!netdev->rtnl_link_ops) | |
94 | return fallback_net; | |
95 | ||
96 | if (!netdev->rtnl_link_ops->get_link_net) | |
97 | return fallback_net; | |
98 | ||
99 | return netdev->rtnl_link_ops->get_link_net(netdev); | |
100 | } | |
101 | ||
1bc4e2b0 | 102 | /** |
7e9a8c2c | 103 | * batadv_mutual_parents() - check if two devices are each others parent |
275019d2 AL |
104 | * @dev1: 1st net dev |
105 | * @net1: 1st devices netns | |
106 | * @dev2: 2nd net dev | |
107 | * @net2: 2nd devices netns | |
1bc4e2b0 AL |
108 | * |
109 | * veth devices come in pairs and each is the parent of the other! | |
110 | * | |
111 | * Return: true if the devices are each others parent, otherwise false | |
112 | */ | |
113 | static bool batadv_mutual_parents(const struct net_device *dev1, | |
88ffc7d0 | 114 | struct net *net1, |
275019d2 | 115 | const struct net_device *dev2, |
88ffc7d0 | 116 | struct net *net2) |
1bc4e2b0 AL |
117 | { |
118 | int dev1_parent_iflink = dev_get_iflink(dev1); | |
119 | int dev2_parent_iflink = dev_get_iflink(dev2); | |
275019d2 AL |
120 | const struct net *dev1_parent_net; |
121 | const struct net *dev2_parent_net; | |
122 | ||
123 | dev1_parent_net = batadv_getlink_net(dev1, net1); | |
124 | dev2_parent_net = batadv_getlink_net(dev2, net2); | |
1bc4e2b0 AL |
125 | |
126 | if (!dev1_parent_iflink || !dev2_parent_iflink) | |
127 | return false; | |
128 | ||
129 | return (dev1_parent_iflink == dev2->ifindex) && | |
275019d2 AL |
130 | (dev2_parent_iflink == dev1->ifindex) && |
131 | net_eq(dev1_parent_net, net2) && | |
132 | net_eq(dev2_parent_net, net1); | |
1bc4e2b0 AL |
133 | } |
134 | ||
b7eddd0b | 135 | /** |
7e9a8c2c | 136 | * batadv_is_on_batman_iface() - check if a device is a batman iface descendant |
b7eddd0b AQ |
137 | * @net_dev: the device to check |
138 | * | |
139 | * If the user creates any virtual device on top of a batman-adv interface, it | |
bccb48c8 SE |
140 | * is important to prevent this new interface from being used to create a new |
141 | * mesh network (this behaviour would lead to a batman-over-batman | |
142 | * configuration). This function recursively checks all the fathers of the | |
143 | * device passed as argument looking for a batman-adv soft interface. | |
b7eddd0b | 144 | * |
62fe710f | 145 | * Return: true if the device is descendant of a batman-adv mesh interface (or |
b7eddd0b AQ |
146 | * if it is a batman-adv interface itself), false otherwise |
147 | */ | |
148 | static bool batadv_is_on_batman_iface(const struct net_device *net_dev) | |
149 | { | |
2cd45a06 | 150 | struct net *net = dev_net(net_dev); |
275019d2 | 151 | struct net_device *parent_dev; |
88ffc7d0 | 152 | struct net *parent_net; |
690bb6fb | 153 | int iflink; |
b7eddd0b AQ |
154 | bool ret; |
155 | ||
156 | /* check if this is a batman-adv mesh interface */ | |
157 | if (batadv_softif_is_valid(net_dev)) | |
158 | return true; | |
159 | ||
690bb6fb | 160 | iflink = dev_get_iflink(net_dev); |
6c1f41af | 161 | if (iflink == 0) |
b7eddd0b AQ |
162 | return false; |
163 | ||
275019d2 AL |
164 | parent_net = batadv_getlink_net(net_dev, net); |
165 | ||
6c1f41af SE |
166 | /* iflink to itself, most likely physical device */ |
167 | if (net == parent_net && iflink == net_dev->ifindex) | |
168 | return false; | |
169 | ||
b7eddd0b | 170 | /* recurse over the parent device */ |
690bb6fb | 171 | parent_dev = __dev_get_by_index((struct net *)parent_net, iflink); |
955d3411 | 172 | if (!parent_dev) { |
6ee3c393 SE |
173 | pr_warn("Cannot find parent device. Skipping batadv-on-batadv check for %s\n", |
174 | net_dev->name); | |
b7eddd0b | 175 | return false; |
955d3411 | 176 | } |
b7eddd0b | 177 | |
275019d2 | 178 | if (batadv_mutual_parents(net_dev, net, parent_dev, parent_net)) |
1bc4e2b0 AL |
179 | return false; |
180 | ||
b7eddd0b AQ |
181 | ret = batadv_is_on_batman_iface(parent_dev); |
182 | ||
b7eddd0b AQ |
183 | return ret; |
184 | } | |
185 | ||
4b426b10 | 186 | static bool batadv_is_valid_iface(const struct net_device *net_dev) |
c6c8fea2 SE |
187 | { |
188 | if (net_dev->flags & IFF_LOOPBACK) | |
4b426b10 | 189 | return false; |
c6c8fea2 SE |
190 | |
191 | if (net_dev->type != ARPHRD_ETHER) | |
4b426b10 | 192 | return false; |
c6c8fea2 SE |
193 | |
194 | if (net_dev->addr_len != ETH_ALEN) | |
4b426b10 | 195 | return false; |
c6c8fea2 SE |
196 | |
197 | /* no batman over batman */ | |
b7eddd0b | 198 | if (batadv_is_on_batman_iface(net_dev)) |
4b426b10 | 199 | return false; |
c6c8fea2 | 200 | |
4b426b10 | 201 | return true; |
c6c8fea2 SE |
202 | } |
203 | ||
de68d100 | 204 | /** |
7e9a8c2c | 205 | * batadv_get_real_netdevice() - check if the given netdev struct is a virtual |
5ed4a460 ML |
206 | * interface on top of another 'real' interface |
207 | * @netdev: the device to check | |
208 | * | |
1942de1b ML |
209 | * Callers must hold the rtnl semaphore. You may want batadv_get_real_netdev() |
210 | * instead of this. | |
211 | * | |
5ed4a460 ML |
212 | * Return: the 'real' net device or the original net device and NULL in case |
213 | * of an error. | |
214 | */ | |
215 | static struct net_device *batadv_get_real_netdevice(struct net_device *netdev) | |
216 | { | |
217 | struct batadv_hard_iface *hard_iface = NULL; | |
218 | struct net_device *real_netdev = NULL; | |
219 | struct net *real_net; | |
220 | struct net *net; | |
6116ba09 | 221 | int iflink; |
5ed4a460 ML |
222 | |
223 | ASSERT_RTNL(); | |
224 | ||
225 | if (!netdev) | |
226 | return NULL; | |
227 | ||
6116ba09 | 228 | iflink = dev_get_iflink(netdev); |
6c1f41af | 229 | if (iflink == 0) { |
5ed4a460 ML |
230 | dev_hold(netdev); |
231 | return netdev; | |
232 | } | |
233 | ||
234 | hard_iface = batadv_hardif_get_by_netdev(netdev); | |
235 | if (!hard_iface || !hard_iface->soft_iface) | |
236 | goto out; | |
237 | ||
238 | net = dev_net(hard_iface->soft_iface); | |
5ed4a460 | 239 | real_net = batadv_getlink_net(netdev, net); |
6c1f41af SE |
240 | |
241 | /* iflink to itself, most likely physical device */ | |
242 | if (net == real_net && netdev->ifindex == iflink) { | |
243 | real_netdev = netdev; | |
244 | dev_hold(real_netdev); | |
245 | goto out; | |
246 | } | |
247 | ||
6116ba09 | 248 | real_netdev = dev_get_by_index(real_net, iflink); |
5ed4a460 ML |
249 | |
250 | out: | |
79a0bffb | 251 | batadv_hardif_put(hard_iface); |
5ed4a460 ML |
252 | return real_netdev; |
253 | } | |
254 | ||
1942de1b | 255 | /** |
7e9a8c2c | 256 | * batadv_get_real_netdev() - check if the given net_device struct is a virtual |
1942de1b | 257 | * interface on top of another 'real' interface |
de68d100 MS |
258 | * @net_device: the device to check |
259 | * | |
1942de1b ML |
260 | * Return: the 'real' net device or the original net device and NULL in case |
261 | * of an error. | |
de68d100 | 262 | */ |
1942de1b ML |
263 | struct net_device *batadv_get_real_netdev(struct net_device *net_device) |
264 | { | |
265 | struct net_device *real_netdev; | |
266 | ||
267 | rtnl_lock(); | |
268 | real_netdev = batadv_get_real_netdevice(net_device); | |
269 | rtnl_unlock(); | |
270 | ||
271 | return real_netdev; | |
272 | } | |
273 | ||
10b1bbb4 | 274 | /** |
7e9a8c2c | 275 | * batadv_is_wext_netdev() - check if the given net_device struct is a |
10b1bbb4 SE |
276 | * wext wifi interface |
277 | * @net_device: the device to check | |
278 | * | |
279 | * Return: true if the net device is a wext wireless device, false | |
280 | * otherwise. | |
281 | */ | |
282 | static bool batadv_is_wext_netdev(struct net_device *net_device) | |
de68d100 | 283 | { |
0c69aecc AQ |
284 | if (!net_device) |
285 | return false; | |
286 | ||
de68d100 MS |
287 | #ifdef CONFIG_WIRELESS_EXT |
288 | /* pre-cfg80211 drivers have to implement WEXT, so it is possible to | |
289 | * check for wireless_handlers != NULL | |
290 | */ | |
291 | if (net_device->wireless_handlers) | |
292 | return true; | |
293 | #endif | |
294 | ||
10b1bbb4 SE |
295 | return false; |
296 | } | |
297 | ||
f44a3ae9 | 298 | /** |
7e9a8c2c | 299 | * batadv_is_cfg80211_netdev() - check if the given net_device struct is a |
f44a3ae9 ML |
300 | * cfg80211 wifi interface |
301 | * @net_device: the device to check | |
302 | * | |
303 | * Return: true if the net device is a cfg80211 wireless device, false | |
304 | * otherwise. | |
305 | */ | |
10b1bbb4 | 306 | static bool batadv_is_cfg80211_netdev(struct net_device *net_device) |
f44a3ae9 ML |
307 | { |
308 | if (!net_device) | |
309 | return false; | |
310 | ||
c304eddc | 311 | #if IS_ENABLED(CONFIG_CFG80211) |
de68d100 MS |
312 | /* cfg80211 drivers have to set ieee80211_ptr */ |
313 | if (net_device->ieee80211_ptr) | |
314 | return true; | |
c304eddc | 315 | #endif |
de68d100 MS |
316 | |
317 | return false; | |
318 | } | |
319 | ||
de68d100 | 320 | /** |
7e9a8c2c | 321 | * batadv_wifi_flags_evaluate() - calculate wifi flags for net_device |
de68d100 MS |
322 | * @net_device: the device to check |
323 | * | |
10b1bbb4 SE |
324 | * Return: batadv_hard_iface_wifi_flags flags of the device |
325 | */ | |
326 | static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) | |
327 | { | |
328 | u32 wifi_flags = 0; | |
5ed4a460 | 329 | struct net_device *real_netdev; |
10b1bbb4 SE |
330 | |
331 | if (batadv_is_wext_netdev(net_device)) | |
332 | wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT; | |
333 | ||
334 | if (batadv_is_cfg80211_netdev(net_device)) | |
335 | wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; | |
336 | ||
5ed4a460 ML |
337 | real_netdev = batadv_get_real_netdevice(net_device); |
338 | if (!real_netdev) | |
339 | return wifi_flags; | |
340 | ||
341 | if (real_netdev == net_device) | |
342 | goto out; | |
343 | ||
344 | if (batadv_is_wext_netdev(real_netdev)) | |
345 | wifi_flags |= BATADV_HARDIF_WIFI_WEXT_INDIRECT; | |
346 | ||
347 | if (batadv_is_cfg80211_netdev(real_netdev)) | |
348 | wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; | |
349 | ||
350 | out: | |
351 | dev_put(real_netdev); | |
10b1bbb4 SE |
352 | return wifi_flags; |
353 | } | |
354 | ||
355 | /** | |
7e9a8c2c | 356 | * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi |
10b1bbb4 SE |
357 | * interface |
358 | * @hard_iface: the device to check | |
359 | * | |
360 | * Return: true if the net device is a cfg80211 wireless device, false | |
361 | * otherwise. | |
362 | */ | |
363 | bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) | |
364 | { | |
365 | u32 allowed_flags = 0; | |
366 | ||
367 | allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; | |
5ed4a460 | 368 | allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; |
10b1bbb4 SE |
369 | |
370 | return !!(hard_iface->wifi_flags & allowed_flags); | |
371 | } | |
372 | ||
373 | /** | |
7e9a8c2c | 374 | * batadv_is_wifi_hardif() - check if the given hardif is a wifi interface |
10b1bbb4 SE |
375 | * @hard_iface: the device to check |
376 | * | |
62fe710f | 377 | * Return: true if the net device is a 802.11 wireless device, false otherwise. |
de68d100 | 378 | */ |
10b1bbb4 | 379 | bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface) |
de68d100 | 380 | { |
10b1bbb4 | 381 | if (!hard_iface) |
0c69aecc AQ |
382 | return false; |
383 | ||
10b1bbb4 | 384 | return hard_iface->wifi_flags != 0; |
de68d100 MS |
385 | } |
386 | ||
3111beed | 387 | /** |
7e9a8c2c | 388 | * batadv_hardif_no_broadcast() - check whether (re)broadcast is necessary |
3111beed LL |
389 | * @if_outgoing: the outgoing interface checked and considered for (re)broadcast |
390 | * @orig_addr: the originator of this packet | |
391 | * @orig_neigh: originator address of the forwarder we just got the packet from | |
392 | * (NULL if we originated) | |
393 | * | |
394 | * Checks whether a packet needs to be (re)broadcasted on the given interface. | |
395 | * | |
396 | * Return: | |
397 | * BATADV_HARDIF_BCAST_NORECIPIENT: No neighbor on interface | |
398 | * BATADV_HARDIF_BCAST_DUPFWD: Just one neighbor, but it is the forwarder | |
399 | * BATADV_HARDIF_BCAST_DUPORIG: Just one neighbor, but it is the originator | |
400 | * BATADV_HARDIF_BCAST_OK: Several neighbors, must broadcast | |
401 | */ | |
402 | int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, | |
403 | u8 *orig_addr, u8 *orig_neigh) | |
404 | { | |
405 | struct batadv_hardif_neigh_node *hardif_neigh; | |
406 | struct hlist_node *first; | |
407 | int ret = BATADV_HARDIF_BCAST_OK; | |
408 | ||
409 | rcu_read_lock(); | |
410 | ||
411 | /* 0 neighbors -> no (re)broadcast */ | |
412 | first = rcu_dereference(hlist_first_rcu(&if_outgoing->neigh_list)); | |
413 | if (!first) { | |
414 | ret = BATADV_HARDIF_BCAST_NORECIPIENT; | |
415 | goto out; | |
416 | } | |
417 | ||
791ad7f5 | 418 | /* >1 neighbors -> (re)broadcast */ |
3111beed LL |
419 | if (rcu_dereference(hlist_next_rcu(first))) |
420 | goto out; | |
421 | ||
422 | hardif_neigh = hlist_entry(first, struct batadv_hardif_neigh_node, | |
423 | list); | |
424 | ||
425 | /* 1 neighbor, is the originator -> no rebroadcast */ | |
426 | if (orig_addr && batadv_compare_eth(hardif_neigh->orig, orig_addr)) { | |
427 | ret = BATADV_HARDIF_BCAST_DUPORIG; | |
428 | /* 1 neighbor, is the one we received from -> no rebroadcast */ | |
429 | } else if (orig_neigh && | |
430 | batadv_compare_eth(hardif_neigh->orig, orig_neigh)) { | |
431 | ret = BATADV_HARDIF_BCAST_DUPFWD; | |
432 | } | |
433 | ||
434 | out: | |
435 | rcu_read_unlock(); | |
436 | return ret; | |
437 | } | |
438 | ||
56303d34 | 439 | static struct batadv_hard_iface * |
18a1cb6e | 440 | batadv_hardif_get_active(const struct net_device *soft_iface) |
c6c8fea2 | 441 | { |
56303d34 | 442 | struct batadv_hard_iface *hard_iface; |
c6c8fea2 SE |
443 | |
444 | rcu_read_lock(); | |
3193e8fd | 445 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
e6c10f43 | 446 | if (hard_iface->soft_iface != soft_iface) |
c6c8fea2 SE |
447 | continue; |
448 | ||
e9a4f295 | 449 | if (hard_iface->if_status == BATADV_IF_ACTIVE && |
7a659d56 | 450 | kref_get_unless_zero(&hard_iface->refcount)) |
c6c8fea2 SE |
451 | goto out; |
452 | } | |
453 | ||
e6c10f43 | 454 | hard_iface = NULL; |
c6c8fea2 SE |
455 | |
456 | out: | |
c6c8fea2 | 457 | rcu_read_unlock(); |
e6c10f43 | 458 | return hard_iface; |
c6c8fea2 SE |
459 | } |
460 | ||
56303d34 SE |
461 | static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv, |
462 | struct batadv_hard_iface *oldif) | |
c6c8fea2 | 463 | { |
56303d34 | 464 | struct batadv_hard_iface *primary_if; |
32ae9b22 | 465 | |
e5d89254 | 466 | primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b22 ML |
467 | if (!primary_if) |
468 | goto out; | |
c6c8fea2 | 469 | |
785ea114 | 470 | batadv_dat_init_own_addr(bat_priv, primary_if); |
08adf151 | 471 | batadv_bla_update_orig_address(bat_priv, primary_if, oldif); |
32ae9b22 | 472 | out: |
79a0bffb | 473 | batadv_hardif_put(primary_if); |
c6c8fea2 SE |
474 | } |
475 | ||
56303d34 SE |
476 | static void batadv_primary_if_select(struct batadv_priv *bat_priv, |
477 | struct batadv_hard_iface *new_hard_iface) | |
c6c8fea2 | 478 | { |
56303d34 | 479 | struct batadv_hard_iface *curr_hard_iface; |
c6c8fea2 | 480 | |
c3caf519 | 481 | ASSERT_RTNL(); |
c6c8fea2 | 482 | |
17a86915 SE |
483 | if (new_hard_iface) |
484 | kref_get(&new_hard_iface->refcount); | |
c6c8fea2 | 485 | |
cf78bb0b AQ |
486 | curr_hard_iface = rcu_replace_pointer(bat_priv->primary_if, |
487 | new_hard_iface, 1); | |
c6c8fea2 | 488 | |
32ae9b22 | 489 | if (!new_hard_iface) |
23721387 | 490 | goto out; |
32ae9b22 | 491 | |
29824a55 | 492 | bat_priv->algo_ops->iface.primary_set(new_hard_iface); |
18a1cb6e | 493 | batadv_primary_if_update_addr(bat_priv, curr_hard_iface); |
23721387 SW |
494 | |
495 | out: | |
79a0bffb | 496 | batadv_hardif_put(curr_hard_iface); |
c6c8fea2 SE |
497 | } |
498 | ||
56303d34 SE |
499 | static bool |
500 | batadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface) | |
c6c8fea2 | 501 | { |
e6c10f43 | 502 | if (hard_iface->net_dev->flags & IFF_UP) |
c6c8fea2 SE |
503 | return true; |
504 | ||
505 | return false; | |
506 | } | |
507 | ||
18a1cb6e | 508 | static void batadv_check_known_mac_addr(const struct net_device *net_dev) |
c6c8fea2 | 509 | { |
56303d34 | 510 | const struct batadv_hard_iface *hard_iface; |
c6c8fea2 SE |
511 | |
512 | rcu_read_lock(); | |
3193e8fd | 513 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
825ffe1f SE |
514 | if (hard_iface->if_status != BATADV_IF_ACTIVE && |
515 | hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED) | |
c6c8fea2 SE |
516 | continue; |
517 | ||
e6c10f43 | 518 | if (hard_iface->net_dev == net_dev) |
c6c8fea2 SE |
519 | continue; |
520 | ||
1eda58bf SE |
521 | if (!batadv_compare_eth(hard_iface->net_dev->dev_addr, |
522 | net_dev->dev_addr)) | |
c6c8fea2 SE |
523 | continue; |
524 | ||
67969581 SE |
525 | pr_warn("The newly added mac address (%pM) already exists on: %s\n", |
526 | net_dev->dev_addr, hard_iface->net_dev->name); | |
527 | pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n"); | |
c6c8fea2 SE |
528 | } |
529 | rcu_read_unlock(); | |
530 | } | |
531 | ||
7bca68c7 SE |
532 | /** |
533 | * batadv_hardif_recalc_extra_skbroom() - Recalculate skbuff extra head/tailroom | |
534 | * @soft_iface: netdev struct of the mesh interface | |
535 | */ | |
536 | static void batadv_hardif_recalc_extra_skbroom(struct net_device *soft_iface) | |
537 | { | |
538 | const struct batadv_hard_iface *hard_iface; | |
539 | unsigned short lower_header_len = ETH_HLEN; | |
540 | unsigned short lower_headroom = 0; | |
541 | unsigned short lower_tailroom = 0; | |
542 | unsigned short needed_headroom; | |
543 | ||
544 | rcu_read_lock(); | |
545 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { | |
546 | if (hard_iface->if_status == BATADV_IF_NOT_IN_USE) | |
547 | continue; | |
548 | ||
549 | if (hard_iface->soft_iface != soft_iface) | |
550 | continue; | |
551 | ||
552 | lower_header_len = max_t(unsigned short, lower_header_len, | |
553 | hard_iface->net_dev->hard_header_len); | |
554 | ||
555 | lower_headroom = max_t(unsigned short, lower_headroom, | |
556 | hard_iface->net_dev->needed_headroom); | |
557 | ||
558 | lower_tailroom = max_t(unsigned short, lower_tailroom, | |
559 | hard_iface->net_dev->needed_tailroom); | |
560 | } | |
561 | rcu_read_unlock(); | |
562 | ||
563 | needed_headroom = lower_headroom + (lower_header_len - ETH_HLEN); | |
564 | needed_headroom += batadv_max_header_len(); | |
565 | ||
4ca23e2c SE |
566 | /* fragmentation headers don't strip the unicast/... header */ |
567 | needed_headroom += sizeof(struct batadv_frag_packet); | |
568 | ||
7bca68c7 SE |
569 | soft_iface->needed_headroom = needed_headroom; |
570 | soft_iface->needed_tailroom = lower_tailroom; | |
571 | } | |
572 | ||
ff15c27c SE |
573 | /** |
574 | * batadv_hardif_min_mtu() - Calculate maximum MTU for soft interface | |
575 | * @soft_iface: netdev struct of the soft interface | |
576 | * | |
577 | * Return: MTU for the soft-interface (limited by the minimal MTU of all active | |
578 | * slave interfaces) | |
579 | */ | |
9563877e | 580 | int batadv_hardif_min_mtu(struct net_device *soft_iface) |
c6c8fea2 | 581 | { |
a19d3d85 | 582 | struct batadv_priv *bat_priv = netdev_priv(soft_iface); |
56303d34 | 583 | const struct batadv_hard_iface *hard_iface; |
930cd6e4 | 584 | int min_mtu = INT_MAX; |
c6c8fea2 SE |
585 | |
586 | rcu_read_lock(); | |
3193e8fd | 587 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
825ffe1f SE |
588 | if (hard_iface->if_status != BATADV_IF_ACTIVE && |
589 | hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED) | |
c6c8fea2 SE |
590 | continue; |
591 | ||
e6c10f43 | 592 | if (hard_iface->soft_iface != soft_iface) |
c6c8fea2 SE |
593 | continue; |
594 | ||
a19d3d85 | 595 | min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu); |
c6c8fea2 SE |
596 | } |
597 | rcu_read_unlock(); | |
a19d3d85 | 598 | |
a19d3d85 ML |
599 | if (atomic_read(&bat_priv->fragmentation) == 0) |
600 | goto out; | |
601 | ||
602 | /* with fragmentation enabled the maximum size of internally generated | |
603 | * packets such as translation table exchanges or tvlv containers, etc | |
604 | * has to be calculated | |
605 | */ | |
606 | min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE); | |
607 | min_mtu -= sizeof(struct batadv_frag_packet); | |
608 | min_mtu *= BATADV_FRAG_MAX_FRAGMENTS; | |
a19d3d85 | 609 | |
c6c8fea2 | 610 | out: |
930cd6e4 AQ |
611 | /* report to the other components the maximum amount of bytes that |
612 | * batman-adv can send over the wire (without considering the payload | |
613 | * overhead). For example, this value is used by TT to compute the | |
21ba5ab2 | 614 | * maximum local table size |
930cd6e4 AQ |
615 | */ |
616 | atomic_set(&bat_priv->packet_size_max, min_mtu); | |
617 | ||
618 | /* the real soft-interface MTU is computed by removing the payload | |
619 | * overhead from the maximum amount of bytes that was just computed. | |
620 | * | |
621 | * However batman-adv does not support MTUs bigger than ETH_DATA_LEN | |
622 | */ | |
623 | return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN); | |
c6c8fea2 SE |
624 | } |
625 | ||
ff15c27c SE |
626 | /** |
627 | * batadv_update_min_mtu() - Adjusts the MTU if a new interface with a smaller | |
628 | * MTU appeared | |
629 | * @soft_iface: netdev struct of the soft interface | |
630 | */ | |
9563877e | 631 | void batadv_update_min_mtu(struct net_device *soft_iface) |
c6c8fea2 | 632 | { |
a19d3d85 | 633 | soft_iface->mtu = batadv_hardif_min_mtu(soft_iface); |
c6c8fea2 | 634 | |
a19d3d85 ML |
635 | /* Check if the local translate table should be cleaned up to match a |
636 | * new (and smaller) MTU. | |
637 | */ | |
638 | batadv_tt_local_resize_to_mtu(soft_iface); | |
c6c8fea2 SE |
639 | } |
640 | ||
56303d34 SE |
641 | static void |
642 | batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) | |
c6c8fea2 | 643 | { |
56303d34 SE |
644 | struct batadv_priv *bat_priv; |
645 | struct batadv_hard_iface *primary_if = NULL; | |
c6c8fea2 | 646 | |
e9a4f295 | 647 | if (hard_iface->if_status != BATADV_IF_INACTIVE) |
32ae9b22 | 648 | goto out; |
c6c8fea2 | 649 | |
e6c10f43 | 650 | bat_priv = netdev_priv(hard_iface->soft_iface); |
c6c8fea2 | 651 | |
29824a55 | 652 | bat_priv->algo_ops->iface.update_mac(hard_iface); |
e9a4f295 | 653 | hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED; |
c6c8fea2 | 654 | |
9cfc7bd6 | 655 | /* the first active interface becomes our primary interface or |
015758d0 | 656 | * the next active interface after the old primary interface was removed |
c6c8fea2 | 657 | */ |
e5d89254 | 658 | primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b22 | 659 | if (!primary_if) |
18a1cb6e | 660 | batadv_primary_if_select(bat_priv, hard_iface); |
c6c8fea2 | 661 | |
3e34819e SE |
662 | batadv_info(hard_iface->soft_iface, "Interface activated: %s\n", |
663 | hard_iface->net_dev->name); | |
c6c8fea2 | 664 | |
9563877e | 665 | batadv_update_min_mtu(hard_iface->soft_iface); |
32ae9b22 | 666 | |
29824a55 AQ |
667 | if (bat_priv->algo_ops->iface.activate) |
668 | bat_priv->algo_ops->iface.activate(hard_iface); | |
b6cf5d49 | 669 | |
32ae9b22 | 670 | out: |
79a0bffb | 671 | batadv_hardif_put(primary_if); |
c6c8fea2 SE |
672 | } |
673 | ||
56303d34 SE |
674 | static void |
675 | batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface) | |
c6c8fea2 | 676 | { |
825ffe1f SE |
677 | if (hard_iface->if_status != BATADV_IF_ACTIVE && |
678 | hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED) | |
c6c8fea2 SE |
679 | return; |
680 | ||
e9a4f295 | 681 | hard_iface->if_status = BATADV_IF_INACTIVE; |
c6c8fea2 | 682 | |
3e34819e SE |
683 | batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n", |
684 | hard_iface->net_dev->name); | |
c6c8fea2 | 685 | |
9563877e | 686 | batadv_update_min_mtu(hard_iface->soft_iface); |
c6c8fea2 SE |
687 | } |
688 | ||
ff15c27c SE |
689 | /** |
690 | * batadv_hardif_enable_interface() - Enslave hard interface to soft interface | |
691 | * @hard_iface: hard interface to add to soft interface | |
fa205602 | 692 | * @soft_iface: netdev struct of the mesh interface |
ff15c27c SE |
693 | * |
694 | * Return: 0 on success or negative error number in case of failure | |
695 | */ | |
56303d34 | 696 | int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, |
fa205602 | 697 | struct net_device *soft_iface) |
c6c8fea2 | 698 | { |
56303d34 | 699 | struct batadv_priv *bat_priv; |
293e9338 | 700 | __be16 ethertype = htons(ETH_P_BATMAN); |
411d6ed9 | 701 | int max_header_len = batadv_max_header_len(); |
e44d8fe2 | 702 | int ret; |
c6c8fea2 | 703 | |
b1cb8a71 SY |
704 | if (hard_iface->net_dev->mtu < ETH_MIN_MTU + max_header_len) |
705 | return -EINVAL; | |
706 | ||
e9a4f295 | 707 | if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
c6c8fea2 SE |
708 | goto out; |
709 | ||
17a86915 | 710 | kref_get(&hard_iface->refcount); |
ed75ccbe | 711 | |
fa205602 | 712 | dev_hold(soft_iface); |
e44d8fe2 | 713 | hard_iface->soft_iface = soft_iface; |
e6c10f43 | 714 | bat_priv = netdev_priv(hard_iface->soft_iface); |
d0b9fd89 | 715 | |
6dffb044 | 716 | ret = netdev_master_upper_dev_link(hard_iface->net_dev, |
42ab19ee | 717 | soft_iface, NULL, NULL, NULL); |
3dbd550b SE |
718 | if (ret) |
719 | goto err_dev; | |
720 | ||
29824a55 | 721 | ret = bat_priv->algo_ops->iface.enable(hard_iface); |
5346c35e | 722 | if (ret < 0) |
3dbd550b | 723 | goto err_upper; |
c6c8fea2 | 724 | |
e9a4f295 | 725 | hard_iface->if_status = BATADV_IF_INACTIVE; |
c6c8fea2 | 726 | |
d7d6de95 | 727 | kref_get(&hard_iface->refcount); |
7e071c79 | 728 | hard_iface->batman_adv_ptype.type = ethertype; |
3193e8fd | 729 | hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv; |
e6c10f43 ML |
730 | hard_iface->batman_adv_ptype.dev = hard_iface->net_dev; |
731 | dev_add_pack(&hard_iface->batman_adv_ptype); | |
c6c8fea2 | 732 | |
3e34819e SE |
733 | batadv_info(hard_iface->soft_iface, "Adding interface: %s\n", |
734 | hard_iface->net_dev->name); | |
c6c8fea2 | 735 | |
0aca2369 | 736 | if (atomic_read(&bat_priv->fragmentation) && |
411d6ed9 | 737 | hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len) |
3e34819e | 738 | batadv_info(hard_iface->soft_iface, |
411d6ed9 | 739 | "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n", |
3e34819e | 740 | hard_iface->net_dev->name, hard_iface->net_dev->mtu, |
411d6ed9 | 741 | ETH_DATA_LEN + max_header_len); |
c6c8fea2 | 742 | |
0aca2369 | 743 | if (!atomic_read(&bat_priv->fragmentation) && |
411d6ed9 | 744 | hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len) |
3e34819e | 745 | batadv_info(hard_iface->soft_iface, |
411d6ed9 | 746 | "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n", |
3e34819e | 747 | hard_iface->net_dev->name, hard_iface->net_dev->mtu, |
411d6ed9 | 748 | ETH_DATA_LEN + max_header_len); |
c6c8fea2 | 749 | |
18a1cb6e SE |
750 | if (batadv_hardif_is_iface_up(hard_iface)) |
751 | batadv_hardif_activate_interface(hard_iface); | |
c6c8fea2 | 752 | else |
3e34819e SE |
753 | batadv_err(hard_iface->soft_iface, |
754 | "Not using interface %s (retrying later): interface not active\n", | |
755 | hard_iface->net_dev->name); | |
c6c8fea2 | 756 | |
7bca68c7 SE |
757 | batadv_hardif_recalc_extra_skbroom(soft_iface); |
758 | ||
9e6b5648 SE |
759 | if (bat_priv->algo_ops->iface.enabled) |
760 | bat_priv->algo_ops->iface.enabled(hard_iface); | |
761 | ||
c6c8fea2 SE |
762 | out: |
763 | return 0; | |
764 | ||
3dbd550b SE |
765 | err_upper: |
766 | netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface); | |
77af7575 | 767 | err_dev: |
3dbd550b | 768 | hard_iface->soft_iface = NULL; |
77af7575 | 769 | dev_put(soft_iface); |
82047ad7 | 770 | batadv_hardif_put(hard_iface); |
e44d8fe2 | 771 | return ret; |
c6c8fea2 SE |
772 | } |
773 | ||
dee222c7 SE |
774 | /** |
775 | * batadv_hardif_cnt() - get number of interfaces enslaved to soft interface | |
776 | * @soft_iface: soft interface to check | |
777 | * | |
778 | * This function is only using RCU for locking - the result can therefore be | |
bccb48c8 | 779 | * off when another function is modifying the list at the same time. The |
dee222c7 SE |
780 | * caller can use the rtnl_lock to make sure that the count is accurate. |
781 | * | |
782 | * Return: number of connected/enslaved hard interfaces | |
783 | */ | |
784 | static size_t batadv_hardif_cnt(const struct net_device *soft_iface) | |
785 | { | |
786 | struct batadv_hard_iface *hard_iface; | |
787 | size_t count = 0; | |
788 | ||
789 | rcu_read_lock(); | |
790 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { | |
791 | if (hard_iface->soft_iface != soft_iface) | |
792 | continue; | |
793 | ||
794 | count++; | |
795 | } | |
796 | rcu_read_unlock(); | |
797 | ||
798 | return count; | |
799 | } | |
800 | ||
ff15c27c SE |
801 | /** |
802 | * batadv_hardif_disable_interface() - Remove hard interface from soft interface | |
803 | * @hard_iface: hard interface to be removed | |
ff15c27c | 804 | */ |
a962cb29 | 805 | void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) |
c6c8fea2 | 806 | { |
56303d34 SE |
807 | struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); |
808 | struct batadv_hard_iface *primary_if = NULL; | |
c6c8fea2 | 809 | |
f2d23861 | 810 | batadv_hardif_deactivate_interface(hard_iface); |
c6c8fea2 | 811 | |
e9a4f295 | 812 | if (hard_iface->if_status != BATADV_IF_INACTIVE) |
32ae9b22 | 813 | goto out; |
c6c8fea2 | 814 | |
3e34819e SE |
815 | batadv_info(hard_iface->soft_iface, "Removing interface: %s\n", |
816 | hard_iface->net_dev->name); | |
e6c10f43 | 817 | dev_remove_pack(&hard_iface->batman_adv_ptype); |
d7d6de95 | 818 | batadv_hardif_put(hard_iface); |
c6c8fea2 | 819 | |
e5d89254 | 820 | primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b22 | 821 | if (hard_iface == primary_if) { |
56303d34 | 822 | struct batadv_hard_iface *new_if; |
c6c8fea2 | 823 | |
18a1cb6e SE |
824 | new_if = batadv_hardif_get_active(hard_iface->soft_iface); |
825 | batadv_primary_if_select(bat_priv, new_if); | |
c6c8fea2 | 826 | |
79a0bffb | 827 | batadv_hardif_put(new_if); |
c6c8fea2 SE |
828 | } |
829 | ||
29824a55 | 830 | bat_priv->algo_ops->iface.disable(hard_iface); |
e9a4f295 | 831 | hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
c6c8fea2 | 832 | |
e6c10f43 | 833 | /* delete all references to this hard_iface */ |
7d211efc | 834 | batadv_purge_orig_ref(bat_priv); |
9455e34c | 835 | batadv_purge_outstanding_packets(bat_priv, hard_iface); |
e6c10f43 | 836 | dev_put(hard_iface->soft_iface); |
c6c8fea2 | 837 | |
a5256f7e | 838 | netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface); |
7bca68c7 | 839 | batadv_hardif_recalc_extra_skbroom(hard_iface->soft_iface); |
a5256f7e | 840 | |
c6c8fea2 | 841 | /* nobody uses this interface anymore */ |
76e9f276 | 842 | if (batadv_hardif_cnt(hard_iface->soft_iface) <= 1) |
8257f55a AQ |
843 | batadv_gw_check_client_stop(bat_priv); |
844 | ||
27915aa6 | 845 | hard_iface->soft_iface = NULL; |
82047ad7 | 846 | batadv_hardif_put(hard_iface); |
32ae9b22 ML |
847 | |
848 | out: | |
79a0bffb | 849 | batadv_hardif_put(primary_if); |
c6c8fea2 SE |
850 | } |
851 | ||
56303d34 | 852 | static struct batadv_hard_iface * |
18a1cb6e | 853 | batadv_hardif_add_interface(struct net_device *net_dev) |
c6c8fea2 | 854 | { |
56303d34 | 855 | struct batadv_hard_iface *hard_iface; |
c6c8fea2 | 856 | |
c3caf519 SE |
857 | ASSERT_RTNL(); |
858 | ||
4b426b10 | 859 | if (!batadv_is_valid_iface(net_dev)) |
c6c8fea2 SE |
860 | goto out; |
861 | ||
862 | dev_hold(net_dev); | |
863 | ||
7db3fc29 | 864 | hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC); |
320f422f | 865 | if (!hard_iface) |
c6c8fea2 | 866 | goto release_dev; |
c6c8fea2 | 867 | |
e6c10f43 ML |
868 | hard_iface->net_dev = net_dev; |
869 | hard_iface->soft_iface = NULL; | |
e9a4f295 | 870 | hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
5bc7c1eb | 871 | |
e6c10f43 | 872 | INIT_LIST_HEAD(&hard_iface->list); |
cef63419 | 873 | INIT_HLIST_HEAD(&hard_iface->neigh_list); |
5bc44dc8 | 874 | |
40e220b4 | 875 | mutex_init(&hard_iface->bat_iv.ogm_buff_mutex); |
cef63419 | 876 | spin_lock_init(&hard_iface->neigh_list_lock); |
b2367e46 | 877 | kref_init(&hard_iface->refcount); |
cef63419 | 878 | |
caf65bfc | 879 | hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; |
10b1bbb4 SE |
880 | hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); |
881 | if (batadv_is_wifi_hardif(hard_iface)) | |
caf65bfc MS |
882 | hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; |
883 | ||
3bda14d0 LL |
884 | atomic_set(&hard_iface->hop_penalty, 0); |
885 | ||
7db682d1 ML |
886 | batadv_v_hardif_init(hard_iface); |
887 | ||
18a1cb6e | 888 | batadv_check_known_mac_addr(hard_iface->net_dev); |
b2367e46 | 889 | kref_get(&hard_iface->refcount); |
3193e8fd | 890 | list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list); |
fb69be69 | 891 | batadv_hardif_generation++; |
c6c8fea2 | 892 | |
e6c10f43 | 893 | return hard_iface; |
c6c8fea2 | 894 | |
c6c8fea2 SE |
895 | release_dev: |
896 | dev_put(net_dev); | |
897 | out: | |
898 | return NULL; | |
899 | } | |
900 | ||
56303d34 | 901 | static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface) |
c6c8fea2 | 902 | { |
c3caf519 SE |
903 | ASSERT_RTNL(); |
904 | ||
c6c8fea2 | 905 | /* first deactivate interface */ |
e9a4f295 | 906 | if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
a962cb29 | 907 | batadv_hardif_disable_interface(hard_iface); |
c6c8fea2 | 908 | |
e9a4f295 | 909 | if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
c6c8fea2 SE |
910 | return; |
911 | ||
e9a4f295 | 912 | hard_iface->if_status = BATADV_IF_TO_BE_REMOVED; |
569c9850 | 913 | batadv_hardif_put(hard_iface); |
c6c8fea2 SE |
914 | } |
915 | ||
6da7be7d SE |
916 | /** |
917 | * batadv_hard_if_event_softif() - Handle events for soft interfaces | |
918 | * @event: NETDEV_* event to handle | |
919 | * @net_dev: net_device which generated an event | |
920 | * | |
921 | * Return: NOTIFY_* result | |
922 | */ | |
923 | static int batadv_hard_if_event_softif(unsigned long event, | |
924 | struct net_device *net_dev) | |
925 | { | |
926 | struct batadv_priv *bat_priv; | |
927 | ||
928 | switch (event) { | |
929 | case NETDEV_REGISTER: | |
6da7be7d SE |
930 | bat_priv = netdev_priv(net_dev); |
931 | batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS); | |
932 | break; | |
6da7be7d SE |
933 | } |
934 | ||
935 | return NOTIFY_DONE; | |
936 | } | |
937 | ||
18a1cb6e SE |
938 | static int batadv_hard_if_event(struct notifier_block *this, |
939 | unsigned long event, void *ptr) | |
c6c8fea2 | 940 | { |
351638e7 | 941 | struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); |
56303d34 SE |
942 | struct batadv_hard_iface *hard_iface; |
943 | struct batadv_hard_iface *primary_if = NULL; | |
944 | struct batadv_priv *bat_priv; | |
c6c8fea2 | 945 | |
6da7be7d SE |
946 | if (batadv_softif_is_valid(net_dev)) |
947 | return batadv_hard_if_event_softif(event, net_dev); | |
37130293 | 948 | |
56303d34 | 949 | hard_iface = batadv_hardif_get_by_netdev(net_dev); |
a1a66b11 AL |
950 | if (!hard_iface && (event == NETDEV_REGISTER || |
951 | event == NETDEV_POST_TYPE_CHANGE)) | |
18a1cb6e | 952 | hard_iface = batadv_hardif_add_interface(net_dev); |
c6c8fea2 | 953 | |
e6c10f43 | 954 | if (!hard_iface) |
c6c8fea2 SE |
955 | goto out; |
956 | ||
957 | switch (event) { | |
958 | case NETDEV_UP: | |
18a1cb6e | 959 | batadv_hardif_activate_interface(hard_iface); |
c6c8fea2 SE |
960 | break; |
961 | case NETDEV_GOING_DOWN: | |
962 | case NETDEV_DOWN: | |
18a1cb6e | 963 | batadv_hardif_deactivate_interface(hard_iface); |
c6c8fea2 SE |
964 | break; |
965 | case NETDEV_UNREGISTER: | |
a1a66b11 | 966 | case NETDEV_PRE_TYPE_CHANGE: |
e6c10f43 | 967 | list_del_rcu(&hard_iface->list); |
fb69be69 | 968 | batadv_hardif_generation++; |
c6c8fea2 | 969 | |
18a1cb6e | 970 | batadv_hardif_remove_interface(hard_iface); |
c6c8fea2 SE |
971 | break; |
972 | case NETDEV_CHANGEMTU: | |
e6c10f43 | 973 | if (hard_iface->soft_iface) |
9563877e | 974 | batadv_update_min_mtu(hard_iface->soft_iface); |
c6c8fea2 SE |
975 | break; |
976 | case NETDEV_CHANGEADDR: | |
e9a4f295 | 977 | if (hard_iface->if_status == BATADV_IF_NOT_IN_USE) |
c6c8fea2 SE |
978 | goto hardif_put; |
979 | ||
18a1cb6e | 980 | batadv_check_known_mac_addr(hard_iface->net_dev); |
c6c8fea2 | 981 | |
e6c10f43 | 982 | bat_priv = netdev_priv(hard_iface->soft_iface); |
29824a55 | 983 | bat_priv->algo_ops->iface.update_mac(hard_iface); |
01c4224b | 984 | |
e5d89254 | 985 | primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b22 ML |
986 | if (!primary_if) |
987 | goto hardif_put; | |
988 | ||
989 | if (hard_iface == primary_if) | |
18a1cb6e | 990 | batadv_primary_if_update_addr(bat_priv, NULL); |
c6c8fea2 | 991 | break; |
ee3b5e9f SE |
992 | case NETDEV_CHANGEUPPER: |
993 | hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); | |
994 | if (batadv_is_wifi_hardif(hard_iface)) | |
995 | hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; | |
996 | break; | |
c6c8fea2 SE |
997 | default: |
998 | break; | |
f81c6224 | 999 | } |
c6c8fea2 SE |
1000 | |
1001 | hardif_put: | |
82047ad7 | 1002 | batadv_hardif_put(hard_iface); |
c6c8fea2 | 1003 | out: |
79a0bffb | 1004 | batadv_hardif_put(primary_if); |
c6c8fea2 SE |
1005 | return NOTIFY_DONE; |
1006 | } | |
1007 | ||
9563877e | 1008 | struct notifier_block batadv_hard_if_notifier = { |
18a1cb6e | 1009 | .notifier_call = batadv_hard_if_event, |
c6c8fea2 | 1010 | }; |