linux/net/batman-adv/hard-interface.c
<<
>>
Prefs
   1/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
   2 *
   3 * Marek Lindner, Simon Wunderlich
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of version 2 of the GNU General Public
   7 * License as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12 * General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17 * 02110-1301, USA
  18 */
  19
  20#include "main.h"
  21#include "hard-interface.h"
  22#include "soft-interface.h"
  23#include "send.h"
  24#include "translation-table.h"
  25#include "routing.h"
  26#include "sysfs.h"
  27#include "originator.h"
  28#include "hash.h"
  29#include "bridge_loop_avoidance.h"
  30
  31#include <linux/if_arp.h>
  32
  33void batadv_hardif_free_rcu(struct rcu_head *rcu)
  34{
  35        struct batadv_hard_iface *hard_iface;
  36
  37        hard_iface = container_of(rcu, struct batadv_hard_iface, rcu);
  38        dev_put(hard_iface->net_dev);
  39        kfree(hard_iface);
  40}
  41
  42struct batadv_hard_iface *
  43batadv_hardif_get_by_netdev(const struct net_device *net_dev)
  44{
  45        struct batadv_hard_iface *hard_iface;
  46
  47        rcu_read_lock();
  48        list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
  49                if (hard_iface->net_dev == net_dev &&
  50                    atomic_inc_not_zero(&hard_iface->refcount))
  51                        goto out;
  52        }
  53
  54        hard_iface = NULL;
  55
  56out:
  57        rcu_read_unlock();
  58        return hard_iface;
  59}
  60
  61static int batadv_is_valid_iface(const struct net_device *net_dev)
  62{
  63        if (net_dev->flags & IFF_LOOPBACK)
  64                return 0;
  65
  66        if (net_dev->type != ARPHRD_ETHER)
  67                return 0;
  68
  69        if (net_dev->addr_len != ETH_ALEN)
  70                return 0;
  71
  72        /* no batman over batman */
  73        if (batadv_softif_is_valid(net_dev))
  74                return 0;
  75
  76        return 1;
  77}
  78
  79static struct batadv_hard_iface *
  80batadv_hardif_get_active(const struct net_device *soft_iface)
  81{
  82        struct batadv_hard_iface *hard_iface;
  83
  84        rcu_read_lock();
  85        list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
  86                if (hard_iface->soft_iface != soft_iface)
  87                        continue;
  88
  89                if (hard_iface->if_status == BATADV_IF_ACTIVE &&
  90                    atomic_inc_not_zero(&hard_iface->refcount))
  91                        goto out;
  92        }
  93
  94        hard_iface = NULL;
  95
  96out:
  97        rcu_read_unlock();
  98        return hard_iface;
  99}
 100
 101static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
 102                                          struct batadv_hard_iface *oldif)
 103{
 104        struct batadv_vis_packet *vis_packet;
 105        struct batadv_hard_iface *primary_if;
 106        struct sk_buff *skb;
 107
 108        primary_if = batadv_primary_if_get_selected(bat_priv);
 109        if (!primary_if)
 110                goto out;
 111
 112        skb = bat_priv->vis.my_info->skb_packet;
 113        vis_packet = (struct batadv_vis_packet *)skb->data;
 114        memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 115        memcpy(vis_packet->sender_orig,
 116               primary_if->net_dev->dev_addr, ETH_ALEN);
 117
 118        batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
 119out:
 120        if (primary_if)
 121                batadv_hardif_free_ref(primary_if);
 122}
 123
 124static void batadv_primary_if_select(struct batadv_priv *bat_priv,
 125                                     struct batadv_hard_iface *new_hard_iface)
 126{
 127        struct batadv_hard_iface *curr_hard_iface;
 128
 129        ASSERT_RTNL();
 130
 131        if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount))
 132                new_hard_iface = NULL;
 133
 134        curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1);
 135        rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);
 136
 137        if (!new_hard_iface)
 138                goto out;
 139
 140        bat_priv->bat_algo_ops->bat_primary_iface_set(new_hard_iface);
 141        batadv_primary_if_update_addr(bat_priv, curr_hard_iface);
 142
 143out:
 144        if (curr_hard_iface)
 145                batadv_hardif_free_ref(curr_hard_iface);
 146}
 147
 148static bool
 149batadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface)
 150{
 151        if (hard_iface->net_dev->flags & IFF_UP)
 152                return true;
 153
 154        return false;
 155}
 156
 157static void batadv_check_known_mac_addr(const struct net_device *net_dev)
 158{
 159        const struct batadv_hard_iface *hard_iface;
 160
 161        rcu_read_lock();
 162        list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
 163                if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
 164                    (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED))
 165                        continue;
 166
 167                if (hard_iface->net_dev == net_dev)
 168                        continue;
 169
 170                if (!batadv_compare_eth(hard_iface->net_dev->dev_addr,
 171                                        net_dev->dev_addr))
 172                        continue;
 173
 174                pr_warn("The newly added mac address (%pM) already exists on: %s\n",
 175                        net_dev->dev_addr, hard_iface->net_dev->name);
 176                pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n");
 177        }
 178        rcu_read_unlock();
 179}
 180
 181int batadv_hardif_min_mtu(struct net_device *soft_iface)
 182{
 183        const struct batadv_priv *bat_priv = netdev_priv(soft_iface);
 184        const struct batadv_hard_iface *hard_iface;
 185        /* allow big frames if all devices are capable to do so
 186         * (have MTU > 1500 + BAT_HEADER_LEN)
 187         */
 188        int min_mtu = ETH_DATA_LEN;
 189
 190        if (atomic_read(&bat_priv->fragmentation))
 191                goto out;
 192
 193        rcu_read_lock();
 194        list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
 195                if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
 196                    (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED))
 197                        continue;
 198
 199                if (hard_iface->soft_iface != soft_iface)
 200                        continue;
 201
 202                min_mtu = min_t(int,
 203                                hard_iface->net_dev->mtu - BATADV_HEADER_LEN,
 204                                min_mtu);
 205        }
 206        rcu_read_unlock();
 207out:
 208        return min_mtu;
 209}
 210
 211/* adjusts the MTU if a new interface with a smaller MTU appeared. */
 212void batadv_update_min_mtu(struct net_device *soft_iface)
 213{
 214        int min_mtu;
 215
 216        min_mtu = batadv_hardif_min_mtu(soft_iface);
 217        if (soft_iface->mtu != min_mtu)
 218                soft_iface->mtu = min_mtu;
 219}
 220
 221static void
 222batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
 223{
 224        struct batadv_priv *bat_priv;
 225        struct batadv_hard_iface *primary_if = NULL;
 226
 227        if (hard_iface->if_status != BATADV_IF_INACTIVE)
 228                goto out;
 229
 230        bat_priv = netdev_priv(hard_iface->soft_iface);
 231
 232        bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface);
 233        hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED;
 234
 235        /* the first active interface becomes our primary interface or
 236         * the next active interface after the old primary interface was removed
 237         */
 238        primary_if = batadv_primary_if_get_selected(bat_priv);
 239        if (!primary_if)
 240                batadv_primary_if_select(bat_priv, hard_iface);
 241
 242        batadv_info(hard_iface->soft_iface, "Interface activated: %s\n",
 243                    hard_iface->net_dev->name);
 244
 245        batadv_update_min_mtu(hard_iface->soft_iface);
 246
 247out:
 248        if (primary_if)
 249                batadv_hardif_free_ref(primary_if);
 250}
 251
 252static void
 253batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
 254{
 255        if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
 256            (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED))
 257                return;
 258
 259        hard_iface->if_status = BATADV_IF_INACTIVE;
 260
 261        batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n",
 262                    hard_iface->net_dev->name);
 263
 264        batadv_update_min_mtu(hard_iface->soft_iface);
 265}
 266
 267int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 268                                   const char *iface_name)
 269{
 270        struct batadv_priv *bat_priv;
 271        struct net_device *soft_iface;
 272        __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN);
 273        int ret;
 274
 275        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
 276                goto out;
 277
 278        if (!atomic_inc_not_zero(&hard_iface->refcount))
 279                goto out;
 280
 281        /* hard-interface is part of a bridge */
 282        if (hard_iface->net_dev->priv_flags & IFF_BRIDGE_PORT)
 283                pr_err("You are about to enable batman-adv on '%s' which already is part of a bridge. Unless you know exactly what you are doing this is probably wrong and won't work the way you think it would.\n",
 284                       hard_iface->net_dev->name);
 285
 286        soft_iface = dev_get_by_name(&init_net, iface_name);
 287
 288        if (!soft_iface) {
 289                soft_iface = batadv_softif_create(iface_name);
 290
 291                if (!soft_iface) {
 292                        ret = -ENOMEM;
 293                        goto err;
 294                }
 295
 296                /* dev_get_by_name() increases the reference counter for us */
 297                dev_hold(soft_iface);
 298        }
 299
 300        if (!batadv_softif_is_valid(soft_iface)) {
 301                pr_err("Can't create batman mesh interface %s: already exists as regular interface\n",
 302                       soft_iface->name);
 303                ret = -EINVAL;
 304                goto err_dev;
 305        }
 306
 307        hard_iface->soft_iface = soft_iface;
 308        bat_priv = netdev_priv(hard_iface->soft_iface);
 309
 310        ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface);
 311        if (ret < 0)
 312                goto err_dev;
 313
 314        hard_iface->if_num = bat_priv->num_ifaces;
 315        bat_priv->num_ifaces++;
 316        hard_iface->if_status = BATADV_IF_INACTIVE;
 317        ret = batadv_orig_hash_add_if(hard_iface, bat_priv->num_ifaces);
 318        if (ret < 0) {
 319                bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
 320                bat_priv->num_ifaces--;
 321                hard_iface->if_status = BATADV_IF_NOT_IN_USE;
 322                goto err_dev;
 323        }
 324
 325        hard_iface->batman_adv_ptype.type = ethertype;
 326        hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
 327        hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
 328        dev_add_pack(&hard_iface->batman_adv_ptype);
 329
 330        atomic_set(&hard_iface->frag_seqno, 1);
 331        batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
 332                    hard_iface->net_dev->name);
 333
 334        if (atomic_read(&bat_priv->fragmentation) &&
 335            hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN)
 336                batadv_info(hard_iface->soft_iface,
 337                            "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 %zi would solve the problem.\n",
 338                            hard_iface->net_dev->name, hard_iface->net_dev->mtu,
 339                            ETH_DATA_LEN + BATADV_HEADER_LEN);
 340
 341        if (!atomic_read(&bat_priv->fragmentation) &&
 342            hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN)
 343                batadv_info(hard_iface->soft_iface,
 344                            "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 %zi.\n",
 345                            hard_iface->net_dev->name, hard_iface->net_dev->mtu,
 346                            ETH_DATA_LEN + BATADV_HEADER_LEN);
 347
 348        if (batadv_hardif_is_iface_up(hard_iface))
 349                batadv_hardif_activate_interface(hard_iface);
 350        else
 351                batadv_err(hard_iface->soft_iface,
 352                           "Not using interface %s (retrying later): interface not active\n",
 353                           hard_iface->net_dev->name);
 354
 355        /* begin scheduling originator messages on that interface */
 356        batadv_schedule_bat_ogm(hard_iface);
 357
 358out:
 359        return 0;
 360
 361err_dev:
 362        dev_put(soft_iface);
 363err:
 364        batadv_hardif_free_ref(hard_iface);
 365        return ret;
 366}
 367
 368void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
 369{
 370        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
 371        struct batadv_hard_iface *primary_if = NULL;
 372
 373        if (hard_iface->if_status == BATADV_IF_ACTIVE)
 374                batadv_hardif_deactivate_interface(hard_iface);
 375
 376        if (hard_iface->if_status != BATADV_IF_INACTIVE)
 377                goto out;
 378
 379        batadv_info(hard_iface->soft_iface, "Removing interface: %s\n",
 380                    hard_iface->net_dev->name);
 381        dev_remove_pack(&hard_iface->batman_adv_ptype);
 382
 383        bat_priv->num_ifaces--;
 384        batadv_orig_hash_del_if(hard_iface, bat_priv->num_ifaces);
 385
 386        primary_if = batadv_primary_if_get_selected(bat_priv);
 387        if (hard_iface == primary_if) {
 388                struct batadv_hard_iface *new_if;
 389
 390                new_if = batadv_hardif_get_active(hard_iface->soft_iface);
 391                batadv_primary_if_select(bat_priv, new_if);
 392
 393                if (new_if)
 394                        batadv_hardif_free_ref(new_if);
 395        }
 396
 397        bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
 398        hard_iface->if_status = BATADV_IF_NOT_IN_USE;
 399
 400        /* delete all references to this hard_iface */
 401        batadv_purge_orig_ref(bat_priv);
 402        batadv_purge_outstanding_packets(bat_priv, hard_iface);
 403        dev_put(hard_iface->soft_iface);
 404
 405        /* nobody uses this interface anymore */
 406        if (!bat_priv->num_ifaces)
 407                batadv_softif_destroy(hard_iface->soft_iface);
 408
 409        hard_iface->soft_iface = NULL;
 410        batadv_hardif_free_ref(hard_iface);
 411
 412out:
 413        if (primary_if)
 414                batadv_hardif_free_ref(primary_if);
 415}
 416
 417static struct batadv_hard_iface *
 418batadv_hardif_add_interface(struct net_device *net_dev)
 419{
 420        struct batadv_hard_iface *hard_iface;
 421        int ret;
 422
 423        ASSERT_RTNL();
 424
 425        ret = batadv_is_valid_iface(net_dev);
 426        if (ret != 1)
 427                goto out;
 428
 429        dev_hold(net_dev);
 430
 431        hard_iface = kmalloc(sizeof(*hard_iface), GFP_ATOMIC);
 432        if (!hard_iface)
 433                goto release_dev;
 434
 435        ret = batadv_sysfs_add_hardif(&hard_iface->hardif_obj, net_dev);
 436        if (ret)
 437                goto free_if;
 438
 439        hard_iface->if_num = -1;
 440        hard_iface->net_dev = net_dev;
 441        hard_iface->soft_iface = NULL;
 442        hard_iface->if_status = BATADV_IF_NOT_IN_USE;
 443        INIT_LIST_HEAD(&hard_iface->list);
 444        /* extra reference for return */
 445        atomic_set(&hard_iface->refcount, 2);
 446
 447        batadv_check_known_mac_addr(hard_iface->net_dev);
 448        list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
 449
 450        /* This can't be called via a bat_priv callback because
 451         * we have no bat_priv yet.
 452         */
 453        atomic_set(&hard_iface->seqno, 1);
 454        hard_iface->packet_buff = NULL;
 455
 456        return hard_iface;
 457
 458free_if:
 459        kfree(hard_iface);
 460release_dev:
 461        dev_put(net_dev);
 462out:
 463        return NULL;
 464}
 465
 466static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
 467{
 468        ASSERT_RTNL();
 469
 470        /* first deactivate interface */
 471        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
 472                batadv_hardif_disable_interface(hard_iface);
 473
 474        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
 475                return;
 476
 477        hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
 478        batadv_sysfs_del_hardif(&hard_iface->hardif_obj);
 479        batadv_hardif_free_ref(hard_iface);
 480}
 481
 482void batadv_hardif_remove_interfaces(void)
 483{
 484        struct batadv_hard_iface *hard_iface, *hard_iface_tmp;
 485
 486        rtnl_lock();
 487        list_for_each_entry_safe(hard_iface, hard_iface_tmp,
 488                                 &batadv_hardif_list, list) {
 489                list_del_rcu(&hard_iface->list);
 490                batadv_hardif_remove_interface(hard_iface);
 491        }
 492        rtnl_unlock();
 493}
 494
 495static int batadv_hard_if_event(struct notifier_block *this,
 496                                unsigned long event, void *ptr)
 497{
 498        struct net_device *net_dev = ptr;
 499        struct batadv_hard_iface *hard_iface;
 500        struct batadv_hard_iface *primary_if = NULL;
 501        struct batadv_priv *bat_priv;
 502
 503        hard_iface = batadv_hardif_get_by_netdev(net_dev);
 504        if (!hard_iface && event == NETDEV_REGISTER)
 505                hard_iface = batadv_hardif_add_interface(net_dev);
 506
 507        if (!hard_iface)
 508                goto out;
 509
 510        switch (event) {
 511        case NETDEV_UP:
 512                batadv_hardif_activate_interface(hard_iface);
 513                break;
 514        case NETDEV_GOING_DOWN:
 515        case NETDEV_DOWN:
 516                batadv_hardif_deactivate_interface(hard_iface);
 517                break;
 518        case NETDEV_UNREGISTER:
 519                list_del_rcu(&hard_iface->list);
 520
 521                batadv_hardif_remove_interface(hard_iface);
 522                break;
 523        case NETDEV_CHANGEMTU:
 524                if (hard_iface->soft_iface)
 525                        batadv_update_min_mtu(hard_iface->soft_iface);
 526                break;
 527        case NETDEV_CHANGEADDR:
 528                if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
 529                        goto hardif_put;
 530
 531                batadv_check_known_mac_addr(hard_iface->net_dev);
 532
 533                bat_priv = netdev_priv(hard_iface->soft_iface);
 534                bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface);
 535
 536                primary_if = batadv_primary_if_get_selected(bat_priv);
 537                if (!primary_if)
 538                        goto hardif_put;
 539
 540                if (hard_iface == primary_if)
 541                        batadv_primary_if_update_addr(bat_priv, NULL);
 542                break;
 543        default:
 544                break;
 545        }
 546
 547hardif_put:
 548        batadv_hardif_free_ref(hard_iface);
 549out:
 550        if (primary_if)
 551                batadv_hardif_free_ref(primary_if);
 552        return NOTIFY_DONE;
 553}
 554
 555/* This function returns true if the interface represented by ifindex is a
 556 * 802.11 wireless device
 557 */
 558bool batadv_is_wifi_iface(int ifindex)
 559{
 560        struct net_device *net_device = NULL;
 561        bool ret = false;
 562
 563        if (ifindex == BATADV_NULL_IFINDEX)
 564                goto out;
 565
 566        net_device = dev_get_by_index(&init_net, ifindex);
 567        if (!net_device)
 568                goto out;
 569
 570#ifdef CONFIG_WIRELESS_EXT
 571        /* pre-cfg80211 drivers have to implement WEXT, so it is possible to
 572         * check for wireless_handlers != NULL
 573         */
 574        if (net_device->wireless_handlers)
 575                ret = true;
 576        else
 577#endif
 578                /* cfg80211 drivers have to set ieee80211_ptr */
 579                if (net_device->ieee80211_ptr)
 580                        ret = true;
 581out:
 582        if (net_device)
 583                dev_put(net_device);
 584        return ret;
 585}
 586
 587struct notifier_block batadv_hard_if_notifier = {
 588        .notifier_call = batadv_hard_if_event,
 589};
 590
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.