linux/drivers/net/wan/dlci.c
<<
>>
Prefs
   1/*
   2 * DLCI         Implementation of Frame Relay protocol for Linux, according to
   3 *              RFC 1490.  This generic device provides en/decapsulation for an
   4 *              underlying hardware driver.  Routes & IPs are assigned to these
   5 *              interfaces.  Requires 'dlcicfg' program to create usable 
   6 *              interfaces, the initial one, 'dlci' is for IOCTL use only.
   7 *
   8 * Version:     @(#)dlci.c      0.35    4 Jan 1997
   9 *
  10 * Author:      Mike McLagan <mike.mclagan@linux.org>
  11 *
  12 * Changes:
  13 *
  14 *              0.15    Mike Mclagan    Packet freeing, bug in kmalloc call
  15 *                                      DLCI_RET handling
  16 *              0.20    Mike McLagan    More conservative on which packets
  17 *                                      are returned for retry and which are
  18 *                                      are dropped.  If DLCI_RET_DROP is
  19 *                                      returned from the FRAD, the packet is
  20 *                                      sent back to Linux for re-transmission
  21 *              0.25    Mike McLagan    Converted to use SIOC IOCTL calls
  22 *              0.30    Jim Freeman     Fixed to allow IPX traffic
  23 *              0.35    Michael Elizabeth       Fixed incorrect memcpy_fromfs
  24 *
  25 *              This program is free software; you can redistribute it and/or
  26 *              modify it under the terms of the GNU General Public License
  27 *              as published by the Free Software Foundation; either version
  28 *              2 of the License, or (at your option) any later version.
  29 */
  30
  31#include <linux/module.h>
  32#include <linux/kernel.h>
  33#include <linux/types.h>
  34#include <linux/fcntl.h>
  35#include <linux/interrupt.h>
  36#include <linux/ptrace.h>
  37#include <linux/ioport.h>
  38#include <linux/in.h>
  39#include <linux/init.h>
  40#include <linux/slab.h>
  41#include <linux/string.h>
  42#include <linux/errno.h>
  43#include <linux/netdevice.h>
  44#include <linux/skbuff.h>
  45#include <linux/if_arp.h>
  46#include <linux/if_frad.h>
  47#include <linux/bitops.h>
  48
  49#include <net/sock.h>
  50
  51#include <asm/system.h>
  52#include <asm/io.h>
  53#include <asm/dma.h>
  54#include <asm/uaccess.h>
  55
  56static const char version[] = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
  57
  58static LIST_HEAD(dlci_devs);
  59
  60static void dlci_setup(struct net_device *);
  61
  62/* 
  63 * these encapsulate the RFC 1490 requirements as well as 
  64 * deal with packet transmission and reception, working with
  65 * the upper network layers 
  66 */
  67
  68static int dlci_header(struct sk_buff *skb, struct net_device *dev, 
  69                       unsigned short type, const void *daddr,
  70                       const void *saddr, unsigned len)
  71{
  72        struct frhdr            hdr;
  73        struct dlci_local       *dlp;
  74        unsigned int            hlen;
  75        char                    *dest;
  76
  77        dlp = dev->priv;
  78
  79        hdr.control = FRAD_I_UI;
  80        switch(type)
  81        {
  82                case ETH_P_IP:
  83                        hdr.IP_NLPID = FRAD_P_IP;
  84                        hlen = sizeof(hdr.control) + sizeof(hdr.IP_NLPID);
  85                        break;
  86
  87                /* feel free to add other types, if necessary */
  88
  89                default:
  90                        hdr.pad = FRAD_P_PADDING;
  91                        hdr.NLPID = FRAD_P_SNAP;
  92                        memset(hdr.OUI, 0, sizeof(hdr.OUI));
  93                        hdr.PID = htons(type);
  94                        hlen = sizeof(hdr);
  95                        break;
  96        }
  97
  98        dest = skb_push(skb, hlen);
  99        if (!dest)
 100                return(0);
 101
 102        memcpy(dest, &hdr, hlen);
 103
 104        return(hlen);
 105}
 106
 107static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
 108{
 109        struct dlci_local *dlp;
 110        struct frhdr            *hdr;
 111        int                                     process, header;
 112
 113        dlp = dev->priv;
 114        if (!pskb_may_pull(skb, sizeof(*hdr))) {
 115                printk(KERN_NOTICE "%s: invalid data no header\n",
 116                       dev->name);
 117                dlp->stats.rx_errors++;
 118                kfree_skb(skb);
 119                return;
 120        }
 121
 122        hdr = (struct frhdr *) skb->data;
 123        process = 0;
 124        header = 0;
 125        skb->dev = dev;
 126
 127        if (hdr->control != FRAD_I_UI)
 128        {
 129                printk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n", dev->name, hdr->control);
 130                dlp->stats.rx_errors++;
 131        }
 132        else
 133                switch(hdr->IP_NLPID)
 134                {
 135                        case FRAD_P_PADDING:
 136                                if (hdr->NLPID != FRAD_P_SNAP)
 137                                {
 138                                        printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->NLPID);
 139                                        dlp->stats.rx_errors++;
 140                                        break;
 141                                }
 142         
 143                                if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0)
 144                                {
 145                                        printk(KERN_NOTICE "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n", dev->name, hdr->OUI[0], hdr->OUI[1], hdr->OUI[2]);
 146                                        dlp->stats.rx_errors++;
 147                                        break;
 148                                }
 149
 150                                /* at this point, it's an EtherType frame */
 151                                header = sizeof(struct frhdr);
 152                                /* Already in network order ! */
 153                                skb->protocol = hdr->PID;
 154                                process = 1;
 155                                break;
 156
 157                        case FRAD_P_IP:
 158                                header = sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
 159                                skb->protocol = htons(ETH_P_IP);
 160                                process = 1;
 161                                break;
 162
 163                        case FRAD_P_SNAP:
 164                        case FRAD_P_Q933:
 165                        case FRAD_P_CLNP:
 166                                printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->pad);
 167                                dlp->stats.rx_errors++;
 168                                break;
 169
 170                        default:
 171                                printk(KERN_NOTICE "%s: Invalid pad byte 0x%02X.\n", dev->name, hdr->pad);
 172                                dlp->stats.rx_errors++;
 173                                break;                          
 174                }
 175
 176        if (process)
 177        {
 178                /* we've set up the protocol, so discard the header */
 179                skb_reset_mac_header(skb);
 180                skb_pull(skb, header);
 181                dlp->stats.rx_bytes += skb->len;
 182                netif_rx(skb);
 183                dlp->stats.rx_packets++;
 184                dev->last_rx = jiffies;
 185        }
 186        else
 187                dev_kfree_skb(skb);
 188}
 189
 190static int dlci_transmit(struct sk_buff *skb, struct net_device *dev)
 191{
 192        struct dlci_local *dlp;
 193        int                                     ret;
 194
 195        ret = 0;
 196
 197        if (!skb || !dev)
 198                return(0);
 199
 200        dlp = dev->priv;
 201
 202        netif_stop_queue(dev);
 203        
 204        ret = dlp->slave->hard_start_xmit(skb, dlp->slave);
 205        switch (ret)
 206        {
 207                case DLCI_RET_OK:
 208                        dlp->stats.tx_packets++;
 209                        ret = 0;
 210                        break;
 211                        case DLCI_RET_ERR:
 212                        dlp->stats.tx_errors++;
 213                        ret = 0;
 214                        break;
 215                        case DLCI_RET_DROP:
 216                        dlp->stats.tx_dropped++;
 217                        ret = 1;
 218                        break;
 219        }
 220        /* Alan Cox recommends always returning 0, and always freeing the packet */
 221        /* experience suggest a slightly more conservative approach */
 222
 223        if (!ret)
 224        {
 225                dev_kfree_skb(skb);
 226                netif_wake_queue(dev);
 227        }
 228        return(ret);
 229}
 230
 231static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, int get)
 232{
 233        struct dlci_conf        config;
 234        struct dlci_local       *dlp;
 235        struct frad_local       *flp;
 236        int                     err;
 237
 238        dlp = dev->priv;
 239
 240        flp = dlp->slave->priv;
 241
 242        if (!get)
 243        {
 244                if(copy_from_user(&config, conf, sizeof(struct dlci_conf)))
 245                        return -EFAULT;
 246                if (config.flags & ~DLCI_VALID_FLAGS)
 247                        return(-EINVAL);
 248                memcpy(&dlp->config, &config, sizeof(struct dlci_conf));
 249                dlp->configured = 1;
 250        }
 251
 252        err = (*flp->dlci_conf)(dlp->slave, dev, get);
 253        if (err)
 254                return(err);
 255
 256        if (get)
 257        {
 258                if(copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf)))
 259                        return -EFAULT;
 260        }
 261
 262        return(0);
 263}
 264
 265static int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 266{
 267        struct dlci_local *dlp;
 268
 269        if (!capable(CAP_NET_ADMIN))
 270                return(-EPERM);
 271
 272        dlp = dev->priv;
 273
 274        switch(cmd)
 275        {
 276                case DLCI_GET_SLAVE:
 277                        if (!*(short *)(dev->dev_addr))
 278                                return(-EINVAL);
 279
 280                        strncpy(ifr->ifr_slave, dlp->slave->name, sizeof(ifr->ifr_slave));
 281                        break;
 282
 283                case DLCI_GET_CONF:
 284                case DLCI_SET_CONF:
 285                        if (!*(short *)(dev->dev_addr))
 286                                return(-EINVAL);
 287
 288                        return(dlci_config(dev, ifr->ifr_data, cmd == DLCI_GET_CONF));
 289                        break;
 290
 291                default: 
 292                        return(-EOPNOTSUPP);
 293        }
 294        return(0);
 295}
 296
 297static int dlci_change_mtu(struct net_device *dev, int new_mtu)
 298{
 299        struct dlci_local *dlp;
 300
 301        dlp = dev->priv;
 302
 303        return((*dlp->slave->change_mtu)(dlp->slave, new_mtu));
 304}
 305
 306static int dlci_open(struct net_device *dev)
 307{
 308        struct dlci_local       *dlp;
 309        struct frad_local       *flp;
 310        int                     err;
 311
 312        dlp = dev->priv;
 313
 314        if (!*(short *)(dev->dev_addr))
 315                return(-EINVAL);
 316
 317        if (!netif_running(dlp->slave))
 318                return(-ENOTCONN);
 319
 320        flp = dlp->slave->priv;
 321        err = (*flp->activate)(dlp->slave, dev);
 322        if (err)
 323                return(err);
 324
 325        netif_start_queue(dev);
 326
 327        return 0;
 328}
 329
 330static int dlci_close(struct net_device *dev)
 331{
 332        struct dlci_local       *dlp;
 333        struct frad_local       *flp;
 334        int                     err;
 335
 336        netif_stop_queue(dev);
 337
 338        dlp = dev->priv;
 339
 340        flp = dlp->slave->priv;
 341        err = (*flp->deactivate)(dlp->slave, dev);
 342
 343        return 0;
 344}
 345
 346static struct net_device_stats *dlci_get_stats(struct net_device *dev)
 347{
 348        struct dlci_local *dlp;
 349
 350        dlp = dev->priv;
 351
 352        return(&dlp->stats);
 353}
 354
 355static int dlci_add(struct dlci_add *dlci)
 356{
 357        struct net_device       *master, *slave;
 358        struct dlci_local       *dlp;
 359        struct frad_local       *flp;
 360        int                     err = -EINVAL;
 361
 362
 363        /* validate slave device */
 364        slave = dev_get_by_name(&init_net, dlci->devname);
 365        if (!slave)
 366                return -ENODEV;
 367
 368        if (slave->type != ARPHRD_FRAD || slave->priv == NULL)
 369                goto err1;
 370
 371        /* create device name */
 372        master = alloc_netdev( sizeof(struct dlci_local), "dlci%d",
 373                              dlci_setup);
 374        if (!master) {
 375                err = -ENOMEM;
 376                goto err1;
 377        }
 378
 379        /* make sure same slave not already registered */
 380        rtnl_lock();
 381        list_for_each_entry(dlp, &dlci_devs, list) {
 382                if (dlp->slave == slave) {
 383                        err = -EBUSY;
 384                        goto err2;
 385                }
 386        }
 387
 388        err = dev_alloc_name(master, master->name);
 389        if (err < 0)
 390                goto err2;
 391
 392        *(short *)(master->dev_addr) = dlci->dlci;
 393
 394        dlp = (struct dlci_local *) master->priv;
 395        dlp->slave = slave;
 396        dlp->master = master;
 397
 398        flp = slave->priv;
 399        err = (*flp->assoc)(slave, master);
 400        if (err < 0)
 401                goto err2;
 402
 403        err = register_netdevice(master);
 404        if (err < 0) 
 405                goto err2;
 406
 407        strcpy(dlci->devname, master->name);
 408
 409        list_add(&dlp->list, &dlci_devs);
 410        rtnl_unlock();
 411
 412        return(0);
 413
 414 err2:
 415        rtnl_unlock();
 416        free_netdev(master);
 417 err1:
 418        dev_put(slave);
 419        return(err);
 420}
 421
 422static int dlci_del(struct dlci_add *dlci)
 423{
 424        struct dlci_local       *dlp;
 425        struct frad_local       *flp;
 426        struct net_device       *master, *slave;
 427        int                     err;
 428
 429        /* validate slave device */
 430        master = __dev_get_by_name(&init_net, dlci->devname);
 431        if (!master)
 432                return(-ENODEV);
 433
 434        if (netif_running(master)) {
 435                return(-EBUSY);
 436        }
 437
 438        dlp = master->priv;
 439        slave = dlp->slave;
 440        flp = slave->priv;
 441
 442        rtnl_lock();
 443        err = (*flp->deassoc)(slave, master);
 444        if (!err) {
 445                list_del(&dlp->list);
 446
 447                unregister_netdevice(master);
 448
 449                dev_put(slave);
 450        }
 451        rtnl_unlock();
 452
 453        return(err);
 454}
 455
 456static int dlci_ioctl(unsigned int cmd, void __user *arg)
 457{
 458        struct dlci_add add;
 459        int err;
 460        
 461        if (!capable(CAP_NET_ADMIN))
 462                return(-EPERM);
 463
 464        if(copy_from_user(&add, arg, sizeof(struct dlci_add)))
 465                return -EFAULT;
 466
 467        switch (cmd)
 468        {
 469                case SIOCADDDLCI:
 470                        err = dlci_add(&add);
 471
 472                        if (!err)
 473                                if(copy_to_user(arg, &add, sizeof(struct dlci_add)))
 474                                        return -EFAULT;
 475                        break;
 476
 477                case SIOCDELDLCI:
 478                        err = dlci_del(&add);
 479                        break;
 480
 481                default:
 482                        err = -EINVAL;
 483        }
 484
 485        return(err);
 486}
 487
 488static const struct header_ops dlci_header_ops = {
 489        .create = dlci_header,
 490};
 491
 492static void dlci_setup(struct net_device *dev)
 493{
 494        struct dlci_local *dlp = dev->priv;
 495
 496        dev->flags              = 0;
 497        dev->open               = dlci_open;
 498        dev->stop               = dlci_close;
 499        dev->do_ioctl           = dlci_dev_ioctl;
 500        dev->hard_start_xmit    = dlci_transmit;
 501        dev->header_ops         = &dlci_header_ops;
 502        dev->get_stats          = dlci_get_stats;
 503        dev->change_mtu         = dlci_change_mtu;
 504        dev->destructor         = free_netdev;
 505
 506        dlp->receive            = dlci_receive;
 507
 508        dev->type               = ARPHRD_DLCI;
 509        dev->hard_header_len    = sizeof(struct frhdr);
 510        dev->addr_len           = sizeof(short);
 511
 512}
 513
 514/* if slave is unregistering, then cleanup master */
 515static int dlci_dev_event(struct notifier_block *unused,
 516                          unsigned long event, void *ptr)
 517{
 518        struct net_device *dev = (struct net_device *) ptr;
 519
 520        if (dev->nd_net != &init_net)
 521                return NOTIFY_DONE;
 522
 523        if (event == NETDEV_UNREGISTER) {
 524                struct dlci_local *dlp;
 525
 526                list_for_each_entry(dlp, &dlci_devs, list) {
 527                        if (dlp->slave == dev) {
 528                                list_del(&dlp->list);
 529                                unregister_netdevice(dlp->master);
 530                                dev_put(dlp->slave);
 531                                break;
 532                        }
 533                }
 534        }
 535        return NOTIFY_DONE;
 536}
 537
 538static struct notifier_block dlci_notifier = {
 539        .notifier_call = dlci_dev_event,
 540};
 541
 542static int __init init_dlci(void)
 543{
 544        dlci_ioctl_set(dlci_ioctl);
 545        register_netdevice_notifier(&dlci_notifier);
 546
 547        printk("%s.\n", version);
 548
 549        return 0;
 550}
 551
 552static void __exit dlci_exit(void)
 553{
 554        struct dlci_local       *dlp, *nxt;
 555        
 556        dlci_ioctl_set(NULL);
 557        unregister_netdevice_notifier(&dlci_notifier);
 558
 559        rtnl_lock();
 560        list_for_each_entry_safe(dlp, nxt, &dlci_devs, list) {
 561                unregister_netdevice(dlp->master);
 562                dev_put(dlp->slave);
 563        }
 564        rtnl_unlock();
 565}
 566
 567module_init(init_dlci);
 568module_exit(dlci_exit);
 569
 570MODULE_AUTHOR("Mike McLagan");
 571MODULE_DESCRIPTION("Frame Relay DLCI layer");
 572MODULE_LICENSE("GPL");
 573