linux/drivers/net/wan/lapbether.c
<<
>>
Prefs
   1/*
   2 *      "LAPB via ethernet" driver release 001
   3 *
   4 *      This code REQUIRES 2.1.15 or higher/ NET3.038
   5 *
   6 *      This module:
   7 *              This module is free software; you can redistribute it and/or
   8 *              modify it under the terms of the GNU General Public License
   9 *              as published by the Free Software Foundation; either version
  10 *              2 of the License, or (at your option) any later version.
  11 *
  12 *      This is a "pseudo" network driver to allow LAPB over Ethernet.
  13 *
  14 *      This driver can use any ethernet destination address, and can be 
  15 *      limited to accept frames from one dedicated ethernet card only.
  16 *
  17 *      History
  18 *      LAPBETH 001     Jonathan Naylor         Cloned from bpqether.c
  19 *      2000-10-29      Henner Eisen    lapb_data_indication() return status.
  20 *      2000-11-14      Henner Eisen    dev_hold/put, NETDEV_GOING_DOWN support
  21 */
  22
  23#include <linux/errno.h>
  24#include <linux/types.h>
  25#include <linux/socket.h>
  26#include <linux/in.h>
  27#include <linux/kernel.h>
  28#include <linux/string.h>
  29#include <linux/net.h>
  30#include <linux/inet.h>
  31#include <linux/netdevice.h>
  32#include <linux/if_arp.h>
  33#include <linux/skbuff.h>
  34#include <net/sock.h>
  35#include <asm/system.h>
  36#include <asm/uaccess.h>
  37#include <linux/mm.h>
  38#include <linux/interrupt.h>
  39#include <linux/notifier.h>
  40#include <linux/stat.h>
  41#include <linux/netfilter.h>
  42#include <linux/module.h>
  43#include <linux/lapb.h>
  44#include <linux/init.h>
  45
  46#include <net/x25device.h>
  47
  48static char bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
  49
  50/* If this number is made larger, check that the temporary string buffer
  51 * in lapbeth_new_device is large enough to store the probe device name.*/
  52#define MAXLAPBDEV 100
  53
  54struct lapbethdev {
  55        struct list_head        node;
  56        struct net_device       *ethdev;        /* link to ethernet device */
  57        struct net_device       *axdev;         /* lapbeth device (lapb#) */
  58};
  59
  60static LIST_HEAD(lapbeth_devices);
  61
  62/* ------------------------------------------------------------------------ */
  63
  64/*
  65 *      Get the LAPB device for the ethernet device
  66 */
  67static struct lapbethdev *lapbeth_get_x25_dev(struct net_device *dev)
  68{
  69        struct lapbethdev *lapbeth;
  70
  71        list_for_each_entry_rcu(lapbeth, &lapbeth_devices, node) {
  72                if (lapbeth->ethdev == dev) 
  73                        return lapbeth;
  74        }
  75        return NULL;
  76}
  77
  78static __inline__ int dev_is_ethdev(struct net_device *dev)
  79{
  80        return dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5);
  81}
  82
  83/* ------------------------------------------------------------------------ */
  84
  85/*
  86 *      Receive a LAPB frame via an ethernet interface.
  87 */
  88static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev)
  89{
  90        int len, err;
  91        struct lapbethdev *lapbeth;
  92
  93        if (dev_net(dev) != &init_net)
  94                goto drop;
  95
  96        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
  97                return NET_RX_DROP;
  98
  99        if (!pskb_may_pull(skb, 2))
 100                goto drop;
 101
 102        rcu_read_lock();
 103        lapbeth = lapbeth_get_x25_dev(dev);
 104        if (!lapbeth)
 105                goto drop_unlock;
 106        if (!netif_running(lapbeth->axdev))
 107                goto drop_unlock;
 108
 109        len = skb->data[0] + skb->data[1] * 256;
 110        dev->stats.rx_packets++;
 111        dev->stats.rx_bytes += len;
 112
 113        skb_pull(skb, 2);       /* Remove the length bytes */
 114        skb_trim(skb, len);     /* Set the length of the data */
 115
 116        if ((err = lapb_data_received(lapbeth->axdev, skb)) != LAPB_OK) {
 117                printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n", err);
 118                goto drop_unlock;
 119        }
 120out:
 121        rcu_read_unlock();
 122        return 0;
 123drop_unlock:
 124        kfree_skb(skb);
 125        goto out;
 126drop:
 127        kfree_skb(skb);
 128        return 0;
 129}
 130
 131static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)
 132{
 133        unsigned char *ptr;
 134
 135        skb_push(skb, 1);
 136
 137        if (skb_cow(skb, 1))
 138                return NET_RX_DROP;
 139
 140        ptr  = skb->data;
 141        *ptr = 0x00;
 142
 143        skb->protocol = x25_type_trans(skb, dev);
 144        return netif_rx(skb);
 145}
 146
 147/*
 148 *      Send a LAPB frame via an ethernet interface
 149 */
 150static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
 151                                      struct net_device *dev)
 152{
 153        int err;
 154
 155        /*
 156         * Just to be *really* sure not to send anything if the interface
 157         * is down, the ethernet device may have gone.
 158         */
 159        if (!netif_running(dev))
 160                goto drop;
 161
 162        switch (skb->data[0]) {
 163        case 0x00:
 164                break;
 165        case 0x01:
 166                if ((err = lapb_connect_request(dev)) != LAPB_OK)
 167                        printk(KERN_ERR "lapbeth: lapb_connect_request "
 168                               "error: %d\n", err);
 169                goto drop;
 170        case 0x02:
 171                if ((err = lapb_disconnect_request(dev)) != LAPB_OK)
 172                        printk(KERN_ERR "lapbeth: lapb_disconnect_request "
 173                               "err: %d\n", err);
 174                /* Fall thru */
 175        default:
 176                goto drop;
 177        }
 178
 179        skb_pull(skb, 1);
 180
 181        if ((err = lapb_data_request(dev, skb)) != LAPB_OK) {
 182                printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err);
 183                goto drop;
 184        }
 185out:
 186        return NETDEV_TX_OK;
 187drop:
 188        kfree_skb(skb);
 189        goto out;
 190}
 191
 192static void lapbeth_data_transmit(struct net_device *ndev, struct sk_buff *skb)
 193{
 194        struct lapbethdev *lapbeth = netdev_priv(ndev);
 195        unsigned char *ptr;
 196        struct net_device *dev;
 197        int size = skb->len;
 198
 199        skb->protocol = htons(ETH_P_X25);
 200
 201        ptr = skb_push(skb, 2);
 202
 203        *ptr++ = size % 256;
 204        *ptr++ = size / 256;
 205
 206        ndev->stats.tx_packets++;
 207        ndev->stats.tx_bytes += size;
 208
 209        skb->dev = dev = lapbeth->ethdev;
 210
 211        dev_hard_header(skb, dev, ETH_P_DEC, bcast_addr, NULL, 0);
 212
 213        dev_queue_xmit(skb);
 214}
 215
 216static void lapbeth_connected(struct net_device *dev, int reason)
 217{
 218        unsigned char *ptr;
 219        struct sk_buff *skb = dev_alloc_skb(1);
 220
 221        if (!skb) {
 222                printk(KERN_ERR "lapbeth: out of memory\n");
 223                return;
 224        }
 225
 226        ptr  = skb_put(skb, 1);
 227        *ptr = 0x01;
 228
 229        skb->protocol = x25_type_trans(skb, dev);
 230        netif_rx(skb);
 231}
 232
 233static void lapbeth_disconnected(struct net_device *dev, int reason)
 234{
 235        unsigned char *ptr;
 236        struct sk_buff *skb = dev_alloc_skb(1);
 237
 238        if (!skb) {
 239                printk(KERN_ERR "lapbeth: out of memory\n");
 240                return;
 241        }
 242
 243        ptr  = skb_put(skb, 1);
 244        *ptr = 0x02;
 245
 246        skb->protocol = x25_type_trans(skb, dev);
 247        netif_rx(skb);
 248}
 249
 250/*
 251 *      Set AX.25 callsign
 252 */
 253static int lapbeth_set_mac_address(struct net_device *dev, void *addr)
 254{
 255        struct sockaddr *sa = addr;
 256        memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
 257        return 0;
 258}
 259
 260
 261static struct lapb_register_struct lapbeth_callbacks = {
 262        .connect_confirmation    = lapbeth_connected,
 263        .connect_indication      = lapbeth_connected,
 264        .disconnect_confirmation = lapbeth_disconnected,
 265        .disconnect_indication   = lapbeth_disconnected,
 266        .data_indication         = lapbeth_data_indication,
 267        .data_transmit           = lapbeth_data_transmit,
 268
 269};
 270
 271/*
 272 * open/close a device
 273 */
 274static int lapbeth_open(struct net_device *dev)
 275{
 276        int err;
 277
 278        if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {
 279                printk(KERN_ERR "lapbeth: lapb_register error - %d\n", err);
 280                return -ENODEV;
 281        }
 282
 283        netif_start_queue(dev);
 284        return 0;
 285}
 286
 287static int lapbeth_close(struct net_device *dev)
 288{
 289        int err;
 290
 291        netif_stop_queue(dev);
 292
 293        if ((err = lapb_unregister(dev)) != LAPB_OK)
 294                printk(KERN_ERR "lapbeth: lapb_unregister error - %d\n", err);
 295
 296        return 0;
 297}
 298
 299/* ------------------------------------------------------------------------ */
 300
 301static const struct net_device_ops lapbeth_netdev_ops = {
 302        .ndo_open            = lapbeth_open,
 303        .ndo_stop            = lapbeth_close,
 304        .ndo_start_xmit      = lapbeth_xmit,
 305        .ndo_set_mac_address = lapbeth_set_mac_address,
 306};
 307
 308static void lapbeth_setup(struct net_device *dev)
 309{
 310        dev->netdev_ops      = &lapbeth_netdev_ops;
 311        dev->destructor      = free_netdev;
 312        dev->type            = ARPHRD_X25;
 313        dev->hard_header_len = 3;
 314        dev->mtu             = 1000;
 315        dev->addr_len        = 0;
 316}
 317
 318/*
 319 *      Setup a new device.
 320 */
 321static int lapbeth_new_device(struct net_device *dev)
 322{
 323        struct net_device *ndev;
 324        struct lapbethdev *lapbeth;
 325        int rc = -ENOMEM;
 326
 327        ASSERT_RTNL();
 328
 329        ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", 
 330                           lapbeth_setup);
 331        if (!ndev)
 332                goto out;
 333
 334        lapbeth = netdev_priv(ndev);
 335        lapbeth->axdev = ndev;
 336
 337        dev_hold(dev);
 338        lapbeth->ethdev = dev;
 339
 340        rc = dev_alloc_name(ndev, ndev->name);
 341        if (rc < 0) 
 342                goto fail;
 343
 344        rc = -EIO;
 345        if (register_netdevice(ndev))
 346                goto fail;
 347
 348        list_add_rcu(&lapbeth->node, &lapbeth_devices);
 349        rc = 0;
 350out:
 351        return rc;
 352fail:
 353        dev_put(dev);
 354        free_netdev(ndev);
 355        kfree(lapbeth);
 356        goto out;
 357}
 358
 359/*
 360 *      Free a lapb network device.
 361 */
 362static void lapbeth_free_device(struct lapbethdev *lapbeth)
 363{
 364        dev_put(lapbeth->ethdev);
 365        list_del_rcu(&lapbeth->node);
 366        unregister_netdevice(lapbeth->axdev);
 367}
 368
 369/*
 370 *      Handle device status changes.
 371 *
 372 * Called from notifier with RTNL held.
 373 */
 374static int lapbeth_device_event(struct notifier_block *this,
 375                                unsigned long event, void *ptr)
 376{
 377        struct lapbethdev *lapbeth;
 378        struct net_device *dev = ptr;
 379
 380        if (dev_net(dev) != &init_net)
 381                return NOTIFY_DONE;
 382
 383        if (!dev_is_ethdev(dev))
 384                return NOTIFY_DONE;
 385
 386        switch (event) {
 387        case NETDEV_UP:
 388                /* New ethernet device -> new LAPB interface     */
 389                if (lapbeth_get_x25_dev(dev) == NULL)
 390                        lapbeth_new_device(dev);
 391                break;
 392        case NETDEV_DOWN:       
 393                /* ethernet device closed -> close LAPB interface */
 394                lapbeth = lapbeth_get_x25_dev(dev);
 395                if (lapbeth) 
 396                        dev_close(lapbeth->axdev);
 397                break;
 398        case NETDEV_UNREGISTER:
 399                /* ethernet device disappears -> remove LAPB interface */
 400                lapbeth = lapbeth_get_x25_dev(dev);
 401                if (lapbeth)
 402                        lapbeth_free_device(lapbeth);
 403                break;
 404        }
 405
 406        return NOTIFY_DONE;
 407}
 408
 409/* ------------------------------------------------------------------------ */
 410
 411static struct packet_type lapbeth_packet_type __read_mostly = {
 412        .type = cpu_to_be16(ETH_P_DEC),
 413        .func = lapbeth_rcv,
 414};
 415
 416static struct notifier_block lapbeth_dev_notifier = {
 417        .notifier_call = lapbeth_device_event,
 418};
 419
 420static const char banner[] __initconst =
 421        KERN_INFO "LAPB Ethernet driver version 0.02\n";
 422
 423static int __init lapbeth_init_driver(void)
 424{
 425        dev_add_pack(&lapbeth_packet_type);
 426
 427        register_netdevice_notifier(&lapbeth_dev_notifier);
 428
 429        printk(banner);
 430
 431        return 0;
 432}
 433module_init(lapbeth_init_driver);
 434
 435static void __exit lapbeth_cleanup_driver(void)
 436{
 437        struct lapbethdev *lapbeth;
 438        struct list_head *entry, *tmp;
 439
 440        dev_remove_pack(&lapbeth_packet_type);
 441        unregister_netdevice_notifier(&lapbeth_dev_notifier);
 442
 443        rtnl_lock();
 444        list_for_each_safe(entry, tmp, &lapbeth_devices) {
 445                lapbeth = list_entry(entry, struct lapbethdev, node);
 446
 447                dev_put(lapbeth->ethdev);
 448                unregister_netdevice(lapbeth->axdev);
 449        }
 450        rtnl_unlock();
 451}
 452module_exit(lapbeth_cleanup_driver);
 453
 454MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
 455MODULE_DESCRIPTION("The unofficial LAPB over Ethernet driver");
 456MODULE_LICENSE("GPL");
 457
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.