| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | | -/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: |
|---|
| 2 | +/* Copyright (C) 2007-2020 B.A.T.M.A.N. contributors: |
|---|
| 3 | 3 | * |
|---|
| 4 | 4 | * Marek Lindner, Simon Wunderlich |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of version 2 of the GNU General Public |
|---|
| 8 | | - * License as published by the Free Software Foundation. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 11 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 13 | | - * General Public License for more details. |
|---|
| 14 | | - * |
|---|
| 15 | | - * You should have received a copy of the GNU General Public License |
|---|
| 16 | | - * along with this program; if not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | 5 | */ |
|---|
| 18 | 6 | |
|---|
| 19 | 7 | #include "hard-interface.h" |
|---|
| .. | .. |
|---|
| 28 | 16 | #include <linux/if_ether.h> |
|---|
| 29 | 17 | #include <linux/kernel.h> |
|---|
| 30 | 18 | #include <linux/kref.h> |
|---|
| 19 | +#include <linux/limits.h> |
|---|
| 31 | 20 | #include <linux/list.h> |
|---|
| 32 | 21 | #include <linux/mutex.h> |
|---|
| 33 | 22 | #include <linux/netdevice.h> |
|---|
| .. | .. |
|---|
| 149 | 138 | * @net_dev: the device to check |
|---|
| 150 | 139 | * |
|---|
| 151 | 140 | * If the user creates any virtual device on top of a batman-adv interface, it |
|---|
| 152 | | - * is important to prevent this new interface to be used to create a new mesh |
|---|
| 153 | | - * network (this behaviour would lead to a batman-over-batman configuration). |
|---|
| 154 | | - * This function recursively checks all the fathers of the device passed as |
|---|
| 155 | | - * argument looking for a batman-adv soft interface. |
|---|
| 141 | + * is important to prevent this new interface from being used to create a new |
|---|
| 142 | + * mesh network (this behaviour would lead to a batman-over-batman |
|---|
| 143 | + * configuration). This function recursively checks all the fathers of the |
|---|
| 144 | + * device passed as argument looking for a batman-adv soft interface. |
|---|
| 156 | 145 | * |
|---|
| 157 | 146 | * Return: true if the device is descendant of a batman-adv mesh interface (or |
|---|
| 158 | 147 | * if it is a batman-adv interface itself), false otherwise |
|---|
| .. | .. |
|---|
| 162 | 151 | struct net *net = dev_net(net_dev); |
|---|
| 163 | 152 | struct net_device *parent_dev; |
|---|
| 164 | 153 | struct net *parent_net; |
|---|
| 154 | + int iflink; |
|---|
| 165 | 155 | bool ret; |
|---|
| 166 | 156 | |
|---|
| 167 | 157 | /* check if this is a batman-adv mesh interface */ |
|---|
| 168 | 158 | if (batadv_softif_is_valid(net_dev)) |
|---|
| 169 | 159 | return true; |
|---|
| 170 | 160 | |
|---|
| 171 | | - /* no more parents..stop recursion */ |
|---|
| 172 | | - if (dev_get_iflink(net_dev) == 0 || |
|---|
| 173 | | - dev_get_iflink(net_dev) == net_dev->ifindex) |
|---|
| 161 | + iflink = dev_get_iflink(net_dev); |
|---|
| 162 | + if (iflink == 0) |
|---|
| 174 | 163 | return false; |
|---|
| 175 | 164 | |
|---|
| 176 | 165 | parent_net = batadv_getlink_net(net_dev, net); |
|---|
| 177 | 166 | |
|---|
| 167 | + /* iflink to itself, most likely physical device */ |
|---|
| 168 | + if (net == parent_net && iflink == net_dev->ifindex) |
|---|
| 169 | + return false; |
|---|
| 170 | + |
|---|
| 178 | 171 | /* recurse over the parent device */ |
|---|
| 179 | | - parent_dev = __dev_get_by_index((struct net *)parent_net, |
|---|
| 180 | | - dev_get_iflink(net_dev)); |
|---|
| 172 | + parent_dev = __dev_get_by_index((struct net *)parent_net, iflink); |
|---|
| 181 | 173 | /* if we got a NULL parent_dev there is something broken.. */ |
|---|
| 182 | 174 | if (!parent_dev) { |
|---|
| 183 | 175 | pr_err("Cannot find parent device\n"); |
|---|
| .. | .. |
|---|
| 227 | 219 | struct net_device *real_netdev = NULL; |
|---|
| 228 | 220 | struct net *real_net; |
|---|
| 229 | 221 | struct net *net; |
|---|
| 230 | | - int ifindex; |
|---|
| 222 | + int iflink; |
|---|
| 231 | 223 | |
|---|
| 232 | 224 | ASSERT_RTNL(); |
|---|
| 233 | 225 | |
|---|
| 234 | 226 | if (!netdev) |
|---|
| 235 | 227 | return NULL; |
|---|
| 236 | 228 | |
|---|
| 237 | | - if (netdev->ifindex == dev_get_iflink(netdev)) { |
|---|
| 229 | + iflink = dev_get_iflink(netdev); |
|---|
| 230 | + if (iflink == 0) { |
|---|
| 238 | 231 | dev_hold(netdev); |
|---|
| 239 | 232 | return netdev; |
|---|
| 240 | 233 | } |
|---|
| .. | .. |
|---|
| 244 | 237 | goto out; |
|---|
| 245 | 238 | |
|---|
| 246 | 239 | net = dev_net(hard_iface->soft_iface); |
|---|
| 247 | | - ifindex = dev_get_iflink(netdev); |
|---|
| 248 | 240 | real_net = batadv_getlink_net(netdev, net); |
|---|
| 249 | | - real_netdev = dev_get_by_index(real_net, ifindex); |
|---|
| 241 | + |
|---|
| 242 | + /* iflink to itself, most likely physical device */ |
|---|
| 243 | + if (net == real_net && netdev->ifindex == iflink) { |
|---|
| 244 | + real_netdev = netdev; |
|---|
| 245 | + dev_hold(real_netdev); |
|---|
| 246 | + goto out; |
|---|
| 247 | + } |
|---|
| 248 | + |
|---|
| 249 | + real_netdev = dev_get_by_index(real_net, iflink); |
|---|
| 250 | 250 | |
|---|
| 251 | 251 | out: |
|---|
| 252 | 252 | if (hard_iface) |
|---|
| .. | .. |
|---|
| 484 | 484 | if (new_hard_iface) |
|---|
| 485 | 485 | kref_get(&new_hard_iface->refcount); |
|---|
| 486 | 486 | |
|---|
| 487 | | - curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1); |
|---|
| 488 | | - rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); |
|---|
| 487 | + curr_hard_iface = rcu_replace_pointer(bat_priv->primary_if, |
|---|
| 488 | + new_hard_iface, 1); |
|---|
| 489 | 489 | |
|---|
| 490 | 490 | if (!new_hard_iface) |
|---|
| 491 | 491 | goto out; |
|---|
| .. | .. |
|---|
| 613 | 613 | /* report to the other components the maximum amount of bytes that |
|---|
| 614 | 614 | * batman-adv can send over the wire (without considering the payload |
|---|
| 615 | 615 | * overhead). For example, this value is used by TT to compute the |
|---|
| 616 | | - * maximum local table table size |
|---|
| 616 | + * maximum local table size |
|---|
| 617 | 617 | */ |
|---|
| 618 | 618 | atomic_set(&bat_priv->packet_size_max, min_mtu); |
|---|
| 619 | 619 | |
|---|
| .. | .. |
|---|
| 632 | 632 | */ |
|---|
| 633 | 633 | void batadv_update_min_mtu(struct net_device *soft_iface) |
|---|
| 634 | 634 | { |
|---|
| 635 | | - soft_iface->mtu = batadv_hardif_min_mtu(soft_iface); |
|---|
| 635 | + struct batadv_priv *bat_priv = netdev_priv(soft_iface); |
|---|
| 636 | + int limit_mtu; |
|---|
| 637 | + int mtu; |
|---|
| 638 | + |
|---|
| 639 | + mtu = batadv_hardif_min_mtu(soft_iface); |
|---|
| 640 | + |
|---|
| 641 | + if (bat_priv->mtu_set_by_user) |
|---|
| 642 | + limit_mtu = bat_priv->mtu_set_by_user; |
|---|
| 643 | + else |
|---|
| 644 | + limit_mtu = ETH_DATA_LEN; |
|---|
| 645 | + |
|---|
| 646 | + mtu = min(mtu, limit_mtu); |
|---|
| 647 | + dev_set_mtu(soft_iface, mtu); |
|---|
| 636 | 648 | |
|---|
| 637 | 649 | /* Check if the local translate table should be cleaned up to match a |
|---|
| 638 | 650 | * new (and smaller) MTU. |
|---|
| .. | .. |
|---|
| 694 | 706 | * @slave: the interface enslaved in another master |
|---|
| 695 | 707 | * @master: the master from which slave has to be removed |
|---|
| 696 | 708 | * |
|---|
| 697 | | - * Invoke ndo_del_slave on master passing slave as argument. In this way slave |
|---|
| 698 | | - * is free'd and master can correctly change its internal state. |
|---|
| 709 | + * Invoke ndo_del_slave on master passing slave as argument. In this way the |
|---|
| 710 | + * slave is free'd and the master can correctly change its internal state. |
|---|
| 699 | 711 | * |
|---|
| 700 | 712 | * Return: 0 on success, a negative value representing the error otherwise |
|---|
| 701 | 713 | */ |
|---|
| .. | .. |
|---|
| 768 | 780 | hard_iface->soft_iface = soft_iface; |
|---|
| 769 | 781 | bat_priv = netdev_priv(hard_iface->soft_iface); |
|---|
| 770 | 782 | |
|---|
| 771 | | - if (bat_priv->num_ifaces >= UINT_MAX) { |
|---|
| 772 | | - ret = -ENOSPC; |
|---|
| 773 | | - goto err_dev; |
|---|
| 774 | | - } |
|---|
| 775 | | - |
|---|
| 776 | 783 | ret = netdev_master_upper_dev_link(hard_iface->net_dev, |
|---|
| 777 | 784 | soft_iface, NULL, NULL, NULL); |
|---|
| 778 | 785 | if (ret) |
|---|
| .. | .. |
|---|
| 782 | 789 | if (ret < 0) |
|---|
| 783 | 790 | goto err_upper; |
|---|
| 784 | 791 | |
|---|
| 785 | | - hard_iface->if_num = bat_priv->num_ifaces; |
|---|
| 786 | | - bat_priv->num_ifaces++; |
|---|
| 787 | 792 | hard_iface->if_status = BATADV_IF_INACTIVE; |
|---|
| 788 | | - ret = batadv_orig_hash_add_if(hard_iface, bat_priv->num_ifaces); |
|---|
| 789 | | - if (ret < 0) { |
|---|
| 790 | | - bat_priv->algo_ops->iface.disable(hard_iface); |
|---|
| 791 | | - bat_priv->num_ifaces--; |
|---|
| 792 | | - hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
|---|
| 793 | | - goto err_upper; |
|---|
| 794 | | - } |
|---|
| 795 | 793 | |
|---|
| 796 | 794 | kref_get(&hard_iface->refcount); |
|---|
| 797 | 795 | hard_iface->batman_adv_ptype.type = ethertype; |
|---|
| .. | .. |
|---|
| 842 | 840 | } |
|---|
| 843 | 841 | |
|---|
| 844 | 842 | /** |
|---|
| 843 | + * batadv_hardif_cnt() - get number of interfaces enslaved to soft interface |
|---|
| 844 | + * @soft_iface: soft interface to check |
|---|
| 845 | + * |
|---|
| 846 | + * This function is only using RCU for locking - the result can therefore be |
|---|
| 847 | + * off when another function is modifying the list at the same time. The |
|---|
| 848 | + * caller can use the rtnl_lock to make sure that the count is accurate. |
|---|
| 849 | + * |
|---|
| 850 | + * Return: number of connected/enslaved hard interfaces |
|---|
| 851 | + */ |
|---|
| 852 | +static size_t batadv_hardif_cnt(const struct net_device *soft_iface) |
|---|
| 853 | +{ |
|---|
| 854 | + struct batadv_hard_iface *hard_iface; |
|---|
| 855 | + size_t count = 0; |
|---|
| 856 | + |
|---|
| 857 | + rcu_read_lock(); |
|---|
| 858 | + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
|---|
| 859 | + if (hard_iface->soft_iface != soft_iface) |
|---|
| 860 | + continue; |
|---|
| 861 | + |
|---|
| 862 | + count++; |
|---|
| 863 | + } |
|---|
| 864 | + rcu_read_unlock(); |
|---|
| 865 | + |
|---|
| 866 | + return count; |
|---|
| 867 | +} |
|---|
| 868 | + |
|---|
| 869 | +/** |
|---|
| 845 | 870 | * batadv_hardif_disable_interface() - Remove hard interface from soft interface |
|---|
| 846 | 871 | * @hard_iface: hard interface to be removed |
|---|
| 847 | 872 | * @autodel: whether to delete soft interface when it doesn't contain any other |
|---|
| .. | .. |
|---|
| 862 | 887 | hard_iface->net_dev->name); |
|---|
| 863 | 888 | dev_remove_pack(&hard_iface->batman_adv_ptype); |
|---|
| 864 | 889 | batadv_hardif_put(hard_iface); |
|---|
| 865 | | - |
|---|
| 866 | | - bat_priv->num_ifaces--; |
|---|
| 867 | | - batadv_orig_hash_del_if(hard_iface, bat_priv->num_ifaces); |
|---|
| 868 | 890 | |
|---|
| 869 | 891 | primary_if = batadv_primary_if_get_selected(bat_priv); |
|---|
| 870 | 892 | if (hard_iface == primary_if) { |
|---|
| .. | .. |
|---|
| 889 | 911 | batadv_hardif_recalc_extra_skbroom(hard_iface->soft_iface); |
|---|
| 890 | 912 | |
|---|
| 891 | 913 | /* nobody uses this interface anymore */ |
|---|
| 892 | | - if (bat_priv->num_ifaces == 0) { |
|---|
| 914 | + if (batadv_hardif_cnt(hard_iface->soft_iface) <= 1) { |
|---|
| 893 | 915 | batadv_gw_check_client_stop(bat_priv); |
|---|
| 894 | 916 | |
|---|
| 895 | 917 | if (autodel == BATADV_IF_CLEANUP_AUTO) |
|---|
| .. | .. |
|---|
| 925 | 947 | if (ret) |
|---|
| 926 | 948 | goto free_if; |
|---|
| 927 | 949 | |
|---|
| 928 | | - hard_iface->if_num = 0; |
|---|
| 929 | 950 | hard_iface->net_dev = net_dev; |
|---|
| 930 | 951 | hard_iface->soft_iface = NULL; |
|---|
| 931 | 952 | hard_iface->if_status = BATADV_IF_NOT_IN_USE; |
|---|
| 932 | 953 | |
|---|
| 933 | | - ret = batadv_debugfs_add_hardif(hard_iface); |
|---|
| 934 | | - if (ret) |
|---|
| 935 | | - goto free_sysfs; |
|---|
| 954 | + batadv_debugfs_add_hardif(hard_iface); |
|---|
| 936 | 955 | |
|---|
| 937 | 956 | INIT_LIST_HEAD(&hard_iface->list); |
|---|
| 938 | 957 | INIT_HLIST_HEAD(&hard_iface->neigh_list); |
|---|
| .. | .. |
|---|
| 946 | 965 | if (batadv_is_wifi_hardif(hard_iface)) |
|---|
| 947 | 966 | hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; |
|---|
| 948 | 967 | |
|---|
| 968 | + atomic_set(&hard_iface->hop_penalty, 0); |
|---|
| 969 | + |
|---|
| 949 | 970 | batadv_v_hardif_init(hard_iface); |
|---|
| 950 | 971 | |
|---|
| 951 | 972 | batadv_check_known_mac_addr(hard_iface->net_dev); |
|---|
| 952 | 973 | kref_get(&hard_iface->refcount); |
|---|
| 953 | 974 | list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list); |
|---|
| 975 | + batadv_hardif_generation++; |
|---|
| 954 | 976 | |
|---|
| 955 | 977 | return hard_iface; |
|---|
| 956 | 978 | |
|---|
| 957 | | -free_sysfs: |
|---|
| 958 | | - batadv_sysfs_del_hardif(&hard_iface->hardif_obj); |
|---|
| 959 | 979 | free_if: |
|---|
| 960 | 980 | kfree(hard_iface); |
|---|
| 961 | 981 | release_dev: |
|---|
| .. | .. |
|---|
| 980 | 1000 | batadv_debugfs_del_hardif(hard_iface); |
|---|
| 981 | 1001 | batadv_sysfs_del_hardif(&hard_iface->hardif_obj); |
|---|
| 982 | 1002 | batadv_hardif_put(hard_iface); |
|---|
| 983 | | -} |
|---|
| 984 | | - |
|---|
| 985 | | -/** |
|---|
| 986 | | - * batadv_hardif_remove_interfaces() - Remove all hard interfaces |
|---|
| 987 | | - */ |
|---|
| 988 | | -void batadv_hardif_remove_interfaces(void) |
|---|
| 989 | | -{ |
|---|
| 990 | | - struct batadv_hard_iface *hard_iface, *hard_iface_tmp; |
|---|
| 991 | | - |
|---|
| 992 | | - rtnl_lock(); |
|---|
| 993 | | - list_for_each_entry_safe(hard_iface, hard_iface_tmp, |
|---|
| 994 | | - &batadv_hardif_list, list) { |
|---|
| 995 | | - list_del_rcu(&hard_iface->list); |
|---|
| 996 | | - batadv_hardif_remove_interface(hard_iface); |
|---|
| 997 | | - } |
|---|
| 998 | | - rtnl_unlock(); |
|---|
| 999 | 1003 | } |
|---|
| 1000 | 1004 | |
|---|
| 1001 | 1005 | /** |
|---|
| .. | .. |
|---|
| 1054 | 1058 | case NETDEV_UNREGISTER: |
|---|
| 1055 | 1059 | case NETDEV_PRE_TYPE_CHANGE: |
|---|
| 1056 | 1060 | list_del_rcu(&hard_iface->list); |
|---|
| 1061 | + batadv_hardif_generation++; |
|---|
| 1057 | 1062 | |
|---|
| 1058 | 1063 | batadv_hardif_remove_interface(hard_iface); |
|---|
| 1059 | 1064 | break; |
|---|