Blame view
net/batman-adv/hard-interface.c
19.2 KB
e19f9759e
|
1 |
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: |
c6c8fea29
|
2 3 4 5 6 7 8 9 10 11 12 13 14 |
* * Marek Lindner, Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License |
ebf38fb7a
|
15 |
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
c6c8fea29
|
16 17 18 |
*/ #include "main.h" |
785ea1144
|
19 |
#include "distributed-arp-table.h" |
c6c8fea29
|
20 21 22 23 24 |
#include "hard-interface.h" #include "soft-interface.h" #include "send.h" #include "translation-table.h" #include "routing.h" |
b706b13b6
|
25 |
#include "sysfs.h" |
5bc7c1eb4
|
26 |
#include "debugfs.h" |
c6c8fea29
|
27 28 |
#include "originator.h" #include "hash.h" |
23721387c
|
29 |
#include "bridge_loop_avoidance.h" |
8257f55ae
|
30 |
#include "gateway_client.h" |
c6c8fea29
|
31 32 |
#include <linux/if_arp.h> |
af5d4f773
|
33 |
#include <linux/if_ether.h> |
c6c8fea29
|
34 |
|
9563877ea
|
35 |
void batadv_hardif_free_rcu(struct rcu_head *rcu) |
c6c8fea29
|
36 |
{ |
56303d34a
|
37 |
struct batadv_hard_iface *hard_iface; |
c6c8fea29
|
38 |
|
56303d34a
|
39 |
hard_iface = container_of(rcu, struct batadv_hard_iface, rcu); |
e6c10f433
|
40 41 |
dev_put(hard_iface->net_dev); kfree(hard_iface); |
c6c8fea29
|
42 |
} |
56303d34a
|
43 44 |
struct batadv_hard_iface * batadv_hardif_get_by_netdev(const struct net_device *net_dev) |
c6c8fea29
|
45 |
{ |
56303d34a
|
46 |
struct batadv_hard_iface *hard_iface; |
c6c8fea29
|
47 48 |
rcu_read_lock(); |
3193e8fdf
|
49 |
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
e6c10f433
|
50 51 |
if (hard_iface->net_dev == net_dev && atomic_inc_not_zero(&hard_iface->refcount)) |
c6c8fea29
|
52 53 |
goto out; } |
e6c10f433
|
54 |
hard_iface = NULL; |
c6c8fea29
|
55 56 |
out: |
c6c8fea29
|
57 |
rcu_read_unlock(); |
e6c10f433
|
58 |
return hard_iface; |
c6c8fea29
|
59 |
} |
b7eddd0b3
|
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
/** * batadv_is_on_batman_iface - check if a device is a batman iface descendant * @net_dev: the device to check * * If the user creates any virtual device on top of a batman-adv interface, it * is important to prevent this new interface to be used to create a new mesh * network (this behaviour would lead to a batman-over-batman configuration). * This function recursively checks all the fathers of the device passed as * argument looking for a batman-adv soft interface. * * Returns true if the device is descendant of a batman-adv mesh interface (or * if it is a batman-adv interface itself), false otherwise */ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) { struct net_device *parent_dev; bool ret; /* check if this is a batman-adv mesh interface */ if (batadv_softif_is_valid(net_dev)) return true; /* no more parents..stop recursion */ |
b6ed54986
|
83 |
if (net_dev->iflink == 0 || net_dev->iflink == net_dev->ifindex) |
b7eddd0b3
|
84 85 86 |
return false; /* recurse over the parent device */ |
16b77695e
|
87 |
parent_dev = __dev_get_by_index(&init_net, net_dev->iflink); |
b7eddd0b3
|
88 89 90 91 92 |
/* if we got a NULL parent_dev there is something broken.. */ if (WARN(!parent_dev, "Cannot find parent device")) return false; ret = batadv_is_on_batman_iface(parent_dev); |
b7eddd0b3
|
93 94 |
return ret; } |
18a1cb6ee
|
95 |
static int batadv_is_valid_iface(const struct net_device *net_dev) |
c6c8fea29
|
96 97 98 99 100 101 102 103 104 105 106 |
{ if (net_dev->flags & IFF_LOOPBACK) return 0; if (net_dev->type != ARPHRD_ETHER) return 0; if (net_dev->addr_len != ETH_ALEN) return 0; /* no batman over batman */ |
b7eddd0b3
|
107 |
if (batadv_is_on_batman_iface(net_dev)) |
c6c8fea29
|
108 |
return 0; |
c6c8fea29
|
109 |
|
c6c8fea29
|
110 111 |
return 1; } |
de68d1003
|
112 113 114 115 116 117 118 |
/** * batadv_is_wifi_netdev - check if the given net_device struct is a wifi * interface * @net_device: the device to check * * Returns true if the net device is a 802.11 wireless device, false otherwise. */ |
0c69aecc5
|
119 |
bool batadv_is_wifi_netdev(struct net_device *net_device) |
de68d1003
|
120 |
{ |
0c69aecc5
|
121 122 |
if (!net_device) return false; |
de68d1003
|
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#ifdef CONFIG_WIRELESS_EXT /* pre-cfg80211 drivers have to implement WEXT, so it is possible to * check for wireless_handlers != NULL */ if (net_device->wireless_handlers) return true; #endif /* cfg80211 drivers have to set ieee80211_ptr */ if (net_device->ieee80211_ptr) return true; return false; } |
56303d34a
|
137 |
static struct batadv_hard_iface * |
18a1cb6ee
|
138 |
batadv_hardif_get_active(const struct net_device *soft_iface) |
c6c8fea29
|
139 |
{ |
56303d34a
|
140 |
struct batadv_hard_iface *hard_iface; |
c6c8fea29
|
141 142 |
rcu_read_lock(); |
3193e8fdf
|
143 |
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
e6c10f433
|
144 |
if (hard_iface->soft_iface != soft_iface) |
c6c8fea29
|
145 |
continue; |
e9a4f295e
|
146 |
if (hard_iface->if_status == BATADV_IF_ACTIVE && |
e6c10f433
|
147 |
atomic_inc_not_zero(&hard_iface->refcount)) |
c6c8fea29
|
148 149 |
goto out; } |
e6c10f433
|
150 |
hard_iface = NULL; |
c6c8fea29
|
151 152 |
out: |
c6c8fea29
|
153 |
rcu_read_unlock(); |
e6c10f433
|
154 |
return hard_iface; |
c6c8fea29
|
155 |
} |
56303d34a
|
156 157 |
static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv, struct batadv_hard_iface *oldif) |
c6c8fea29
|
158 |
{ |
56303d34a
|
159 |
struct batadv_hard_iface *primary_if; |
32ae9b221
|
160 |
|
e5d89254b
|
161 |
primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b221
|
162 163 |
if (!primary_if) goto out; |
c6c8fea29
|
164 |
|
785ea1144
|
165 |
batadv_dat_init_own_addr(bat_priv, primary_if); |
08adf1512
|
166 |
batadv_bla_update_orig_address(bat_priv, primary_if, oldif); |
32ae9b221
|
167 168 |
out: if (primary_if) |
e5d89254b
|
169 |
batadv_hardif_free_ref(primary_if); |
c6c8fea29
|
170 |
} |
56303d34a
|
171 172 |
static void batadv_primary_if_select(struct batadv_priv *bat_priv, struct batadv_hard_iface *new_hard_iface) |
c6c8fea29
|
173 |
{ |
56303d34a
|
174 |
struct batadv_hard_iface *curr_hard_iface; |
c6c8fea29
|
175 |
|
c3caf5196
|
176 |
ASSERT_RTNL(); |
c6c8fea29
|
177 |
|
32ae9b221
|
178 179 |
if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount)) new_hard_iface = NULL; |
c6c8fea29
|
180 |
|
728cbc6ac
|
181 |
curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1); |
32ae9b221
|
182 |
rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); |
c6c8fea29
|
183 |
|
32ae9b221
|
184 |
if (!new_hard_iface) |
23721387c
|
185 |
goto out; |
32ae9b221
|
186 |
|
cd8b78e7e
|
187 |
bat_priv->bat_algo_ops->bat_primary_iface_set(new_hard_iface); |
18a1cb6ee
|
188 |
batadv_primary_if_update_addr(bat_priv, curr_hard_iface); |
23721387c
|
189 190 191 |
out: if (curr_hard_iface) |
e5d89254b
|
192 |
batadv_hardif_free_ref(curr_hard_iface); |
c6c8fea29
|
193 |
} |
56303d34a
|
194 195 |
static bool batadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface) |
c6c8fea29
|
196 |
{ |
e6c10f433
|
197 |
if (hard_iface->net_dev->flags & IFF_UP) |
c6c8fea29
|
198 199 200 201 |
return true; return false; } |
18a1cb6ee
|
202 |
static void batadv_check_known_mac_addr(const struct net_device *net_dev) |
c6c8fea29
|
203 |
{ |
56303d34a
|
204 |
const struct batadv_hard_iface *hard_iface; |
c6c8fea29
|
205 206 |
rcu_read_lock(); |
3193e8fdf
|
207 |
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
e9a4f295e
|
208 209 |
if ((hard_iface->if_status != BATADV_IF_ACTIVE) && (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)) |
c6c8fea29
|
210 |
continue; |
e6c10f433
|
211 |
if (hard_iface->net_dev == net_dev) |
c6c8fea29
|
212 |
continue; |
1eda58bfc
|
213 214 |
if (!batadv_compare_eth(hard_iface->net_dev->dev_addr, net_dev->dev_addr)) |
c6c8fea29
|
215 |
continue; |
679695813
|
216 217 218 219 220 |
pr_warn("The newly added mac address (%pM) already exists on: %s ", net_dev->dev_addr, hard_iface->net_dev->name); pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems! "); |
c6c8fea29
|
221 222 223 |
} rcu_read_unlock(); } |
9563877ea
|
224 |
int batadv_hardif_min_mtu(struct net_device *soft_iface) |
c6c8fea29
|
225 |
{ |
a19d3d85e
|
226 |
struct batadv_priv *bat_priv = netdev_priv(soft_iface); |
56303d34a
|
227 |
const struct batadv_hard_iface *hard_iface; |
930cd6e46
|
228 |
int min_mtu = INT_MAX; |
c6c8fea29
|
229 230 |
rcu_read_lock(); |
3193e8fdf
|
231 |
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
e9a4f295e
|
232 233 |
if ((hard_iface->if_status != BATADV_IF_ACTIVE) && (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)) |
c6c8fea29
|
234 |
continue; |
e6c10f433
|
235 |
if (hard_iface->soft_iface != soft_iface) |
c6c8fea29
|
236 |
continue; |
a19d3d85e
|
237 |
min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu); |
c6c8fea29
|
238 239 |
} rcu_read_unlock(); |
a19d3d85e
|
240 |
|
a19d3d85e
|
241 242 243 244 245 246 247 248 249 250 |
if (atomic_read(&bat_priv->fragmentation) == 0) goto out; /* with fragmentation enabled the maximum size of internally generated * packets such as translation table exchanges or tvlv containers, etc * has to be calculated */ min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE); min_mtu -= sizeof(struct batadv_frag_packet); min_mtu *= BATADV_FRAG_MAX_FRAGMENTS; |
a19d3d85e
|
251 |
|
c6c8fea29
|
252 |
out: |
930cd6e46
|
253 254 255 256 257 258 259 260 261 262 263 264 265 |
/* report to the other components the maximum amount of bytes that * batman-adv can send over the wire (without considering the payload * overhead). For example, this value is used by TT to compute the * maximum local table table size */ atomic_set(&bat_priv->packet_size_max, min_mtu); /* the real soft-interface MTU is computed by removing the payload * overhead from the maximum amount of bytes that was just computed. * * However batman-adv does not support MTUs bigger than ETH_DATA_LEN */ return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN); |
c6c8fea29
|
266 267 268 |
} /* adjusts the MTU if a new interface with a smaller MTU appeared. */ |
9563877ea
|
269 |
void batadv_update_min_mtu(struct net_device *soft_iface) |
c6c8fea29
|
270 |
{ |
a19d3d85e
|
271 |
soft_iface->mtu = batadv_hardif_min_mtu(soft_iface); |
c6c8fea29
|
272 |
|
a19d3d85e
|
273 274 275 276 |
/* Check if the local translate table should be cleaned up to match a * new (and smaller) MTU. */ batadv_tt_local_resize_to_mtu(soft_iface); |
c6c8fea29
|
277 |
} |
56303d34a
|
278 279 |
static void batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) |
c6c8fea29
|
280 |
{ |
56303d34a
|
281 282 |
struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL; |
c6c8fea29
|
283 |
|
e9a4f295e
|
284 |
if (hard_iface->if_status != BATADV_IF_INACTIVE) |
32ae9b221
|
285 |
goto out; |
c6c8fea29
|
286 |
|
e6c10f433
|
287 |
bat_priv = netdev_priv(hard_iface->soft_iface); |
c6c8fea29
|
288 |
|
c32293983
|
289 |
bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface); |
e9a4f295e
|
290 |
hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED; |
c6c8fea29
|
291 |
|
9cfc7bd60
|
292 |
/* the first active interface becomes our primary interface or |
015758d00
|
293 |
* the next active interface after the old primary interface was removed |
c6c8fea29
|
294 |
*/ |
e5d89254b
|
295 |
primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b221
|
296 |
if (!primary_if) |
18a1cb6ee
|
297 |
batadv_primary_if_select(bat_priv, hard_iface); |
c6c8fea29
|
298 |
|
3e34819e0
|
299 300 301 |
batadv_info(hard_iface->soft_iface, "Interface activated: %s ", hard_iface->net_dev->name); |
c6c8fea29
|
302 |
|
9563877ea
|
303 |
batadv_update_min_mtu(hard_iface->soft_iface); |
32ae9b221
|
304 305 306 |
out: if (primary_if) |
e5d89254b
|
307 |
batadv_hardif_free_ref(primary_if); |
c6c8fea29
|
308 |
} |
56303d34a
|
309 310 |
static void batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface) |
c6c8fea29
|
311 |
{ |
e9a4f295e
|
312 313 |
if ((hard_iface->if_status != BATADV_IF_ACTIVE) && (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)) |
c6c8fea29
|
314 |
return; |
e9a4f295e
|
315 |
hard_iface->if_status = BATADV_IF_INACTIVE; |
c6c8fea29
|
316 |
|
3e34819e0
|
317 318 319 |
batadv_info(hard_iface->soft_iface, "Interface deactivated: %s ", hard_iface->net_dev->name); |
c6c8fea29
|
320 |
|
9563877ea
|
321 |
batadv_update_min_mtu(hard_iface->soft_iface); |
c6c8fea29
|
322 |
} |
cb4b0d486
|
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
/** * batadv_master_del_slave - remove hard_iface from the current master interface * @slave: the interface enslaved in another master * @master: the master from which slave has to be removed * * Invoke ndo_del_slave on master passing slave as argument. In this way slave * is free'd and master can correctly change its internal state. * Return 0 on success, a negative value representing the error otherwise */ static int batadv_master_del_slave(struct batadv_hard_iface *slave, struct net_device *master) { int ret; if (!master) return 0; ret = -EBUSY; if (master->netdev_ops->ndo_del_slave) ret = master->netdev_ops->ndo_del_slave(master, slave->net_dev); return ret; } |
56303d34a
|
346 |
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, |
9563877ea
|
347 |
const char *iface_name) |
c6c8fea29
|
348 |
{ |
56303d34a
|
349 |
struct batadv_priv *bat_priv; |
cb4b0d486
|
350 |
struct net_device *soft_iface, *master; |
293e93385
|
351 |
__be16 ethertype = htons(ETH_P_BATMAN); |
411d6ed93
|
352 |
int max_header_len = batadv_max_header_len(); |
e44d8fe2b
|
353 |
int ret; |
c6c8fea29
|
354 |
|
e9a4f295e
|
355 |
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
c6c8fea29
|
356 |
goto out; |
e6c10f433
|
357 |
if (!atomic_inc_not_zero(&hard_iface->refcount)) |
ed75ccbe2
|
358 |
goto out; |
e44d8fe2b
|
359 |
soft_iface = dev_get_by_name(&init_net, iface_name); |
c6c8fea29
|
360 |
|
e44d8fe2b
|
361 |
if (!soft_iface) { |
04b482a21
|
362 |
soft_iface = batadv_softif_create(iface_name); |
c6c8fea29
|
363 |
|
e44d8fe2b
|
364 365 |
if (!soft_iface) { ret = -ENOMEM; |
c6c8fea29
|
366 |
goto err; |
e44d8fe2b
|
367 |
} |
c6c8fea29
|
368 369 |
/* dev_get_by_name() increases the reference counter for us */ |
e44d8fe2b
|
370 371 |
dev_hold(soft_iface); } |
04b482a21
|
372 |
if (!batadv_softif_is_valid(soft_iface)) { |
86ceb3605
|
373 374 |
pr_err("Can't create batman mesh interface %s: already exists as regular interface ", |
e44d8fe2b
|
375 |
soft_iface->name); |
e44d8fe2b
|
376 |
ret = -EINVAL; |
77af7575c
|
377 |
goto err_dev; |
c6c8fea29
|
378 |
} |
cb4b0d486
|
379 380 381 382 383 384 385 |
/* check if the interface is enslaved in another virtual one and * in that case unlink it first */ master = netdev_master_upper_dev_get(hard_iface->net_dev); ret = batadv_master_del_slave(hard_iface, master); if (ret) goto err_dev; |
e44d8fe2b
|
386 |
hard_iface->soft_iface = soft_iface; |
e6c10f433
|
387 |
bat_priv = netdev_priv(hard_iface->soft_iface); |
d0b9fd89c
|
388 |
|
3dbd550b8
|
389 390 391 |
ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface); if (ret) goto err_dev; |
77af7575c
|
392 |
ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface); |
5346c35eb
|
393 |
if (ret < 0) |
3dbd550b8
|
394 |
goto err_upper; |
c6c8fea29
|
395 |
|
e6c10f433
|
396 |
hard_iface->if_num = bat_priv->num_ifaces; |
c6c8fea29
|
397 |
bat_priv->num_ifaces++; |
e9a4f295e
|
398 |
hard_iface->if_status = BATADV_IF_INACTIVE; |
624463079
|
399 400 401 402 403 |
ret = batadv_orig_hash_add_if(hard_iface, bat_priv->num_ifaces); if (ret < 0) { bat_priv->bat_algo_ops->bat_iface_disable(hard_iface); bat_priv->num_ifaces--; hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
3dbd550b8
|
404 |
goto err_upper; |
624463079
|
405 |
} |
c6c8fea29
|
406 |
|
7e071c79a
|
407 |
hard_iface->batman_adv_ptype.type = ethertype; |
3193e8fdf
|
408 |
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv; |
e6c10f433
|
409 410 |
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev; dev_add_pack(&hard_iface->batman_adv_ptype); |
c6c8fea29
|
411 |
|
3e34819e0
|
412 413 414 |
batadv_info(hard_iface->soft_iface, "Adding interface: %s ", hard_iface->net_dev->name); |
c6c8fea29
|
415 |
|
0aca2369b
|
416 |
if (atomic_read(&bat_priv->fragmentation) && |
411d6ed93
|
417 |
hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len) |
3e34819e0
|
418 |
batadv_info(hard_iface->soft_iface, |
411d6ed93
|
419 420 |
"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. ", |
3e34819e0
|
421 |
hard_iface->net_dev->name, hard_iface->net_dev->mtu, |
411d6ed93
|
422 |
ETH_DATA_LEN + max_header_len); |
c6c8fea29
|
423 |
|
0aca2369b
|
424 |
if (!atomic_read(&bat_priv->fragmentation) && |
411d6ed93
|
425 |
hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len) |
3e34819e0
|
426 |
batadv_info(hard_iface->soft_iface, |
411d6ed93
|
427 428 |
"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. ", |
3e34819e0
|
429 |
hard_iface->net_dev->name, hard_iface->net_dev->mtu, |
411d6ed93
|
430 |
ETH_DATA_LEN + max_header_len); |
c6c8fea29
|
431 |
|
18a1cb6ee
|
432 433 |
if (batadv_hardif_is_iface_up(hard_iface)) batadv_hardif_activate_interface(hard_iface); |
c6c8fea29
|
434 |
else |
3e34819e0
|
435 436 437 438 |
batadv_err(hard_iface->soft_iface, "Not using interface %s (retrying later): interface not active ", hard_iface->net_dev->name); |
c6c8fea29
|
439 440 |
/* begin scheduling originator messages on that interface */ |
9455e34cb
|
441 |
batadv_schedule_bat_ogm(hard_iface); |
c6c8fea29
|
442 443 444 |
out: return 0; |
3dbd550b8
|
445 446 |
err_upper: netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface); |
77af7575c
|
447 |
err_dev: |
3dbd550b8
|
448 |
hard_iface->soft_iface = NULL; |
77af7575c
|
449 |
dev_put(soft_iface); |
c6c8fea29
|
450 |
err: |
e5d89254b
|
451 |
batadv_hardif_free_ref(hard_iface); |
e44d8fe2b
|
452 |
return ret; |
c6c8fea29
|
453 |
} |
a15fd3612
|
454 455 |
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface, enum batadv_hard_if_cleanup autodel) |
c6c8fea29
|
456 |
{ |
56303d34a
|
457 458 |
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_hard_iface *primary_if = NULL; |
c6c8fea29
|
459 |
|
e9a4f295e
|
460 |
if (hard_iface->if_status == BATADV_IF_ACTIVE) |
18a1cb6ee
|
461 |
batadv_hardif_deactivate_interface(hard_iface); |
c6c8fea29
|
462 |
|
e9a4f295e
|
463 |
if (hard_iface->if_status != BATADV_IF_INACTIVE) |
32ae9b221
|
464 |
goto out; |
c6c8fea29
|
465 |
|
3e34819e0
|
466 467 468 |
batadv_info(hard_iface->soft_iface, "Removing interface: %s ", hard_iface->net_dev->name); |
e6c10f433
|
469 |
dev_remove_pack(&hard_iface->batman_adv_ptype); |
c6c8fea29
|
470 471 |
bat_priv->num_ifaces--; |
7d211efc5
|
472 |
batadv_orig_hash_del_if(hard_iface, bat_priv->num_ifaces); |
c6c8fea29
|
473 |
|
e5d89254b
|
474 |
primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b221
|
475 |
if (hard_iface == primary_if) { |
56303d34a
|
476 |
struct batadv_hard_iface *new_if; |
c6c8fea29
|
477 |
|
18a1cb6ee
|
478 479 |
new_if = batadv_hardif_get_active(hard_iface->soft_iface); batadv_primary_if_select(bat_priv, new_if); |
c6c8fea29
|
480 481 |
if (new_if) |
e5d89254b
|
482 |
batadv_hardif_free_ref(new_if); |
c6c8fea29
|
483 |
} |
00a50076a
|
484 |
bat_priv->bat_algo_ops->bat_iface_disable(hard_iface); |
e9a4f295e
|
485 |
hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
c6c8fea29
|
486 |
|
e6c10f433
|
487 |
/* delete all references to this hard_iface */ |
7d211efc5
|
488 |
batadv_purge_orig_ref(bat_priv); |
9455e34cb
|
489 |
batadv_purge_outstanding_packets(bat_priv, hard_iface); |
e6c10f433
|
490 |
dev_put(hard_iface->soft_iface); |
c6c8fea29
|
491 492 |
/* nobody uses this interface anymore */ |
8257f55ae
|
493 494 495 496 497 498 |
if (!bat_priv->num_ifaces) { batadv_gw_check_client_stop(bat_priv); if (autodel == BATADV_IF_CLEANUP_AUTO) batadv_softif_destroy_sysfs(hard_iface->soft_iface); } |
c6c8fea29
|
499 |
|
3dbd550b8
|
500 |
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface); |
e6c10f433
|
501 |
hard_iface->soft_iface = NULL; |
e5d89254b
|
502 |
batadv_hardif_free_ref(hard_iface); |
32ae9b221
|
503 504 505 |
out: if (primary_if) |
e5d89254b
|
506 |
batadv_hardif_free_ref(primary_if); |
c6c8fea29
|
507 |
} |
5bc44dc84
|
508 509 510 511 512 513 514 515 516 517 518 519 520 |
/** * batadv_hardif_remove_interface_finish - cleans up the remains of a hardif * @work: work queue item * * Free the parts of the hard interface which can not be removed under * rtnl lock (to prevent deadlock situations). */ static void batadv_hardif_remove_interface_finish(struct work_struct *work) { struct batadv_hard_iface *hard_iface; hard_iface = container_of(work, struct batadv_hard_iface, cleanup_work); |
5bc7c1eb4
|
521 |
batadv_debugfs_del_hardif(hard_iface); |
5bc44dc84
|
522 523 524 |
batadv_sysfs_del_hardif(&hard_iface->hardif_obj); batadv_hardif_free_ref(hard_iface); } |
56303d34a
|
525 |
static struct batadv_hard_iface * |
18a1cb6ee
|
526 |
batadv_hardif_add_interface(struct net_device *net_dev) |
c6c8fea29
|
527 |
{ |
56303d34a
|
528 |
struct batadv_hard_iface *hard_iface; |
c6c8fea29
|
529 |
int ret; |
c3caf5196
|
530 |
ASSERT_RTNL(); |
18a1cb6ee
|
531 |
ret = batadv_is_valid_iface(net_dev); |
c6c8fea29
|
532 533 534 535 |
if (ret != 1) goto out; dev_hold(net_dev); |
7db3fc291
|
536 |
hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC); |
320f422f6
|
537 |
if (!hard_iface) |
c6c8fea29
|
538 |
goto release_dev; |
c6c8fea29
|
539 |
|
5853e22c5
|
540 |
ret = batadv_sysfs_add_hardif(&hard_iface->hardif_obj, net_dev); |
c6c8fea29
|
541 542 |
if (ret) goto free_if; |
e6c10f433
|
543 544 545 |
hard_iface->if_num = -1; hard_iface->net_dev = net_dev; hard_iface->soft_iface = NULL; |
e9a4f295e
|
546 |
hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
5bc7c1eb4
|
547 548 549 550 |
ret = batadv_debugfs_add_hardif(hard_iface); if (ret) goto free_sysfs; |
e6c10f433
|
551 |
INIT_LIST_HEAD(&hard_iface->list); |
5bc44dc84
|
552 553 |
INIT_WORK(&hard_iface->cleanup_work, batadv_hardif_remove_interface_finish); |
caf65bfcc
|
554 555 556 |
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; if (batadv_is_wifi_netdev(net_dev)) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; |
ed75ccbe2
|
557 |
/* extra reference for return */ |
e6c10f433
|
558 |
atomic_set(&hard_iface->refcount, 2); |
c6c8fea29
|
559 |
|
18a1cb6ee
|
560 |
batadv_check_known_mac_addr(hard_iface->net_dev); |
3193e8fdf
|
561 |
list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list); |
c6c8fea29
|
562 |
|
e6c10f433
|
563 |
return hard_iface; |
c6c8fea29
|
564 |
|
5bc7c1eb4
|
565 566 |
free_sysfs: batadv_sysfs_del_hardif(&hard_iface->hardif_obj); |
c6c8fea29
|
567 |
free_if: |
e6c10f433
|
568 |
kfree(hard_iface); |
c6c8fea29
|
569 570 571 572 573 |
release_dev: dev_put(net_dev); out: return NULL; } |
56303d34a
|
574 |
static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface) |
c6c8fea29
|
575 |
{ |
c3caf5196
|
576 |
ASSERT_RTNL(); |
c6c8fea29
|
577 |
/* first deactivate interface */ |
e9a4f295e
|
578 |
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
a15fd3612
|
579 580 |
batadv_hardif_disable_interface(hard_iface, BATADV_IF_CLEANUP_AUTO); |
c6c8fea29
|
581 |
|
e9a4f295e
|
582 |
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
c6c8fea29
|
583 |
return; |
e9a4f295e
|
584 |
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED; |
5bc44dc84
|
585 |
queue_work(batadv_event_workqueue, &hard_iface->cleanup_work); |
c6c8fea29
|
586 |
} |
9563877ea
|
587 |
void batadv_hardif_remove_interfaces(void) |
c6c8fea29
|
588 |
{ |
56303d34a
|
589 |
struct batadv_hard_iface *hard_iface, *hard_iface_tmp; |
c6c8fea29
|
590 |
|
c3caf5196
|
591 |
rtnl_lock(); |
e6c10f433
|
592 |
list_for_each_entry_safe(hard_iface, hard_iface_tmp, |
3193e8fdf
|
593 |
&batadv_hardif_list, list) { |
e6c10f433
|
594 |
list_del_rcu(&hard_iface->list); |
18a1cb6ee
|
595 |
batadv_hardif_remove_interface(hard_iface); |
c6c8fea29
|
596 597 598 |
} rtnl_unlock(); } |
18a1cb6ee
|
599 600 |
static int batadv_hard_if_event(struct notifier_block *this, unsigned long event, void *ptr) |
c6c8fea29
|
601 |
{ |
351638e7d
|
602 |
struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); |
56303d34a
|
603 604 605 |
struct batadv_hard_iface *hard_iface; struct batadv_hard_iface *primary_if = NULL; struct batadv_priv *bat_priv; |
c6c8fea29
|
606 |
|
37130293f
|
607 608 |
if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) { batadv_sysfs_add_meshif(net_dev); |
5d2c05b21
|
609 610 |
bat_priv = netdev_priv(net_dev); batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS); |
37130293f
|
611 612 |
return NOTIFY_DONE; } |
56303d34a
|
613 |
hard_iface = batadv_hardif_get_by_netdev(net_dev); |
e6c10f433
|
614 |
if (!hard_iface && event == NETDEV_REGISTER) |
18a1cb6ee
|
615 |
hard_iface = batadv_hardif_add_interface(net_dev); |
c6c8fea29
|
616 |
|
e6c10f433
|
617 |
if (!hard_iface) |
c6c8fea29
|
618 619 620 621 |
goto out; switch (event) { case NETDEV_UP: |
18a1cb6ee
|
622 |
batadv_hardif_activate_interface(hard_iface); |
c6c8fea29
|
623 624 625 |
break; case NETDEV_GOING_DOWN: case NETDEV_DOWN: |
18a1cb6ee
|
626 |
batadv_hardif_deactivate_interface(hard_iface); |
c6c8fea29
|
627 628 |
break; case NETDEV_UNREGISTER: |
e6c10f433
|
629 |
list_del_rcu(&hard_iface->list); |
c6c8fea29
|
630 |
|
18a1cb6ee
|
631 |
batadv_hardif_remove_interface(hard_iface); |
c6c8fea29
|
632 633 |
break; case NETDEV_CHANGEMTU: |
e6c10f433
|
634 |
if (hard_iface->soft_iface) |
9563877ea
|
635 |
batadv_update_min_mtu(hard_iface->soft_iface); |
c6c8fea29
|
636 637 |
break; case NETDEV_CHANGEADDR: |
e9a4f295e
|
638 |
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE) |
c6c8fea29
|
639 |
goto hardif_put; |
18a1cb6ee
|
640 |
batadv_check_known_mac_addr(hard_iface->net_dev); |
c6c8fea29
|
641 |
|
e6c10f433
|
642 |
bat_priv = netdev_priv(hard_iface->soft_iface); |
c32293983
|
643 |
bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface); |
01c4224b5
|
644 |
|
e5d89254b
|
645 |
primary_if = batadv_primary_if_get_selected(bat_priv); |
32ae9b221
|
646 647 648 649 |
if (!primary_if) goto hardif_put; if (hard_iface == primary_if) |
18a1cb6ee
|
650 |
batadv_primary_if_update_addr(bat_priv, NULL); |
c6c8fea29
|
651 652 653 |
break; default: break; |
f81c62242
|
654 |
} |
c6c8fea29
|
655 656 |
hardif_put: |
e5d89254b
|
657 |
batadv_hardif_free_ref(hard_iface); |
c6c8fea29
|
658 |
out: |
32ae9b221
|
659 |
if (primary_if) |
e5d89254b
|
660 |
batadv_hardif_free_ref(primary_if); |
c6c8fea29
|
661 662 |
return NOTIFY_DONE; } |
9563877ea
|
663 |
struct notifier_block batadv_hard_if_notifier = { |
18a1cb6ee
|
664 |
.notifier_call = batadv_hard_if_event, |
c6c8fea29
|
665 |
}; |