linux/net/phonet/pn_dev.c
<<
>>
Prefs
   1/*
   2 * File: pn_dev.c
   3 *
   4 * Phonet network device
   5 *
   6 * Copyright (C) 2008 Nokia Corporation.
   7 *
   8 * Authors: Sakari Ailus <sakari.ailus@nokia.com>
   9 *          RĂ©mi Denis-Courmont
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License
  13 * version 2 as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  23 * 02110-1301 USA
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/net.h>
  28#include <linux/slab.h>
  29#include <linux/netdevice.h>
  30#include <linux/phonet.h>
  31#include <linux/proc_fs.h>
  32#include <linux/if_arp.h>
  33#include <net/sock.h>
  34#include <net/netns/generic.h>
  35#include <net/phonet/pn_dev.h>
  36
  37struct phonet_routes {
  38        struct mutex            lock;
  39        struct net_device       *table[64];
  40};
  41
  42struct phonet_net {
  43        struct phonet_device_list pndevs;
  44        struct phonet_routes routes;
  45};
  46
  47static int phonet_net_id __read_mostly;
  48
  49static struct phonet_net *phonet_pernet(struct net *net)
  50{
  51        BUG_ON(!net);
  52
  53        return net_generic(net, phonet_net_id);
  54}
  55
  56struct phonet_device_list *phonet_device_list(struct net *net)
  57{
  58        struct phonet_net *pnn = phonet_pernet(net);
  59        return &pnn->pndevs;
  60}
  61
  62/* Allocate new Phonet device. */
  63static struct phonet_device *__phonet_device_alloc(struct net_device *dev)
  64{
  65        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
  66        struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC);
  67        if (pnd == NULL)
  68                return NULL;
  69        pnd->netdev = dev;
  70        bitmap_zero(pnd->addrs, 64);
  71
  72        BUG_ON(!mutex_is_locked(&pndevs->lock));
  73        list_add_rcu(&pnd->list, &pndevs->list);
  74        return pnd;
  75}
  76
  77static struct phonet_device *__phonet_get(struct net_device *dev)
  78{
  79        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
  80        struct phonet_device *pnd;
  81
  82        BUG_ON(!mutex_is_locked(&pndevs->lock));
  83        list_for_each_entry(pnd, &pndevs->list, list) {
  84                if (pnd->netdev == dev)
  85                        return pnd;
  86        }
  87        return NULL;
  88}
  89
  90static struct phonet_device *__phonet_get_rcu(struct net_device *dev)
  91{
  92        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
  93        struct phonet_device *pnd;
  94
  95        list_for_each_entry_rcu(pnd, &pndevs->list, list) {
  96                if (pnd->netdev == dev)
  97                        return pnd;
  98        }
  99        return NULL;
 100}
 101
 102static void phonet_device_destroy(struct net_device *dev)
 103{
 104        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
 105        struct phonet_device *pnd;
 106
 107        ASSERT_RTNL();
 108
 109        mutex_lock(&pndevs->lock);
 110        pnd = __phonet_get(dev);
 111        if (pnd)
 112                list_del_rcu(&pnd->list);
 113        mutex_unlock(&pndevs->lock);
 114
 115        if (pnd) {
 116                u8 addr;
 117
 118                for_each_set_bit(addr, pnd->addrs, 64)
 119                        phonet_address_notify(RTM_DELADDR, dev, addr);
 120                kfree(pnd);
 121        }
 122}
 123
 124struct net_device *phonet_device_get(struct net *net)
 125{
 126        struct phonet_device_list *pndevs = phonet_device_list(net);
 127        struct phonet_device *pnd;
 128        struct net_device *dev = NULL;
 129
 130        rcu_read_lock();
 131        list_for_each_entry_rcu(pnd, &pndevs->list, list) {
 132                dev = pnd->netdev;
 133                BUG_ON(!dev);
 134
 135                if ((dev->reg_state == NETREG_REGISTERED) &&
 136                        ((pnd->netdev->flags & IFF_UP)) == IFF_UP)
 137                        break;
 138                dev = NULL;
 139        }
 140        if (dev)
 141                dev_hold(dev);
 142        rcu_read_unlock();
 143        return dev;
 144}
 145
 146int phonet_address_add(struct net_device *dev, u8 addr)
 147{
 148        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
 149        struct phonet_device *pnd;
 150        int err = 0;
 151
 152        mutex_lock(&pndevs->lock);
 153        /* Find or create Phonet-specific device data */
 154        pnd = __phonet_get(dev);
 155        if (pnd == NULL)
 156                pnd = __phonet_device_alloc(dev);
 157        if (unlikely(pnd == NULL))
 158                err = -ENOMEM;
 159        else if (test_and_set_bit(addr >> 2, pnd->addrs))
 160                err = -EEXIST;
 161        mutex_unlock(&pndevs->lock);
 162        return err;
 163}
 164
 165int phonet_address_del(struct net_device *dev, u8 addr)
 166{
 167        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
 168        struct phonet_device *pnd;
 169        int err = 0;
 170
 171        mutex_lock(&pndevs->lock);
 172        pnd = __phonet_get(dev);
 173        if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) {
 174                err = -EADDRNOTAVAIL;
 175                pnd = NULL;
 176        } else if (bitmap_empty(pnd->addrs, 64))
 177                list_del_rcu(&pnd->list);
 178        else
 179                pnd = NULL;
 180        mutex_unlock(&pndevs->lock);
 181
 182        if (pnd)
 183                kfree_rcu(pnd, rcu);
 184
 185        return err;
 186}
 187
 188/* Gets a source address toward a destination, through a interface. */
 189u8 phonet_address_get(struct net_device *dev, u8 daddr)
 190{
 191        struct phonet_device *pnd;
 192        u8 saddr;
 193
 194        rcu_read_lock();
 195        pnd = __phonet_get_rcu(dev);
 196        if (pnd) {
 197                BUG_ON(bitmap_empty(pnd->addrs, 64));
 198
 199                /* Use same source address as destination, if possible */
 200                if (test_bit(daddr >> 2, pnd->addrs))
 201                        saddr = daddr;
 202                else
 203                        saddr = find_first_bit(pnd->addrs, 64) << 2;
 204        } else
 205                saddr = PN_NO_ADDR;
 206        rcu_read_unlock();
 207
 208        if (saddr == PN_NO_ADDR) {
 209                /* Fallback to another device */
 210                struct net_device *def_dev;
 211
 212                def_dev = phonet_device_get(dev_net(dev));
 213                if (def_dev) {
 214                        if (def_dev != dev)
 215                                saddr = phonet_address_get(def_dev, daddr);
 216                        dev_put(def_dev);
 217                }
 218        }
 219        return saddr;
 220}
 221
 222int phonet_address_lookup(struct net *net, u8 addr)
 223{
 224        struct phonet_device_list *pndevs = phonet_device_list(net);
 225        struct phonet_device *pnd;
 226        int err = -EADDRNOTAVAIL;
 227
 228        rcu_read_lock();
 229        list_for_each_entry_rcu(pnd, &pndevs->list, list) {
 230                /* Don't allow unregistering devices! */
 231                if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
 232                                ((pnd->netdev->flags & IFF_UP)) != IFF_UP)
 233                        continue;
 234
 235                if (test_bit(addr >> 2, pnd->addrs)) {
 236                        err = 0;
 237                        goto found;
 238                }
 239        }
 240found:
 241        rcu_read_unlock();
 242        return err;
 243}
 244
 245/* automatically configure a Phonet device, if supported */
 246static int phonet_device_autoconf(struct net_device *dev)
 247{
 248        struct if_phonet_req req;
 249        int ret;
 250
 251        if (!dev->netdev_ops->ndo_do_ioctl)
 252                return -EOPNOTSUPP;
 253
 254        ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req,
 255                                                SIOCPNGAUTOCONF);
 256        if (ret < 0)
 257                return ret;
 258
 259        ASSERT_RTNL();
 260        ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device);
 261        if (ret)
 262                return ret;
 263        phonet_address_notify(RTM_NEWADDR, dev,
 264                                req.ifr_phonet_autoconf.device);
 265        return 0;
 266}
 267
 268static void phonet_route_autodel(struct net_device *dev)
 269{
 270        struct phonet_net *pnn = phonet_pernet(dev_net(dev));
 271        unsigned int i;
 272        DECLARE_BITMAP(deleted, 64);
 273
 274        /* Remove left-over Phonet routes */
 275        bitmap_zero(deleted, 64);
 276        mutex_lock(&pnn->routes.lock);
 277        for (i = 0; i < 64; i++)
 278                if (dev == pnn->routes.table[i]) {
 279                        RCU_INIT_POINTER(pnn->routes.table[i], NULL);
 280                        set_bit(i, deleted);
 281                }
 282        mutex_unlock(&pnn->routes.lock);
 283
 284        if (bitmap_empty(deleted, 64))
 285                return; /* short-circuit RCU */
 286        synchronize_rcu();
 287        for_each_set_bit(i, deleted, 64) {
 288                rtm_phonet_notify(RTM_DELROUTE, dev, i);
 289                dev_put(dev);
 290        }
 291}
 292
 293/* notify Phonet of device events */
 294static int phonet_device_notify(struct notifier_block *me, unsigned long what,
 295                                void *arg)
 296{
 297        struct net_device *dev = arg;
 298
 299        switch (what) {
 300        case NETDEV_REGISTER:
 301                if (dev->type == ARPHRD_PHONET)
 302                        phonet_device_autoconf(dev);
 303                break;
 304        case NETDEV_UNREGISTER:
 305                phonet_device_destroy(dev);
 306                phonet_route_autodel(dev);
 307                break;
 308        }
 309        return 0;
 310
 311}
 312
 313static struct notifier_block phonet_device_notifier = {
 314        .notifier_call = phonet_device_notify,
 315        .priority = 0,
 316};
 317
 318/* Per-namespace Phonet devices handling */
 319static int __net_init phonet_init_net(struct net *net)
 320{
 321        struct phonet_net *pnn = phonet_pernet(net);
 322
 323        if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops))
 324                return -ENOMEM;
 325
 326        INIT_LIST_HEAD(&pnn->pndevs.list);
 327        mutex_init(&pnn->pndevs.lock);
 328        mutex_init(&pnn->routes.lock);
 329        return 0;
 330}
 331
 332static void __net_exit phonet_exit_net(struct net *net)
 333{
 334        proc_net_remove(net, "phonet");
 335}
 336
 337static struct pernet_operations phonet_net_ops = {
 338        .init = phonet_init_net,
 339        .exit = phonet_exit_net,
 340        .id   = &phonet_net_id,
 341        .size = sizeof(struct phonet_net),
 342};
 343
 344/* Initialize Phonet devices list */
 345int __init phonet_device_init(void)
 346{
 347        int err = register_pernet_subsys(&phonet_net_ops);
 348        if (err)
 349                return err;
 350
 351        proc_net_fops_create(&init_net, "pnresource", 0, &pn_res_seq_fops);
 352        register_netdevice_notifier(&phonet_device_notifier);
 353        err = phonet_netlink_register();
 354        if (err)
 355                phonet_device_exit();
 356        return err;
 357}
 358
 359void phonet_device_exit(void)
 360{
 361        rtnl_unregister_all(PF_PHONET);
 362        unregister_netdevice_notifier(&phonet_device_notifier);
 363        unregister_pernet_subsys(&phonet_net_ops);
 364        proc_net_remove(&init_net, "pnresource");
 365}
 366
 367int phonet_route_add(struct net_device *dev, u8 daddr)
 368{
 369        struct phonet_net *pnn = phonet_pernet(dev_net(dev));
 370        struct phonet_routes *routes = &pnn->routes;
 371        int err = -EEXIST;
 372
 373        daddr = daddr >> 2;
 374        mutex_lock(&routes->lock);
 375        if (routes->table[daddr] == NULL) {
 376                rcu_assign_pointer(routes->table[daddr], dev);
 377                dev_hold(dev);
 378                err = 0;
 379        }
 380        mutex_unlock(&routes->lock);
 381        return err;
 382}
 383
 384int phonet_route_del(struct net_device *dev, u8 daddr)
 385{
 386        struct phonet_net *pnn = phonet_pernet(dev_net(dev));
 387        struct phonet_routes *routes = &pnn->routes;
 388
 389        daddr = daddr >> 2;
 390        mutex_lock(&routes->lock);
 391        if (dev == routes->table[daddr])
 392                RCU_INIT_POINTER(routes->table[daddr], NULL);
 393        else
 394                dev = NULL;
 395        mutex_unlock(&routes->lock);
 396
 397        if (!dev)
 398                return -ENOENT;
 399        synchronize_rcu();
 400        dev_put(dev);
 401        return 0;
 402}
 403
 404struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr)
 405{
 406        struct phonet_net *pnn = phonet_pernet(net);
 407        struct phonet_routes *routes = &pnn->routes;
 408        struct net_device *dev;
 409
 410        daddr >>= 2;
 411        dev = rcu_dereference(routes->table[daddr]);
 412        return dev;
 413}
 414
 415struct net_device *phonet_route_output(struct net *net, u8 daddr)
 416{
 417        struct phonet_net *pnn = phonet_pernet(net);
 418        struct phonet_routes *routes = &pnn->routes;
 419        struct net_device *dev;
 420
 421        daddr >>= 2;
 422        rcu_read_lock();
 423        dev = rcu_dereference(routes->table[daddr]);
 424        if (dev)
 425                dev_hold(dev);
 426        rcu_read_unlock();
 427
 428        if (!dev)
 429                dev = phonet_device_get(net); /* Default route */
 430        return dev;
 431}
 432
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.