ppp: Move the PPP drivers
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>
Sun, 31 Jul 2011 09:38:19 +0000 (02:38 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Sat, 27 Aug 2011 07:58:26 +0000 (00:58 -0700)
Move the PPP drivers into drivers/net/ppp/ and make the
necessary Kconfig and Makefile changes.

CC: Paul Mackerras <paulus@samba.org>
CC: Frank Cusack <fcusack@fcusack.com>
CC: Michal Ostrowski <mostrows@speakeasy.net>
CC: Michal Ostrowski <mostrows@earthlink.net>
CC: Dmitry Kozlov <xeb@mail.ru>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
25 files changed:
MAINTAINERS
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/bsd_comp.c [deleted file]
drivers/net/ppp/Kconfig [new file with mode: 0644]
drivers/net/ppp/Makefile [new file with mode: 0644]
drivers/net/ppp/bsd_comp.c [new file with mode: 0644]
drivers/net/ppp/ppp_async.c [new file with mode: 0644]
drivers/net/ppp/ppp_deflate.c [new file with mode: 0644]
drivers/net/ppp/ppp_generic.c [new file with mode: 0644]
drivers/net/ppp/ppp_mppe.c [new file with mode: 0644]
drivers/net/ppp/ppp_mppe.h [new file with mode: 0644]
drivers/net/ppp/ppp_synctty.c [new file with mode: 0644]
drivers/net/ppp/pppoe.c [new file with mode: 0644]
drivers/net/ppp/pppox.c [new file with mode: 0644]
drivers/net/ppp/pptp.c [new file with mode: 0644]
drivers/net/ppp_async.c [deleted file]
drivers/net/ppp_deflate.c [deleted file]
drivers/net/ppp_generic.c [deleted file]
drivers/net/ppp_mppe.c [deleted file]
drivers/net/ppp_mppe.h [deleted file]
drivers/net/ppp_synctty.c [deleted file]
drivers/net/pppoe.c [deleted file]
drivers/net/pppox.c [deleted file]
drivers/net/pptp.c [deleted file]

index 2777088..c5ec925 100644 (file)
@@ -5066,7 +5066,7 @@ PPP PROTOCOL DRIVERS AND COMPRESSORS
 M:     Paul Mackerras <paulus@samba.org>
 L:     linux-ppp@vger.kernel.org
 S:     Maintained
-F:     drivers/net/ppp_*
+F:     drivers/net/ppp/ppp_*
 
 PPP OVER ATM (RFC 2364)
 M:     Mitchell Blank Jr <mitch@sfgoth.com>
@@ -5077,8 +5077,8 @@ F:        include/linux/atmppp.h
 PPP OVER ETHERNET
 M:     Michal Ostrowski <mostrows@earthlink.net>
 S:     Maintained
-F:     drivers/net/pppoe.c
-F:     drivers/net/pppox.c
+F:     drivers/net/ppp/pppoe.c
+F:     drivers/net/ppp/pppox.c
 
 PPP OVER L2TP
 M:     James Chapman <jchapman@katalix.com>
@@ -5099,7 +5099,7 @@ PPTP DRIVER
 M:     Dmitry Kozlov <xeb@mail.ru>
 L:     netdev@vger.kernel.org
 S:     Maintained
-F:     drivers/net/pptp.c
+F:     drivers/net/ppp/pptp.c
 W:     http://sourceforge.net/projects/accel-pptp
 
 PREEMPTIBLE KERNEL
index 7bdc22b..c5e2a38 100644 (file)
@@ -207,6 +207,8 @@ source "drivers/net/usb/Kconfig"
 
 source "drivers/net/pcmcia/Kconfig"
 
+source "drivers/net/ppp/Kconfig"
+
 source "drivers/net/wan/Kconfig"
 
 source "drivers/atm/Kconfig"
@@ -337,174 +339,6 @@ config PLIP
          will be called plip. If unsure, say Y or M, in case you buy
          a laptop later.
 
-config PPP
-       tristate "PPP (point-to-point protocol) support"
-       select SLHC
-       ---help---
-         PPP (Point to Point Protocol) is a newer and better SLIP.  It serves
-         the same purpose: sending Internet traffic over telephone (and other
-         serial) lines.  Ask your access provider if they support it, because
-         otherwise you can't use it; most Internet access providers these
-         days support PPP rather than SLIP.
-
-         To use PPP, you need an additional program called pppd as described
-         in the PPP-HOWTO, available at
-         <http://www.tldp.org/docs.html#howto>.  Make sure that you have
-         the version of pppd recommended in <file:Documentation/Changes>.
-         The PPP option enlarges your kernel by about 16 KB.
-
-         There are actually two versions of PPP: the traditional PPP for
-         asynchronous lines, such as regular analog phone lines, and
-         synchronous PPP which can be used over digital ISDN lines for
-         example.  If you want to use PPP over phone lines or other
-         asynchronous serial lines, you need to say Y (or M) here and also to
-         the next option, "PPP support for async serial ports".  For PPP over
-         synchronous lines, you should say Y (or M) here and to "Support
-         synchronous PPP", below.
-
-         If you said Y to "Version information on all symbols" above, then
-         you cannot compile the PPP driver into the kernel; you can then only
-         compile it as a module. To compile this driver as a module, choose M
-         here. The module will be called ppp_generic.
-
-config PPP_MULTILINK
-       bool "PPP multilink support (EXPERIMENTAL)"
-       depends on PPP && EXPERIMENTAL
-       help
-         PPP multilink is a protocol (defined in RFC 1990) which allows you
-         to combine several (logical or physical) lines into one logical PPP
-         connection, so that you can utilize your full bandwidth.
-
-         This has to be supported at the other end as well and you need a
-         version of the pppd daemon which understands the multilink protocol.
-
-         If unsure, say N.
-
-config PPP_FILTER
-       bool "PPP filtering"
-       depends on PPP
-       help
-         Say Y here if you want to be able to filter the packets passing over
-         PPP interfaces.  This allows you to control which packets count as
-         activity (i.e. which packets will reset the idle timer or bring up
-         a demand-dialed link) and which packets are to be dropped entirely.
-         You need to say Y here if you wish to use the pass-filter and
-         active-filter options to pppd.
-
-         If unsure, say N.
-
-config PPP_ASYNC
-       tristate "PPP support for async serial ports"
-       depends on PPP
-       select CRC_CCITT
-       ---help---
-         Say Y (or M) here if you want to be able to use PPP over standard
-         asynchronous serial ports, such as COM1 or COM2 on a PC.  If you use
-         a modem (not a synchronous or ISDN modem) to contact your ISP, you
-         need this option.
-
-         To compile this driver as a module, choose M here.
-
-         If unsure, say Y.
-
-config PPP_SYNC_TTY
-       tristate "PPP support for sync tty ports"
-       depends on PPP
-       help
-         Say Y (or M) here if you want to be able to use PPP over synchronous
-         (HDLC) tty devices, such as the SyncLink adapter. These devices
-         are often used for high-speed leased lines like T1/E1.
-
-         To compile this driver as a module, choose M here.
-
-config PPP_DEFLATE
-       tristate "PPP Deflate compression"
-       depends on PPP
-       select ZLIB_INFLATE
-       select ZLIB_DEFLATE
-       ---help---
-         Support for the Deflate compression method for PPP, which uses the
-         Deflate algorithm (the same algorithm that gzip uses) to compress
-         each PPP packet before it is sent over the wire.  The machine at the
-         other end of the PPP link (usually your ISP) has to support the
-         Deflate compression method as well for this to be useful.  Even if
-         they don't support it, it is safe to say Y here.
-
-         To compile this driver as a module, choose M here.
-
-config PPP_BSDCOMP
-       tristate "PPP BSD-Compress compression"
-       depends on PPP
-       ---help---
-         Support for the BSD-Compress compression method for PPP, which uses
-         the LZW compression method to compress each PPP packet before it is
-         sent over the wire. The machine at the other end of the PPP link
-         (usually your ISP) has to support the BSD-Compress compression
-         method as well for this to be useful. Even if they don't support it,
-         it is safe to say Y here.
-
-         The PPP Deflate compression method ("PPP Deflate compression",
-         above) is preferable to BSD-Compress, because it compresses better
-         and is patent-free.
-
-         Note that the BSD compression code will always be compiled as a
-         module; it is called bsd_comp and will show up in the directory
-         modules once you have said "make modules". If unsure, say N.
-
-config PPP_MPPE
-       tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)"
-       depends on PPP && EXPERIMENTAL
-       select CRYPTO
-       select CRYPTO_SHA1
-       select CRYPTO_ARC4
-       select CRYPTO_ECB
-       ---help---
-         Support for the MPPE Encryption protocol, as employed by the
-         Microsoft Point-to-Point Tunneling Protocol.
-
-         See http://pptpclient.sourceforge.net/ for information on
-         configuring PPTP clients and servers to utilize this method.
-
-config PPPOE
-       tristate "PPP over Ethernet (EXPERIMENTAL)"
-       depends on EXPERIMENTAL && PPP
-       help
-         Support for PPP over Ethernet.
-
-         This driver requires the latest version of pppd from the CVS
-         repository at cvs.samba.org.  Alternatively, see the 
-         RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
-         which contains instruction on how to use this driver (under 
-         the heading "Kernel mode PPPoE").
-
-config PPTP
-       tristate "PPP over IPv4 (PPTP) (EXPERIMENTAL)"
-       depends on EXPERIMENTAL && PPP && NET_IPGRE_DEMUX
-       help
-         Support for PPP over IPv4.(Point-to-Point Tunneling Protocol)
-
-         This driver requires pppd plugin to work in client mode or
-         modified pptpd (poptop) to work in server mode.
-         See http://accel-pptp.sourceforge.net/ for information how to
-         utilize this module.
-
-config PPPOATM
-       tristate "PPP over ATM"
-       depends on ATM && PPP
-       help
-         Support PPP (Point to Point Protocol) encapsulated in ATM frames.
-         This implementation does not yet comply with section 8 of RFC2364,
-         which can lead to bad results if the ATM peer loses state and
-         changes its encapsulation unilaterally.
-
-config PPPOL2TP
-       tristate "PPP over L2TP (EXPERIMENTAL)"
-       depends on EXPERIMENTAL && L2TP && PPP
-       help
-         Support for PPP-over-L2TP socket family. L2TP is a protocol
-         used by ISPs and enterprises to tunnel PPP traffic over UDP
-         tunnels. L2TP is replacing PPTP for VPN uses.
-
 config SLIP
        tristate "SLIP (serial line) support"
        ---help---
index 3087b27..a397f1e 100644 (file)
@@ -22,15 +22,6 @@ obj-$(CONFIG_RIONET) += rionet.o
 
 obj-$(CONFIG_NET) += Space.o loopback.o
 obj-$(CONFIG_NET_SB1000) += sb1000.o
-obj-$(CONFIG_PPP) += ppp_generic.o
-obj-$(CONFIG_PPP_ASYNC) += ppp_async.o
-obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o
-obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate.o
-obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
-obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o
-obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
-obj-$(CONFIG_PPPOL2TP) += pppox.o
-obj-$(CONFIG_PPTP) += pppox.o pptp.o
 obj-$(CONFIG_SLIP) += slip.o
 obj-$(CONFIG_SLHC) += slhc.o
 
@@ -48,6 +39,15 @@ obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_DEV_APPLETALK) += appletalk/
 obj-$(CONFIG_ETHERNET) += ethernet/
 obj-$(CONFIG_FDDI) += fddi/
+onj-$(CONFIG_PPP) += ppp/
+obj-$(CONFIG_PPP_ASYNC) += ppp/
+obj-$(CONFIG_PPP_BSDCOMP) += ppp/
+obj-$(CONFIG_PPP_DEFLATE) += ppp/
+obj-$(CONFIG_PPP_MPPE) += ppp/
+obj-$(CONFIG_PPP_SYNC_TTY) += ppp/
+obj-$(CONFIG_PPPOE) += ppp/
+obj-$(CONFIG_PPPOL2TP) += ppp/
+obj-$(CONFIG_PPTP) += ppp/
 obj-$(CONFIG_TR) += tokenring/
 obj-$(CONFIG_WAN) += wan/
 obj-$(CONFIG_ARCNET) += arcnet/
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
deleted file mode 100644 (file)
index a9b759a..0000000
+++ /dev/null
@@ -1,1170 +0,0 @@
-/*
- * Update: The Berkeley copyright was changed, and the change
- * is retroactive to all "true" BSD software (ie everything
- * from UCB as opposed to other peoples code that just carried
- * the same license). The new copyright doesn't clash with the
- * GPL, so the module-only restriction has been removed..
- */
-
-/* Because this code is derived from the 4.3BSD compress source:
- *
- * Copyright (c) 1985, 1986 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * James A. Woods, derived from original work by Spencer Thomas
- * and Joseph Orost.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * This version is for use with contiguous buffers on Linux-derived systems.
- *
- *  ==FILEVERSION 20000226==
- *
- *  NOTE TO MAINTAINERS:
- *     If you modify this file at all, please set the number above to the
- *     date of the modification as YYMMDD (year month day).
- *     bsd_comp.c is shipped with a PPP distribution as well as with
- *     the kernel; if everyone increases the FILEVERSION number above,
- *     then scripts can do the right thing when deciding whether to
- *     install a new bsd_comp.c file. Don't change the format of that
- *     line otherwise, so the installation script can recognize it.
- *
- * From: bsd_comp.c,v 1.3 1994/12/08 01:59:58 paulus Exp
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/string.h>
-
-#include <linux/ppp_defs.h>
-
-#undef   PACKETPTR
-#define  PACKETPTR 1
-#include <linux/ppp-comp.h>
-#undef   PACKETPTR
-
-#include <asm/byteorder.h>
-
-/*
- * PPP "BSD compress" compression
- *  The differences between this compression and the classic BSD LZW
- *  source are obvious from the requirement that the classic code worked
- *  with files while this handles arbitrarily long streams that
- *  are broken into packets.  They are:
- *
- *     When the code size expands, a block of junk is not emitted by
- *         the compressor and not expected by the decompressor.
- *
- *     New codes are not necessarily assigned every time an old
- *         code is output by the compressor.  This is because a packet
- *         end forces a code to be emitted, but does not imply that a
- *         new sequence has been seen.
- *
- *     The compression ratio is checked at the first end of a packet
- *         after the appropriate gap.  Besides simplifying and speeding
- *         things up, this makes it more likely that the transmitter
- *         and receiver will agree when the dictionary is cleared when
- *         compression is not going well.
- */
-
-/*
- * Macros to extract protocol version and number of bits
- * from the third byte of the BSD Compress CCP configuration option.
- */
-
-#define BSD_VERSION(x) ((x) >> 5)
-#define BSD_NBITS(x)   ((x) & 0x1F)
-
-#define BSD_CURRENT_VERSION    1
-
-/*
- * A dictionary for doing BSD compress.
- */
-
-struct bsd_dict {
-    union {                            /* hash value */
-       unsigned long   fcode;
-       struct {
-#if defined(__LITTLE_ENDIAN)           /* Little endian order */
-           unsigned short      prefix; /* preceding code */
-           unsigned char       suffix; /* last character of new code */
-           unsigned char       pad;
-#elif defined(__BIG_ENDIAN)            /* Big endian order */
-           unsigned char       pad;
-           unsigned char       suffix; /* last character of new code */
-           unsigned short      prefix; /* preceding code */
-#else
-#error Endianness not defined...
-#endif
-       } hs;
-    } f;
-    unsigned short codem1;             /* output of hash table -1 */
-    unsigned short cptr;               /* map code to hash table entry */
-};
-
-struct bsd_db {
-    int            totlen;                     /* length of this structure */
-    unsigned int   hsize;              /* size of the hash table */
-    unsigned char  hshift;             /* used in hash function */
-    unsigned char  n_bits;             /* current bits/code */
-    unsigned char  maxbits;            /* maximum bits/code */
-    unsigned char  debug;              /* non-zero if debug desired */
-    unsigned char  unit;               /* ppp unit number */
-    unsigned short seqno;              /* sequence # of next packet */
-    unsigned int   mru;                        /* size of receive (decompress) bufr */
-    unsigned int   maxmaxcode;         /* largest valid code */
-    unsigned int   max_ent;            /* largest code in use */
-    unsigned int   in_count;           /* uncompressed bytes, aged */
-    unsigned int   bytes_out;          /* compressed bytes, aged */
-    unsigned int   ratio;              /* recent compression ratio */
-    unsigned int   checkpoint;         /* when to next check the ratio */
-    unsigned int   clear_count;                /* times dictionary cleared */
-    unsigned int   incomp_count;       /* incompressible packets */
-    unsigned int   incomp_bytes;       /* incompressible bytes */
-    unsigned int   uncomp_count;       /* uncompressed packets */
-    unsigned int   uncomp_bytes;       /* uncompressed bytes */
-    unsigned int   comp_count;         /* compressed packets */
-    unsigned int   comp_bytes;         /* compressed bytes */
-    unsigned short  *lens;             /* array of lengths of codes */
-    struct bsd_dict *dict;             /* dictionary */
-};
-
-#define BSD_OVHD       2               /* BSD compress overhead/packet */
-#define MIN_BSD_BITS   9
-#define BSD_INIT_BITS  MIN_BSD_BITS
-#define MAX_BSD_BITS   15
-
-static void    bsd_free (void *state);
-static void    *bsd_alloc(unsigned char *options, int opt_len, int decomp);
-static void    *bsd_comp_alloc (unsigned char *options, int opt_len);
-static void    *bsd_decomp_alloc (unsigned char *options, int opt_len);
-
-static int     bsd_init        (void *db, unsigned char *options,
-                                int opt_len, int unit, int debug, int decomp);
-static int     bsd_comp_init   (void *state, unsigned char *options,
-                                int opt_len, int unit, int opthdr, int debug);
-static int     bsd_decomp_init (void *state, unsigned char *options,
-                                int opt_len, int unit, int opthdr, int mru,
-                                int debug);
-
-static void    bsd_reset (void *state);
-static void    bsd_comp_stats (void *state, struct compstat *stats);
-
-static int     bsd_compress (void *state, unsigned char *rptr,
-                             unsigned char *obuf, int isize, int osize);
-static void    bsd_incomp (void *state, unsigned char *ibuf, int icnt);
-
-static int     bsd_decompress (void *state, unsigned char *ibuf, int isize,
-                               unsigned char *obuf, int osize);
-
-/* These are in ppp_generic.c */
-extern int  ppp_register_compressor   (struct compressor *cp);
-extern void ppp_unregister_compressor (struct compressor *cp);
-
-/*
- * the next two codes should not be changed lightly, as they must not
- * lie within the contiguous general code space.
- */
-#define CLEAR  256                     /* table clear output code */
-#define FIRST  257                     /* first free entry */
-#define LAST   255
-
-#define MAXCODE(b)     ((1 << (b)) - 1)
-#define BADCODEM1      MAXCODE(MAX_BSD_BITS)
-
-#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
-                                        ^ (unsigned long)(prefix))
-#define BSD_KEY(prefix,suffix)         ((((unsigned long)(suffix)) << 16) \
-                                        + (unsigned long)(prefix))
-
-#define CHECK_GAP      10000           /* Ratio check interval */
-
-#define RATIO_SCALE_LOG        8
-#define RATIO_SCALE    (1<<RATIO_SCALE_LOG)
-#define RATIO_MAX      (0x7fffffff>>RATIO_SCALE_LOG)
-
-/*
- * clear the dictionary
- */
-
-static void
-bsd_clear(struct bsd_db *db)
-{
-    db->clear_count++;
-    db->max_ent      = FIRST-1;
-    db->n_bits       = BSD_INIT_BITS;
-    db->bytes_out    = 0;
-    db->in_count     = 0;
-    db->ratio       = 0;
-    db->checkpoint   = CHECK_GAP;
-}
-
-/*
- * If the dictionary is full, then see if it is time to reset it.
- *
- * Compute the compression ratio using fixed-point arithmetic
- * with 8 fractional bits.
- *
- * Since we have an infinite stream instead of a single file,
- * watch only the local compression ratio.
- *
- * Since both peers must reset the dictionary at the same time even in
- * the absence of CLEAR codes (while packets are incompressible), they
- * must compute the same ratio.
- */
-
-static int bsd_check (struct bsd_db *db)       /* 1=output CLEAR */
-  {
-    unsigned int new_ratio;
-
-    if (db->in_count >= db->checkpoint)
-      {
-       /* age the ratio by limiting the size of the counts */
-       if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
-         {
-           db->in_count  -= (db->in_count  >> 2);
-           db->bytes_out -= (db->bytes_out >> 2);
-         }
-
-       db->checkpoint = db->in_count + CHECK_GAP;
-
-       if (db->max_ent >= db->maxmaxcode)
-         {
-           /* Reset the dictionary only if the ratio is worse,
-            * or if it looks as if it has been poisoned
-            * by incompressible data.
-            *
-            * This does not overflow, because
-            *  db->in_count <= RATIO_MAX.
-            */
-
-           new_ratio = db->in_count << RATIO_SCALE_LOG;
-           if (db->bytes_out != 0)
-             {
-               new_ratio /= db->bytes_out;
-             }
-
-           if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
-             {
-               bsd_clear (db);
-               return 1;
-             }
-           db->ratio = new_ratio;
-         }
-      }
-    return 0;
-  }
-
-/*
- * Return statistics.
- */
-
-static void bsd_comp_stats (void *state, struct compstat *stats)
-  {
-    struct bsd_db *db = (struct bsd_db *) state;
-
-    stats->unc_bytes    = db->uncomp_bytes;
-    stats->unc_packets  = db->uncomp_count;
-    stats->comp_bytes   = db->comp_bytes;
-    stats->comp_packets = db->comp_count;
-    stats->inc_bytes    = db->incomp_bytes;
-    stats->inc_packets  = db->incomp_count;
-    stats->in_count     = db->in_count;
-    stats->bytes_out    = db->bytes_out;
-  }
-
-/*
- * Reset state, as on a CCP ResetReq.
- */
-
-static void bsd_reset (void *state)
-  {
-    struct bsd_db *db = (struct bsd_db *) state;
-
-    bsd_clear(db);
-
-    db->seqno       = 0;
-    db->clear_count = 0;
-  }
-
-/*
- * Release the compression structure
- */
-
-static void bsd_free (void *state)
-{
-       struct bsd_db *db = state;
-
-       if (!db)
-               return;
-
-/*
- * Release the dictionary
- */
-       vfree(db->dict);
-       db->dict = NULL;
-/*
- * Release the string buffer
- */
-       vfree(db->lens);
-       db->lens = NULL;
-/*
- * Finally release the structure itself.
- */
-       kfree(db);
-}
-
-/*
- * Allocate space for a (de) compressor.
- */
-
-static void *bsd_alloc (unsigned char *options, int opt_len, int decomp)
-  {
-    int bits;
-    unsigned int hsize, hshift, maxmaxcode;
-    struct bsd_db *db;
-
-    if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3
-       || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
-      {
-       return NULL;
-      }
-
-    bits = BSD_NBITS(options[2]);
-
-    switch (bits)
-      {
-    case 9:                    /* needs 82152 for both directions */
-    case 10:                   /* needs 84144 */
-    case 11:                   /* needs 88240 */
-    case 12:                   /* needs 96432 */
-       hsize = 5003;
-       hshift = 4;
-       break;
-    case 13:                   /* needs 176784 */
-       hsize = 9001;
-       hshift = 5;
-       break;
-    case 14:                   /* needs 353744 */
-       hsize = 18013;
-       hshift = 6;
-       break;
-    case 15:                   /* needs 691440 */
-       hsize = 35023;
-       hshift = 7;
-       break;
-    case 16:                   /* needs 1366160--far too much, */
-       /* hsize = 69001; */    /* and 69001 is too big for cptr */
-       /* hshift = 8; */       /* in struct bsd_db */
-       /* break; */
-    default:
-       return NULL;
-      }
-/*
- * Allocate the main control structure for this instance.
- */
-    maxmaxcode = MAXCODE(bits);
-    db         = kzalloc(sizeof (struct bsd_db),
-                                           GFP_KERNEL);
-    if (!db)
-      {
-       return NULL;
-      }
-
-/*
- * Allocate space for the dictionary. This may be more than one page in
- * length.
- */
-    db->dict = vmalloc(hsize * sizeof(struct bsd_dict));
-    if (!db->dict)
-      {
-       bsd_free (db);
-       return NULL;
-      }
-
-/*
- * If this is the compression buffer then there is no length data.
- */
-    if (!decomp)
-      {
-       db->lens = NULL;
-      }
-/*
- * For decompression, the length information is needed as well.
- */
-    else
-      {
-        db->lens = vmalloc((maxmaxcode + 1) * sizeof(db->lens[0]));
-       if (!db->lens)
-         {
-           bsd_free (db);
-           return NULL;
-         }
-      }
-/*
- * Initialize the data information for the compression code
- */
-    db->totlen     = sizeof (struct bsd_db)   +
-                   (sizeof (struct bsd_dict) * hsize);
-
-    db->hsize      = hsize;
-    db->hshift     = hshift;
-    db->maxmaxcode = maxmaxcode;
-    db->maxbits    = bits;
-
-    return (void *) db;
-  }
-
-static void *bsd_comp_alloc (unsigned char *options, int opt_len)
-  {
-    return bsd_alloc (options, opt_len, 0);
-  }
-
-static void *bsd_decomp_alloc (unsigned char *options, int opt_len)
-  {
-    return bsd_alloc (options, opt_len, 1);
-  }
-
-/*
- * Initialize the database.
- */
-
-static int bsd_init (void *state, unsigned char *options,
-                    int opt_len, int unit, int debug, int decomp)
-  {
-    struct bsd_db *db = state;
-    int indx;
-
-    if ((opt_len != 3) || (options[0] != CI_BSD_COMPRESS) || (options[1] != 3)
-       || (BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
-       || (BSD_NBITS(options[2]) != db->maxbits)
-       || (decomp && db->lens == NULL))
-      {
-       return 0;
-      }
-
-    if (decomp)
-      {
-       indx = LAST;
-       do
-         {
-           db->lens[indx] = 1;
-         }
-       while (indx-- > 0);
-      }
-
-    indx = db->hsize;
-    while (indx-- != 0)
-      {
-       db->dict[indx].codem1 = BADCODEM1;
-       db->dict[indx].cptr   = 0;
-      }
-
-    db->unit = unit;
-    db->mru  = 0;
-#ifndef DEBUG
-    if (debug)
-#endif
-      db->debug = 1;
-
-    bsd_reset(db);
-
-    return 1;
-  }
-
-static int bsd_comp_init (void *state, unsigned char *options,
-                         int opt_len, int unit, int opthdr, int debug)
-  {
-    return bsd_init (state, options, opt_len, unit, debug, 0);
-  }
-
-static int bsd_decomp_init (void *state, unsigned char *options,
-                           int opt_len, int unit, int opthdr, int mru,
-                           int debug)
-  {
-    return bsd_init (state, options, opt_len, unit, debug, 1);
-  }
-
-/*
- * Obtain pointers to the various structures in the compression tables
- */
-
-#define dict_ptrx(p,idx) &(p->dict[idx])
-#define lens_ptrx(p,idx) &(p->lens[idx])
-
-#ifdef DEBUG
-static unsigned short *lens_ptr(struct bsd_db *db, int idx)
-  {
-    if ((unsigned int) idx > (unsigned int) db->maxmaxcode)
-      {
-       printk ("<9>ppp: lens_ptr(%d) > max\n", idx);
-       idx = 0;
-      }
-    return lens_ptrx (db, idx);
-  }
-
-static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
-  {
-    if ((unsigned int) idx >= (unsigned int) db->hsize)
-      {
-       printk ("<9>ppp: dict_ptr(%d) > max\n", idx);
-       idx = 0;
-      }
-    return dict_ptrx (db, idx);
-  }
-
-#else
-#define lens_ptr(db,idx) lens_ptrx(db,idx)
-#define dict_ptr(db,idx) dict_ptrx(db,idx)
-#endif
-
-/*
- * compress a packet
- *
- *     The result of this function is the size of the compressed
- *     packet. A zero is returned if the packet was not compressed
- *     for some reason, such as the size being larger than uncompressed.
- *
- *     One change from the BSD compress command is that when the
- *     code size expands, we do not output a bunch of padding.
- */
-
-static int bsd_compress (void *state, unsigned char *rptr, unsigned char *obuf,
-                        int isize, int osize)
-  {
-    struct bsd_db *db;
-    int hshift;
-    unsigned int max_ent;
-    unsigned int n_bits;
-    unsigned int bitno;
-    unsigned long accm;
-    int ent;
-    unsigned long fcode;
-    struct bsd_dict *dictp;
-    unsigned char c;
-    int hval;
-    int disp;
-    int ilen;
-    int mxcode;
-    unsigned char *wptr;
-    int olen;
-
-#define PUTBYTE(v)                     \
-  {                                    \
-    ++olen;                            \
-    if (wptr)                          \
-      {                                        \
-       *wptr++ = (unsigned char) (v);  \
-       if (olen >= osize)              \
-         {                             \
-           wptr = NULL;                \
-         }                             \
-      }                                        \
-  }
-
-#define OUTPUT(ent)                    \
-  {                                    \
-    bitno -= n_bits;                   \
-    accm |= ((ent) << bitno);          \
-    do                                 \
-      {                                        \
-       PUTBYTE(accm >> 24);            \
-       accm <<= 8;                     \
-       bitno += 8;                     \
-      }                                        \
-    while (bitno <= 24);               \
-  }
-
-  /*
-   * If the protocol is not in the range we're interested in,
-   * just return without compressing the packet.  If it is,
-   * the protocol becomes the first byte to compress.
-   */
-
-    ent = PPP_PROTOCOL(rptr);
-    if (ent < 0x21 || ent > 0xf9)
-      {
-       return 0;
-      }
-
-    db      = (struct bsd_db *) state;
-    hshift  = db->hshift;
-    max_ent = db->max_ent;
-    n_bits  = db->n_bits;
-    bitno   = 32;
-    accm    = 0;
-    mxcode  = MAXCODE (n_bits);
-
-    /* Initialize the output pointers */
-    wptr  = obuf;
-    olen  = PPP_HDRLEN + BSD_OVHD;
-
-    if (osize > isize)
-      {
-       osize = isize;
-      }
-
-    /* This is the PPP header information */
-    if (wptr)
-      {
-       *wptr++ = PPP_ADDRESS(rptr);
-       *wptr++ = PPP_CONTROL(rptr);
-       *wptr++ = 0;
-       *wptr++ = PPP_COMP;
-       *wptr++ = db->seqno >> 8;
-       *wptr++ = db->seqno;
-      }
-
-    /* Skip the input header */
-    rptr  += PPP_HDRLEN;
-    isize -= PPP_HDRLEN;
-    ilen   = ++isize;  /* Low byte of protocol is counted as input */
-
-    while (--ilen > 0)
-      {
-       c     = *rptr++;
-       fcode = BSD_KEY  (ent, c);
-       hval  = BSD_HASH (ent, c, hshift);
-       dictp = dict_ptr (db, hval);
-
-       /* Validate and then check the entry. */
-       if (dictp->codem1 >= max_ent)
-         {
-           goto nomatch;
-         }
-
-       if (dictp->f.fcode == fcode)
-         {
-           ent = dictp->codem1 + 1;
-           continue;   /* found (prefix,suffix) */
-         }
-
-       /* continue probing until a match or invalid entry */
-       disp = (hval == 0) ? 1 : hval;
-
-       do
-         {
-           hval += disp;
-           if (hval >= db->hsize)
-             {
-               hval -= db->hsize;
-             }
-           dictp = dict_ptr (db, hval);
-           if (dictp->codem1 >= max_ent)
-             {
-               goto nomatch;
-             }
-         }
-       while (dictp->f.fcode != fcode);
-
-       ent = dictp->codem1 + 1;        /* finally found (prefix,suffix) */
-       continue;
-
-nomatch:
-       OUTPUT(ent);            /* output the prefix */
-
-       /* code -> hashtable */
-       if (max_ent < db->maxmaxcode)
-         {
-           struct bsd_dict *dictp2;
-           struct bsd_dict *dictp3;
-           int    indx;
-
-           /* expand code size if needed */
-           if (max_ent >= mxcode)
-             {
-               db->n_bits = ++n_bits;
-               mxcode     = MAXCODE (n_bits);
-             }
-
-           /* Invalidate old hash table entry using
-            * this code, and then take it over.
-            */
-
-           dictp2 = dict_ptr (db, max_ent + 1);
-           indx   = dictp2->cptr;
-           dictp3 = dict_ptr (db, indx);
-
-           if (dictp3->codem1 == max_ent)
-             {
-               dictp3->codem1 = BADCODEM1;
-             }
-
-           dictp2->cptr   = hval;
-           dictp->codem1  = max_ent;
-           dictp->f.fcode = fcode;
-           db->max_ent    = ++max_ent;
-
-           if (db->lens)
-             {
-               unsigned short *len1 = lens_ptr (db, max_ent);
-               unsigned short *len2 = lens_ptr (db, ent);
-               *len1 = *len2 + 1;
-             }
-         }
-       ent = c;
-      }
-
-    OUTPUT(ent);               /* output the last code */
-
-    db->bytes_out    += olen - PPP_HDRLEN - BSD_OVHD;
-    db->uncomp_bytes += isize;
-    db->in_count     += isize;
-    ++db->uncomp_count;
-    ++db->seqno;
-
-    if (bitno < 32)
-      {
-       ++db->bytes_out; /* must be set before calling bsd_check */
-      }
-
-    /*
-     * Generate the clear command if needed
-     */
-
-    if (bsd_check(db))
-      {
-       OUTPUT (CLEAR);
-      }
-
-    /*
-     * Pad dribble bits of last code with ones.
-     * Do not emit a completely useless byte of ones.
-     */
-
-    if (bitno != 32)
-      {
-       PUTBYTE((accm | (0xff << (bitno-8))) >> 24);
-      }
-
-    /*
-     * Increase code size if we would have without the packet
-     * boundary because the decompressor will do so.
-     */
-
-    if (max_ent >= mxcode && max_ent < db->maxmaxcode)
-      {
-       db->n_bits++;
-      }
-
-    /* If output length is too large then this is an incomplete frame. */
-    if (wptr == NULL)
-      {
-       ++db->incomp_count;
-       db->incomp_bytes += isize;
-       olen              = 0;
-      }
-    else /* Count the number of compressed frames */
-      {
-       ++db->comp_count;
-       db->comp_bytes += olen;
-      }
-
-    /* Return the resulting output length */
-    return olen;
-#undef OUTPUT
-#undef PUTBYTE
-  }
-
-/*
- * Update the "BSD Compress" dictionary on the receiver for
- * incompressible data by pretending to compress the incoming data.
- */
-
-static void bsd_incomp (void *state, unsigned char *ibuf, int icnt)
-  {
-    (void) bsd_compress (state, ibuf, (char *) 0, icnt, 0);
-  }
-
-/*
- * Decompress "BSD Compress".
- *
- * Because of patent problems, we return DECOMP_ERROR for errors
- * found by inspecting the input data and for system problems, but
- * DECOMP_FATALERROR for any errors which could possibly be said to
- * be being detected "after" decompression.  For DECOMP_ERROR,
- * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
- * infringing a patent of Motorola's if we do, so we take CCP down
- * instead.
- *
- * Given that the frame has the correct sequence number and a good FCS,
- * errors such as invalid codes in the input most likely indicate a
- * bug, so we return DECOMP_FATALERROR for them in order to turn off
- * compression, even though they are detected by inspecting the input.
- */
-
-static int bsd_decompress (void *state, unsigned char *ibuf, int isize,
-                          unsigned char *obuf, int osize)
-  {
-    struct bsd_db *db;
-    unsigned int max_ent;
-    unsigned long accm;
-    unsigned int bitno;                /* 1st valid bit in accm */
-    unsigned int n_bits;
-    unsigned int tgtbitno;     /* bitno when we have a code */
-    struct bsd_dict *dictp;
-    int explen;
-    int seq;
-    unsigned int incode;
-    unsigned int oldcode;
-    unsigned int finchar;
-    unsigned char *p;
-    unsigned char *wptr;
-    int adrs;
-    int ctrl;
-    int ilen;
-    int codelen;
-    int extra;
-
-    db       = (struct bsd_db *) state;
-    max_ent  = db->max_ent;
-    accm     = 0;
-    bitno    = 32;             /* 1st valid bit in accm */
-    n_bits   = db->n_bits;
-    tgtbitno = 32 - n_bits;    /* bitno when we have a code */
-
-    /*
-     * Save the address/control from the PPP header
-     * and then get the sequence number.
-     */
-
-    adrs  = PPP_ADDRESS (ibuf);
-    ctrl  = PPP_CONTROL (ibuf);
-
-    seq   = (ibuf[4] << 8) + ibuf[5];
-
-    ibuf += (PPP_HDRLEN + 2);
-    ilen  = isize - (PPP_HDRLEN + 2);
-
-    /*
-     * Check the sequence number and give up if it differs from
-     * the value we're expecting.
-     */
-
-    if (seq != db->seqno)
-      {
-       if (db->debug)
-         {
-           printk("bsd_decomp%d: bad sequence # %d, expected %d\n",
-                  db->unit, seq, db->seqno - 1);
-         }
-       return DECOMP_ERROR;
-      }
-
-    ++db->seqno;
-    db->bytes_out += ilen;
-
-    /*
-     * Fill in the ppp header, but not the last byte of the protocol
-     * (that comes from the decompressed data).
-     */
-
-    wptr    = obuf;
-    *wptr++ = adrs;
-    *wptr++ = ctrl;
-    *wptr++ = 0;
-
-    oldcode = CLEAR;
-    explen  = 3;
-
-    /*
-     * Keep the checkpoint correctly so that incompressible packets
-     * clear the dictionary at the proper times.
-     */
-
-    for (;;)
-      {
-       if (ilen-- <= 0)
-         {
-           db->in_count += (explen - 3); /* don't count the header */
-           break;
-         }
-
-       /*
-        * Accumulate bytes until we have a complete code.
-        * Then get the next code, relying on the 32-bit,
-        * unsigned accm to mask the result.
-        */
-
-       bitno -= 8;
-       accm  |= *ibuf++ << bitno;
-       if (tgtbitno < bitno)
-         {
-           continue;
-         }
-
-       incode = accm >> tgtbitno;
-       accm <<= n_bits;
-       bitno += n_bits;
-
-       /*
-        * The dictionary must only be cleared at the end of a packet.
-        */
-
-       if (incode == CLEAR)
-         {
-           if (ilen > 0)
-             {
-               if (db->debug)
-                 {
-                   printk("bsd_decomp%d: bad CLEAR\n", db->unit);
-                 }
-               return DECOMP_FATALERROR;       /* probably a bug */
-             }
-
-           bsd_clear(db);
-           break;
-         }
-
-       if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
-           || (incode > max_ent && oldcode == CLEAR))
-         {
-           if (db->debug)
-             {
-               printk("bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
-                      db->unit, incode, oldcode);
-               printk("max_ent=0x%x explen=%d seqno=%d\n",
-                      max_ent, explen, db->seqno);
-             }
-           return DECOMP_FATALERROR;   /* probably a bug */
-         }
-
-       /* Special case for KwKwK string. */
-       if (incode > max_ent)
-         {
-           finchar = oldcode;
-           extra   = 1;
-         }
-       else
-         {
-           finchar = incode;
-           extra   = 0;
-         }
-
-       codelen = *(lens_ptr (db, finchar));
-       explen += codelen + extra;
-       if (explen > osize)
-         {
-           if (db->debug)
-             {
-               printk("bsd_decomp%d: ran out of mru\n", db->unit);
-#ifdef DEBUG
-               printk("  len=%d, finchar=0x%x, codelen=%d, explen=%d\n",
-                      ilen, finchar, codelen, explen);
-#endif
-             }
-           return DECOMP_FATALERROR;
-         }
-
-       /*
-        * Decode this code and install it in the decompressed buffer.
-        */
-
-       wptr += codelen;
-       p     = wptr;
-       while (finchar > LAST)
-         {
-           struct bsd_dict *dictp2 = dict_ptr (db, finchar);
-
-           dictp = dict_ptr (db, dictp2->cptr);
-#ifdef DEBUG
-           if (--codelen <= 0 || dictp->codem1 != finchar-1)
-             {
-               if (codelen <= 0)
-                 {
-                   printk("bsd_decomp%d: fell off end of chain ", db->unit);
-                   printk("0x%x at 0x%x by 0x%x, max_ent=0x%x\n",
-                          incode, finchar, dictp2->cptr, max_ent);
-                 }
-               else
-                 {
-                   if (dictp->codem1 != finchar-1)
-                     {
-                       printk("bsd_decomp%d: bad code chain 0x%x "
-                              "finchar=0x%x ",
-                              db->unit, incode, finchar);
-
-                       printk("oldcode=0x%x cptr=0x%x codem1=0x%x\n",
-                              oldcode, dictp2->cptr, dictp->codem1);
-                     }
-                 }
-               return DECOMP_FATALERROR;
-             }
-#endif
-           *--p    = dictp->f.hs.suffix;
-           finchar = dictp->f.hs.prefix;
-         }
-       *--p = finchar;
-
-#ifdef DEBUG
-       if (--codelen != 0)
-         {
-           printk("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n",
-                  db->unit, codelen, incode, max_ent);
-         }
-#endif
-
-       if (extra)              /* the KwKwK case again */
-         {
-           *wptr++ = finchar;
-         }
-
-       /*
-        * If not first code in a packet, and
-        * if not out of code space, then allocate a new code.
-        *
-        * Keep the hash table correct so it can be used
-        * with uncompressed packets.
-        */
-
-       if (oldcode != CLEAR && max_ent < db->maxmaxcode)
-         {
-           struct bsd_dict *dictp2, *dictp3;
-           unsigned short  *lens1,  *lens2;
-           unsigned long fcode;
-           int hval, disp, indx;
-
-           fcode = BSD_KEY(oldcode,finchar);
-           hval  = BSD_HASH(oldcode,finchar,db->hshift);
-           dictp = dict_ptr (db, hval);
-
-           /* look for a free hash table entry */
-           if (dictp->codem1 < max_ent)
-             {
-               disp = (hval == 0) ? 1 : hval;
-               do
-                 {
-                   hval += disp;
-                   if (hval >= db->hsize)
-                     {
-                       hval -= db->hsize;
-                     }
-                   dictp = dict_ptr (db, hval);
-                 }
-               while (dictp->codem1 < max_ent);
-             }
-
-           /*
-            * Invalidate previous hash table entry
-            * assigned this code, and then take it over
-            */
-
-           dictp2 = dict_ptr (db, max_ent + 1);
-           indx   = dictp2->cptr;
-           dictp3 = dict_ptr (db, indx);
-
-           if (dictp3->codem1 == max_ent)
-             {
-               dictp3->codem1 = BADCODEM1;
-             }
-
-           dictp2->cptr   = hval;
-           dictp->codem1  = max_ent;
-           dictp->f.fcode = fcode;
-           db->max_ent    = ++max_ent;
-
-           /* Update the length of this string. */
-           lens1  = lens_ptr (db, max_ent);
-           lens2  = lens_ptr (db, oldcode);
-           *lens1 = *lens2 + 1;
-
-           /* Expand code size if needed. */
-           if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
-             {
-               db->n_bits = ++n_bits;
-               tgtbitno   = 32-n_bits;
-             }
-         }
-       oldcode = incode;
-      }
-
-    ++db->comp_count;
-    ++db->uncomp_count;
-    db->comp_bytes   += isize - BSD_OVHD - PPP_HDRLEN;
-    db->uncomp_bytes += explen;
-
-    if (bsd_check(db))
-      {
-       if (db->debug)
-         {
-           printk("bsd_decomp%d: peer should have cleared dictionary on %d\n",
-                  db->unit, db->seqno - 1);
-         }
-      }
-    return explen;
-  }
-
-/*************************************************************
- * Table of addresses for the BSD compression module
- *************************************************************/
-
-static struct compressor ppp_bsd_compress = {
-       .compress_proto =       CI_BSD_COMPRESS,
-       .comp_alloc =           bsd_comp_alloc,
-       .comp_free =            bsd_free,
-       .comp_init =            bsd_comp_init,
-       .comp_reset =           bsd_reset,
-       .compress =             bsd_compress,
-       .comp_stat =            bsd_comp_stats,
-       .decomp_alloc =         bsd_decomp_alloc,
-       .decomp_free =          bsd_free,
-       .decomp_init =          bsd_decomp_init,
-       .decomp_reset =         bsd_reset,
-       .decompress =           bsd_decompress,
-       .incomp =               bsd_incomp,
-       .decomp_stat =          bsd_comp_stats,
-       .owner =                THIS_MODULE
-};
-
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-static int __init bsdcomp_init(void)
-{
-       int answer = ppp_register_compressor(&ppp_bsd_compress);
-       if (answer == 0)
-               printk(KERN_INFO "PPP BSD Compression module registered\n");
-       return answer;
-}
-
-static void __exit bsdcomp_cleanup(void)
-{
-       ppp_unregister_compressor(&ppp_bsd_compress);
-}
-
-module_init(bsdcomp_init);
-module_exit(bsdcomp_cleanup);
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("ppp-compress-" __stringify(CI_BSD_COMPRESS));
diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig
new file mode 100644 (file)
index 0000000..872df3e
--- /dev/null
@@ -0,0 +1,175 @@
+#
+# PPP network device configuration
+#
+
+config PPP
+       tristate "PPP (point-to-point protocol) support"
+       select SLHC
+       ---help---
+         PPP (Point to Point Protocol) is a newer and better SLIP.  It serves
+         the same purpose: sending Internet traffic over telephone (and other
+         serial) lines.  Ask your access provider if they support it, because
+         otherwise you can't use it; most Internet access providers these
+         days support PPP rather than SLIP.
+
+         To use PPP, you need an additional program called pppd as described
+         in the PPP-HOWTO, available at
+         <http://www.tldp.org/docs.html#howto>.  Make sure that you have
+         the version of pppd recommended in <file:Documentation/Changes>.
+         The PPP option enlarges your kernel by about 16 KB.
+
+         There are actually two versions of PPP: the traditional PPP for
+         asynchronous lines, such as regular analog phone lines, and
+         synchronous PPP which can be used over digital ISDN lines for
+         example.  If you want to use PPP over phone lines or other
+         asynchronous serial lines, you need to say Y (or M) here and also to
+         the next option, "PPP support for async serial ports".  For PPP over
+         synchronous lines, you should say Y (or M) here and to "Support
+         synchronous PPP", below.
+
+         If you said Y to "Version information on all symbols" above, then
+         you cannot compile the PPP driver into the kernel; you can then only
+         compile it as a module. To compile this driver as a module, choose M
+         here. The module will be called ppp_generic.
+
+if PPP
+
+config PPP_BSDCOMP
+       tristate "PPP BSD-Compress compression"
+       depends on PPP
+       ---help---
+         Support for the BSD-Compress compression method for PPP, which uses
+         the LZW compression method to compress each PPP packet before it is
+         sent over the wire. The machine at the other end of the PPP link
+         (usually your ISP) has to support the BSD-Compress compression
+         method as well for this to be useful. Even if they don't support it,
+         it is safe to say Y here.
+
+         The PPP Deflate compression method ("PPP Deflate compression",
+         above) is preferable to BSD-Compress, because it compresses better
+         and is patent-free.
+
+         Note that the BSD compression code will always be compiled as a
+         module; it is called bsd_comp and will show up in the directory
+         modules once you have said "make modules". If unsure, say N.
+
+config PPP_DEFLATE
+       tristate "PPP Deflate compression"
+       depends on PPP
+       select ZLIB_INFLATE
+       select ZLIB_DEFLATE
+       ---help---
+         Support for the Deflate compression method for PPP, which uses the
+         Deflate algorithm (the same algorithm that gzip uses) to compress
+         each PPP packet before it is sent over the wire.  The machine at the
+         other end of the PPP link (usually your ISP) has to support the
+         Deflate compression method as well for this to be useful.  Even if
+         they don't support it, it is safe to say Y here.
+
+         To compile this driver as a module, choose M here.
+
+config PPP_FILTER
+       bool "PPP filtering"
+       depends on PPP
+       ---help---
+         Say Y here if you want to be able to filter the packets passing over
+         PPP interfaces.  This allows you to control which packets count as
+         activity (i.e. which packets will reset the idle timer or bring up
+         a demand-dialed link) and which packets are to be dropped entirely.
+         You need to say Y here if you wish to use the pass-filter and
+         active-filter options to pppd.
+
+         If unsure, say N.
+
+config PPP_MPPE
+       tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)"
+       depends on PPP && EXPERIMENTAL
+       select CRYPTO
+       select CRYPTO_SHA1
+       select CRYPTO_ARC4
+       select CRYPTO_ECB
+       ---help---
+         Support for the MPPE Encryption protocol, as employed by the
+         Microsoft Point-to-Point Tunneling Protocol.
+
+         See http://pptpclient.sourceforge.net/ for information on
+         configuring PPTP clients and servers to utilize this method.
+
+config PPP_MULTILINK
+       bool "PPP multilink support (EXPERIMENTAL)"
+       depends on PPP && EXPERIMENTAL
+       ---help---
+         PPP multilink is a protocol (defined in RFC 1990) which allows you
+         to combine several (logical or physical) lines into one logical PPP
+         connection, so that you can utilize your full bandwidth.
+
+         This has to be supported at the other end as well and you need a
+         version of the pppd daemon which understands the multilink protocol.
+
+         If unsure, say N.
+
+config PPPOATM
+       tristate "PPP over ATM"
+       depends on ATM && PPP
+       ---help---
+         Support PPP (Point to Point Protocol) encapsulated in ATM frames.
+         This implementation does not yet comply with section 8 of RFC2364,
+         which can lead to bad results if the ATM peer loses state and
+         changes its encapsulation unilaterally.
+
+config PPPOE
+       tristate "PPP over Ethernet (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && PPP
+       ---help---
+         Support for PPP over Ethernet.
+
+         This driver requires the latest version of pppd from the CVS
+         repository at cvs.samba.org.  Alternatively, see the
+         RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
+         which contains instruction on how to use this driver (under
+         the heading "Kernel mode PPPoE").
+
+config PPTP
+       tristate "PPP over IPv4 (PPTP) (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && PPP && NET_IPGRE_DEMUX
+       ---help---
+         Support for PPP over IPv4.(Point-to-Point Tunneling Protocol)
+
+         This driver requires pppd plugin to work in client mode or
+         modified pptpd (poptop) to work in server mode.
+         See http://accel-pptp.sourceforge.net/ for information how to
+         utilize this module.
+
+config PPPOL2TP
+       tristate "PPP over L2TP (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && L2TP && PPP
+       ---help---
+         Support for PPP-over-L2TP socket family. L2TP is a protocol
+         used by ISPs and enterprises to tunnel PPP traffic over UDP
+         tunnels. L2TP is replacing PPTP for VPN uses.
+
+config PPP_ASYNC
+       tristate "PPP support for async serial ports"
+       depends on PPP
+       select CRC_CCITT
+       ---help---
+         Say Y (or M) here if you want to be able to use PPP over standard
+         asynchronous serial ports, such as COM1 or COM2 on a PC.  If you use
+         a modem (not a synchronous or ISDN modem) to contact your ISP, you
+         need this option.
+
+         To compile this driver as a module, choose M here.
+
+         If unsure, say Y.
+
+config PPP_SYNC_TTY
+       tristate "PPP support for sync tty ports"
+       depends on PPP
+       ---help---
+         Say Y (or M) here if you want to be able to use PPP over synchronous
+         (HDLC) tty devices, such as the SyncLink adapter. These devices
+         are often used for high-speed leased lines like T1/E1.
+
+         To compile this driver as a module, choose M here.
+
+endif # PPP
diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile
new file mode 100644 (file)
index 0000000..a6b6297
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for the Linux PPP network device drivers.
+#
+
+obj-$(CONFIG_PPP) += ppp_generic.o
+obj-$(CONFIG_PPP_ASYNC) += ppp_async.o
+obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
+obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate.o
+obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o
+obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o
+obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
+obj-$(CONFIG_PPPOL2TP) += pppox.o
+obj-$(CONFIG_PPTP) += pppox.o pptp.o
diff --git a/drivers/net/ppp/bsd_comp.c b/drivers/net/ppp/bsd_comp.c
new file mode 100644 (file)
index 0000000..a9b759a
--- /dev/null
@@ -0,0 +1,1170 @@
+/*
+ * Update: The Berkeley copyright was changed, and the change
+ * is retroactive to all "true" BSD software (ie everything
+ * from UCB as opposed to other peoples code that just carried
+ * the same license). The new copyright doesn't clash with the
+ * GPL, so the module-only restriction has been removed..
+ */
+
+/* Because this code is derived from the 4.3BSD compress source:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This version is for use with contiguous buffers on Linux-derived systems.
+ *
+ *  ==FILEVERSION 20000226==
+ *
+ *  NOTE TO MAINTAINERS:
+ *     If you modify this file at all, please set the number above to the
+ *     date of the modification as YYMMDD (year month day).
+ *     bsd_comp.c is shipped with a PPP distribution as well as with
+ *     the kernel; if everyone increases the FILEVERSION number above,
+ *     then scripts can do the right thing when deciding whether to
+ *     install a new bsd_comp.c file. Don't change the format of that
+ *     line otherwise, so the installation script can recognize it.
+ *
+ * From: bsd_comp.c,v 1.3 1994/12/08 01:59:58 paulus Exp
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+
+#include <linux/ppp_defs.h>
+
+#undef   PACKETPTR
+#define  PACKETPTR 1
+#include <linux/ppp-comp.h>
+#undef   PACKETPTR
+
+#include <asm/byteorder.h>
+
+/*
+ * PPP "BSD compress" compression
+ *  The differences between this compression and the classic BSD LZW
+ *  source are obvious from the requirement that the classic code worked
+ *  with files while this handles arbitrarily long streams that
+ *  are broken into packets.  They are:
+ *
+ *     When the code size expands, a block of junk is not emitted by
+ *         the compressor and not expected by the decompressor.
+ *
+ *     New codes are not necessarily assigned every time an old
+ *         code is output by the compressor.  This is because a packet
+ *         end forces a code to be emitted, but does not imply that a
+ *         new sequence has been seen.
+ *
+ *     The compression ratio is checked at the first end of a packet
+ *         after the appropriate gap.  Besides simplifying and speeding
+ *         things up, this makes it more likely that the transmitter
+ *         and receiver will agree when the dictionary is cleared when
+ *         compression is not going well.
+ */
+
+/*
+ * Macros to extract protocol version and number of bits
+ * from the third byte of the BSD Compress CCP configuration option.
+ */
+
+#define BSD_VERSION(x) ((x) >> 5)
+#define BSD_NBITS(x)   ((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION    1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+    union {                            /* hash value */
+       unsigned long   fcode;
+       struct {
+#if defined(__LITTLE_ENDIAN)           /* Little endian order */
+           unsigned short      prefix; /* preceding code */
+           unsigned char       suffix; /* last character of new code */
+           unsigned char       pad;
+#elif defined(__BIG_ENDIAN)            /* Big endian order */
+           unsigned char       pad;
+           unsigned char       suffix; /* last character of new code */
+           unsigned short      prefix; /* preceding code */
+#else
+#error Endianness not defined...
+#endif
+       } hs;
+    } f;
+    unsigned short codem1;             /* output of hash table -1 */
+    unsigned short cptr;               /* map code to hash table entry */
+};
+
+struct bsd_db {
+    int            totlen;                     /* length of this structure */
+    unsigned int   hsize;              /* size of the hash table */
+    unsigned char  hshift;             /* used in hash function */
+    unsigned char  n_bits;             /* current bits/code */
+    unsigned char  maxbits;            /* maximum bits/code */
+    unsigned char  debug;              /* non-zero if debug desired */
+    unsigned char  unit;               /* ppp unit number */
+    unsigned short seqno;              /* sequence # of next packet */
+    unsigned int   mru;                        /* size of receive (decompress) bufr */
+    unsigned int   maxmaxcode;         /* largest valid code */
+    unsigned int   max_ent;            /* largest code in use */
+    unsigned int   in_count;           /* uncompressed bytes, aged */
+    unsigned int   bytes_out;          /* compressed bytes, aged */
+    unsigned int   ratio;              /* recent compression ratio */
+    unsigned int   checkpoint;         /* when to next check the ratio */
+    unsigned int   clear_count;                /* times dictionary cleared */
+    unsigned int   incomp_count;       /* incompressible packets */
+    unsigned int   incomp_bytes;       /* incompressible bytes */
+    unsigned int   uncomp_count;       /* uncompressed packets */
+    unsigned int   uncomp_bytes;       /* uncompressed bytes */
+    unsigned int   comp_count;         /* compressed packets */
+    unsigned int   comp_bytes;         /* compressed bytes */
+    unsigned short  *lens;             /* array of lengths of codes */
+    struct bsd_dict *dict;             /* dictionary */
+};
+
+#define BSD_OVHD       2               /* BSD compress overhead/packet */
+#define MIN_BSD_BITS   9
+#define BSD_INIT_BITS  MIN_BSD_BITS
+#define MAX_BSD_BITS   15
+
+static void    bsd_free (void *state);
+static void    *bsd_alloc(unsigned char *options, int opt_len, int decomp);
+static void    *bsd_comp_alloc (unsigned char *options, int opt_len);
+static void    *bsd_decomp_alloc (unsigned char *options, int opt_len);
+
+static int     bsd_init        (void *db, unsigned char *options,
+                                int opt_len, int unit, int debug, int decomp);
+static int     bsd_comp_init   (void *state, unsigned char *options,
+                                int opt_len, int unit, int opthdr, int debug);
+static int     bsd_decomp_init (void *state, unsigned char *options,
+                                int opt_len, int unit, int opthdr, int mru,
+                                int debug);
+
+static void    bsd_reset (void *state);
+static void    bsd_comp_stats (void *state, struct compstat *stats);
+
+static int     bsd_compress (void *state, unsigned char *rptr,
+                             unsigned char *obuf, int isize, int osize);
+static void    bsd_incomp (void *state, unsigned char *ibuf, int icnt);
+
+static int     bsd_decompress (void *state, unsigned char *ibuf, int isize,
+                               unsigned char *obuf, int osize);
+
+/* These are in ppp_generic.c */
+extern int  ppp_register_compressor   (struct compressor *cp);
+extern void ppp_unregister_compressor (struct compressor *cp);
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR  256                     /* table clear output code */
+#define FIRST  257                     /* first free entry */
+#define LAST   255
+
+#define MAXCODE(b)     ((1 << (b)) - 1)
+#define BADCODEM1      MAXCODE(MAX_BSD_BITS)
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+                                        ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix)         ((((unsigned long)(suffix)) << 16) \
+                                        + (unsigned long)(prefix))
+
+#define CHECK_GAP      10000           /* Ratio check interval */
+
+#define RATIO_SCALE_LOG        8
+#define RATIO_SCALE    (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX      (0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void
+bsd_clear(struct bsd_db *db)
+{
+    db->clear_count++;
+    db->max_ent      = FIRST-1;
+    db->n_bits       = BSD_INIT_BITS;
+    db->bytes_out    = 0;
+    db->in_count     = 0;
+    db->ratio       = 0;
+    db->checkpoint   = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+
+static int bsd_check (struct bsd_db *db)       /* 1=output CLEAR */
+  {
+    unsigned int new_ratio;
+
+    if (db->in_count >= db->checkpoint)
+      {
+       /* age the ratio by limiting the size of the counts */
+       if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+         {
+           db->in_count  -= (db->in_count  >> 2);
+           db->bytes_out -= (db->bytes_out >> 2);
+         }
+
+       db->checkpoint = db->in_count + CHECK_GAP;
+
+       if (db->max_ent >= db->maxmaxcode)
+         {
+           /* Reset the dictionary only if the ratio is worse,
+            * or if it looks as if it has been poisoned
+            * by incompressible data.
+            *
+            * This does not overflow, because
+            *  db->in_count <= RATIO_MAX.
+            */
+
+           new_ratio = db->in_count << RATIO_SCALE_LOG;
+           if (db->bytes_out != 0)
+             {
+               new_ratio /= db->bytes_out;
+             }
+
+           if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+             {
+               bsd_clear (db);
+               return 1;
+             }
+           db->ratio = new_ratio;
+         }
+      }
+    return 0;
+  }
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_comp_stats (void *state, struct compstat *stats)
+  {
+    struct bsd_db *db = (struct bsd_db *) state;
+
+    stats->unc_bytes    = db->uncomp_bytes;
+    stats->unc_packets  = db->uncomp_count;
+    stats->comp_bytes   = db->comp_bytes;
+    stats->comp_packets = db->comp_count;
+    stats->inc_bytes    = db->incomp_bytes;
+    stats->inc_packets  = db->incomp_count;
+    stats->in_count     = db->in_count;
+    stats->bytes_out    = db->bytes_out;
+  }
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+
+static void bsd_reset (void *state)
+  {
+    struct bsd_db *db = (struct bsd_db *) state;
+
+    bsd_clear(db);
+
+    db->seqno       = 0;
+    db->clear_count = 0;
+  }
+
+/*
+ * Release the compression structure
+ */
+
+static void bsd_free (void *state)
+{
+       struct bsd_db *db = state;
+
+       if (!db)
+               return;
+
+/*
+ * Release the dictionary
+ */
+       vfree(db->dict);
+       db->dict = NULL;
+/*
+ * Release the string buffer
+ */
+       vfree(db->lens);
+       db->lens = NULL;
+/*
+ * Finally release the structure itself.
+ */
+       kfree(db);
+}
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+
+static void *bsd_alloc (unsigned char *options, int opt_len, int decomp)
+  {
+    int bits;
+    unsigned int hsize, hshift, maxmaxcode;
+    struct bsd_db *db;
+
+    if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3
+       || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
+      {
+       return NULL;
+      }
+
+    bits = BSD_NBITS(options[2]);
+
+    switch (bits)
+      {
+    case 9:                    /* needs 82152 for both directions */
+    case 10:                   /* needs 84144 */
+    case 11:                   /* needs 88240 */
+    case 12:                   /* needs 96432 */
+       hsize = 5003;
+       hshift = 4;
+       break;
+    case 13:                   /* needs 176784 */
+       hsize = 9001;
+       hshift = 5;
+       break;
+    case 14:                   /* needs 353744 */
+       hsize = 18013;
+       hshift = 6;
+       break;
+    case 15:                   /* needs 691440 */
+       hsize = 35023;
+       hshift = 7;
+       break;
+    case 16:                   /* needs 1366160--far too much, */
+       /* hsize = 69001; */    /* and 69001 is too big for cptr */
+       /* hshift = 8; */       /* in struct bsd_db */
+       /* break; */
+    default:
+       return NULL;
+      }
+/*
+ * Allocate the main control structure for this instance.
+ */
+    maxmaxcode = MAXCODE(bits);
+    db         = kzalloc(sizeof (struct bsd_db),
+                                           GFP_KERNEL);
+    if (!db)
+      {
+       return NULL;
+      }
+
+/*
+ * Allocate space for the dictionary. This may be more than one page in
+ * length.
+ */
+    db->dict = vmalloc(hsize * sizeof(struct bsd_dict));
+    if (!db->dict)
+      {
+       bsd_free (db);
+       return NULL;
+      }
+
+/*
+ * If this is the compression buffer then there is no length data.
+ */
+    if (!decomp)
+      {
+       db->lens = NULL;
+      }
+/*
+ * For decompression, the length information is needed as well.
+ */
+    else
+      {
+        db->lens = vmalloc((maxmaxcode + 1) * sizeof(db->lens[0]));
+       if (!db->lens)
+         {
+           bsd_free (db);
+           return NULL;
+         }
+      }
+/*
+ * Initialize the data information for the compression code
+ */
+    db->totlen     = sizeof (struct bsd_db)   +
+                   (sizeof (struct bsd_dict) * hsize);
+
+    db->hsize      = hsize;
+    db->hshift     = hshift;
+    db->maxmaxcode = maxmaxcode;
+    db->maxbits    = bits;
+
+    return (void *) db;
+  }
+
+static void *bsd_comp_alloc (unsigned char *options, int opt_len)
+  {
+    return bsd_alloc (options, opt_len, 0);
+  }
+
+static void *bsd_decomp_alloc (unsigned char *options, int opt_len)
+  {
+    return bsd_alloc (options, opt_len, 1);
+  }
+
+/*
+ * Initialize the database.
+ */
+
+static int bsd_init (void *state, unsigned char *options,
+                    int opt_len, int unit, int debug, int decomp)
+  {
+    struct bsd_db *db = state;
+    int indx;
+
+    if ((opt_len != 3) || (options[0] != CI_BSD_COMPRESS) || (options[1] != 3)
+       || (BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
+       || (BSD_NBITS(options[2]) != db->maxbits)
+       || (decomp && db->lens == NULL))
+      {
+       return 0;
+      }
+
+    if (decomp)
+      {
+       indx = LAST;
+       do
+         {
+           db->lens[indx] = 1;
+         }
+       while (indx-- > 0);
+      }
+
+    indx = db->hsize;
+    while (indx-- != 0)
+      {
+       db->dict[indx].codem1 = BADCODEM1;
+       db->dict[indx].cptr   = 0;
+      }
+
+    db->unit = unit;
+    db->mru  = 0;
+#ifndef DEBUG
+    if (debug)
+#endif
+      db->debug = 1;
+
+    bsd_reset(db);
+
+    return 1;
+  }
+
+static int bsd_comp_init (void *state, unsigned char *options,
+                         int opt_len, int unit, int opthdr, int debug)
+  {
+    return bsd_init (state, options, opt_len, unit, debug, 0);
+  }
+
+static int bsd_decomp_init (void *state, unsigned char *options,
+                           int opt_len, int unit, int opthdr, int mru,
+                           int debug)
+  {
+    return bsd_init (state, options, opt_len, unit, debug, 1);
+  }
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+  {
+    if ((unsigned int) idx > (unsigned int) db->maxmaxcode)
+      {
+       printk ("<9>ppp: lens_ptr(%d) > max\n", idx);
+       idx = 0;
+      }
+    return lens_ptrx (db, idx);
+  }
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+  {
+    if ((unsigned int) idx >= (unsigned int) db->hsize)
+      {
+       printk ("<9>ppp: dict_ptr(%d) > max\n", idx);
+       idx = 0;
+      }
+    return dict_ptrx (db, idx);
+  }
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ *
+ *     The result of this function is the size of the compressed
+ *     packet. A zero is returned if the packet was not compressed
+ *     for some reason, such as the size being larger than uncompressed.
+ *
+ *     One change from the BSD compress command is that when the
+ *     code size expands, we do not output a bunch of padding.
+ */
+
+static int bsd_compress (void *state, unsigned char *rptr, unsigned char *obuf,
+                        int isize, int osize)
+  {
+    struct bsd_db *db;
+    int hshift;
+    unsigned int max_ent;
+    unsigned int n_bits;
+    unsigned int bitno;
+    unsigned long accm;
+    int ent;
+    unsigned long fcode;
+    struct bsd_dict *dictp;
+    unsigned char c;
+    int hval;
+    int disp;
+    int ilen;
+    int mxcode;
+    unsigned char *wptr;
+    int olen;
+
+#define PUTBYTE(v)                     \
+  {                                    \
+    ++olen;                            \
+    if (wptr)                          \
+      {                                        \
+       *wptr++ = (unsigned char) (v);  \
+       if (olen >= osize)              \
+         {                             \
+           wptr = NULL;                \
+         }                             \
+      }                                        \
+  }
+
+#define OUTPUT(ent)                    \
+  {                                    \
+    bitno -= n_bits;                   \
+    accm |= ((ent) << bitno);          \
+    do                                 \
+      {                                        \
+       PUTBYTE(accm >> 24);            \
+       accm <<= 8;                     \
+       bitno += 8;                     \
+      }                                        \
+    while (bitno <= 24);               \
+  }
+
+  /*
+   * If the protocol is not in the range we're interested in,
+   * just return without compressing the packet.  If it is,
+   * the protocol becomes the first byte to compress.
+   */
+
+    ent = PPP_PROTOCOL(rptr);
+    if (ent < 0x21 || ent > 0xf9)
+      {
+       return 0;
+      }
+
+    db      = (struct bsd_db *) state;
+    hshift  = db->hshift;
+    max_ent = db->max_ent;
+    n_bits  = db->n_bits;
+    bitno   = 32;
+    accm    = 0;
+    mxcode  = MAXCODE (n_bits);
+
+    /* Initialize the output pointers */
+    wptr  = obuf;
+    olen  = PPP_HDRLEN + BSD_OVHD;
+
+    if (osize > isize)
+      {
+       osize = isize;
+      }
+
+    /* This is the PPP header information */
+    if (wptr)
+      {
+       *wptr++ = PPP_ADDRESS(rptr);
+       *wptr++ = PPP_CONTROL(rptr);
+       *wptr++ = 0;
+       *wptr++ = PPP_COMP;
+       *wptr++ = db->seqno >> 8;
+       *wptr++ = db->seqno;
+      }
+
+    /* Skip the input header */
+    rptr  += PPP_HDRLEN;
+    isize -= PPP_HDRLEN;
+    ilen   = ++isize;  /* Low byte of protocol is counted as input */
+
+    while (--ilen > 0)
+      {
+       c     = *rptr++;
+       fcode = BSD_KEY  (ent, c);
+       hval  = BSD_HASH (ent, c, hshift);
+       dictp = dict_ptr (db, hval);
+
+       /* Validate and then check the entry. */
+       if (dictp->codem1 >= max_ent)
+         {
+           goto nomatch;
+         }
+
+       if (dictp->f.fcode == fcode)
+         {
+           ent = dictp->codem1 + 1;
+           continue;   /* found (prefix,suffix) */
+         }
+
+       /* continue probing until a match or invalid entry */
+       disp = (hval == 0) ? 1 : hval;
+
+       do
+         {
+           hval += disp;
+           if (hval >= db->hsize)
+             {
+               hval -= db->hsize;
+             }
+           dictp = dict_ptr (db, hval);
+           if (dictp->codem1 >= max_ent)
+             {
+               goto nomatch;
+             }
+         }
+       while (dictp->f.fcode != fcode);
+
+       ent = dictp->codem1 + 1;        /* finally found (prefix,suffix) */
+       continue;
+
+nomatch:
+       OUTPUT(ent);            /* output the prefix */
+
+       /* code -> hashtable */
+       if (max_ent < db->maxmaxcode)
+         {
+           struct bsd_dict *dictp2;
+           struct bsd_dict *dictp3;
+           int    indx;
+
+           /* expand code size if needed */
+           if (max_ent >= mxcode)
+             {
+               db->n_bits = ++n_bits;
+               mxcode     = MAXCODE (n_bits);
+             }
+
+           /* Invalidate old hash table entry using
+            * this code, and then take it over.
+            */
+
+           dictp2 = dict_ptr (db, max_ent + 1);
+           indx   = dictp2->cptr;
+           dictp3 = dict_ptr (db, indx);
+
+           if (dictp3->codem1 == max_ent)
+             {
+               dictp3->codem1 = BADCODEM1;
+             }
+
+           dictp2->cptr   = hval;
+           dictp->codem1  = max_ent;
+           dictp->f.fcode = fcode;
+           db->max_ent    = ++max_ent;
+
+           if (db->lens)
+             {
+               unsigned short *len1 = lens_ptr (db, max_ent);
+               unsigned short *len2 = lens_ptr (db, ent);
+               *len1 = *len2 + 1;
+             }
+         }
+       ent = c;
+      }
+
+    OUTPUT(ent);               /* output the last code */
+
+    db->bytes_out    += olen - PPP_HDRLEN - BSD_OVHD;
+    db->uncomp_bytes += isize;
+    db->in_count     += isize;
+    ++db->uncomp_count;
+    ++db->seqno;
+
+    if (bitno < 32)
+      {
+       ++db->bytes_out; /* must be set before calling bsd_check */
+      }
+
+    /*
+     * Generate the clear command if needed
+     */
+
+    if (bsd_check(db))
+      {
+       OUTPUT (CLEAR);
+      }
+
+    /*
+     * Pad dribble bits of last code with ones.
+     * Do not emit a completely useless byte of ones.
+     */
+
+    if (bitno != 32)
+      {
+       PUTBYTE((accm | (0xff << (bitno-8))) >> 24);
+      }
+
+    /*
+     * Increase code size if we would have without the packet
+     * boundary because the decompressor will do so.
+     */
+
+    if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+      {
+       db->n_bits++;
+      }
+
+    /* If output length is too large then this is an incomplete frame. */
+    if (wptr == NULL)
+      {
+       ++db->incomp_count;
+       db->incomp_bytes += isize;
+       olen              = 0;
+      }
+    else /* Count the number of compressed frames */
+      {
+       ++db->comp_count;
+       db->comp_bytes += olen;
+      }
+
+    /* Return the resulting output length */
+    return olen;
+#undef OUTPUT
+#undef PUTBYTE
+  }
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+
+static void bsd_incomp (void *state, unsigned char *ibuf, int icnt)
+  {
+    (void) bsd_compress (state, ibuf, (char *) 0, icnt, 0);
+  }
+
+/*
+ * Decompress "BSD Compress".
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression.  For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+
+static int bsd_decompress (void *state, unsigned char *ibuf, int isize,
+                          unsigned char *obuf, int osize)
+  {
+    struct bsd_db *db;
+    unsigned int max_ent;
+    unsigned long accm;
+    unsigned int bitno;                /* 1st valid bit in accm */
+    unsigned int n_bits;
+    unsigned int tgtbitno;     /* bitno when we have a code */
+    struct bsd_dict *dictp;
+    int explen;
+    int seq;
+    unsigned int incode;
+    unsigned int oldcode;
+    unsigned int finchar;
+    unsigned char *p;
+    unsigned char *wptr;
+    int adrs;
+    int ctrl;
+    int ilen;
+    int codelen;
+    int extra;
+
+    db       = (struct bsd_db *) state;
+    max_ent  = db->max_ent;
+    accm     = 0;
+    bitno    = 32;             /* 1st valid bit in accm */
+    n_bits   = db->n_bits;
+    tgtbitno = 32 - n_bits;    /* bitno when we have a code */
+
+    /*
+     * Save the address/control from the PPP header
+     * and then get the sequence number.
+     */
+
+    adrs  = PPP_ADDRESS (ibuf);
+    ctrl  = PPP_CONTROL (ibuf);
+
+    seq   = (ibuf[4] << 8) + ibuf[5];
+
+    ibuf += (PPP_HDRLEN + 2);
+    ilen  = isize - (PPP_HDRLEN + 2);
+
+    /*
+     * Check the sequence number and give up if it differs from
+     * the value we're expecting.
+     */
+
+    if (seq != db->seqno)
+      {
+       if (db->debug)
+         {
+           printk("bsd_decomp%d: bad sequence # %d, expected %d\n",
+                  db->unit, seq, db->seqno - 1);
+         }
+       return DECOMP_ERROR;
+      }
+
+    ++db->seqno;
+    db->bytes_out += ilen;
+
+    /*
+     * Fill in the ppp header, but not the last byte of the protocol
+     * (that comes from the decompressed data).
+     */
+
+    wptr    = obuf;
+    *wptr++ = adrs;
+    *wptr++ = ctrl;
+    *wptr++ = 0;
+
+    oldcode = CLEAR;
+    explen  = 3;
+
+    /*
+     * Keep the checkpoint correctly so that incompressible packets
+     * clear the dictionary at the proper times.
+     */
+
+    for (;;)
+      {
+       if (ilen-- <= 0)
+         {
+           db->in_count += (explen - 3); /* don't count the header */
+           break;
+         }
+
+       /*
+        * Accumulate bytes until we have a complete code.
+        * Then get the next code, relying on the 32-bit,
+        * unsigned accm to mask the result.
+        */
+
+       bitno -= 8;
+       accm  |= *ibuf++ << bitno;
+       if (tgtbitno < bitno)
+         {
+           continue;
+         }
+
+       incode = accm >> tgtbitno;
+       accm <<= n_bits;
+       bitno += n_bits;
+
+       /*
+        * The dictionary must only be cleared at the end of a packet.
+        */
+
+       if (incode == CLEAR)
+         {
+           if (ilen > 0)
+             {
+               if (db->debug)
+                 {
+                   printk("bsd_decomp%d: bad CLEAR\n", db->unit);
+                 }
+               return DECOMP_FATALERROR;       /* probably a bug */
+             }
+
+           bsd_clear(db);
+           break;
+         }
+
+       if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+           || (incode > max_ent && oldcode == CLEAR))
+         {
+           if (db->debug)
+             {
+               printk("bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+                      db->unit, incode, oldcode);
+               printk("max_ent=0x%x explen=%d seqno=%d\n",
+                      max_ent, explen, db->seqno);
+             }
+           return DECOMP_FATALERROR;   /* probably a bug */
+         }
+
+       /* Special case for KwKwK string. */
+       if (incode > max_ent)
+         {
+           finchar = oldcode;
+           extra   = 1;
+         }
+       else
+         {
+           finchar = incode;
+           extra   = 0;
+         }
+
+       codelen = *(lens_ptr (db, finchar));
+       explen += codelen + extra;
+       if (explen > osize)
+         {
+           if (db->debug)
+             {
+               printk("bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+               printk("  len=%d, finchar=0x%x, codelen=%d, explen=%d\n",
+                      ilen, finchar, codelen, explen);
+#endif
+             }
+           return DECOMP_FATALERROR;
+         }
+
+       /*
+        * Decode this code and install it in the decompressed buffer.
+        */
+
+       wptr += codelen;
+       p     = wptr;
+       while (finchar > LAST)
+         {
+           struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+
+           dictp = dict_ptr (db, dictp2->cptr);
+#ifdef DEBUG
+           if (--codelen <= 0 || dictp->codem1 != finchar-1)
+             {
+               if (codelen <= 0)
+                 {
+                   printk("bsd_decomp%d: fell off end of chain ", db->unit);
+                   printk("0x%x at 0x%x by 0x%x, max_ent=0x%x\n",
+                          incode, finchar, dictp2->cptr, max_ent);
+                 }
+               else
+                 {
+                   if (dictp->codem1 != finchar-1)
+                     {
+                       printk("bsd_decomp%d: bad code chain 0x%x "
+                              "finchar=0x%x ",
+                              db->unit, incode, finchar);
+
+                       printk("oldcode=0x%x cptr=0x%x codem1=0x%x\n",
+                              oldcode, dictp2->cptr, dictp->codem1);
+                     }
+                 }
+               return DECOMP_FATALERROR;
+             }
+#endif
+           *--p    = dictp->f.hs.suffix;
+           finchar = dictp->f.hs.prefix;
+         }
+       *--p = finchar;
+
+#ifdef DEBUG
+       if (--codelen != 0)
+         {
+           printk("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n",
+                  db->unit, codelen, incode, max_ent);
+         }
+#endif
+
+       if (extra)              /* the KwKwK case again */
+         {
+           *wptr++ = finchar;
+         }
+
+       /*
+        * If not first code in a packet, and
+        * if not out of code space, then allocate a new code.
+        *
+        * Keep the hash table correct so it can be used
+        * with uncompressed packets.
+        */
+
+       if (oldcode != CLEAR && max_ent < db->maxmaxcode)
+         {
+           struct bsd_dict *dictp2, *dictp3;
+           unsigned short  *lens1,  *lens2;
+           unsigned long fcode;
+           int hval, disp, indx;
+
+           fcode = BSD_KEY(oldcode,finchar);
+           hval  = BSD_HASH(oldcode,finchar,db->hshift);
+           dictp = dict_ptr (db, hval);
+
+           /* look for a free hash table entry */
+           if (dictp->codem1 < max_ent)
+             {
+               disp = (hval == 0) ? 1 : hval;
+               do
+                 {
+                   hval += disp;
+                   if (hval >= db->hsize)
+                     {
+                       hval -= db->hsize;
+                     }
+                   dictp = dict_ptr (db, hval);
+                 }
+               while (dictp->codem1 < max_ent);
+             }
+
+           /*
+            * Invalidate previous hash table entry
+            * assigned this code, and then take it over
+            */
+
+           dictp2 = dict_ptr (db, max_ent + 1);
+           indx   = dictp2->cptr;
+           dictp3 = dict_ptr (db, indx);
+
+           if (dictp3->codem1 == max_ent)
+             {
+               dictp3->codem1 = BADCODEM1;
+             }
+
+           dictp2->cptr   = hval;
+           dictp->codem1  = max_ent;
+           dictp->f.fcode = fcode;
+           db->max_ent    = ++max_ent;
+
+           /* Update the length of this string. */
+           lens1  = lens_ptr (db, max_ent);
+           lens2  = lens_ptr (db, oldcode);
+           *lens1 = *lens2 + 1;
+
+           /* Expand code size if needed. */
+           if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
+             {
+               db->n_bits = ++n_bits;
+               tgtbitno   = 32-n_bits;
+             }
+         }
+       oldcode = incode;
+      }
+
+    ++db->comp_count;
+    ++db->uncomp_count;
+    db->comp_bytes   += isize - BSD_OVHD - PPP_HDRLEN;
+    db->uncomp_bytes += explen;
+
+    if (bsd_check(db))
+      {
+       if (db->debug)
+         {
+           printk("bsd_decomp%d: peer should have cleared dictionary on %d\n",
+                  db->unit, db->seqno - 1);
+         }
+      }
+    return explen;
+  }
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct compressor ppp_bsd_compress = {
+       .compress_proto =       CI_BSD_COMPRESS,
+       .comp_alloc =           bsd_comp_alloc,
+       .comp_free =            bsd_free,
+       .comp_init =            bsd_comp_init,
+       .comp_reset =           bsd_reset,
+       .compress =             bsd_compress,
+       .comp_stat =            bsd_comp_stats,
+       .decomp_alloc =         bsd_decomp_alloc,
+       .decomp_free =          bsd_free,
+       .decomp_init =          bsd_decomp_init,
+       .decomp_reset =         bsd_reset,
+       .decompress =           bsd_decompress,
+       .incomp =               bsd_incomp,
+       .decomp_stat =          bsd_comp_stats,
+       .owner =                THIS_MODULE
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+static int __init bsdcomp_init(void)
+{
+       int answer = ppp_register_compressor(&ppp_bsd_compress);
+       if (answer == 0)
+               printk(KERN_INFO "PPP BSD Compression module registered\n");
+       return answer;
+}
+
+static void __exit bsdcomp_cleanup(void)
+{
+       ppp_unregister_compressor(&ppp_bsd_compress);
+}
+
+module_init(bsdcomp_init);
+module_exit(bsdcomp_cleanup);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("ppp-compress-" __stringify(CI_BSD_COMPRESS));
diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c
new file mode 100644 (file)
index 0000000..c6ba643
--- /dev/null
@@ -0,0 +1,1028 @@
+/*
+ * PPP async serial channel driver for Linux.
+ *
+ * Copyright 1999 Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ *
+ * This driver provides the encapsulation and framing for sending
+ * and receiving PPP frames over async serial lines.  It relies on
+ * the generic PPP layer to give it frames to send and to process
+ * received frames.  It implements the PPP line discipline.
+ *
+ * Part of the code in this driver was inspired by the old async-only
+ * PPP driver, written by Michael Callahan and Al Longyear, and
+ * subsequently hacked by Paul Mackerras.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/tty.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/crc-ccitt.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+
+#define PPP_VERSION    "2.4.2"
+
+#define OBUFSIZE       4096
+
+/* Structure for storing local state. */
+struct asyncppp {
+       struct tty_struct *tty;
+       unsigned int    flags;
+       unsigned int    state;
+       unsigned int    rbits;
+       int             mru;
+       spinlock_t      xmit_lock;
+       spinlock_t      recv_lock;
+       unsigned long   xmit_flags;
+       u32             xaccm[8];
+       u32             raccm;
+       unsigned int    bytes_sent;
+       unsigned int    bytes_rcvd;
+
+       struct sk_buff  *tpkt;
+       int             tpkt_pos;
+       u16             tfcs;
+       unsigned char   *optr;
+       unsigned char   *olim;
+       unsigned long   last_xmit;
+
+       struct sk_buff  *rpkt;
+       int             lcp_fcs;
+       struct sk_buff_head rqueue;
+
+       struct tasklet_struct tsk;
+
+       atomic_t        refcnt;
+       struct semaphore dead_sem;
+       struct ppp_channel chan;        /* interface to generic ppp layer */
+       unsigned char   obuf[OBUFSIZE];
+};
+
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP    0
+#define XMIT_FULL      1
+#define XMIT_BUSY      2
+
+/* State bits */
+#define SC_TOSS                1
+#define SC_ESCAPE      2
+#define SC_PREV_ERROR  4
+
+/* Bits in rbits */
+#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
+
+static int flag_time = HZ;
+module_param(flag_time, int, 0);
+MODULE_PARM_DESC(flag_time, "ppp_async: interval between flagged packets (in clock ticks)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_PPP);
+
+/*
+ * Prototypes.
+ */
+static int ppp_async_encode(struct asyncppp *ap);
+static int ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb);
+static int ppp_async_push(struct asyncppp *ap);
+static void ppp_async_flush_output(struct asyncppp *ap);
+static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
+                           char *flags, int count);
+static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
+                          unsigned long arg);
+static void ppp_async_process(unsigned long arg);
+
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+                          int len, int inbound);
+
+static const struct ppp_channel_ops async_ops = {
+       .start_xmit = ppp_async_send,
+       .ioctl      = ppp_async_ioctl,
+};
+
+/*
+ * Routines implementing the PPP line discipline.
+ */
+
+/*
+ * We have a potential race on dereferencing tty->disc_data,
+ * because the tty layer provides no locking at all - thus one
+ * cpu could be running ppp_asynctty_receive while another
+ * calls ppp_asynctty_close, which zeroes tty->disc_data and
+ * frees the memory that ppp_asynctty_receive is using.  The best
+ * way to fix this is to use a rwlock in the tty struct, but for now
+ * we use a single global rwlock for all ttys in ppp line discipline.
+ *
+ * FIXME: this is no longer true. The _close path for the ldisc is
+ * now guaranteed to be sane.
+ */
+static DEFINE_RWLOCK(disc_data_lock);
+
+static struct asyncppp *ap_get(struct tty_struct *tty)
+{
+       struct asyncppp *ap;
+
+       read_lock(&disc_data_lock);
+       ap = tty->disc_data;
+       if (ap != NULL)
+               atomic_inc(&ap->refcnt);
+       read_unlock(&disc_data_lock);
+       return ap;
+}
+
+static void ap_put(struct asyncppp *ap)
+{
+       if (atomic_dec_and_test(&ap->refcnt))
+               up(&ap->dead_sem);
+}
+
+/*
+ * Called when a tty is put into PPP line discipline. Called in process
+ * context.
+ */
+static int
+ppp_asynctty_open(struct tty_struct *tty)
+{
+       struct asyncppp *ap;
+       int err;
+       int speed;
+
+       if (tty->ops->write == NULL)
+               return -EOPNOTSUPP;
+
+       err = -ENOMEM;
+       ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+       if (!ap)
+               goto out;
+
+       /* initialize the asyncppp structure */
+       ap->tty = tty;
+       ap->mru = PPP_MRU;
+       spin_lock_init(&ap->xmit_lock);
+       spin_lock_init(&ap->recv_lock);
+       ap->xaccm[0] = ~0U;
+       ap->xaccm[3] = 0x60000000U;
+       ap->raccm = ~0U;
+       ap->optr = ap->obuf;
+       ap->olim = ap->obuf;
+       ap->lcp_fcs = -1;
+
+       skb_queue_head_init(&ap->rqueue);
+       tasklet_init(&ap->tsk, ppp_async_process, (unsigned long) ap);
+
+       atomic_set(&ap->refcnt, 1);
+       sema_init(&ap->dead_sem, 0);
+
+       ap->chan.private = ap;
+       ap->chan.ops = &async_ops;
+       ap->chan.mtu = PPP_MRU;
+       speed = tty_get_baud_rate(tty);
+       ap->chan.speed = speed;
+       err = ppp_register_channel(&ap->chan);
+       if (err)
+               goto out_free;
+
+       tty->disc_data = ap;
+       tty->receive_room = 65536;
+       return 0;
+
+ out_free:
+       kfree(ap);
+ out:
+       return err;
+}
+
+/*
+ * Called when the tty is put into another line discipline
+ * or it hangs up.  We have to wait for any cpu currently
+ * executing in any of the other ppp_asynctty_* routines to
+ * finish before we can call ppp_unregister_channel and free
+ * the asyncppp struct.  This routine must be called from
+ * process context, not interrupt or softirq context.
+ */
+static void
+ppp_asynctty_close(struct tty_struct *tty)
+{
+       struct asyncppp *ap;
+
+       write_lock_irq(&disc_data_lock);
+       ap = tty->disc_data;
+       tty->disc_data = NULL;
+       write_unlock_irq(&disc_data_lock);
+       if (!ap)
+               return;
+
+       /*
+        * We have now ensured that nobody can start using ap from now
+        * on, but we have to wait for all existing users to finish.
+        * Note that ppp_unregister_channel ensures that no calls to
+        * our channel ops (i.e. ppp_async_send/ioctl) are in progress
+        * by the time it returns.
+        */
+       if (!atomic_dec_and_test(&ap->refcnt))
+               down(&ap->dead_sem);
+       tasklet_kill(&ap->tsk);
+
+       ppp_unregister_channel(&ap->chan);
+       kfree_skb(ap->rpkt);
+       skb_queue_purge(&ap->rqueue);
+       kfree_skb(ap->tpkt);
+       kfree(ap);
+}
+
+/*
+ * Called on tty hangup in process context.
+ *
+ * Wait for I/O to driver to complete and unregister PPP channel.
+ * This is already done by the close routine, so just call that.
+ */
+static int ppp_asynctty_hangup(struct tty_struct *tty)
+{
+       ppp_asynctty_close(tty);
+       return 0;
+}
+
+/*
+ * Read does nothing - no data is ever available this way.
+ * Pppd reads and writes packets via /dev/ppp instead.
+ */
+static ssize_t
+ppp_asynctty_read(struct tty_struct *tty, struct file *file,
+                 unsigned char __user *buf, size_t count)
+{
+       return -EAGAIN;
+}
+
+/*
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
+ */
+static ssize_t
+ppp_asynctty_write(struct tty_struct *tty, struct file *file,
+                  const unsigned char *buf, size_t count)
+{
+       return -EAGAIN;
+}
+
+/*
+ * Called in process context only. May be re-entered by multiple
+ * ioctl calling threads.
+ */
+
+static int
+ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+                  unsigned int cmd, unsigned long arg)
+{
+       struct asyncppp *ap = ap_get(tty);
+       int err, val;
+       int __user *p = (int __user *)arg;
+
+       if (!ap)
+               return -ENXIO;
+       err = -EFAULT;
+       switch (cmd) {
+       case PPPIOCGCHAN:
+               err = -EFAULT;
+               if (put_user(ppp_channel_index(&ap->chan), p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGUNIT:
+               err = -EFAULT;
+               if (put_user(ppp_unit_number(&ap->chan), p))
+                       break;
+               err = 0;
+               break;
+
+       case TCFLSH:
+               /* flush our buffers and the serial port's buffer */
+               if (arg == TCIOFLUSH || arg == TCOFLUSH)
+                       ppp_async_flush_output(ap);
+               err = tty_perform_flush(tty, arg);
+               break;
+
+       case FIONREAD:
+               val = 0;
+               if (put_user(val, p))
+                       break;
+               err = 0;
+               break;
+
+       default:
+               /* Try the various mode ioctls */
+               err = tty_mode_ioctl(tty, file, cmd, arg);
+       }
+
+       ap_put(ap);
+       return err;
+}
+
+/* No kernel lock - fine */
+static unsigned int
+ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+{
+       return 0;
+}
+
+/* May sleep, don't call from interrupt level or with interrupts disabled */
+static void
+ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
+                 char *cflags, int count)
+{
+       struct asyncppp *ap = ap_get(tty);
+       unsigned long flags;
+
+       if (!ap)
+               return;
+       spin_lock_irqsave(&ap->recv_lock, flags);
+       ppp_async_input(ap, buf, cflags, count);
+       spin_unlock_irqrestore(&ap->recv_lock, flags);
+       if (!skb_queue_empty(&ap->rqueue))
+               tasklet_schedule(&ap->tsk);
+       ap_put(ap);
+       tty_unthrottle(tty);
+}
+
+static void
+ppp_asynctty_wakeup(struct tty_struct *tty)
+{
+       struct asyncppp *ap = ap_get(tty);
+
+       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+       if (!ap)
+               return;
+       set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+       tasklet_schedule(&ap->tsk);
+       ap_put(ap);
+}
+
+
+static struct tty_ldisc_ops ppp_ldisc = {
+       .owner  = THIS_MODULE,
+       .magic  = TTY_LDISC_MAGIC,
+       .name   = "ppp",
+       .open   = ppp_asynctty_open,
+       .close  = ppp_asynctty_close,
+       .hangup = ppp_asynctty_hangup,
+       .read   = ppp_asynctty_read,
+       .write  = ppp_asynctty_write,
+       .ioctl  = ppp_asynctty_ioctl,
+       .poll   = ppp_asynctty_poll,
+       .receive_buf = ppp_asynctty_receive,
+       .write_wakeup = ppp_asynctty_wakeup,
+};
+
+static int __init
+ppp_async_init(void)
+{
+       int err;
+
+       err = tty_register_ldisc(N_PPP, &ppp_ldisc);
+       if (err != 0)
+               printk(KERN_ERR "PPP_async: error %d registering line disc.\n",
+                      err);
+       return err;
+}
+
+/*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+       struct asyncppp *ap = chan->private;
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       int err, val;
+       u32 accm[8];
+
+       err = -EFAULT;
+       switch (cmd) {
+       case PPPIOCGFLAGS:
+               val = ap->flags | ap->rbits;
+               if (put_user(val, p))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSFLAGS:
+               if (get_user(val, p))
+                       break;
+               ap->flags = val & ~SC_RCV_BITS;
+               spin_lock_irq(&ap->recv_lock);
+               ap->rbits = val & SC_RCV_BITS;
+               spin_unlock_irq(&ap->recv_lock);
+               err = 0;
+               break;
+
+       case PPPIOCGASYNCMAP:
+               if (put_user(ap->xaccm[0], (u32 __user *)argp))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSASYNCMAP:
+               if (get_user(ap->xaccm[0], (u32 __user *)argp))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGRASYNCMAP:
+               if (put_user(ap->raccm, (u32 __user *)argp))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSRASYNCMAP:
+               if (get_user(ap->raccm, (u32 __user *)argp))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGXASYNCMAP:
+               if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSXASYNCMAP:
+               if (copy_from_user(accm, argp, sizeof(accm)))
+                       break;
+               accm[2] &= ~0x40000000U;        /* can't escape 0x5e */
+               accm[3] |= 0x60000000U;         /* must escape 0x7d, 0x7e */
+               memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+               err = 0;
+               break;
+
+       case PPPIOCGMRU:
+               if (put_user(ap->mru, p))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSMRU:
+               if (get_user(val, p))
+                       break;
+               if (val < PPP_MRU)
+                       val = PPP_MRU;
+               ap->mru = val;
+               err = 0;
+               break;
+
+       default:
+               err = -ENOTTY;
+       }
+
+       return err;
+}
+
+/*
+ * This is called at softirq level to deliver received packets
+ * to the ppp_generic code, and to tell the ppp_generic code
+ * if we can accept more output now.
+ */
+static void ppp_async_process(unsigned long arg)
+{
+       struct asyncppp *ap = (struct asyncppp *) arg;
+       struct sk_buff *skb;
+
+       /* process received packets */
+       while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
+               if (skb->cb[0])
+                       ppp_input_error(&ap->chan, 0);
+               ppp_input(&ap->chan, skb);
+       }
+
+       /* try to push more stuff out */
+       if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_async_push(ap))
+               ppp_output_wakeup(&ap->chan);
+}
+
+/*
+ * Procedures for encapsulation and framing.
+ */
+
+/*
+ * Procedure to encode the data for async serial transmission.
+ * Does octet stuffing (escaping), puts the address/control bytes
+ * on if A/C compression is disabled, and does protocol compression.
+ * Assumes ap->tpkt != 0 on entry.
+ * Returns 1 if we finished the current frame, 0 otherwise.
+ */
+
+#define PUT_BYTE(ap, buf, c, islcp)    do {            \
+       if ((islcp && c < 0x20) || (ap->xaccm[c >> 5] & (1 << (c & 0x1f)))) {\
+               *buf++ = PPP_ESCAPE;                    \
+               *buf++ = c ^ PPP_TRANS;                 \
+       } else                                          \
+               *buf++ = c;                             \
+} while (0)
+
+static int
+ppp_async_encode(struct asyncppp *ap)
+{
+       int fcs, i, count, c, proto;
+       unsigned char *buf, *buflim;
+       unsigned char *data;
+       int islcp;
+
+       buf = ap->obuf;
+       ap->olim = buf;
+       ap->optr = buf;
+       i = ap->tpkt_pos;
+       data = ap->tpkt->data;
+       count = ap->tpkt->len;
+       fcs = ap->tfcs;
+       proto = get_unaligned_be16(data);
+
+       /*
+        * LCP packets with code values between 1 (configure-reqest)
+        * and 7 (code-reject) must be sent as though no options
+        * had been negotiated.
+        */
+       islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
+
+       if (i == 0) {
+               if (islcp)
+                       async_lcp_peek(ap, data, count, 0);
+
+               /*
+                * Start of a new packet - insert the leading FLAG
+                * character if necessary.
+                */
+               if (islcp || flag_time == 0 ||
+                   time_after_eq(jiffies, ap->last_xmit + flag_time))
+                       *buf++ = PPP_FLAG;
+               ap->last_xmit = jiffies;
+               fcs = PPP_INITFCS;
+
+               /*
+                * Put in the address/control bytes if necessary
+                */
+               if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
+                       PUT_BYTE(ap, buf, 0xff, islcp);
+                       fcs = PPP_FCS(fcs, 0xff);
+                       PUT_BYTE(ap, buf, 0x03, islcp);
+                       fcs = PPP_FCS(fcs, 0x03);
+               }
+       }
+
+       /*
+        * Once we put in the last byte, we need to put in the FCS
+        * and closing flag, so make sure there is at least 7 bytes
+        * of free space in the output buffer.
+        */
+       buflim = ap->obuf + OBUFSIZE - 6;
+       while (i < count && buf < buflim) {
+               c = data[i++];
+               if (i == 1 && c == 0 && (ap->flags & SC_COMP_PROT))
+                       continue;       /* compress protocol field */
+               fcs = PPP_FCS(fcs, c);
+               PUT_BYTE(ap, buf, c, islcp);
+       }
+
+       if (i < count) {
+               /*
+                * Remember where we are up to in this packet.
+                */
+               ap->olim = buf;
+               ap->tpkt_pos = i;
+               ap->tfcs = fcs;
+               return 0;
+       }
+
+       /*
+        * We have finished the packet.  Add the FCS and flag.
+        */
+       fcs = ~fcs;
+       c = fcs & 0xff;
+       PUT_BYTE(ap, buf, c, islcp);
+       c = (fcs >> 8) & 0xff;
+       PUT_BYTE(ap, buf, c, islcp);
+       *buf++ = PPP_FLAG;
+       ap->olim = buf;
+
+       kfree_skb(ap->tpkt);
+       ap->tpkt = NULL;
+       return 1;
+}
+
+/*
+ * Transmit-side routines.
+ */
+
+/*
+ * Send a packet to the peer over an async tty line.
+ * Returns 1 iff the packet was accepted.
+ * If the packet was not accepted, we will call ppp_output_wakeup
+ * at some later time.
+ */
+static int
+ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb)
+{
+       struct asyncppp *ap = chan->private;
+
+       ppp_async_push(ap);
+
+       if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
+               return 0;       /* already full */
+       ap->tpkt = skb;
+       ap->tpkt_pos = 0;
+
+       ppp_async_push(ap);
+       return 1;
+}
+
+/*
+ * Push as much data as possible out to the tty.
+ */
+static int
+ppp_async_push(struct asyncppp *ap)
+{
+       int avail, sent, done = 0;
+       struct tty_struct *tty = ap->tty;
+       int tty_stuffed = 0;
+
+       /*
+        * We can get called recursively here if the tty write
+        * function calls our wakeup function.  This can happen
+        * for example on a pty with both the master and slave
+        * set to PPP line discipline.
+        * We use the XMIT_BUSY bit to detect this and get out,
+        * leaving the XMIT_WAKEUP bit set to tell the other
+        * instance that it may now be able to write more now.
+        */
+       if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags))
+               return 0;
+       spin_lock_bh(&ap->xmit_lock);
+       for (;;) {
+               if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
+                       tty_stuffed = 0;
+               if (!tty_stuffed && ap->optr < ap->olim) {
+                       avail = ap->olim - ap->optr;
+                       set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+                       sent = tty->ops->write(tty, ap->optr, avail);
+                       if (sent < 0)
+                               goto flush;     /* error, e.g. loss of CD */
+                       ap->optr += sent;
+                       if (sent < avail)
+                               tty_stuffed = 1;
+                       continue;
+               }
+               if (ap->optr >= ap->olim && ap->tpkt) {
+                       if (ppp_async_encode(ap)) {
+                               /* finished processing ap->tpkt */
+                               clear_bit(XMIT_FULL, &ap->xmit_flags);
+                               done = 1;
+                       }
+                       continue;
+               }
+               /*
+                * We haven't made any progress this time around.
+                * Clear XMIT_BUSY to let other callers in, but
+                * after doing so we have to check if anyone set
+                * XMIT_WAKEUP since we last checked it.  If they
+                * did, we should try again to set XMIT_BUSY and go
+                * around again in case XMIT_BUSY was still set when
+                * the other caller tried.
+                */
+               clear_bit(XMIT_BUSY, &ap->xmit_flags);
+               /* any more work to do? if not, exit the loop */
+               if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
+                     (!tty_stuffed && ap->tpkt)))
+                       break;
+               /* more work to do, see if we can do it now */
+               if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags))
+                       break;
+       }
+       spin_unlock_bh(&ap->xmit_lock);
+       return done;
+
+flush:
+       clear_bit(XMIT_BUSY, &ap->xmit_flags);
+       if (ap->tpkt) {
+               kfree_skb(ap->tpkt);
+               ap->tpkt = NULL;
+               clear_bit(XMIT_FULL, &ap->xmit_flags);
+               done = 1;
+       }
+       ap->optr = ap->olim;
+       spin_unlock_bh(&ap->xmit_lock);
+       return done;
+}
+
+/*
+ * Flush output from our internal buffers.
+ * Called for the TCFLSH ioctl. Can be entered in parallel
+ * but this is covered by the xmit_lock.
+ */
+static void
+ppp_async_flush_output(struct asyncppp *ap)
+{
+       int done = 0;
+
+       spin_lock_bh(&ap->xmit_lock);
+       ap->optr = ap->olim;
+       if (ap->tpkt != NULL) {
+               kfree_skb(ap->tpkt);
+               ap->tpkt = NULL;
+               clear_bit(XMIT_FULL, &ap->xmit_flags);
+               done = 1;
+       }
+       spin_unlock_bh(&ap->xmit_lock);
+       if (done)
+               ppp_output_wakeup(&ap->chan);
+}
+
+/*
+ * Receive-side routines.
+ */
+
+/* see how many ordinary chars there are at the start of buf */
+static inline int
+scan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count)
+{
+       int i, c;
+
+       for (i = 0; i < count; ++i) {
+               c = buf[i];
+               if (c == PPP_ESCAPE || c == PPP_FLAG ||
+                   (c < 0x20 && (ap->raccm & (1 << c)) != 0))
+                       break;
+       }
+       return i;
+}
+
+/* called when a flag is seen - do end-of-packet processing */
+static void
+process_input_packet(struct asyncppp *ap)
+{
+       struct sk_buff *skb;
+       unsigned char *p;
+       unsigned int len, fcs, proto;
+
+       skb = ap->rpkt;
+       if (ap->state & (SC_TOSS | SC_ESCAPE))
+               goto err;
+
+       if (skb == NULL)
+               return;         /* 0-length packet */
+
+       /* check the FCS */
+       p = skb->data;
+       len = skb->len;
+       if (len < 3)
+               goto err;       /* too short */
+       fcs = PPP_INITFCS;
+       for (; len > 0; --len)
+               fcs = PPP_FCS(fcs, *p++);
+       if (fcs != PPP_GOODFCS)
+               goto err;       /* bad FCS */
+       skb_trim(skb, skb->len - 2);
+
+       /* check for address/control and protocol compression */
+       p = skb->data;
+       if (p[0] == PPP_ALLSTATIONS) {
+               /* chop off address/control */
+               if (p[1] != PPP_UI || skb->len < 3)
+                       goto err;
+               p = skb_pull(skb, 2);
+       }
+       proto = p[0];
+       if (proto & 1) {
+               /* protocol is compressed */
+               skb_push(skb, 1)[0] = 0;
+       } else {
+               if (skb->len < 2)
+                       goto err;
+               proto = (proto << 8) + p[1];
+               if (proto == PPP_LCP)
+                       async_lcp_peek(ap, p, skb->len, 1);
+       }
+
+       /* queue the frame to be processed */
+       skb->cb[0] = ap->state;
+       skb_queue_tail(&ap->rqueue, skb);
+       ap->rpkt = NULL;
+       ap->state = 0;
+       return;
+
+ err:
+       /* frame had an error, remember that, reset SC_TOSS & SC_ESCAPE */
+       ap->state = SC_PREV_ERROR;
+       if (skb) {
+               /* make skb appear as freshly allocated */
+               skb_trim(skb, 0);
+               skb_reserve(skb, - skb_headroom(skb));
+       }
+}
+
+/* Called when the tty driver has data for us. Runs parallel with the
+   other ldisc functions but will not be re-entered */
+
+static void
+ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
+               char *flags, int count)
+{
+       struct sk_buff *skb;
+       int c, i, j, n, s, f;
+       unsigned char *sp;
+
+       /* update bits used for 8-bit cleanness detection */
+       if (~ap->rbits & SC_RCV_BITS) {
+               s = 0;
+               for (i = 0; i < count; ++i) {
+                       c = buf[i];
+                       if (flags && flags[i] != 0)
+                               continue;
+                       s |= (c & 0x80)? SC_RCV_B7_1: SC_RCV_B7_0;
+                       c = ((c >> 4) ^ c) & 0xf;
+                       s |= (0x6996 & (1 << c))? SC_RCV_ODDP: SC_RCV_EVNP;
+               }
+               ap->rbits |= s;
+       }
+
+       while (count > 0) {
+               /* scan through and see how many chars we can do in bulk */
+               if ((ap->state & SC_ESCAPE) && buf[0] == PPP_ESCAPE)
+                       n = 1;
+               else
+                       n = scan_ordinary(ap, buf, count);
+
+               f = 0;
+               if (flags && (ap->state & SC_TOSS) == 0) {
+                       /* check the flags to see if any char had an error */
+                       for (j = 0; j < n; ++j)
+                               if ((f = flags[j]) != 0)
+                                       break;
+               }
+               if (f != 0) {
+                       /* start tossing */
+                       ap->state |= SC_TOSS;
+
+               } else if (n > 0 && (ap->state & SC_TOSS) == 0) {
+                       /* stuff the chars in the skb */
+                       skb = ap->rpkt;
+                       if (!skb) {
+                               skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2);
+                               if (!skb)
+                                       goto nomem;
+                               ap->rpkt = skb;
+                       }
+                       if (skb->len == 0) {
+                               /* Try to get the payload 4-byte aligned.
+                                * This should match the
+                                * PPP_ALLSTATIONS/PPP_UI/compressed tests in
+                                * process_input_packet, but we do not have
+                                * enough chars here to test buf[1] and buf[2].
+                                */
+                               if (buf[0] != PPP_ALLSTATIONS)
+                                       skb_reserve(skb, 2 + (buf[0] & 1));
+                       }
+                       if (n > skb_tailroom(skb)) {
+                               /* packet overflowed MRU */
+                               ap->state |= SC_TOSS;
+                       } else {
+                               sp = skb_put(skb, n);
+                               memcpy(sp, buf, n);
+                               if (ap->state & SC_ESCAPE) {
+                                       sp[0] ^= PPP_TRANS;
+                                       ap->state &= ~SC_ESCAPE;
+                               }
+                       }
+               }
+
+               if (n >= count)
+                       break;
+
+               c = buf[n];
+               if (flags != NULL && flags[n] != 0) {
+                       ap->state |= SC_TOSS;
+               } else if (c == PPP_FLAG) {
+                       process_input_packet(ap);
+               } else if (c == PPP_ESCAPE) {
+                       ap->state |= SC_ESCAPE;
+               } else if (I_IXON(ap->tty)) {
+                       if (c == START_CHAR(ap->tty))
+                               start_tty(ap->tty);
+                       else if (c == STOP_CHAR(ap->tty))
+                               stop_tty(ap->tty);
+               }
+               /* otherwise it's a char in the recv ACCM */
+               ++n;
+
+               buf += n;
+               if (flags)
+                       flags += n;
+               count -= n;
+       }
+       return;
+
+ nomem:
+       printk(KERN_ERR "PPPasync: no memory (input pkt)\n");
+       ap->state |= SC_TOSS;
+}
+
+/*
+ * We look at LCP frames going past so that we can notice
+ * and react to the LCP configure-ack from the peer.
+ * In the situation where the peer has been sent a configure-ack
+ * already, LCP is up once it has sent its configure-ack
+ * so the immediately following packet can be sent with the
+ * configured LCP options.  This allows us to process the following
+ * packet correctly without pppd needing to respond quickly.
+ *
+ * We only respond to the received configure-ack if we have just
+ * sent a configure-request, and the configure-ack contains the
+ * same data (this is checked using a 16-bit crc of the data).
+ */
+#define CONFREQ                1       /* LCP code field values */
+#define CONFACK                2
+#define LCP_MRU                1       /* LCP option numbers */
+#define LCP_ASYNCMAP   2
+
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+                          int len, int inbound)
+{
+       int dlen, fcs, i, code;
+       u32 val;
+
+       data += 2;              /* skip protocol bytes */
+       len -= 2;
+       if (len < 4)            /* 4 = code, ID, length */
+               return;
+       code = data[0];
+       if (code != CONFACK && code != CONFREQ)
+               return;
+       dlen = get_unaligned_be16(data + 2);
+       if (len < dlen)
+               return;         /* packet got truncated or length is bogus */
+
+       if (code == (inbound? CONFACK: CONFREQ)) {
+               /*
+                * sent confreq or received confack:
+                * calculate the crc of the data from the ID field on.
+                */
+               fcs = PPP_INITFCS;
+               for (i = 1; i < dlen; ++i)
+                       fcs = PPP_FCS(fcs, data[i]);
+
+               if (!inbound) {
+                       /* outbound confreq - remember the crc for later */
+                       ap->lcp_fcs = fcs;
+                       return;
+               }
+
+               /* received confack, check the crc */
+               fcs ^= ap->lcp_fcs;
+               ap->lcp_fcs = -1;
+               if (fcs != 0)
+                       return;
+       } else if (inbound)
+               return; /* not interested in received confreq */
+
+       /* process the options in the confack */
+       data += 4;
+       dlen -= 4;
+       /* data[0] is code, data[1] is length */
+       while (dlen >= 2 && dlen >= data[1] && data[1] >= 2) {
+               switch (data[0]) {
+               case LCP_MRU:
+                       val = get_unaligned_be16(data + 2);
+                       if (inbound)
+                               ap->mru = val;
+                       else
+                               ap->chan.mtu = val;
+                       break;
+               case LCP_ASYNCMAP:
+                       val = get_unaligned_be32(data + 2);
+                       if (inbound)
+                               ap->raccm = val;
+                       else
+                               ap->xaccm[0] = val;
+                       break;
+               }
+               dlen -= data[1];
+               data += data[1];
+       }
+}
+
+static void __exit ppp_async_cleanup(void)
+{
+       if (tty_unregister_ldisc(N_PPP) != 0)
+               printk(KERN_ERR "failed to unregister PPP line discipline\n");
+}
+
+module_init(ppp_async_init);
+module_exit(ppp_async_cleanup);
diff --git a/drivers/net/ppp/ppp_deflate.c b/drivers/net/ppp/ppp_deflate.c
new file mode 100644 (file)
index 0000000..1dbdf82
--- /dev/null
@@ -0,0 +1,653 @@
+/*
+ *  ==FILEVERSION 980319==
+ *
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ * This version is for use with Linux kernel 1.3.X.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * From: deflate.c,v 1.1 1996/01/18 03:17:48 paulus Exp
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <linux/ppp_defs.h>
+#include <linux/ppp-comp.h>
+
+#include <linux/zlib.h>
+#include <asm/unaligned.h>
+
+/*
+ * State for a Deflate (de)compressor.
+ */
+struct ppp_deflate_state {
+    int                seqno;
+    int                w_size;
+    int                unit;
+    int                mru;
+    int                debug;
+    z_stream   strm;
+    struct compstat stats;
+};
+
+#define DEFLATE_OVHD   2               /* Deflate overhead/packet */
+
+static void    *z_comp_alloc(unsigned char *options, int opt_len);
+static void    *z_decomp_alloc(unsigned char *options, int opt_len);
+static void    z_comp_free(void *state);
+static void    z_decomp_free(void *state);
+static int     z_comp_init(void *state, unsigned char *options,
+                                int opt_len,
+                                int unit, int hdrlen, int debug);
+static int     z_decomp_init(void *state, unsigned char *options,
+                                  int opt_len,
+                                  int unit, int hdrlen, int mru, int debug);
+static int     z_compress(void *state, unsigned char *rptr,
+                               unsigned char *obuf,
+                               int isize, int osize);
+static void    z_incomp(void *state, unsigned char *ibuf, int icnt);
+static int     z_decompress(void *state, unsigned char *ibuf,
+                               int isize, unsigned char *obuf, int osize);
+static void    z_comp_reset(void *state);
+static void    z_decomp_reset(void *state);
+static void    z_comp_stats(void *state, struct compstat *stats);
+
+/**
+ *     z_comp_free - free the memory used by a compressor
+ *     @arg:   pointer to the private state for the compressor.
+ */
+static void z_comp_free(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (state) {
+               zlib_deflateEnd(&state->strm);
+               vfree(state->strm.workspace);
+               kfree(state);
+       }
+}
+
+/**
+ *     z_comp_alloc - allocate space for a compressor.
+ *     @options: pointer to CCP option data
+ *     @opt_len: length of the CCP option at @options.
+ *
+ *     The @options pointer points to the a buffer containing the
+ *     CCP option data for the compression being negotiated.  It is
+ *     formatted according to RFC1979, and describes the window
+ *     size that the peer is requesting that we use in compressing
+ *     data to be sent to it.
+ *
+ *     Returns the pointer to the private state for the compressor,
+ *     or NULL if we could not allocate enough memory.
+ */
+static void *z_comp_alloc(unsigned char *options, int opt_len)
+{
+       struct ppp_deflate_state *state;
+       int w_size;
+
+       if (opt_len != CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return NULL;
+       w_size = DEFLATE_SIZE(options[2]);
+       if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+               return NULL;
+
+       state = kzalloc(sizeof(*state),
+                                                    GFP_KERNEL);
+       if (state == NULL)
+               return NULL;
+
+       state->strm.next_in   = NULL;
+       state->w_size         = w_size;
+       state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8));
+       if (state->strm.workspace == NULL)
+               goto out_free;
+
+       if (zlib_deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION,
+                        DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY)
+           != Z_OK)
+               goto out_free;
+       return (void *) state;
+
+out_free:
+       z_comp_free(state);
+       return NULL;
+}
+
+/**
+ *     z_comp_init - initialize a previously-allocated compressor.
+ *     @arg:   pointer to the private state for the compressor
+ *     @options: pointer to the CCP option data describing the
+ *             compression that was negotiated with the peer
+ *     @opt_len: length of the CCP option data at @options
+ *     @unit:  PPP unit number for diagnostic messages
+ *     @hdrlen: ignored (present for backwards compatibility)
+ *     @debug: debug flag; if non-zero, debug messages are printed.
+ *
+ *     The CCP options described by @options must match the options
+ *     specified when the compressor was allocated.  The compressor
+ *     history is reset.  Returns 0 for failure (CCP options don't
+ *     match) or 1 for success.
+ */
+static int z_comp_init(void *arg, unsigned char *options, int opt_len,
+                      int unit, int hdrlen, int debug)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (opt_len < CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           DEFLATE_SIZE(options[2]) != state->w_size ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return 0;
+
+       state->seqno = 0;
+       state->unit  = unit;
+       state->debug = debug;
+
+       zlib_deflateReset(&state->strm);
+
+       return 1;
+}
+
+/**
+ *     z_comp_reset - reset a previously-allocated compressor.
+ *     @arg:   pointer to private state for the compressor.
+ *
+ *     This clears the history for the compressor and makes it
+ *     ready to start emitting a new compressed stream.
+ */
+static void z_comp_reset(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       state->seqno = 0;
+       zlib_deflateReset(&state->strm);
+}
+
+/**
+ *     z_compress - compress a PPP packet with Deflate compression.
+ *     @arg:   pointer to private state for the compressor
+ *     @rptr:  uncompressed packet (input)
+ *     @obuf:  compressed packet (output)
+ *     @isize: size of uncompressed packet
+ *     @osize: space available at @obuf
+ *
+ *     Returns the length of the compressed packet, or 0 if the
+ *     packet is incompressible.
+ */
+static int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf,
+              int isize, int osize)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+       int r, proto, off, olen, oavail;
+       unsigned char *wptr;
+
+       /*
+        * Check that the protocol is in the range we handle.
+        */
+       proto = PPP_PROTOCOL(rptr);
+       if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+               return 0;
+
+       /* Don't generate compressed packets which are larger than
+          the uncompressed packet. */
+       if (osize > isize)
+               osize = isize;
+
+       wptr = obuf;
+
+       /*
+        * Copy over the PPP header and store the 2-byte sequence number.
+        */
+       wptr[0] = PPP_ADDRESS(rptr);
+       wptr[1] = PPP_CONTROL(rptr);
+       put_unaligned_be16(PPP_COMP, wptr + 2);
+       wptr += PPP_HDRLEN;
+       put_unaligned_be16(state->seqno, wptr);
+       wptr += DEFLATE_OVHD;
+       olen = PPP_HDRLEN + DEFLATE_OVHD;
+       state->strm.next_out = wptr;
+       state->strm.avail_out = oavail = osize - olen;
+       ++state->seqno;
+
+       off = (proto > 0xff) ? 2 : 3;   /* skip 1st proto byte if 0 */
+       rptr += off;
+       state->strm.next_in = rptr;
+       state->strm.avail_in = (isize - off);
+
+       for (;;) {
+               r = zlib_deflate(&state->strm, Z_PACKET_FLUSH);
+               if (r != Z_OK) {
+                       if (state->debug)
+                               printk(KERN_ERR
+                                      "z_compress: deflate returned %d\n", r);
+                       break;
+               }
+               if (state->strm.avail_out == 0) {
+                       olen += oavail;
+                       state->strm.next_out = NULL;
+                       state->strm.avail_out = oavail = 1000000;
+               } else {
+                       break;          /* all done */
+               }
+       }
+       olen += oavail - state->strm.avail_out;
+
+       /*
+        * See if we managed to reduce the size of the packet.
+        */
+       if (olen < isize) {
+               state->stats.comp_bytes += olen;
+               state->stats.comp_packets++;
+       } else {
+               state->stats.inc_bytes += isize;
+               state->stats.inc_packets++;
+               olen = 0;
+       }
+       state->stats.unc_bytes += isize;
+       state->stats.unc_packets++;
+
+       return olen;
+}
+
+/**
+ *     z_comp_stats - return compression statistics for a compressor
+ *             or decompressor.
+ *     @arg:   pointer to private space for the (de)compressor
+ *     @stats: pointer to a struct compstat to receive the result.
+ */
+static void z_comp_stats(void *arg, struct compstat *stats)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       *stats = state->stats;
+}
+
+/**
+ *     z_decomp_free - Free the memory used by a decompressor.
+ *     @arg:   pointer to private space for the decompressor.
+ */
+static void z_decomp_free(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (state) {
+               zlib_inflateEnd(&state->strm);
+               vfree(state->strm.workspace);
+               kfree(state);
+       }
+}
+
+/**
+ *     z_decomp_alloc - allocate space for a decompressor.
+ *     @options: pointer to CCP option data
+ *     @opt_len: length of the CCP option at @options.
+ *
+ *     The @options pointer points to the a buffer containing the
+ *     CCP option data for the compression being negotiated.  It is
+ *     formatted according to RFC1979, and describes the window
+ *     size that we are requesting the peer to use in compressing
+ *     data to be sent to us.
+ *
+ *     Returns the pointer to the private state for the decompressor,
+ *     or NULL if we could not allocate enough memory.
+ */
+static void *z_decomp_alloc(unsigned char *options, int opt_len)
+{
+       struct ppp_deflate_state *state;
+       int w_size;
+
+       if (opt_len != CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return NULL;
+       w_size = DEFLATE_SIZE(options[2]);
+       if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+               return NULL;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return NULL;
+
+       state->w_size         = w_size;
+       state->strm.next_out  = NULL;
+       state->strm.workspace = vmalloc(zlib_inflate_workspacesize());
+       if (state->strm.workspace == NULL)
+               goto out_free;
+
+       if (zlib_inflateInit2(&state->strm, -w_size) != Z_OK)
+               goto out_free;
+       return (void *) state;
+
+out_free:
+       z_decomp_free(state);
+       return NULL;
+}
+
+/**
+ *     z_decomp_init - initialize a previously-allocated decompressor.
+ *     @arg:   pointer to the private state for the decompressor
+ *     @options: pointer to the CCP option data describing the
+ *             compression that was negotiated with the peer
+ *     @opt_len: length of the CCP option data at @options
+ *     @unit:  PPP unit number for diagnostic messages
+ *     @hdrlen: ignored (present for backwards compatibility)
+ *     @mru:   maximum length of decompressed packets
+ *     @debug: debug flag; if non-zero, debug messages are printed.
+ *
+ *     The CCP options described by @options must match the options
+ *     specified when the decompressor was allocated.  The decompressor
+ *     history is reset.  Returns 0 for failure (CCP options don't
+ *     match) or 1 for success.
+ */
+static int z_decomp_init(void *arg, unsigned char *options, int opt_len,
+                        int unit, int hdrlen, int mru, int debug)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (opt_len < CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           DEFLATE_SIZE(options[2]) != state->w_size ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return 0;
+
+       state->seqno = 0;
+       state->unit  = unit;
+       state->debug = debug;
+       state->mru   = mru;
+
+       zlib_inflateReset(&state->strm);
+
+       return 1;
+}
+
+/**
+ *     z_decomp_reset - reset a previously-allocated decompressor.
+ *     @arg:   pointer to private state for the decompressor.
+ *
+ *     This clears the history for the decompressor and makes it
+ *     ready to receive a new compressed stream.
+ */
+static void z_decomp_reset(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       state->seqno = 0;
+       zlib_inflateReset(&state->strm);
+}
+
+/**
+ *     z_decompress - decompress a Deflate-compressed packet.
+ *     @arg:   pointer to private state for the decompressor
+ *     @ibuf:  pointer to input (compressed) packet data
+ *     @isize: length of input packet
+ *     @obuf:  pointer to space for output (decompressed) packet
+ *     @osize: amount of space available at @obuf
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression.  For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int z_decompress(void *arg, unsigned char *ibuf, int isize,
+                unsigned char *obuf, int osize)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+       int olen, seq, r;
+       int decode_proto, overflow;
+       unsigned char overflow_buf[1];
+
+       if (isize <= PPP_HDRLEN + DEFLATE_OVHD) {
+               if (state->debug)
+                       printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n",
+                              state->unit, isize);
+               return DECOMP_ERROR;
+       }
+
+       /* Check the sequence number. */
+       seq = get_unaligned_be16(ibuf + PPP_HDRLEN);
+       if (seq != (state->seqno & 0xffff)) {
+               if (state->debug)
+                       printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
+                              state->unit, seq, state->seqno & 0xffff);
+               return DECOMP_ERROR;
+       }
+       ++state->seqno;
+
+       /*
+        * Fill in the first part of the PPP header.  The protocol field
+        * comes from the decompressed data.
+        */
+       obuf[0] = PPP_ADDRESS(ibuf);
+       obuf[1] = PPP_CONTROL(ibuf);
+       obuf[2] = 0;
+
+       /*
+        * Set up to call inflate.  We set avail_out to 1 initially so we can
+        * look at the first byte of the output and decide whether we have
+        * a 1-byte or 2-byte protocol field.
+        */
+       state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD;
+       state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD);
+       state->strm.next_out = obuf + 3;
+       state->strm.avail_out = 1;
+       decode_proto = 1;
+       overflow = 0;
+
+       /*
+        * Call inflate, supplying more input or output as needed.
+        */
+       for (;;) {
+               r = zlib_inflate(&state->strm, Z_PACKET_FLUSH);
+               if (r != Z_OK) {
+                       if (state->debug)
+                               printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n",
+                                      state->unit, r, (state->strm.msg? state->strm.msg: ""));
+                       return DECOMP_FATALERROR;
+               }
+               if (state->strm.avail_out != 0)
+                       break;          /* all done */
+               if (decode_proto) {
+                       state->strm.avail_out = osize - PPP_HDRLEN;
+                       if ((obuf[3] & 1) == 0) {
+                               /* 2-byte protocol field */
+                               obuf[2] = obuf[3];
+                               --state->strm.next_out;
+                               ++state->strm.avail_out;
+                       }
+                       decode_proto = 0;
+               } else if (!overflow) {
+                       /*
+                        * We've filled up the output buffer; the only way to
+                        * find out whether inflate has any more characters
+                        * left is to give it another byte of output space.
+                        */
+                       state->strm.next_out = overflow_buf;
+                       state->strm.avail_out = 1;
+                       overflow = 1;
+               } else {
+                       if (state->debug)
+                               printk(KERN_DEBUG "z_decompress%d: ran out of mru\n",
+                                      state->unit);
+                       return DECOMP_FATALERROR;
+               }
+       }
+
+       if (decode_proto) {
+               if (state->debug)
+                       printk(KERN_DEBUG "z_decompress%d: didn't get proto\n",
+                              state->unit);
+               return DECOMP_ERROR;
+       }
+
+       olen = osize + overflow - state->strm.avail_out;
+       state->stats.unc_bytes += olen;
+       state->stats.unc_packets++;
+       state->stats.comp_bytes += isize;
+       state->stats.comp_packets++;
+
+       return olen;
+}
+
+/**
+ *     z_incomp - add incompressible input data to the history.
+ *     @arg:   pointer to private state for the decompressor
+ *     @ibuf:  pointer to input packet data
+ *     @icnt:  length of input data.
+ */
+static void z_incomp(void *arg, unsigned char *ibuf, int icnt)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+       int proto, r;
+
+       /*
+        * Check that the protocol is one we handle.
+        */
+       proto = PPP_PROTOCOL(ibuf);
+       if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+               return;
+
+       ++state->seqno;
+
+       /*
+        * We start at the either the 1st or 2nd byte of the protocol field,
+        * depending on whether the protocol value is compressible.
+        */
+       state->strm.next_in = ibuf + 3;
+       state->strm.avail_in = icnt - 3;
+       if (proto > 0xff) {
+               --state->strm.next_in;
+               ++state->strm.avail_in;
+       }
+
+       r = zlib_inflateIncomp(&state->strm);
+       if (r != Z_OK) {
+               /* gak! */
+               if (state->debug) {
+                       printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n",
+                              state->unit, r, (state->strm.msg? state->strm.msg: ""));
+               }
+               return;
+       }
+
+       /*
+        * Update stats.
+        */
+       state->stats.inc_bytes += icnt;
+       state->stats.inc_packets++;
+       state->stats.unc_bytes += icnt;
+       state->stats.unc_packets++;
+}
+
+/*************************************************************
+ * Module interface table
+ *************************************************************/
+
+/* These are in ppp_generic.c */
+extern int  ppp_register_compressor   (struct compressor *cp);
+extern void ppp_unregister_compressor (struct compressor *cp);
+
+/*
+ * Procedures exported to if_ppp.c.
+ */
+static struct compressor ppp_deflate = {
+       .compress_proto =       CI_DEFLATE,
+       .comp_alloc =           z_comp_alloc,
+       .comp_free =            z_comp_free,
+       .comp_init =            z_comp_init,
+       .comp_reset =           z_comp_reset,
+       .compress =             z_compress,
+       .comp_stat =            z_comp_stats,
+       .decomp_alloc =         z_decomp_alloc,
+       .decomp_free =          z_decomp_free,
+       .decomp_init =          z_decomp_init,
+       .decomp_reset =         z_decomp_reset,
+       .decompress =           z_decompress,
+       .incomp =               z_incomp,
+       .decomp_stat =          z_comp_stats,
+       .owner =                THIS_MODULE
+};
+
+static struct compressor ppp_deflate_draft = {
+       .compress_proto =       CI_DEFLATE_DRAFT,
+       .comp_alloc =           z_comp_alloc,
+       .comp_free =            z_comp_free,
+       .comp_init =            z_comp_init,
+       .comp_reset =           z_comp_reset,
+       .compress =             z_compress,
+       .comp_stat =            z_comp_stats,
+       .decomp_alloc =         z_decomp_alloc,
+       .decomp_free =          z_decomp_free,
+       .decomp_init =          z_decomp_init,
+       .decomp_reset =         z_decomp_reset,
+       .decompress =           z_decompress,
+       .incomp =               z_incomp,
+       .decomp_stat =          z_comp_stats,
+       .owner =                THIS_MODULE
+};
+
+static int __init deflate_init(void)
+{
+        int answer = ppp_register_compressor(&ppp_deflate);
+        if (answer == 0)
+                printk(KERN_INFO
+                      "PPP Deflate Compression module registered\n");
+       ppp_register_compressor(&ppp_deflate_draft);
+        return answer;
+}
+
+static void __exit deflate_cleanup(void)
+{
+       ppp_unregister_compressor(&ppp_deflate);
+       ppp_unregister_compressor(&ppp_deflate_draft);
+}
+
+module_init(deflate_init);
+module_exit(deflate_cleanup);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE));
+MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT));
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
new file mode 100644 (file)
index 0000000..10e5d98
--- /dev/null
@@ -0,0 +1,2954 @@
+/*
+ * Generic PPP layer for Linux.
+ *
+ * Copyright 1999-2002 Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ *
+ * The generic PPP layer handles the PPP network interfaces, the
+ * /dev/ppp device, packet and VJ compression, and multilink.
+ * It talks to PPP `channels' via the interface defined in
+ * include/linux/ppp_channel.h.  Channels provide the basic means for
+ * sending and receiving PPP frames on some kind of communications
+ * channel.
+ *
+ * Part of the code in this driver was inspired by the old async-only
+ * PPP driver, written by Michael Callahan and Al Longyear, and
+ * subsequently hacked by Paul Mackerras.
+ *
+ * ==FILEVERSION 20041108==
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/ppp_defs.h>
+#include <linux/filter.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+#include <linux/ppp-comp.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/spinlock.h>
+#include <linux/rwsem.h>
+#include <linux/stddef.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <net/slhc_vj.h>
+#include <linux/atomic.h>
+
+#include <linux/nsproxy.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+
+#define PPP_VERSION    "2.4.2"
+
+/*
+ * Network protocols we support.
+ */
+#define NP_IP  0               /* Internet Protocol V4 */
+#define NP_IPV6        1               /* Internet Protocol V6 */
+#define NP_IPX 2               /* IPX protocol */
+#define NP_AT  3               /* Appletalk protocol */
+#define NP_MPLS_UC 4           /* MPLS unicast */
+#define NP_MPLS_MC 5           /* MPLS multicast */
+#define NUM_NP 6               /* Number of NPs. */
+
+#define MPHDRLEN       6       /* multilink protocol header length */
+#define MPHDRLEN_SSN   4       /* ditto with short sequence numbers */
+
+/*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel.  In both cases, file->private_data
+ * points to one of these.
+ */
+struct ppp_file {
+       enum {
+               INTERFACE=1, CHANNEL
+       }               kind;
+       struct sk_buff_head xq;         /* pppd transmit queue */
+       struct sk_buff_head rq;         /* receive queue for pppd */
+       wait_queue_head_t rwait;        /* for poll on reading /dev/ppp */
+       atomic_t        refcnt;         /* # refs (incl /dev/ppp attached) */
+       int             hdrlen;         /* space to leave for headers */
+       int             index;          /* interface unit / channel number */
+       int             dead;           /* unit/channel has been shut down */
+};
+
+#define PF_TO_X(pf, X)         container_of(pf, X, file)
+
+#define PF_TO_PPP(pf)          PF_TO_X(pf, struct ppp)
+#define PF_TO_CHANNEL(pf)      PF_TO_X(pf, struct channel)
+
+/*
+ * Data structure describing one ppp unit.
+ * A ppp unit corresponds to a ppp network interface device
+ * and represents a multilink bundle.
+ * It can have 0 or more ppp channels connected to it.
+ */
+struct ppp {
+       struct ppp_file file;           /* stuff for read/write/poll 0 */
+       struct file     *owner;         /* file that owns this unit 48 */
+       struct list_head channels;      /* list of attached channels 4c */
+       int             n_channels;     /* how many channels are attached 54 */
+       spinlock_t      rlock;          /* lock for receive side 58 */
+       spinlock_t      wlock;          /* lock for transmit side 5c */
+       int             mru;            /* max receive unit 60 */
+       unsigned int    flags;          /* control bits 64 */
+       unsigned int    xstate;         /* transmit state bits 68 */
+       unsigned int    rstate;         /* receive state bits 6c */
+       int             debug;          /* debug flags 70 */
+       struct slcompress *vj;          /* state for VJ header compression */
+       enum NPmode     npmode[NUM_NP]; /* what to do with each net proto 78 */
+       struct sk_buff  *xmit_pending;  /* a packet ready to go out 88 */
+       struct compressor *xcomp;       /* transmit packet compressor 8c */
+       void            *xc_state;      /* its internal state 90 */
+       struct compressor *rcomp;       /* receive decompressor 94 */
+       void            *rc_state;      /* its internal state 98 */
+       unsigned long   last_xmit;      /* jiffies when last pkt sent 9c */
+       unsigned long   last_recv;      /* jiffies when last pkt rcvd a0 */
+       struct net_device *dev;         /* network interface device a4 */
+       int             closing;        /* is device closing down? a8 */
+#ifdef CONFIG_PPP_MULTILINK
+       int             nxchan;         /* next channel to send something on */
+       u32             nxseq;          /* next sequence number to send */
+       int             mrru;           /* MP: max reconst. receive unit */
+       u32             nextseq;        /* MP: seq no of next packet */
+       u32             minseq;         /* MP: min of most recent seqnos */
+       struct sk_buff_head mrq;        /* MP: receive reconstruction queue */
+#endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+       struct sock_filter *pass_filter;        /* filter for packets to pass */
+       struct sock_filter *active_filter;/* filter for pkts to reset idle */
+       unsigned pass_len, active_len;
+#endif /* CONFIG_PPP_FILTER */
+       struct net      *ppp_net;       /* the net we belong to */
+};
+
+/*
+ * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP,
+ * SC_MUST_COMP
+ * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
+ * Bits in xstate: SC_COMP_RUN
+ */
+#define SC_FLAG_BITS   (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
+                        |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \
+                        |SC_COMP_TCP|SC_REJ_COMP_TCP|SC_MUST_COMP)
+
+/*
+ * Private data structure for each channel.
+ * This includes the data structure used for multilink.
+ */
+struct channel {
+       struct ppp_file file;           /* stuff for read/write/poll */
+       struct list_head list;          /* link in all/new_channels list */
+       struct ppp_channel *chan;       /* public channel data structure */
+       struct rw_semaphore chan_sem;   /* protects `chan' during chan ioctl */
+       spinlock_t      downl;          /* protects `chan', file.xq dequeue */
+       struct ppp      *ppp;           /* ppp unit we're connected to */
+       struct net      *chan_net;      /* the net channel belongs to */
+       struct list_head clist;         /* link in list of channels per unit */
+       rwlock_t        upl;            /* protects `ppp' */
+#ifdef CONFIG_PPP_MULTILINK
+       u8              avail;          /* flag used in multilink stuff */
+       u8              had_frag;       /* >= 1 fragments have been sent */
+       u32             lastseq;        /* MP: last sequence # received */
+       int             speed;          /* speed of the corresponding ppp channel*/
+#endif /* CONFIG_PPP_MULTILINK */
+};
+
+/*
+ * SMP locking issues:
+ * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
+ * list and the ppp.n_channels field, you need to take both locks
+ * before you modify them.
+ * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
+ * channel.downl.
+ */
+
+static DEFINE_MUTEX(ppp_mutex);
+static atomic_t ppp_unit_count = ATOMIC_INIT(0);
+static atomic_t channel_count = ATOMIC_INIT(0);
+
+/* per-net private data for this module */
+static int ppp_net_id __read_mostly;
+struct ppp_net {
+       /* units to ppp mapping */
+       struct idr units_idr;
+
+       /*
+        * all_ppp_mutex protects the units_idr mapping.
+        * It also ensures that finding a ppp unit in the units_idr
+        * map and updating its file.refcnt field is atomic.
+        */
+       struct mutex all_ppp_mutex;
+
+       /* channels */
+       struct list_head all_channels;
+       struct list_head new_channels;
+       int last_channel_index;
+
+       /*
+        * all_channels_lock protects all_channels and
+        * last_channel_index, and the atomicity of find
+        * a channel and updating its file.refcnt field.
+        */
+       spinlock_t all_channels_lock;
+};
+
+/* Get the PPP protocol number from a skb */
+#define PPP_PROTO(skb) get_unaligned_be16((skb)->data)
+
+/* We limit the length of ppp->file.rq to this (arbitrary) value */
+#define PPP_MAX_RQLEN  32
+
+/*
+ * Maximum number of multilink fragments queued up.
+ * This has to be large enough to cope with the maximum latency of
+ * the slowest channel relative to the others.  Strictly it should
+ * depend on the number of channels and their characteristics.
+ */
+#define PPP_MP_MAX_QLEN        128
+
+/* Multilink header bits. */
+#define B      0x80            /* this fragment begins a packet */
+#define E      0x40            /* this fragment ends a packet */
+
+/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
+#define seq_before(a, b)       ((s32)((a) - (b)) < 0)
+#define seq_after(a, b)                ((s32)((a) - (b)) > 0)
+
+/* Prototypes. */
+static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
+                       struct file *file, unsigned int cmd, unsigned long arg);
+static void ppp_xmit_process(struct ppp *ppp);
+static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_push(struct ppp *ppp);
+static void ppp_channel_push(struct channel *pch);
+static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
+                             struct channel *pch);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
+                                           struct sk_buff *skb);
+#ifdef CONFIG_PPP_MULTILINK
+static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
+                               struct channel *pch);
+static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
+#endif /* CONFIG_PPP_MULTILINK */
+static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
+static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
+static void ppp_ccp_closed(struct ppp *ppp);
+static struct compressor *find_compressor(int type);
+static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
+static struct ppp *ppp_create_interface(struct net *net, int unit, int *retp);
+static void init_ppp_file(struct ppp_file *pf, int kind);
+static void ppp_shutdown_interface(struct ppp *ppp);
+static void ppp_destroy_interface(struct ppp *ppp);
+static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit);
+static struct channel *ppp_find_channel(struct ppp_net *pn, int unit);
+static int ppp_connect_channel(struct channel *pch, int unit);
+static int ppp_disconnect_channel(struct channel *pch);
+static void ppp_destroy_channel(struct channel *pch);
+static int unit_get(struct idr *p, void *ptr);
+static int unit_set(struct idr *p, void *ptr, int n);
+static void unit_put(struct idr *p, int n);
+static void *unit_find(struct idr *p, int n);
+
+static struct class *ppp_class;
+
+/* per net-namespace data */
+static inline struct ppp_net *ppp_pernet(struct net *net)
+{
+       BUG_ON(!net);
+
+       return net_generic(net, ppp_net_id);
+}
+
+/* Translates a PPP protocol number to a NP index (NP == network protocol) */
+static inline int proto_to_npindex(int proto)
+{
+       switch (proto) {
+       case PPP_IP:
+               return NP_IP;
+       case PPP_IPV6:
+               return NP_IPV6;
+       case PPP_IPX:
+               return NP_IPX;
+       case PPP_AT:
+               return NP_AT;
+       case PPP_MPLS_UC:
+               return NP_MPLS_UC;
+       case PPP_MPLS_MC:
+               return NP_MPLS_MC;
+       }
+       return -EINVAL;
+}
+
+/* Translates an NP index into a PPP protocol number */
+static const int npindex_to_proto[NUM_NP] = {
+       PPP_IP,
+       PPP_IPV6,
+       PPP_IPX,
+       PPP_AT,
+       PPP_MPLS_UC,
+       PPP_MPLS_MC,
+};
+
+/* Translates an ethertype into an NP index */
+static inline int ethertype_to_npindex(int ethertype)
+{
+       switch (ethertype) {
+       case ETH_P_IP:
+               return NP_IP;
+       case ETH_P_IPV6:
+               return NP_IPV6;
+       case ETH_P_IPX:
+               return NP_IPX;
+       case ETH_P_PPPTALK:
+       case ETH_P_ATALK:
+               return NP_AT;
+       case ETH_P_MPLS_UC:
+               return NP_MPLS_UC;
+       case ETH_P_MPLS_MC:
+               return NP_MPLS_MC;
+       }
+       return -1;
+}
+
+/* Translates an NP index into an ethertype */
+static const int npindex_to_ethertype[NUM_NP] = {
+       ETH_P_IP,
+       ETH_P_IPV6,
+       ETH_P_IPX,
+       ETH_P_PPPTALK,
+       ETH_P_MPLS_UC,
+       ETH_P_MPLS_MC,
+};
+
+/*
+ * Locking shorthand.
+ */
+#define ppp_xmit_lock(ppp)     spin_lock_bh(&(ppp)->wlock)
+#define ppp_xmit_unlock(ppp)   spin_unlock_bh(&(ppp)->wlock)
+#define ppp_recv_lock(ppp)     spin_lock_bh(&(ppp)->rlock)
+#define ppp_recv_unlock(ppp)   spin_unlock_bh(&(ppp)->rlock)
+#define ppp_lock(ppp)          do { ppp_xmit_lock(ppp); \
+                                    ppp_recv_lock(ppp); } while (0)
+#define ppp_unlock(ppp)                do { ppp_recv_unlock(ppp); \
+                                    ppp_xmit_unlock(ppp); } while (0)
+
+/*
+ * /dev/ppp device routines.
+ * The /dev/ppp device is used by pppd to control the ppp unit.
+ * It supports the read, write, ioctl and poll functions.
+ * Open instances of /dev/ppp can be in one of three states:
+ * unattached, attached to a ppp unit, or attached to a ppp channel.
+ */
+static int ppp_open(struct inode *inode, struct file *file)
+{
+       /*
+        * This could (should?) be enforced by the permissions on /dev/ppp.
+        */
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+       return 0;
+}
+
+static int ppp_release(struct inode *unused, struct file *file)
+{
+       struct ppp_file *pf = file->private_data;
+       struct ppp *ppp;
+
+       if (pf) {
+               file->private_data = NULL;
+               if (pf->kind == INTERFACE) {
+                       ppp = PF_TO_PPP(pf);
+                       if (file == ppp->owner)
+                               ppp_shutdown_interface(ppp);
+               }
+               if (atomic_dec_and_test(&pf->refcnt)) {
+                       switch (pf->kind) {
+                       case INTERFACE:
+                               ppp_destroy_interface(PF_TO_PPP(pf));
+                               break;
+                       case CHANNEL:
+                               ppp_destroy_channel(PF_TO_CHANNEL(pf));
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+static ssize_t ppp_read(struct file *file, char __user *buf,
+                       size_t count, loff_t *ppos)
+{
+       struct ppp_file *pf = file->private_data;
+       DECLARE_WAITQUEUE(wait, current);
+       ssize_t ret;
+       struct sk_buff *skb = NULL;
+       struct iovec iov;
+
+       ret = count;
+
+       if (!pf)
+               return -ENXIO;
+       add_wait_queue(&pf->rwait, &wait);
+       for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               skb = skb_dequeue(&pf->rq);
+               if (skb)
+                       break;
+               ret = 0;
+               if (pf->dead)
+                       break;
+               if (pf->kind == INTERFACE) {
+                       /*
+                        * Return 0 (EOF) on an interface that has no
+                        * channels connected, unless it is looping
+                        * network traffic (demand mode).
+                        */
+                       struct ppp *ppp = PF_TO_PPP(pf);
+                       if (ppp->n_channels == 0 &&
+                           (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+                               break;
+               }
+               ret = -EAGAIN;
+               if (file->f_flags & O_NONBLOCK)
+                       break;
+               ret = -ERESTARTSYS;
+               if (signal_pending(current))
+                       break;
+               schedule();
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&pf->rwait, &wait);
+
+       if (!skb)
+               goto out;
+
+       ret = -EOVERFLOW;
+       if (skb->len > count)
+               goto outf;
+       ret = -EFAULT;
+       iov.iov_base = buf;
+       iov.iov_len = count;
+       if (skb_copy_datagram_iovec(skb, 0, &iov, skb->len))
+               goto outf;
+       ret = skb->len;
+
+ outf:
+       kfree_skb(skb);
+ out:
+       return ret;
+}
+
+static ssize_t ppp_write(struct file *file, const char __user *buf,
+                        size_t count, loff_t *ppos)
+{
+       struct ppp_file *pf = file->private_data;
+       struct sk_buff *skb;
+       ssize_t ret;
+
+       if (!pf)
+               return -ENXIO;
+       ret = -ENOMEM;
+       skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
+       if (!skb)
+               goto out;
+       skb_reserve(skb, pf->hdrlen);
+       ret = -EFAULT;
+       if (copy_from_user(skb_put(skb, count), buf, count)) {
+               kfree_skb(skb);
+               goto out;
+       }
+
+       skb_queue_tail(&pf->xq, skb);
+
+       switch (pf->kind) {
+       case INTERFACE:
+               ppp_xmit_process(PF_TO_PPP(pf));
+               break;
+       case CHANNEL:
+               ppp_channel_push(PF_TO_CHANNEL(pf));
+               break;
+       }
+
+       ret = count;
+
+ out:
+       return ret;
+}
+
+/* No kernel lock - fine */
+static unsigned int ppp_poll(struct file *file, poll_table *wait)
+{
+       struct ppp_file *pf = file->private_data;
+       unsigned int mask;
+
+       if (!pf)
+               return 0;
+       poll_wait(file, &pf->rwait, wait);
+       mask = POLLOUT | POLLWRNORM;
+       if (skb_peek(&pf->rq))
+               mask |= POLLIN | POLLRDNORM;
+       if (pf->dead)
+               mask |= POLLHUP;
+       else if (pf->kind == INTERFACE) {
+               /* see comment in ppp_read */
+               struct ppp *ppp = PF_TO_PPP(pf);
+               if (ppp->n_channels == 0 &&
+                   (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+                       mask |= POLLIN | POLLRDNORM;
+       }
+
+       return mask;
+}
+
+#ifdef CONFIG_PPP_FILTER
+static int get_filter(void __user *arg, struct sock_filter **p)
+{
+       struct sock_fprog uprog;
+       struct sock_filter *code = NULL;
+       int len, err;
+
+       if (copy_from_user(&uprog, arg, sizeof(uprog)))
+               return -EFAULT;
+
+       if (!uprog.len) {
+               *p = NULL;
+               return 0;
+       }
+
+       len = uprog.len * sizeof(struct sock_filter);
+       code = memdup_user(uprog.filter, len);
+       if (IS_ERR(code))
+               return PTR_ERR(code);
+
+       err = sk_chk_filter(code, uprog.len);
+       if (err) {
+               kfree(code);
+               return err;
+       }
+
+       *p = code;
+       return uprog.len;
+}
+#endif /* CONFIG_PPP_FILTER */
+
+static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct ppp_file *pf = file->private_data;
+       struct ppp *ppp;
+       int err = -EFAULT, val, val2, i;
+       struct ppp_idle idle;
+       struct npioctl npi;
+       int unit, cflags;
+       struct slcompress *vj;
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+
+       if (!pf)
+               return ppp_unattached_ioctl(current->nsproxy->net_ns,
+                                       pf, file, cmd, arg);
+
+       if (cmd == PPPIOCDETACH) {
+               /*
+                * We have to be careful here... if the file descriptor
+                * has been dup'd, we could have another process in the
+                * middle of a poll using the same file *, so we had
+                * better not free the interface data structures -
+                * instead we fail the ioctl.  Even in this case, we
+                * shut down the interface if we are the owner of it.
+                * Actually, we should get rid of PPPIOCDETACH, userland
+                * (i.e. pppd) could achieve the same effect by closing
+                * this fd and reopening /dev/ppp.
+                */
+               err = -EINVAL;
+               mutex_lock(&ppp_mutex);
+               if (pf->kind == INTERFACE) {
+                       ppp = PF_TO_PPP(pf);
+                       if (file == ppp->owner)
+                               ppp_shutdown_interface(ppp);
+               }
+               if (atomic_long_read(&file->f_count) <= 2) {
+                       ppp_release(NULL, file);
+                       err = 0;
+               } else
+                       pr_warn("PPPIOCDETACH file->f_count=%ld\n",
+                               atomic_long_read(&file->f_count));
+               mutex_unlock(&ppp_mutex);
+               return err;
+       }
+
+       if (pf->kind == CHANNEL) {
+               struct channel *pch;
+               struct ppp_channel *chan;
+
+               mutex_lock(&ppp_mutex);
+               pch = PF_TO_CHANNEL(pf);
+
+               switch (cmd) {
+               case PPPIOCCONNECT:
+                       if (get_user(unit, p))
+                               break;
+                       err = ppp_connect_channel(pch, unit);
+                       break;
+
+               case PPPIOCDISCONN:
+                       err = ppp_disconnect_channel(pch);
+                       break;
+
+               default:
+                       down_read(&pch->chan_sem);
+                       chan = pch->chan;
+                       err = -ENOTTY;
+                       if (chan && chan->ops->ioctl)
+                               err = chan->ops->ioctl(chan, cmd, arg);
+                       up_read(&pch->chan_sem);
+               }
+               mutex_unlock(&ppp_mutex);
+               return err;
+       }
+
+       if (pf->kind != INTERFACE) {
+               /* can't happen */
+               pr_err("PPP: not interface or channel??\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&ppp_mutex);
+       ppp = PF_TO_PPP(pf);
+       switch (cmd) {
+       case PPPIOCSMRU:
+               if (get_user(val, p))
+                       break;
+               ppp->mru = val;
+               err = 0;
+               break;
+
+       case PPPIOCSFLAGS:
+               if (get_user(val, p))
+                       break;
+               ppp_lock(ppp);
+               cflags = ppp->flags & ~val;
+               ppp->flags = val & SC_FLAG_BITS;
+               ppp_unlock(ppp);
+               if (cflags & SC_CCP_OPEN)
+                       ppp_ccp_closed(ppp);
+               err = 0;
+               break;
+
+       case PPPIOCGFLAGS:
+               val = ppp->flags | ppp->xstate | ppp->rstate;
+               if (put_user(val, p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCSCOMPRESS:
+               err = ppp_set_compress(ppp, arg);
+               break;
+
+       case PPPIOCGUNIT:
+               if (put_user(ppp->file.index, p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCSDEBUG:
+               if (get_user(val, p))
+                       break;
+               ppp->debug = val;
+               err = 0;
+               break;
+
+       case PPPIOCGDEBUG:
+               if (put_user(ppp->debug, p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGIDLE:
+               idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+               idle.recv_idle = (jiffies - ppp->last_recv) / HZ;
+               if (copy_to_user(argp, &idle, sizeof(idle)))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCSMAXCID:
+               if (get_user(val, p))
+                       break;
+               val2 = 15;
+               if ((val >> 16) != 0) {
+                       val2 = val >> 16;
+                       val &= 0xffff;
+               }
+               vj = slhc_init(val2+1, val+1);
+               if (!vj) {
+                       netdev_err(ppp->dev,
+                                  "PPP: no memory (VJ compressor)\n");
+                       err = -ENOMEM;
+                       break;
+               }
+               ppp_lock(ppp);
+               if (ppp->vj)
+                       slhc_free(ppp->vj);
+               ppp->vj = vj;
+               ppp_unlock(ppp);
+               err = 0;
+               break;
+
+       case PPPIOCGNPMODE:
+       case PPPIOCSNPMODE:
+               if (copy_from_user(&npi, argp, sizeof(npi)))
+                       break;
+               err = proto_to_npindex(npi.protocol);
+               if (err < 0)
+                       break;
+               i = err;
+               if (cmd == PPPIOCGNPMODE) {
+                       err = -EFAULT;
+                       npi.mode = ppp->npmode[i];
+                       if (copy_to_user(argp, &npi, sizeof(npi)))
+                               break;
+               } else {
+                       ppp->npmode[i] = npi.mode;
+                       /* we may be able to transmit more packets now (??) */
+                       netif_wake_queue(ppp->dev);
+               }
+               err = 0;
+               break;
+
+#ifdef CONFIG_PPP_FILTER
+       case PPPIOCSPASS:
+       {
+               struct sock_filter *code;
+               err = get_filter(argp, &code);
+               if (err >= 0) {
+                       ppp_lock(ppp);
+                       kfree(ppp->pass_filter);
+                       ppp->pass_filter = code;
+                       ppp->pass_len = err;
+                       ppp_unlock(ppp);
+                       err = 0;
+               }
+               break;
+       }
+       case PPPIOCSACTIVE:
+       {
+               struct sock_filter *code;
+               err = get_filter(argp, &code);
+               if (err >= 0) {
+                       ppp_lock(ppp);
+                       kfree(ppp->active_filter);
+                       ppp->active_filter = code;
+                       ppp->active_len = err;
+                       ppp_unlock(ppp);
+                       err = 0;
+               }
+               break;
+       }
+#endif /* CONFIG_PPP_FILTER */
+
+#ifdef CONFIG_PPP_MULTILINK
+       case PPPIOCSMRRU:
+               if (get_user(val, p))
+                       break;
+               ppp_recv_lock(ppp);
+               ppp->mrru = val;
+               ppp_recv_unlock(ppp);
+               err = 0;
+               break;
+#endif /* CONFIG_PPP_MULTILINK */
+
+       default:
+               err = -ENOTTY;
+       }
+       mutex_unlock(&ppp_mutex);
+       return err;
+}
+
+static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
+                       struct file *file, unsigned int cmd, unsigned long arg)
+{
+       int unit, err = -EFAULT;
+       struct ppp *ppp;
+       struct channel *chan;
+       struct ppp_net *pn;
+       int __user *p = (int __user *)arg;
+
+       mutex_lock(&ppp_mutex);
+       switch (cmd) {
+       case PPPIOCNEWUNIT:
+               /* Create a new ppp unit */
+               if (get_user(unit, p))
+                       break;
+               ppp = ppp_create_interface(net, unit, &err);
+               if (!ppp)
+                       break;
+               file->private_data = &ppp->file;
+               ppp->owner = file;
+               err = -EFAULT;
+               if (put_user(ppp->file.index, p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCATTACH:
+               /* Attach to an existing ppp unit */
+               if (get_user(unit, p))
+                       break;
+               err = -ENXIO;
+               pn = ppp_pernet(net);
+               mutex_lock(&pn->all_ppp_mutex);
+               ppp = ppp_find_unit(pn, unit);
+               if (ppp) {
+                       atomic_inc(&ppp->file.refcnt);
+                       file->private_data = &ppp->file;
+                       err = 0;
+               }
+               mutex_unlock(&pn->all_ppp_mutex);
+               break;
+
+       case PPPIOCATTCHAN:
+               if (get_user(unit, p))
+                       break;
+               err = -ENXIO;
+               pn = ppp_pernet(net);
+               spin_lock_bh(&pn->all_channels_lock);
+               chan = ppp_find_channel(pn, unit);
+               if (chan) {
+                       atomic_inc(&chan->file.refcnt);
+                       file->private_data = &chan->file;
+                       err = 0;
+               }
+               spin_unlock_bh(&pn->all_channels_lock);
+               break;
+
+       default:
+               err = -ENOTTY;
+       }
+       mutex_unlock(&ppp_mutex);
+       return err;
+}
+
+static const struct file_operations ppp_device_fops = {
+       .owner          = THIS_MODULE,
+       .read           = ppp_read,
+       .write          = ppp_write,
+       .poll           = ppp_poll,
+       .unlocked_ioctl = ppp_ioctl,
+       .open           = ppp_open,
+       .release        = ppp_release,
+       .llseek         = noop_llseek,
+};
+
+static __net_init int ppp_init_net(struct net *net)
+{
+       struct ppp_net *pn = net_generic(net, ppp_net_id);
+
+       idr_init(&pn->units_idr);
+       mutex_init(&pn->all_ppp_mutex);
+
+       INIT_LIST_HEAD(&pn->all_channels);
+       INIT_LIST_HEAD(&pn->new_channels);
+
+       spin_lock_init(&pn->all_channels_lock);
+
+       return 0;
+}
+
+static __net_exit void ppp_exit_net(struct net *net)
+{
+       struct ppp_net *pn = net_generic(net, ppp_net_id);
+
+       idr_destroy(&pn->units_idr);
+}
+
+static struct pernet_operations ppp_net_ops = {
+       .init = ppp_init_net,
+       .exit = ppp_exit_net,
+       .id   = &ppp_net_id,
+       .size = sizeof(struct ppp_net),
+};
+
+#define PPP_MAJOR      108
+
+/* Called at boot time if ppp is compiled into the kernel,
+   or at module load time (from init_module) if compiled as a module. */
+static int __init ppp_init(void)
+{
+       int err;
+
+       pr_info("PPP generic driver version " PPP_VERSION "\n");
+
+       err = register_pernet_device(&ppp_net_ops);
+       if (err) {
+               pr_err("failed to register PPP pernet device (%d)\n", err);
+               goto out;
+       }
+
+       err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
+       if (err) {
+               pr_err("failed to register PPP device (%d)\n", err);
+               goto out_net;
+       }
+
+       ppp_class = class_create(THIS_MODULE, "ppp");
+       if (IS_ERR(ppp_class)) {
+               err = PTR_ERR(ppp_class);
+               goto out_chrdev;
+       }
+
+       /* not a big deal if we fail here :-) */
+       device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
+
+       return 0;
+
+out_chrdev:
+       unregister_chrdev(PPP_MAJOR, "ppp");
+out_net:
+       unregister_pernet_device(&ppp_net_ops);
+out:
+       return err;
+}
+
+/*
+ * Network interface unit routines.
+ */
+static netdev_tx_t
+ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ppp *ppp = netdev_priv(dev);
+       int npi, proto;
+       unsigned char *pp;
+
+       npi = ethertype_to_npindex(ntohs(skb->protocol));
+       if (npi < 0)
+               goto outf;
+
+       /* Drop, accept or reject the packet */
+       switch (ppp->npmode[npi]) {
+       case NPMODE_PASS:
+               break;
+       case NPMODE_QUEUE:
+               /* it would be nice to have a way to tell the network
+                  system to queue this one up for later. */
+               goto outf;
+       case NPMODE_DROP:
+       case NPMODE_ERROR:
+               goto outf;
+       }
+
+       /* Put the 2-byte PPP protocol number on the front,
+          making sure there is room for the address and control fields. */
+       if (skb_cow_head(skb, PPP_HDRLEN))
+               goto outf;
+
+       pp = skb_push(skb, 2);
+       proto = npindex_to_proto[npi];
+       put_unaligned_be16(proto, pp);
+
+       netif_stop_queue(dev);
+       skb_queue_tail(&ppp->file.xq, skb);
+       ppp_xmit_process(ppp);
+       return NETDEV_TX_OK;
+
+ outf:
+       kfree_skb(skb);
+       ++dev->stats.tx_dropped;
+       return NETDEV_TX_OK;
+}
+
+static int
+ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct ppp *ppp = netdev_priv(dev);
+       int err = -EFAULT;
+       void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data;
+       struct ppp_stats stats;
+       struct ppp_comp_stats cstats;
+       char *vers;
+
+       switch (cmd) {
+       case SIOCGPPPSTATS:
+               ppp_get_stats(ppp, &stats);
+               if (copy_to_user(addr, &stats, sizeof(stats)))
+                       break;
+               err = 0;
+               break;
+
+       case SIOCGPPPCSTATS:
+               memset(&cstats, 0, sizeof(cstats));
+               if (ppp->xc_state)
+                       ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c);
+               if (ppp->rc_state)
+                       ppp->rcomp->decomp_stat(ppp->rc_state, &cstats.d);
+               if (copy_to_user(addr, &cstats, sizeof(cstats)))
+                       break;
+               err = 0;
+               break;
+
+       case SIOCGPPPVER:
+               vers = PPP_VERSION;
+               if (copy_to_user(addr, vers, strlen(vers) + 1))
+                       break;
+               err = 0;
+               break;
+
+       default:
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
+static const struct net_device_ops ppp_netdev_ops = {
+       .ndo_start_xmit = ppp_start_xmit,
+       .ndo_do_ioctl   = ppp_net_ioctl,
+};
+
+static void ppp_setup(struct net_device *dev)
+{
+       dev->netdev_ops = &ppp_netdev_ops;
+       dev->hard_header_len = PPP_HDRLEN;
+       dev->mtu = PPP_MTU;
+       dev->addr_len = 0;
+       dev->tx_queue_len = 3;
+       dev->type = ARPHRD_PPP;
+       dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+       dev->features |= NETIF_F_NETNS_LOCAL;
+       dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+}
+
+/*
+ * Transmit-side routines.
+ */
+
+/*
+ * Called to do any work queued up on the transmit side
+ * that can now be done.
+ */
+static void
+ppp_xmit_process(struct ppp *ppp)
+{
+       struct sk_buff *skb;
+
+       ppp_xmit_lock(ppp);
+       if (!ppp->closing) {
+               ppp_push(ppp);
+               while (!ppp->xmit_pending &&
+                      (skb = skb_dequeue(&ppp->file.xq)))
+                       ppp_send_frame(ppp, skb);
+               /* If there's no work left to do, tell the core net
+                  code that we can accept some more. */
+               if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq))
+                       netif_wake_queue(ppp->dev);
+       }
+       ppp_xmit_unlock(ppp);
+}
+
+static inline struct sk_buff *
+pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
+{
+       struct sk_buff *new_skb;
+       int len;
+       int new_skb_size = ppp->dev->mtu +
+               ppp->xcomp->comp_extra + ppp->dev->hard_header_len;
+       int compressor_skb_size = ppp->dev->mtu +
+               ppp->xcomp->comp_extra + PPP_HDRLEN;
+       new_skb = alloc_skb(new_skb_size, GFP_ATOMIC);
+       if (!new_skb) {
+               if (net_ratelimit())
+                       netdev_err(ppp->dev, "PPP: no memory (comp pkt)\n");
+               return NULL;
+       }
+       if (ppp->dev->hard_header_len > PPP_HDRLEN)
+               skb_reserve(new_skb,
+                           ppp->dev->hard_header_len - PPP_HDRLEN);
+
+       /* compressor still expects A/C bytes in hdr */
+       len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2,
+                                  new_skb->data, skb->len + 2,
+                                  compressor_skb_size);
+       if (len > 0 && (ppp->flags & SC_CCP_UP)) {
+               kfree_skb(skb);
+               skb = new_skb;
+               skb_put(skb, len);
+               skb_pull(skb, 2);       /* pull off A/C bytes */
+       } else if (len == 0) {
+               /* didn't compress, or CCP not up yet */
+               kfree_skb(new_skb);
+               new_skb = skb;
+       } else {
+               /*
+                * (len < 0)
+                * MPPE requires that we do not send unencrypted
+                * frames.  The compressor will return -1 if we
+                * should drop the frame.  We cannot simply test
+                * the compress_proto because MPPE and MPPC share
+                * the same number.
+                */
+               if (net_ratelimit())
+                       netdev_err(ppp->dev, "ppp: compressor dropped pkt\n");
+               kfree_skb(skb);
+               kfree_skb(new_skb);
+               new_skb = NULL;
+       }
+       return new_skb;
+}
+
+/*
+ * Compress and send a frame.
+ * The caller should have locked the xmit path,
+ * and xmit_pending should be 0.
+ */
+static void
+ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+       int proto = PPP_PROTO(skb);
+       struct sk_buff *new_skb;
+       int len;
+       unsigned char *cp;
+
+       if (proto < 0x8000) {
+#ifdef CONFIG_PPP_FILTER
+               /* check if we should pass this packet */
+               /* the filter instructions are constructed assuming
+                  a four-byte PPP header on each packet */
+               *skb_push(skb, 2) = 1;
+               if (ppp->pass_filter &&
+                   sk_run_filter(skb, ppp->pass_filter) == 0) {
+                       if (ppp->debug & 1)
+                               netdev_printk(KERN_DEBUG, ppp->dev,
+                                             "PPP: outbound frame "
+                                             "not passed\n");
+                       kfree_skb(skb);
+                       return;
+               }
+               /* if this packet passes the active filter, record the time */
+               if (!(ppp->active_filter &&
+                     sk_run_filter(skb, ppp->active_filter) == 0))
+                       ppp->last_xmit = jiffies;
+               skb_pull(skb, 2);
+#else
+               /* for data packets, record the time */
+               ppp->last_xmit = jiffies;
+#endif /* CONFIG_PPP_FILTER */
+       }
+
+       ++ppp->dev->stats.tx_packets;
+       ppp->dev->stats.tx_bytes += skb->len - 2;
+
+       switch (proto) {
+       case PPP_IP:
+               if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0)
+                       break;
+               /* try to do VJ TCP header compression */
+               new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2,
+                                   GFP_ATOMIC);
+               if (!new_skb) {
+                       netdev_err(ppp->dev, "PPP: no memory (VJ comp pkt)\n");
+                       goto drop;
+               }
+               skb_reserve(new_skb, ppp->dev->hard_header_len - 2);
+               cp = skb->data + 2;
+               len = slhc_compress(ppp->vj, cp, skb->len - 2,
+                                   new_skb->data + 2, &cp,
+                                   !(ppp->flags & SC_NO_TCP_CCID));
+               if (cp == skb->data + 2) {
+                       /* didn't compress */
+                       kfree_skb(new_skb);
+               } else {
+                       if (cp[0] & SL_TYPE_COMPRESSED_TCP) {
+                               proto = PPP_VJC_COMP;
+                               cp[0] &= ~SL_TYPE_COMPRESSED_TCP;
+                       } else {
+                               proto = PPP_VJC_UNCOMP;
+                               cp[0] = skb->data[2];
+                       }
+                       kfree_skb(skb);
+                       skb = new_skb;
+                       cp = skb_put(skb, len + 2);
+                       cp[0] = 0;
+                       cp[1] = proto;
+               }
+               break;
+
+       case PPP_CCP:
+               /* peek at outbound CCP frames */
+               ppp_ccp_peek(ppp, skb, 0);
+               break;
+       }
+
+       /* try to do packet compression */
+       if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state &&
+           proto != PPP_LCP && proto != PPP_CCP) {
+               if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) {
+                       if (net_ratelimit())
+                               netdev_err(ppp->dev,
+                                          "ppp: compression required but "
+                                          "down - pkt dropped.\n");
+                       goto drop;
+               }
+               skb = pad_compress_skb(ppp, skb);
+               if (!skb)
+                       goto drop;
+       }
+
+       /*
+        * If we are waiting for traffic (demand dialling),
+        * queue it up for pppd to receive.
+        */
+       if (ppp->flags & SC_LOOP_TRAFFIC) {
+               if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
+                       goto drop;
+               skb_queue_tail(&ppp->file.rq, skb);
+               wake_up_interruptible(&ppp->file.rwait);
+               return;
+       }
+
+       ppp->xmit_pending = skb;
+       ppp_push(ppp);
+       return;
+
+ drop:
+       kfree_skb(skb);
+       ++ppp->dev->stats.tx_errors;
+}
+
+/*
+ * Try to send the frame in xmit_pending.
+ * The caller should have the xmit path locked.
+ */
+static void
+ppp_push(struct ppp *ppp)
+{
+       struct list_head *list;
+       struct channel *pch;
+       struct sk_buff *skb = ppp->xmit_pending;
+
+       if (!skb)
+               return;
+
+       list = &ppp->channels;
+       if (list_empty(list)) {
+               /* nowhere to send the packet, just drop it */
+               ppp->xmit_pending = NULL;
+               kfree_skb(skb);
+               return;
+       }
+
+       if ((ppp->flags & SC_MULTILINK) == 0) {
+               /* not doing multilink: send it down the first channel */
+               list = list->next;
+               pch = list_entry(list, struct channel, clist);
+
+               spin_lock_bh(&pch->downl);
+               if (pch->chan) {
+                       if (pch->chan->ops->start_xmit(pch->chan, skb))
+                               ppp->xmit_pending = NULL;
+               } else {
+                       /* channel got unregistered */
+                       kfree_skb(skb);
+                       ppp->xmit_pending = NULL;
+               }
+               spin_unlock_bh(&pch->downl);
+               return;
+       }
+
+#ifdef CONFIG_PPP_MULTILINK
+       /* Multilink: fragment the packet over as many links
+          as can take the packet at the moment. */
+       if (!ppp_mp_explode(ppp, skb))
+               return;
+#endif /* CONFIG_PPP_MULTILINK */
+
+       ppp->xmit_pending = NULL;
+       kfree_skb(skb);
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+static bool mp_protocol_compress __read_mostly = true;
+module_param(mp_protocol_compress, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mp_protocol_compress,
+                "compress protocol id in multilink fragments");
+
+/*
+ * Divide a packet to be transmitted into fragments and
+ * send them out the individual links.
+ */
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
+{
+       int len, totlen;
+       int i, bits, hdrlen, mtu;
+       int flen;
+       int navail, nfree, nzero;
+       int nbigger;
+       int totspeed;
+       int totfree;
+       unsigned char *p, *q;
+       struct list_head *list;
+       struct channel *pch;
+       struct sk_buff *frag;
+       struct ppp_channel *chan;
+
+       totspeed = 0; /*total bitrate of the bundle*/
+       nfree = 0; /* # channels which have no packet already queued */
+       navail = 0; /* total # of usable channels (not deregistered) */
+       nzero = 0; /* number of channels with zero speed associated*/
+       totfree = 0; /*total # of channels available and
+                                 *having no queued packets before
+                                 *starting the fragmentation*/
+
+       hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+       i = 0;
+       list_for_each_entry(pch, &ppp->channels, clist) {
+               if (pch->chan) {
+                       pch->avail = 1;
+                       navail++;
+                       pch->speed = pch->chan->speed;
+               } else {
+                       pch->avail = 0;
+               }
+               if (pch->avail) {
+                       if (skb_queue_empty(&pch->file.xq) ||
+                               !pch->had_frag) {
+                                       if (pch->speed == 0)
+                                               nzero++;
+                                       else
+                                               totspeed += pch->speed;
+
+                                       pch->avail = 2;
+                                       ++nfree;
+                                       ++totfree;
+                               }
+                       if (!pch->had_frag && i < ppp->nxchan)
+                               ppp->nxchan = i;
+               }
+               ++i;
+       }
+       /*
+        * Don't start sending this packet unless at least half of
+        * the channels are free.  This gives much better TCP
+        * performance if we have a lot of channels.
+        */
+       if (nfree == 0 || nfree < navail / 2)
+               return 0; /* can't take now, leave it in xmit_pending */
+
+       /* Do protocol field compression */
+       p = skb->data;
+       len = skb->len;
+       if (*p == 0 && mp_protocol_compress) {
+               ++p;
+               --len;
+       }
+
+       totlen = len;
+       nbigger = len % nfree;
+
+       /* skip to the channel after the one we last used
+          and start at that one */
+       list = &ppp->channels;
+       for (i = 0; i < ppp->nxchan; ++i) {
+               list = list->next;
+               if (list == &ppp->channels) {
+                       i = 0;
+                       break;
+               }
+       }
+
+       /* create a fragment for each channel */
+       bits = B;
+       while (len > 0) {
+               list = list->next;
+               if (list == &ppp->channels) {
+                       i = 0;
+                       continue;
+               }
+               pch = list_entry(list, struct channel, clist);
+               ++i;
+               if (!pch->avail)
+                       continue;
+
+               /*
+                * Skip this channel if it has a fragment pending already and
+                * we haven't given a fragment to all of the free channels.
+                */
+               if (pch->avail == 1) {
+                       if (nfree > 0)
+                               continue;
+               } else {
+                       pch->avail = 1;
+               }
+
+               /* check the channel's mtu and whether it is still attached. */
+               spin_lock_bh(&pch->downl);
+               if (pch->chan == NULL) {
+                       /* can't use this channel, it's being deregistered */
+                       if (pch->speed == 0)
+                               nzero--;
+                       else
+                               totspeed -= pch->speed;
+
+                       spin_unlock_bh(&pch->downl);
+                       pch->avail = 0;
+                       totlen = len;
+                       totfree--;
+                       nfree--;
+                       if (--navail == 0)
+                               break;
+                       continue;
+               }
+
+               /*
+               *if the channel speed is not set divide
+               *the packet evenly among the free channels;
+               *otherwise divide it according to the speed
+               *of the channel we are going to transmit on
+               */
+               flen = len;
+               if (nfree > 0) {
+                       if (pch->speed == 0) {
+                               flen = len/nfree;
+                               if (nbigger > 0) {
+                                       flen++;
+                                       nbigger--;
+                               }
+                       } else {
+                               flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) /
+                                       ((totspeed*totfree)/pch->speed)) - hdrlen;
+                               if (nbigger > 0) {
+                                       flen += ((totfree - nzero)*pch->speed)/totspeed;
+                                       nbigger -= ((totfree - nzero)*pch->speed)/
+                                                       totspeed;
+                               }
+                       }
+                       nfree--;
+               }
+
+               /*
+                *check if we are on the last channel or
+                *we exceded the length of the data to
+                *fragment
+                */
+               if ((nfree <= 0) || (flen > len))
+                       flen = len;
+               /*
+                *it is not worth to tx on slow channels:
+                *in that case from the resulting flen according to the
+                *above formula will be equal or less than zero.
+                *Skip the channel in this case
+                */
+               if (flen <= 0) {
+                       pch->avail = 2;
+                       spin_unlock_bh(&pch->downl);
+                       continue;
+               }
+
+               mtu = pch->chan->mtu - hdrlen;
+               if (mtu < 4)
+                       mtu = 4;
+               if (flen > mtu)
+                       flen = mtu;
+               if (flen == len)
+                       bits |= E;
+               frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC);
+               if (!frag)
+                       goto noskb;
+               q = skb_put(frag, flen + hdrlen);
+
+               /* make the MP header */
+               put_unaligned_be16(PPP_MP, q);
+               if (ppp->flags & SC_MP_XSHORTSEQ) {
+                       q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
+                       q[3] = ppp->nxseq;
+               } else {
+                       q[2] = bits;
+                       q[3] = ppp->nxseq >> 16;
+                       q[4] = ppp->nxseq >> 8;
+                       q[5] = ppp->nxseq;
+               }
+
+               memcpy(q + hdrlen, p, flen);
+
+               /* try to send it down the channel */
+               chan = pch->chan;
+               if (!skb_queue_empty(&pch->file.xq) ||
+                       !chan->ops->start_xmit(chan, frag))
+                       skb_queue_tail(&pch->file.xq, frag);
+               pch->had_frag = 1;
+               p += flen;
+               len -= flen;
+               ++ppp->nxseq;
+               bits = 0;
+               spin_unlock_bh(&pch->downl);
+       }
+       ppp->nxchan = i;
+
+       return 1;
+
+ noskb:
+       spin_unlock_bh(&pch->downl);
+       if (ppp->debug & 1)
+               netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
+       ++ppp->dev->stats.tx_errors;
+       ++ppp->nxseq;
+       return 1;       /* abandon the frame */
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Try to send data out on a channel.
+ */
+static void
+ppp_channel_push(struct channel *pch)
+{
+       struct sk_buff *skb;
+       struct ppp *ppp;
+
+       spin_lock_bh(&pch->downl);
+       if (pch->chan) {
+               while (!skb_queue_empty(&pch->file.xq)) {
+                       skb = skb_dequeue(&pch->file.xq);
+                       if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+                               /* put the packet back and try again later */
+                               skb_queue_head(&pch->file.xq, skb);
+                               break;
+                       }
+               }
+       } else {
+               /* channel got deregistered */
+               skb_queue_purge(&pch->file.xq);
+       }
+       spin_unlock_bh(&pch->downl);
+       /* see if there is anything from the attached unit to be sent */
+       if (skb_queue_empty(&pch->file.xq)) {
+               read_lock_bh(&pch->upl);
+               ppp = pch->ppp;
+               if (ppp)
+                       ppp_xmit_process(ppp);
+               read_unlock_bh(&pch->upl);
+       }
+}
+
+/*
+ * Receive-side routines.
+ */
+
+struct ppp_mp_skb_parm {
+       u32             sequence;
+       u8              BEbits;
+};
+#define PPP_MP_CB(skb) ((struct ppp_mp_skb_parm *)((skb)->cb))
+
+static inline void
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+       ppp_recv_lock(ppp);
+       if (!ppp->closing)
+               ppp_receive_frame(ppp, skb, pch);
+       else
+               kfree_skb(skb);
+       ppp_recv_unlock(ppp);
+}
+
+void
+ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
+{
+       struct channel *pch = chan->ppp;
+       int proto;
+
+       if (!pch) {
+               kfree_skb(skb);
+               return;
+       }
+
+       read_lock_bh(&pch->upl);
+       if (!pskb_may_pull(skb, 2)) {
+               kfree_skb(skb);
+               if (pch->ppp) {
+                       ++pch->ppp->dev->stats.rx_length_errors;
+                       ppp_receive_error(pch->ppp);
+               }
+               goto done;
+       }
+
+       proto = PPP_PROTO(skb);
+       if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
+               /* put it on the channel queue */
+               skb_queue_tail(&pch->file.rq, skb);
+               /* drop old frames if queue too long */
+               while (pch->file.rq.qlen > PPP_MAX_RQLEN &&
+                      (skb = skb_dequeue(&pch->file.rq)))
+                       kfree_skb(skb);
+               wake_up_interruptible(&pch->file.rwait);
+       } else {
+               ppp_do_recv(pch->ppp, skb, pch);
+       }
+
+done:
+       read_unlock_bh(&pch->upl);
+}
+
+/* Put a 0-length skb in the receive queue as an error indication */
+void
+ppp_input_error(struct ppp_channel *chan, int code)
+{
+       struct channel *pch = chan->ppp;
+       struct sk_buff *skb;
+
+       if (!pch)
+               return;
+
+       read_lock_bh(&pch->upl);
+       if (pch->ppp) {
+               skb = alloc_skb(0, GFP_ATOMIC);
+               if (skb) {
+                       skb->len = 0;           /* probably unnecessary */
+                       skb->cb[0] = code;
+                       ppp_do_recv(pch->ppp, skb, pch);
+               }
+       }
+       read_unlock_bh(&pch->upl);
+}
+
+/*
+ * We come in here to process a received frame.
+ * The receive side of the ppp unit is locked.
+ */
+static void
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+       /* note: a 0-length skb is used as an error indication */
+       if (skb->len > 0) {
+#ifdef CONFIG_PPP_MULTILINK
+               /* XXX do channel-level decompression here */
+               if (PPP_PROTO(skb) == PPP_MP)
+                       ppp_receive_mp_frame(ppp, skb, pch);
+               else
+#endif /* CONFIG_PPP_MULTILINK */
+                       ppp_receive_nonmp_frame(ppp, skb);
+       } else {
+               kfree_skb(skb);
+               ppp_receive_error(ppp);
+       }
+}
+
+static void
+ppp_receive_error(struct ppp *ppp)
+{
+       ++ppp->dev->stats.rx_errors;
+       if (ppp->vj)
+               slhc_toss(ppp->vj);
+}
+
+static void
+ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+       struct sk_buff *ns;
+       int proto, len, npi;
+
+       /*
+        * Decompress the frame, if compressed.
+        * Note that some decompressors need to see uncompressed frames
+        * that come in as well as compressed frames.
+        */
+       if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) &&
+           (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
+               skb = ppp_decompress_frame(ppp, skb);
+
+       if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
+               goto err;
+
+       proto = PPP_PROTO(skb);
+       switch (proto) {
+       case PPP_VJC_COMP:
+               /* decompress VJ compressed packets */
+               if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
+                       goto err;
+
+               if (skb_tailroom(skb) < 124 || skb_cloned(skb)) {
+                       /* copy to a new sk_buff with more tailroom */
+                       ns = dev_alloc_skb(skb->len + 128);
+                       if (!ns) {
+                               netdev_err(ppp->dev, "PPP: no memory "
+                                          "(VJ decomp)\n");
+                               goto err;
+                       }
+                       skb_reserve(ns, 2);
+                       skb_copy_bits(skb, 0, skb_put(ns, skb->len), skb->len);
+                       kfree_skb(skb);
+                       skb = ns;
+               }
+               else
+                       skb->ip_summed = CHECKSUM_NONE;
+
+               len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
+               if (len <= 0) {
+                       netdev_printk(KERN_DEBUG, ppp->dev,
+                                     "PPP: VJ decompression error\n");
+                       goto err;
+               }
+               len += 2;
+               if (len > skb->len)
+                       skb_put(skb, len - skb->len);
+               else if (len < skb->len)
+                       skb_trim(skb, len);
+               proto = PPP_IP;
+               break;
+
+       case PPP_VJC_UNCOMP:
+               if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
+                       goto err;
+
+               /* Until we fix the decompressor need to make sure
+                * data portion is linear.
+                */
+               if (!pskb_may_pull(skb, skb->len))
+                       goto err;
+
+               if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) {
+                       netdev_err(ppp->dev, "PPP: VJ uncompressed error\n");
+                       goto err;
+               }
+               proto = PPP_IP;
+               break;
+
+       case PPP_CCP:
+               ppp_ccp_peek(ppp, skb, 1);
+               break;
+       }
+
+       ++ppp->dev->stats.rx_packets;
+       ppp->dev->stats.rx_bytes += skb->len - 2;
+
+       npi = proto_to_npindex(proto);
+       if (npi < 0) {
+               /* control or unknown frame - pass it to pppd */
+               skb_queue_tail(&ppp->file.rq, skb);
+               /* limit queue length by dropping old frames */
+               while (ppp->file.rq.qlen > PPP_MAX_RQLEN &&
+                      (skb = skb_dequeue(&ppp->file.rq)))
+                       kfree_skb(skb);
+               /* wake up any process polling or blocking on read */
+               wake_up_interruptible(&ppp->file.rwait);
+
+       } else {
+               /* network protocol frame - give it to the kernel */
+
+#ifdef CONFIG_PPP_FILTER
+               /* check if the packet passes the pass and active filters */
+               /* the filter instructions are constructed assuming
+                  a four-byte PPP header on each packet */
+               if (ppp->pass_filter || ppp->active_filter) {
+                       if (skb_cloned(skb) &&
+                           pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+                               goto err;
+
+                       *skb_push(skb, 2) = 0;
+                       if (ppp->pass_filter &&
+                           sk_run_filter(skb, ppp->pass_filter) == 0) {
+                               if (ppp->debug & 1)
+                                       netdev_printk(KERN_DEBUG, ppp->dev,
+                                                     "PPP: inbound frame "
+                                                     "not passed\n");
+                               kfree_skb(skb);
+                               return;
+                       }
+                       if (!(ppp->active_filter &&
+                             sk_run_filter(skb, ppp->active_filter) == 0))
+                               ppp->last_recv = jiffies;
+                       __skb_pull(skb, 2);
+               } else
+#endif /* CONFIG_PPP_FILTER */
+                       ppp->last_recv = jiffies;
+
+               if ((ppp->dev->flags & IFF_UP) == 0 ||
+                   ppp->npmode[npi] != NPMODE_PASS) {
+                       kfree_skb(skb);
+               } else {
+                       /* chop off protocol */
+                       skb_pull_rcsum(skb, 2);
+                       skb->dev = ppp->dev;
+                       skb->protocol = htons(npindex_to_ethertype[npi]);
+                       skb_reset_mac_header(skb);
+                       netif_rx(skb);
+               }
+       }
+       return;
+
+ err:
+       kfree_skb(skb);
+       ppp_receive_error(ppp);
+}
+
+static struct sk_buff *
+ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+       int proto = PPP_PROTO(skb);
+       struct sk_buff *ns;
+       int len;
+
+       /* Until we fix all the decompressor's need to make sure
+        * data portion is linear.
+        */
+       if (!pskb_may_pull(skb, skb->len))
+               goto err;
+
+       if (proto == PPP_COMP) {
+               int obuff_size;
+
+               switch(ppp->rcomp->compress_proto) {
+               case CI_MPPE:
+                       obuff_size = ppp->mru + PPP_HDRLEN + 1;
+                       break;
+               default:
+                       obuff_size = ppp->mru + PPP_HDRLEN;
+                       break;
+               }
+
+               ns = dev_alloc_skb(obuff_size);
+               if (!ns) {
+                       netdev_err(ppp->dev, "ppp_decompress_frame: "
+                                  "no memory\n");
+                       goto err;
+               }
+               /* the decompressor still expects the A/C bytes in the hdr */
+               len = ppp->rcomp->decompress(ppp->rc_state, skb->data - 2,
+                               skb->len + 2, ns->data, obuff_size);
+               if (len < 0) {
+                       /* Pass the compressed frame to pppd as an
+                          error indication. */
+                       if (len == DECOMP_FATALERROR)
+                               ppp->rstate |= SC_DC_FERROR;
+                       kfree_skb(ns);
+                       goto err;
+               }
+
+               kfree_skb(skb);
+               skb = ns;
+               skb_put(skb, len);
+               skb_pull(skb, 2);       /* pull off the A/C bytes */
+
+       } else {
+               /* Uncompressed frame - pass to decompressor so it
+                  can update its dictionary if necessary. */
+               if (ppp->rcomp->incomp)
+                       ppp->rcomp->incomp(ppp->rc_state, skb->data - 2,
+                                          skb->len + 2);
+       }
+
+       return skb;
+
+ err:
+       ppp->rstate |= SC_DC_ERROR;
+       ppp_receive_error(ppp);
+       return skb;
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Receive a multilink frame.
+ * We put it on the reconstruction queue and then pull off
+ * as many completed frames as we can.
+ */
+static void
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+       u32 mask, seq;
+       struct channel *ch;
+       int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+
+       if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0)
+               goto err;               /* no good, throw it away */
+
+       /* Decode sequence number and begin/end bits */
+       if (ppp->flags & SC_MP_SHORTSEQ) {
+               seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
+               mask = 0xfff;
+       } else {
+               seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
+               mask = 0xffffff;
+       }
+       PPP_MP_CB(skb)->BEbits = skb->data[2];
+       skb_pull(skb, mphdrlen);        /* pull off PPP and MP headers */
+
+       /*
+        * Do protocol ID decompression on the first fragment of each packet.
+        */
+       if ((PPP_MP_CB(skb)->BEbits & B) && (skb->data[0] & 1))
+               *skb_push(skb, 1) = 0;
+
+       /*
+        * Expand sequence number to 32 bits, making it as close
+        * as possible to ppp->minseq.
+        */
+       seq |= ppp->minseq & ~mask;
+       if ((int)(ppp->minseq - seq) > (int)(mask >> 1))
+               seq += mask + 1;
+       else if ((int)(seq - ppp->minseq) > (int)(mask >> 1))
+               seq -= mask + 1;        /* should never happen */
+       PPP_MP_CB(skb)->sequence = seq;
+       pch->lastseq = seq;
+
+       /*
+        * If this packet comes before the next one we were expecting,
+        * drop it.
+        */
+       if (seq_before(seq, ppp->nextseq)) {
+               kfree_skb(skb);
+               ++ppp->dev->stats.rx_dropped;
+               ppp_receive_error(ppp);
+               return;
+       }
+
+       /*
+        * Reevaluate minseq, the minimum over all channels of the
+        * last sequence number received on each channel.  Because of
+        * the increasing sequence number rule, we know that any fragment
+        * before `minseq' which hasn't arrived is never going to arrive.
+        * The list of channels can't change because we have the receive
+        * side of the ppp unit locked.
+        */
+       list_for_each_entry(ch, &ppp->channels, clist) {
+               if (seq_before(ch->lastseq, seq))
+                       seq = ch->lastseq;
+       }
+       if (seq_before(ppp->minseq, seq))
+               ppp->minseq = seq;
+
+       /* Put the fragment on the reconstruction queue */
+       ppp_mp_insert(ppp, skb);
+
+       /* If the queue is getting long, don't wait any longer for packets
+          before the start of the queue. */
+       if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN) {
+               struct sk_buff *mskb = skb_peek(&ppp->mrq);
+               if (seq_before(ppp->minseq, PPP_MP_CB(mskb)->sequence))
+                       ppp->minseq = PPP_MP_CB(mskb)->sequence;
+       }
+
+       /* Pull completed packets off the queue and receive them. */
+       while ((skb = ppp_mp_reconstruct(ppp))) {
+               if (pskb_may_pull(skb, 2))
+                       ppp_receive_nonmp_frame(ppp, skb);
+               else {
+                       ++ppp->dev->stats.rx_length_errors;
+                       kfree_skb(skb);
+                       ppp_receive_error(ppp);
+               }
+       }
+
+       return;
+
+ err:
+       kfree_skb(skb);
+       ppp_receive_error(ppp);
+}
+
+/*
+ * Insert a fragment on the MP reconstruction queue.
+ * The queue is ordered by increasing sequence number.
+ */
+static void
+ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
+{
+       struct sk_buff *p;
+       struct sk_buff_head *list = &ppp->mrq;
+       u32 seq = PPP_MP_CB(skb)->sequence;
+
+       /* N.B. we don't need to lock the list lock because we have the
+          ppp unit receive-side lock. */
+       skb_queue_walk(list, p) {
+               if (seq_before(seq, PPP_MP_CB(p)->sequence))
+                       break;
+       }
+       __skb_queue_before(list, p, skb);
+}
+
+/*
+ * Reconstruct a packet from the MP fragment queue.
+ * We go through increasing sequence numbers until we find a
+ * complete packet, or we get to the sequence number for a fragment
+ * which hasn't arrived but might still do so.
+ */
+static struct sk_buff *
+ppp_mp_reconstruct(struct ppp *ppp)
+{
+       u32 seq = ppp->nextseq;
+       u32 minseq = ppp->minseq;
+       struct sk_buff_head *list = &ppp->mrq;
+       struct sk_buff *p, *tmp;
+       struct sk_buff *head, *tail;
+       struct sk_buff *skb = NULL;
+       int lost = 0, len = 0;
+
+       if (ppp->mrru == 0)     /* do nothing until mrru is set */
+               return NULL;
+       head = list->next;
+       tail = NULL;
+       skb_queue_walk_safe(list, p, tmp) {
+       again:
+               if (seq_before(PPP_MP_CB(p)->sequence, seq)) {
+                       /* this can't happen, anyway ignore the skb */
+                       netdev_err(ppp->dev, "ppp_mp_reconstruct bad "
+                                  "seq %u < %u\n",
+                                  PPP_MP_CB(p)->sequence, seq);
+                       __skb_unlink(p, list);
+                       kfree_skb(p);
+                       continue;
+               }
+               if (PPP_MP_CB(p)->sequence != seq) {
+                       /* Fragment `seq' is missing.  If it is after
+                          minseq, it might arrive later, so stop here. */
+                       if (seq_after(seq, minseq))
+                               break;
+                       /* Fragment `seq' is lost, keep going. */
+                       lost = 1;
+                       seq = seq_before(minseq, PPP_MP_CB(p)->sequence)?
+                               minseq + 1: PPP_MP_CB(p)->sequence;
+                       goto again;
+               }
+
+               /*
+                * At this point we know that all the fragments from
+                * ppp->nextseq to seq are either present or lost.
+                * Also, there are no complete packets in the queue
+                * that have no missing fragments and end before this
+                * fragment.
+                */
+
+               /* B bit set indicates this fragment starts a packet */
+               if (PPP_MP_CB(p)->BEbits & B) {
+                       head = p;
+                       lost = 0;
+                       len = 0;
+               }
+
+               len += p->len;
+
+               /* Got a complete packet yet? */
+               if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) &&
+                   (PPP_MP_CB(head)->BEbits & B)) {
+                       if (len > ppp->mrru + 2) {
+                               ++ppp->dev->stats.rx_length_errors;
+                               netdev_printk(KERN_DEBUG, ppp->dev,
+                                             "PPP: reconstructed packet"
+                                             " is too long (%d)\n", len);
+                       } else {
+                               tail = p;
+                               break;
+                       }
+                       ppp->nextseq = seq + 1;
+               }
+
+               /*
+                * If this is the ending fragment of a packet,
+                * and we haven't found a complete valid packet yet,
+                * we can discard up to and including this fragment.
+                */
+               if (PPP_MP_CB(p)->BEbits & E) {
+                       struct sk_buff *tmp2;
+
+                       skb_queue_reverse_walk_from_safe(list, p, tmp2) {
+                               __skb_unlink(p, list);
+                               kfree_skb(p);
+                       }
+                       head = skb_peek(list);
+                       if (!head)
+                               break;
+               }
+               ++seq;
+       }
+
+       /* If we have a complete packet, copy it all into one skb. */
+       if (tail != NULL) {
+               /* If we have discarded any fragments,
+                  signal a receive error. */
+               if (PPP_MP_CB(head)->sequence != ppp->nextseq) {
+                       if (ppp->debug & 1)
+                               netdev_printk(KERN_DEBUG, ppp->dev,
+                                             "  missed pkts %u..%u\n",
+                                             ppp->nextseq,
+                                             PPP_MP_CB(head)->sequence-1);
+                       ++ppp->dev->stats.rx_dropped;
+                       ppp_receive_error(ppp);
+               }
+
+               skb = head;
+               if (head != tail) {
+                       struct sk_buff **fragpp = &skb_shinfo(skb)->frag_list;
+                       p = skb_queue_next(list, head);
+                       __skb_unlink(skb, list);
+                       skb_queue_walk_from_safe(list, p, tmp) {
+                               __skb_unlink(p, list);
+                               *fragpp = p;
+                               p->next = NULL;
+                               fragpp = &p->next;
+
+                               skb->len += p->len;
+                               skb->data_len += p->len;
+                               skb->truesize += p->len;
+
+                               if (p == tail)
+                                       break;
+                       }
+               } else {
+                       __skb_unlink(skb, list);
+               }
+
+               ppp->nextseq = PPP_MP_CB(tail)->sequence + 1;
+       }
+
+       return skb;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Channel interface.
+ */
+
+/* Create a new, unattached ppp channel. */
+int ppp_register_channel(struct ppp_channel *chan)
+{
+       return ppp_register_net_channel(current->nsproxy->net_ns, chan);
+}
+
+/* Create a new, unattached ppp channel for specified net. */
+int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
+{
+       struct channel *pch;
+       struct ppp_net *pn;
+
+       pch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+       if (!pch)
+               return -ENOMEM;
+
+       pn = ppp_pernet(net);
+
+       pch->ppp = NULL;
+       pch->chan = chan;
+       pch->chan_net = net;
+       chan->ppp = pch;
+       init_ppp_file(&pch->file, CHANNEL);
+       pch->file.hdrlen = chan->hdrlen;
+#ifdef CONFIG_PPP_MULTILINK
+       pch->lastseq = -1;
+#endif /* CONFIG_PPP_MULTILINK */
+       init_rwsem(&pch->chan_sem);
+       spin_lock_init(&pch->downl);
+       rwlock_init(&pch->upl);
+
+       spin_lock_bh(&pn->all_channels_lock);
+       pch->file.index = ++pn->last_channel_index;
+       list_add(&pch->list, &pn->new_channels);
+       atomic_inc(&channel_count);
+       spin_unlock_bh(&pn->all_channels_lock);
+
+       return 0;
+}
+
+/*
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+       struct channel *pch = chan->ppp;
+
+       if (pch)
+               return pch->file.index;
+       return -1;
+}
+
+/*
+ * Return the PPP unit number to which a channel is connected.
+ */
+int ppp_unit_number(struct ppp_channel *chan)
+{
+       struct channel *pch = chan->ppp;
+       int unit = -1;
+
+       if (pch) {
+               read_lock_bh(&pch->upl);
+               if (pch->ppp)
+                       unit = pch->ppp->file.index;
+               read_unlock_bh(&pch->upl);
+       }
+       return unit;
+}
+
+/*
+ * Return the PPP device interface name of a channel.
+ */
+char *ppp_dev_name(struct ppp_channel *chan)
+{
+       struct channel *pch = chan->ppp;
+       char *name = NULL;
+
+       if (pch) {
+               read_lock_bh(&pch->upl);
+               if (pch->ppp && pch->ppp->dev)
+                       name = pch->ppp->dev->name;
+               read_unlock_bh(&pch->upl);
+       }
+       return name;
+}
+
+
+/*
+ * Disconnect a channel from the generic layer.
+ * This must be called in process context.
+ */
+void
+ppp_unregister_channel(struct ppp_channel *chan)
+{
+       struct channel *pch = chan->ppp;
+       struct ppp_net *pn;
+
+       if (!pch)
+               return;         /* should never happen */
+
+       chan->ppp = NULL;
+
+       /*
+        * This ensures that we have returned from any calls into the
+        * the channel's start_xmit or ioctl routine before we proceed.
+        */
+       down_write(&pch->chan_sem);
+       spin_lock_bh(&pch->downl);
+       pch->chan = NULL;
+       spin_unlock_bh(&pch->downl);
+       up_write(&pch->chan_sem);
+       ppp_disconnect_channel(pch);
+
+       pn = ppp_pernet(pch->chan_net);
+       spin_lock_bh(&pn->all_channels_lock);
+       list_del(&pch->list);
+       spin_unlock_bh(&pn->all_channels_lock);
+
+       pch->file.dead = 1;
+       wake_up_interruptible(&pch->file.rwait);
+       if (atomic_dec_and_test(&pch->file.refcnt))
+               ppp_destroy_channel(pch);
+}
+
+/*
+ * Callback from a channel when it can accept more to transmit.
+ * This should be called at BH/softirq level, not interrupt level.
+ */
+void
+ppp_output_wakeup(struct ppp_channel *chan)
+{
+       struct channel *pch = chan->ppp;
+
+       if (!pch)
+               return;
+       ppp_channel_push(pch);
+}
+
+/*
+ * Compression control.
+ */
+
+/* Process the PPPIOCSCOMPRESS ioctl. */
+static int
+ppp_set_compress(struct ppp *ppp, unsigned long arg)
+{
+       int err;
+       struct compressor *cp, *ocomp;
+       struct ppp_option_data data;
+       void *state, *ostate;
+       unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
+
+       err = -EFAULT;
+       if (copy_from_user(&data, (void __user *) arg, sizeof(data)) ||
+           (data.length <= CCP_MAX_OPTION_LENGTH &&
+            copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
+               goto out;
+       err = -EINVAL;
+       if (data.length > CCP_MAX_OPTION_LENGTH ||
+           ccp_option[1] < 2 || ccp_option[1] > data.length)
+               goto out;
+
+       cp = try_then_request_module(
+               find_compressor(ccp_option[0]),
+               "ppp-compress-%d", ccp_option[0]);
+       if (!cp)
+               goto out;
+
+       err = -ENOBUFS;
+       if (data.transmit) {
+               state = cp->comp_alloc(ccp_option, data.length);
+               if (state) {
+                       ppp_xmit_lock(ppp);
+                       ppp->xstate &= ~SC_COMP_RUN;
+                       ocomp = ppp->xcomp;
+                       ostate = ppp->xc_state;
+                       ppp->xcomp = cp;
+                       ppp->xc_state = state;
+                       ppp_xmit_unlock(ppp);
+                       if (ostate) {
+                               ocomp->comp_free(ostate);
+                               module_put(ocomp->owner);
+                       }
+                       err = 0;
+               } else
+                       module_put(cp->owner);
+
+       } else {
+               state = cp->decomp_alloc(ccp_option, data.length);
+               if (state) {
+                       ppp_recv_lock(ppp);
+                       ppp->rstate &= ~SC_DECOMP_RUN;
+                       ocomp = ppp->rcomp;
+                       ostate = ppp->rc_state;
+                       ppp->rcomp = cp;
+                       ppp->rc_state = state;
+                       ppp_recv_unlock(ppp);
+                       if (ostate) {
+                               ocomp->decomp_free(ostate);
+                               module_put(ocomp->owner);
+                       }
+                       err = 0;
+               } else
+                       module_put(cp->owner);
+       }
+
+ out:
+       return err;
+}
+
+/*
+ * Look at a CCP packet and update our state accordingly.
+ * We assume the caller has the xmit or recv path locked.
+ */
+static void
+ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
+{
+       unsigned char *dp;
+       int len;
+
+       if (!pskb_may_pull(skb, CCP_HDRLEN + 2))
+               return; /* no header */
+       dp = skb->data + 2;
+
+       switch (CCP_CODE(dp)) {
+       case CCP_CONFREQ:
+
+               /* A ConfReq starts negotiation of compression
+                * in one direction of transmission,
+                * and hence brings it down...but which way?
+                *
+                * Remember:
+                * A ConfReq indicates what the sender would like to receive
+                */
+               if(inbound)
+                       /* He is proposing what I should send */
+                       ppp->xstate &= ~SC_COMP_RUN;
+               else
+                       /* I am proposing to what he should send */
+                       ppp->rstate &= ~SC_DECOMP_RUN;
+
+               break;
+
+       case CCP_TERMREQ:
+       case CCP_TERMACK:
+               /*
+                * CCP is going down, both directions of transmission
+                */
+               ppp->rstate &= ~SC_DECOMP_RUN;
+               ppp->xstate &= ~SC_COMP_RUN;
+               break;
+
+       case CCP_CONFACK:
+               if ((ppp->flags & (SC_CCP_OPEN | SC_CCP_UP)) != SC_CCP_OPEN)
+                       break;
+               len = CCP_LENGTH(dp);
+               if (!pskb_may_pull(skb, len + 2))
+                       return;         /* too short */
+               dp += CCP_HDRLEN;
+               len -= CCP_HDRLEN;
+               if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp))
+                       break;
+               if (inbound) {
+                       /* we will start receiving compressed packets */
+                       if (!ppp->rc_state)
+                               break;
+                       if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
+                                       ppp->file.index, 0, ppp->mru, ppp->debug)) {
+                               ppp->rstate |= SC_DECOMP_RUN;
+                               ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
+                       }
+               } else {
+                       /* we will soon start sending compressed packets */
+                       if (!ppp->xc_state)
+                               break;
+                       if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
+                                       ppp->file.index, 0, ppp->debug))
+                               ppp->xstate |= SC_COMP_RUN;
+               }
+               break;
+
+       case CCP_RESETACK:
+               /* reset the [de]compressor */
+               if ((ppp->flags & SC_CCP_UP) == 0)
+                       break;
+               if (inbound) {
+                       if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)) {
+                               ppp->rcomp->decomp_reset(ppp->rc_state);
+                               ppp->rstate &= ~SC_DC_ERROR;
+                       }
+               } else {
+                       if (ppp->xc_state && (ppp->xstate & SC_COMP_RUN))
+                               ppp->xcomp->comp_reset(ppp->xc_state);
+               }
+               break;
+       }
+}
+
+/* Free up compression resources. */
+static void
+ppp_ccp_closed(struct ppp *ppp)
+{
+       void *xstate, *rstate;
+       struct compressor *xcomp, *rcomp;
+
+       ppp_lock(ppp);
+       ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
+       ppp->xstate = 0;
+       xcomp = ppp->xcomp;
+       xstate = ppp->xc_state;
+       ppp->xc_state = NULL;
+       ppp->rstate = 0;
+       rcomp = ppp->rcomp;
+       rstate = ppp->rc_state;
+       ppp->rc_state = NULL;
+       ppp_unlock(ppp);
+
+       if (xstate) {
+               xcomp->comp_free(xstate);
+               module_put(xcomp->owner);
+       }
+       if (rstate) {
+               rcomp->decomp_free(rstate);
+               module_put(rcomp->owner);
+       }
+}
+
+/* List of compressors. */
+static LIST_HEAD(compressor_list);
+static DEFINE_SPINLOCK(compressor_list_lock);
+
+struct compressor_entry {
+       struct list_head list;
+       struct compressor *comp;
+};
+
+static struct compressor_entry *
+find_comp_entry(int proto)
+{
+       struct compressor_entry *ce;
+
+       list_for_each_entry(ce, &compressor_list, list) {
+               if (ce->comp->compress_proto == proto)
+                       return ce;
+       }
+       return NULL;
+}
+
+/* Register a compressor */
+int
+ppp_register_compressor(struct compressor *cp)
+{
+       struct compressor_entry *ce;
+       int ret;
+       spin_lock(&compressor_list_lock);
+       ret = -EEXIST;
+       if (find_comp_entry(cp->compress_proto))
+               goto out;
+       ret = -ENOMEM;
+       ce = kmalloc(sizeof(struct compressor_entry), GFP_ATOMIC);
+       if (!ce)
+               goto out;
+       ret = 0;
+       ce->comp = cp;
+       list_add(&ce->list, &compressor_list);
+ out:
+       spin_unlock(&compressor_list_lock);
+       return ret;
+}
+
+/* Unregister a compressor */
+void
+ppp_unregister_compressor(struct compressor *cp)
+{
+       struct compressor_entry *ce;
+
+       spin_lock(&compressor_list_lock);
+       ce = find_comp_entry(cp->compress_proto);
+       if (ce && ce->comp == cp) {
+               list_del(&ce->list);
+               kfree(ce);
+       }
+       spin_unlock(&compressor_list_lock);
+}
+
+/* Find a compressor. */
+static struct compressor *
+find_compressor(int type)
+{
+       struct compressor_entry *ce;
+       struct compressor *cp = NULL;
+
+       spin_lock(&compressor_list_lock);
+       ce = find_comp_entry(type);
+       if (ce) {
+               cp = ce->comp;
+               if (!try_module_get(cp->owner))
+                       cp = NULL;
+       }
+       spin_unlock(&compressor_list_lock);
+       return cp;
+}
+
+/*
+ * Miscelleneous stuff.
+ */
+
+static void
+ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
+{
+       struct slcompress *vj = ppp->vj;
+
+       memset(st, 0, sizeof(*st));
+       st->p.ppp_ipackets = ppp->dev->stats.rx_packets;
+       st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
+       st->p.ppp_ibytes = ppp->dev->stats.rx_bytes;
+       st->p.ppp_opackets = ppp->dev->stats.tx_packets;
+       st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
+       st->p.ppp_obytes = ppp->dev->stats.tx_bytes;
+       if (!vj)
+               return;
+       st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
+       st->vj.vjs_compressed = vj->sls_o_compressed;
+       st->vj.vjs_searches = vj->sls_o_searches;
+       st->vj.vjs_misses = vj->sls_o_misses;
+       st->vj.vjs_errorin = vj->sls_i_error;
+       st->vj.vjs_tossed = vj->sls_i_tossed;
+       st->vj.vjs_uncompressedin = vj->sls_i_uncompressed;
+       st->vj.vjs_compressedin = vj->sls_i_compressed;
+}
+
+/*
+ * Stuff for handling the lists of ppp units and channels
+ * and for initialization.
+ */
+
+/*
+ * Create a new ppp interface unit.  Fails if it can't allocate memory
+ * or if there is already a unit with the requested number.
+ * unit == -1 means allocate a new number.
+ */
+static struct ppp *
+ppp_create_interface(struct net *net, int unit, int *retp)
+{
+       struct ppp *ppp;
+       struct ppp_net *pn;
+       struct net_device *dev = NULL;
+       int ret = -ENOMEM;
+       int i;
+
+       dev = alloc_netdev(sizeof(struct ppp), "", ppp_setup);
+       if (!dev)
+               goto out1;
+
+       pn = ppp_pernet(net);
+
+       ppp = netdev_priv(dev);
+       ppp->dev = dev;
+       ppp->mru = PPP_MRU;
+       init_ppp_file(&ppp->file, INTERFACE);
+       ppp->file.hdrlen = PPP_HDRLEN - 2;      /* don't count proto bytes */
+       for (i = 0; i < NUM_NP; ++i)
+               ppp->npmode[i] = NPMODE_PASS;
+       INIT_LIST_HEAD(&ppp->channels);
+       spin_lock_init(&ppp->rlock);
+       spin_lock_init(&ppp->wlock);
+#ifdef CONFIG_PPP_MULTILINK
+       ppp->minseq = -1;
+       skb_queue_head_init(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
+
+       /*
+        * drum roll: don't forget to set
+        * the net device is belong to
+        */
+       dev_net_set(dev, net);
+
+       mutex_lock(&pn->all_ppp_mutex);
+
+       if (unit < 0) {
+               unit = unit_get(&pn->units_idr, ppp);
+               if (unit < 0) {
+                       ret = unit;
+                       goto out2;
+               }
+       } else {
+               ret = -EEXIST;
+               if (unit_find(&pn->units_idr, unit))
+                       goto out2; /* unit already exists */
+               /*
+                * if caller need a specified unit number
+                * lets try to satisfy him, otherwise --
+                * he should better ask us for new unit number
+                *
+                * NOTE: yes I know that returning EEXIST it's not
+                * fair but at least pppd will ask us to allocate
+                * new unit in this case so user is happy :)
+                */
+               unit = unit_set(&pn->units_idr, ppp, unit);
+               if (unit < 0)
+                       goto out2;
+       }
+
+       /* Initialize the new ppp unit */
+       ppp->file.index = unit;
+       sprintf(dev->name, "ppp%d", unit);
+
+       ret = register_netdev(dev);
+       if (ret != 0) {
+               unit_put(&pn->units_idr, unit);
+               netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n",
+                          dev->name, ret);
+               goto out2;
+       }
+
+       ppp->ppp_net = net;
+
+       atomic_inc(&ppp_unit_count);
+       mutex_unlock(&pn->all_ppp_mutex);
+
+       *retp = 0;
+       return ppp;
+
+out2:
+       mutex_unlock(&pn->all_ppp_mutex);
+       free_netdev(dev);
+out1:
+       *retp = ret;
+       return NULL;
+}
+
+/*
+ * Initialize a ppp_file structure.
+ */
+static void
+init_ppp_file(struct ppp_file *pf, int kind)
+{
+       pf->kind = kind;
+       skb_queue_head_init(&pf->xq);
+       skb_queue_head_init(&pf->rq);
+       atomic_set(&pf->refcnt, 1);
+       init_waitqueue_head(&pf->rwait);
+}
+
+/*
+ * Take down a ppp interface unit - called when the owning file
+ * (the one that created the unit) is closed or detached.
+ */
+static void ppp_shutdown_interface(struct ppp *ppp)
+{
+       struct ppp_net *pn;
+
+       pn = ppp_pernet(ppp->ppp_net);
+       mutex_lock(&pn->all_ppp_mutex);
+
+       /* This will call dev_close() for us. */
+       ppp_lock(ppp);
+       if (!ppp->closing) {
+               ppp->closing = 1;
+               ppp_unlock(ppp);
+               unregister_netdev(ppp->dev);
+               unit_put(&pn->units_idr, ppp->file.index);
+       } else
+               ppp_unlock(ppp);
+
+       ppp->file.dead = 1;
+       ppp->owner = NULL;
+       wake_up_interruptible(&ppp->file.rwait);
+
+       mutex_unlock(&pn->all_ppp_mutex);
+}
+
+/*
+ * Free the memory used by a ppp unit.  This is only called once
+ * there are no channels connected to the unit and no file structs
+ * that reference the unit.
+ */
+static void ppp_destroy_interface(struct ppp *ppp)
+{
+       atomic_dec(&ppp_unit_count);
+
+       if (!ppp->file.dead || ppp->n_channels) {
+               /* "can't happen" */
+               netdev_err(ppp->dev, "ppp: destroying ppp struct %p "
+                          "but dead=%d n_channels=%d !\n",
+                          ppp, ppp->file.dead, ppp->n_channels);
+               return;
+       }
+
+       ppp_ccp_closed(ppp);
+       if (ppp->vj) {
+               slhc_free(ppp->vj);
+               ppp->vj = NULL;
+       }
+       skb_queue_purge(&ppp->file.xq);
+       skb_queue_purge(&ppp->file.rq);
+#ifdef CONFIG_PPP_MULTILINK
+       skb_queue_purge(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+       kfree(ppp->pass_filter);
+       ppp->pass_filter = NULL;
+       kfree(ppp->active_filter);
+       ppp->active_filter = NULL;
+#endif /* CONFIG_PPP_FILTER */
+
+       kfree_skb(ppp->xmit_pending);
+
+       free_netdev(ppp->dev);
+}
+
+/*
+ * Locate an existing ppp unit.
+ * The caller should have locked the all_ppp_mutex.
+ */
+static struct ppp *
+ppp_find_unit(struct ppp_net *pn, int unit)
+{
+       return unit_find(&pn->units_idr, unit);
+}
+
+/*
+ * Locate an existing ppp channel.
+ * The caller should have locked the all_channels_lock.
+ * First we look in the new_channels list, then in the
+ * all_channels list.  If found in the new_channels list,
+ * we move it to the all_channels list.  This is for speed
+ * when we have a lot of channels in use.
+ */
+static struct channel *
+ppp_find_channel(struct ppp_net *pn, int unit)
+{
+       struct channel *pch;
+
+       list_for_each_entry(pch, &pn->new_channels, list) {
+               if (pch->file.index == unit) {
+                       list_move(&pch->list, &pn->all_channels);
+                       return pch;
+               }
+       }
+
+       list_for_each_entry(pch, &pn->all_channels, list) {
+               if (pch->file.index == unit)
+                       return pch;
+       }
+
+       return NULL;
+}
+
+/*
+ * Connect a PPP channel to a PPP interface unit.
+ */
+static int
+ppp_connect_channel(struct channel *pch, int unit)
+{
+       struct ppp *ppp;
+       struct ppp_net *pn;
+       int ret = -ENXIO;
+       int hdrlen;
+
+       pn = ppp_pernet(pch->chan_net);
+
+       mutex_lock(&pn->all_ppp_mutex);
+       ppp = ppp_find_unit(pn, unit);
+       if (!ppp)
+               goto out;
+       write_lock_bh(&pch->upl);
+       ret = -EINVAL;
+       if (pch->ppp)
+               goto outl;
+
+       ppp_lock(ppp);
+       if (pch->file.hdrlen > ppp->file.hdrlen)
+               ppp->file.hdrlen = pch->file.hdrlen;
+       hdrlen = pch->file.hdrlen + 2;  /* for protocol bytes */
+       if (hdrlen > ppp->dev->hard_header_len)
+               ppp->dev->hard_header_len = hdrlen;
+       list_add_tail(&pch->clist, &ppp->channels);
+       ++ppp->n_channels;
+       pch->ppp = ppp;
+       atomic_inc(&ppp->file.refcnt);
+       ppp_unlock(ppp);
+       ret = 0;
+
+ outl:
+       write_unlock_bh(&pch->upl);
+ out:
+       mutex_unlock(&pn->all_ppp_mutex);
+       return ret;
+}
+
+/*
+ * Disconnect a channel from its ppp unit.
+ */
+static int
+ppp_disconnect_channel(struct channel *pch)
+{
+       struct ppp *ppp;
+       int err = -EINVAL;
+
+       write_lock_bh(&pch->upl);
+       ppp = pch->ppp;
+       pch->ppp = NULL;
+       write_unlock_bh(&pch->upl);
+       if (ppp) {
+               /* remove it from the ppp unit's list */
+               ppp_lock(ppp);
+               list_del(&pch->clist);
+               if (--ppp->n_channels == 0)
+                       wake_up_interruptible(&ppp->file.rwait);
+               ppp_unlock(ppp);
+               if (atomic_dec_and_test(&ppp->file.refcnt))
+                       ppp_destroy_interface(ppp);
+               err = 0;
+       }
+       return err;
+}
+
+/*
+ * Free up the resources used by a ppp channel.
+ */
+static void ppp_destroy_channel(struct channel *pch)
+{
+       atomic_dec(&channel_count);
+
+       if (!pch->file.dead) {
+               /* "can't happen" */
+               pr_err("ppp: destroying undead channel %p !\n", pch);
+               return;
+       }
+       skb_queue_purge(&pch->file.xq);
+       skb_queue_purge(&pch->file.rq);
+       kfree(pch);
+}
+
+static void __exit ppp_cleanup(void)
+{
+       /* should never happen */
+       if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
+               pr_err("PPP: removing module but units remain!\n");
+       unregister_chrdev(PPP_MAJOR, "ppp");
+       device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
+       class_destroy(ppp_class);
+       unregister_pernet_device(&ppp_net_ops);
+}
+
+/*
+ * Units handling. Caller must protect concurrent access
+ * by holding all_ppp_mutex
+ */
+
+static int __unit_alloc(struct idr *p, void *ptr, int n)
+{
+       int unit, err;
+
+again:
+       if (!idr_pre_get(p, GFP_KERNEL)) {
+               pr_err("PPP: No free memory for idr\n");
+               return -ENOMEM;
+       }
+
+       err = idr_get_new_above(p, ptr, n, &unit);
+       if (err < 0) {
+               if (err == -EAGAIN)
+                       goto again;
+               return err;
+       }
+
+       return unit;
+}
+
+/* associate pointer with specified number */
+static int unit_set(struct idr *p, void *ptr, int n)
+{
+       int unit;
+
+       unit = __unit_alloc(p, ptr, n);
+       if (unit < 0)
+               return unit;
+       else if (unit != n) {
+               idr_remove(p, unit);
+               return -EINVAL;
+       }
+
+       return unit;
+}
+
+/* get new free unit number and associate pointer with it */
+static int unit_get(struct idr *p, void *ptr)
+{
+       return __unit_alloc(p, ptr, 0);
+}
+
+/* put unit number back to a pool */
+static void unit_put(struct idr *p, int n)
+{
+       idr_remove(p, n);
+}
+
+/* get pointer associated with the number */
+static void *unit_find(struct idr *p, int n)
+{
+       return idr_find(p, n);
+}
+
+/* Module/initialization stuff */
+
+module_init(ppp_init);
+module_exit(ppp_cleanup);
+
+EXPORT_SYMBOL(ppp_register_net_channel);
+EXPORT_SYMBOL(ppp_register_channel);
+EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_unit_number);
+EXPORT_SYMBOL(ppp_dev_name);
+EXPORT_SYMBOL(ppp_input);
+EXPORT_SYMBOL(ppp_input_error);
+EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0);
+MODULE_ALIAS("devname:ppp");
diff --git a/drivers/net/ppp/ppp_mppe.c b/drivers/net/ppp/ppp_mppe.c
new file mode 100644 (file)
index 0000000..9a1849a
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * ppp_mppe.c - interface MPPE to the PPP code.
+ * This version is for use with Linux kernel 2.6.14+
+ *
+ * By Frank Cusack <fcusack@fcusack.com>.
+ * Copyright (c) 2002,2003,2004 Google, Inc.
+ * All rights reserved.
+ *
+ * License:
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Changelog:
+ *      08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
+ *                 Only need extra skb padding on transmit, not receive.
+ *      06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
+ *                 Use Linux kernel 2.6 arc4 and sha1 routines rather than
+ *                 providing our own.
+ *      2/15/04 - TS: added #include <version.h> and testing for Kernel
+ *                    version before using
+ *                    MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
+ *                    deprecated in 2.6
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/mm.h>
+#include <linux/ppp_defs.h>
+#include <linux/ppp-comp.h>
+#include <linux/scatterlist.h>
+#include <asm/unaligned.h>
+
+#include "ppp_mppe.h"
+
+MODULE_AUTHOR("Frank Cusack <fcusack@fcusack.com>");
+MODULE_DESCRIPTION("Point-to-Point Protocol Microsoft Point-to-Point Encryption support");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("ppp-compress-" __stringify(CI_MPPE));
+MODULE_VERSION("1.0.2");
+
+static unsigned int
+setup_sg(struct scatterlist *sg, const void *address, unsigned int length)
+{
+       sg_set_buf(sg, address, length);
+       return length;
+}
+
+#define SHA1_PAD_SIZE 40
+
+/*
+ * kernel crypto API needs its arguments to be in kmalloc'd memory, not in the module
+ * static data area.  That means sha_pad needs to be kmalloc'd.
+ */
+
+struct sha_pad {
+       unsigned char sha_pad1[SHA1_PAD_SIZE];
+       unsigned char sha_pad2[SHA1_PAD_SIZE];
+};
+static struct sha_pad *sha_pad;
+
+static inline void sha_pad_init(struct sha_pad *shapad)
+{
+       memset(shapad->sha_pad1, 0x00, sizeof(shapad->sha_pad1));
+       memset(shapad->sha_pad2, 0xF2, sizeof(shapad->sha_pad2));
+}
+
+/*
+ * State for an MPPE (de)compressor.
+ */
+struct ppp_mppe_state {
+       struct crypto_blkcipher *arc4;
+       struct crypto_hash *sha1;
+       unsigned char *sha1_digest;
+       unsigned char master_key[MPPE_MAX_KEY_LEN];
+       unsigned char session_key[MPPE_MAX_KEY_LEN];
+       unsigned keylen;        /* key length in bytes             */
+       /* NB: 128-bit == 16, 40-bit == 8! */
+       /* If we want to support 56-bit,   */
+       /* the unit has to change to bits  */
+       unsigned char bits;     /* MPPE control bits */
+       unsigned ccount;        /* 12-bit coherency count (seqno)  */
+       unsigned stateful;      /* stateful mode flag */
+       int discard;            /* stateful mode packet loss flag */
+       int sanity_errors;      /* take down LCP if too many */
+       int unit;
+       int debug;
+       struct compstat stats;
+};
+
+/* struct ppp_mppe_state.bits definitions */
+#define MPPE_BIT_A     0x80    /* Encryption table were (re)inititalized */
+#define MPPE_BIT_B     0x40    /* MPPC only (not implemented) */
+#define MPPE_BIT_C     0x20    /* MPPC only (not implemented) */
+#define MPPE_BIT_D     0x10    /* This is an encrypted frame */
+
+#define MPPE_BIT_FLUSHED       MPPE_BIT_A
+#define MPPE_BIT_ENCRYPTED     MPPE_BIT_D
+
+#define MPPE_BITS(p) ((p)[4] & 0xf0)
+#define MPPE_CCOUNT(p) ((((p)[4] & 0x0f) << 8) + (p)[5])
+#define MPPE_CCOUNT_SPACE 0x1000       /* The size of the ccount space */
+
+#define MPPE_OVHD      2       /* MPPE overhead/packet */
+#define SANITY_MAX     1600    /* Max bogon factor we will tolerate */
+
+/*
+ * Key Derivation, from RFC 3078, RFC 3079.
+ * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
+ */
+static void get_new_key_from_sha(struct ppp_mppe_state * state)
+{
+       struct hash_desc desc;
+       struct scatterlist sg[4];
+       unsigned int nbytes;
+
+       sg_init_table(sg, 4);
+
+       nbytes = setup_sg(&sg[0], state->master_key, state->keylen);
+       nbytes += setup_sg(&sg[1], sha_pad->sha_pad1,
+                          sizeof(sha_pad->sha_pad1));
+       nbytes += setup_sg(&sg[2], state->session_key, state->keylen);
+       nbytes += setup_sg(&sg[3], sha_pad->sha_pad2,
+                          sizeof(sha_pad->sha_pad2));
+
+       desc.tfm = state->sha1;
+       desc.flags = 0;
+
+       crypto_hash_digest(&desc, sg, nbytes, state->sha1_digest);
+}
+
+/*
+ * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
+ * Well, not what's written there, but rather what they meant.
+ */
+static void mppe_rekey(struct ppp_mppe_state * state, int initial_key)
+{
+       struct scatterlist sg_in[1], sg_out[1];
+       struct blkcipher_desc desc = { .tfm = state->arc4 };
+
+       get_new_key_from_sha(state);
+       if (!initial_key) {
+               crypto_blkcipher_setkey(state->arc4, state->sha1_digest,
+                                       state->keylen);
+               sg_init_table(sg_in, 1);
+               sg_init_table(sg_out, 1);
+               setup_sg(sg_in, state->sha1_digest, state->keylen);
+               setup_sg(sg_out, state->session_key, state->keylen);
+               if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+                                            state->keylen) != 0) {
+                   printk(KERN_WARNING "mppe_rekey: cipher_encrypt failed\n");
+               }
+       } else {
+               memcpy(state->session_key, state->sha1_digest, state->keylen);
+       }
+       if (state->keylen == 8) {
+               /* See RFC 3078 */
+               state->session_key[0] = 0xd1;
+               state->session_key[1] = 0x26;
+               state->session_key[2] = 0x9e;
+       }
+       crypto_blkcipher_setkey(state->arc4, state->session_key, state->keylen);
+}
+
+/*
+ * Allocate space for a (de)compressor.
+ */
+static void *mppe_alloc(unsigned char *options, int optlen)
+{
+       struct ppp_mppe_state *state;
+       unsigned int digestsize;
+
+       if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
+           options[0] != CI_MPPE || options[1] != CILEN_MPPE)
+               goto out;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               goto out;
+
+
+       state->arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(state->arc4)) {
+               state->arc4 = NULL;
+               goto out_free;
+       }
+
+       state->sha1 = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(state->sha1)) {
+               state->sha1 = NULL;
+               goto out_free;
+       }
+
+       digestsize = crypto_hash_digestsize(state->sha1);
+       if (digestsize < MPPE_MAX_KEY_LEN)
+               goto out_free;
+
+       state->sha1_digest = kmalloc(digestsize, GFP_KERNEL);
+       if (!state->sha1_digest)
+               goto out_free;
+
+       /* Save keys. */
+       memcpy(state->master_key, &options[CILEN_MPPE],
+              sizeof(state->master_key));
+       memcpy(state->session_key, state->master_key,
+              sizeof(state->master_key));
+
+       /*
+        * We defer initial key generation until mppe_init(), as mppe_alloc()
+        * is called frequently during negotiation.
+        */
+
+       return (void *)state;
+
+       out_free:
+           if (state->sha1_digest)
+               kfree(state->sha1_digest);
+           if (state->sha1)
+               crypto_free_hash(state->sha1);
+           if (state->arc4)
+               crypto_free_blkcipher(state->arc4);
+           kfree(state);
+       out:
+       return NULL;
+}
+
+/*
+ * Deallocate space for a (de)compressor.
+ */
+static void mppe_free(void *arg)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+       if (state) {
+           if (state->sha1_digest)
+               kfree(state->sha1_digest);
+           if (state->sha1)
+               crypto_free_hash(state->sha1);
+           if (state->arc4)
+               crypto_free_blkcipher(state->arc4);
+           kfree(state);
+       }
+}
+
+/*
+ * Initialize (de)compressor state.
+ */
+static int
+mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
+         const char *debugstr)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+       unsigned char mppe_opts;
+
+       if (optlen != CILEN_MPPE ||
+           options[0] != CI_MPPE || options[1] != CILEN_MPPE)
+               return 0;
+
+       MPPE_CI_TO_OPTS(&options[2], mppe_opts);
+       if (mppe_opts & MPPE_OPT_128)
+               state->keylen = 16;
+       else if (mppe_opts & MPPE_OPT_40)
+               state->keylen = 8;
+       else {
+               printk(KERN_WARNING "%s[%d]: unknown key length\n", debugstr,
+                      unit);
+               return 0;
+       }
+       if (mppe_opts & MPPE_OPT_STATEFUL)
+               state->stateful = 1;
+
+       /* Generate the initial session key. */
+       mppe_rekey(state, 1);
+
+       if (debug) {
+               int i;
+               char mkey[sizeof(state->master_key) * 2 + 1];
+               char skey[sizeof(state->session_key) * 2 + 1];
+
+               printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n",
+                      debugstr, unit, (state->keylen == 16) ? 128 : 40,
+                      (state->stateful) ? "stateful" : "stateless");
+
+               for (i = 0; i < sizeof(state->master_key); i++)
+                       sprintf(mkey + i * 2, "%02x", state->master_key[i]);
+               for (i = 0; i < sizeof(state->session_key); i++)
+                       sprintf(skey + i * 2, "%02x", state->session_key[i]);
+               printk(KERN_DEBUG
+                      "%s[%d]: keys: master: %s initial session: %s\n",
+                      debugstr, unit, mkey, skey);
+       }
+
+       /*
+        * Initialize the coherency count.  The initial value is not specified
+        * in RFC 3078, but we can make a reasonable assumption that it will
+        * start at 0.  Setting it to the max here makes the comp/decomp code
+        * do the right thing (determined through experiment).
+        */
+       state->ccount = MPPE_CCOUNT_SPACE - 1;
+
+       /*
+        * Note that even though we have initialized the key table, we don't
+        * set the FLUSHED bit.  This is contrary to RFC 3078, sec. 3.1.
+        */
+       state->bits = MPPE_BIT_ENCRYPTED;
+
+       state->unit = unit;
+       state->debug = debug;
+
+       return 1;
+}
+
+static int
+mppe_comp_init(void *arg, unsigned char *options, int optlen, int unit,
+              int hdrlen, int debug)
+{
+       /* ARGSUSED */
+       return mppe_init(arg, options, optlen, unit, debug, "mppe_comp_init");
+}
+
+/*
+ * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
+ * tell the compressor to rekey.  Note that we MUST NOT rekey for
+ * every CCP Reset-Request; we only rekey on the next xmit packet.
+ * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
+ * So, rekeying for every CCP Reset-Request is broken as the peer will not
+ * know how many times we've rekeyed.  (If we rekey and THEN get another
+ * CCP Reset-Request, we must rekey again.)
+ */
+static void mppe_comp_reset(void *arg)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+
+       state->bits |= MPPE_BIT_FLUSHED;
+}
+
+/*
+ * Compress (encrypt) a packet.
+ * It's strange to call this a compressor, since the output is always
+ * MPPE_OVHD + 2 bytes larger than the input.
+ */
+static int
+mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
+             int isize, int osize)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+       struct blkcipher_desc desc = { .tfm = state->arc4 };
+       int proto;
+       struct scatterlist sg_in[1], sg_out[1];
+
+       /*
+        * Check that the protocol is in the range we handle.
+        */
+       proto = PPP_PROTOCOL(ibuf);
+       if (proto < 0x0021 || proto > 0x00fa)
+               return 0;
+
+       /* Make sure we have enough room to generate an encrypted packet. */
+       if (osize < isize + MPPE_OVHD + 2) {
+               /* Drop the packet if we should encrypt it, but can't. */
+               printk(KERN_DEBUG "mppe_compress[%d]: osize too small! "
+                      "(have: %d need: %d)\n", state->unit,
+                      osize, osize + MPPE_OVHD + 2);
+               return -1;
+       }
+
+       osize = isize + MPPE_OVHD + 2;
+
+       /*
+        * Copy over the PPP header and set control bits.
+        */
+       obuf[0] = PPP_ADDRESS(ibuf);
+       obuf[1] = PPP_CONTROL(ibuf);
+       put_unaligned_be16(PPP_COMP, obuf + 2);
+       obuf += PPP_HDRLEN;
+
+       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+       if (state->debug >= 7)
+               printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
+                      state->ccount);
+       put_unaligned_be16(state->ccount, obuf);
+
+       if (!state->stateful || /* stateless mode     */
+           ((state->ccount & 0xff) == 0xff) || /* "flag" packet      */
+           (state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request  */
+               /* We must rekey */
+               if (state->debug && state->stateful)
+                       printk(KERN_DEBUG "mppe_compress[%d]: rekeying\n",
+                              state->unit);
+               mppe_rekey(state, 0);
+               state->bits |= MPPE_BIT_FLUSHED;
+       }
+       obuf[0] |= state->bits;
+       state->bits &= ~MPPE_BIT_FLUSHED;       /* reset for next xmit */
+
+       obuf += MPPE_OVHD;
+       ibuf += 2;              /* skip to proto field */
+       isize -= 2;
+
+       /* Encrypt packet */
+       sg_init_table(sg_in, 1);
+       sg_init_table(sg_out, 1);
+       setup_sg(sg_in, ibuf, isize);
+       setup_sg(sg_out, obuf, osize);
+       if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in, isize) != 0) {
+               printk(KERN_DEBUG "crypto_cypher_encrypt failed\n");
+               return -1;
+       }
+
+       state->stats.unc_bytes += isize;
+       state->stats.unc_packets++;
+       state->stats.comp_bytes += osize;
+       state->stats.comp_packets++;
+
+       return osize;
+}
+
+/*
+ * Since every frame grows by MPPE_OVHD + 2 bytes, this is always going
+ * to look bad ... and the longer the link is up the worse it will get.
+ */
+static void mppe_comp_stats(void *arg, struct compstat *stats)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+
+       *stats = state->stats;
+}
+
+static int
+mppe_decomp_init(void *arg, unsigned char *options, int optlen, int unit,
+                int hdrlen, int mru, int debug)
+{
+       /* ARGSUSED */
+       return mppe_init(arg, options, optlen, unit, debug, "mppe_decomp_init");
+}
+
+/*
+ * We received a CCP Reset-Ack.  Just ignore it.
+ */
+static void mppe_decomp_reset(void *arg)
+{
+       /* ARGSUSED */
+       return;
+}
+
+/*
+ * Decompress (decrypt) an MPPE packet.
+ */
+static int
+mppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
+               int osize)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+       struct blkcipher_desc desc = { .tfm = state->arc4 };
+       unsigned ccount;
+       int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
+       int sanity = 0;
+       struct scatterlist sg_in[1], sg_out[1];
+
+       if (isize <= PPP_HDRLEN + MPPE_OVHD) {
+               if (state->debug)
+                       printk(KERN_DEBUG
+                              "mppe_decompress[%d]: short pkt (%d)\n",
+                              state->unit, isize);
+               return DECOMP_ERROR;
+       }
+
+       /*
+        * Make sure we have enough room to decrypt the packet.
+        * Note that for our test we only subtract 1 byte whereas in
+        * mppe_compress() we added 2 bytes (+MPPE_OVHD);
+        * this is to account for possible PFC.
+        */
+       if (osize < isize - MPPE_OVHD - 1) {
+               printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! "
+                      "(have: %d need: %d)\n", state->unit,
+                      osize, isize - MPPE_OVHD - 1);
+               return DECOMP_ERROR;
+       }
+       osize = isize - MPPE_OVHD - 2;  /* assume no PFC */
+
+       ccount = MPPE_CCOUNT(ibuf);
+       if (state->debug >= 7)
+               printk(KERN_DEBUG "mppe_decompress[%d]: ccount %d\n",
+                      state->unit, ccount);
+
+       /* sanity checks -- terminate with extreme prejudice */
+       if (!(MPPE_BITS(ibuf) & MPPE_BIT_ENCRYPTED)) {
+               printk(KERN_DEBUG
+                      "mppe_decompress[%d]: ENCRYPTED bit not set!\n",
+                      state->unit);
+               state->sanity_errors += 100;
+               sanity = 1;
+       }
+       if (!state->stateful && !flushed) {
+               printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
+                      "stateless mode!\n", state->unit);
+               state->sanity_errors += 100;
+               sanity = 1;
+       }
+       if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
+               printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set on "
+                      "flag packet!\n", state->unit);
+               state->sanity_errors += 100;
+               sanity = 1;
+       }
+
+       if (sanity) {
+               if (state->sanity_errors < SANITY_MAX)
+                       return DECOMP_ERROR;
+               else
+                       /*
+                        * Take LCP down if the peer is sending too many bogons.
+                        * We don't want to do this for a single or just a few
+                        * instances since it could just be due to packet corruption.
+                        */
+                       return DECOMP_FATALERROR;
+       }
+
+       /*
+        * Check the coherency count.
+        */
+
+       if (!state->stateful) {
+               /* RFC 3078, sec 8.1.  Rekey for every packet. */
+               while (state->ccount != ccount) {
+                       mppe_rekey(state, 0);
+                       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+               }
+       } else {
+               /* RFC 3078, sec 8.2. */
+               if (!state->discard) {
+                       /* normal state */
+                       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+                       if (ccount != state->ccount) {
+                               /*
+                                * (ccount > state->ccount)
+                                * Packet loss detected, enter the discard state.
+                                * Signal the peer to rekey (by sending a CCP Reset-Request).
+                                */
+                               state->discard = 1;
+                               return DECOMP_ERROR;
+                       }
+               } else {
+                       /* discard state */
+                       if (!flushed) {
+                               /* ccp.c will be silent (no additional CCP Reset-Requests). */
+                               return DECOMP_ERROR;
+                       } else {
+                               /* Rekey for every missed "flag" packet. */
+                               while ((ccount & ~0xff) !=
+                                      (state->ccount & ~0xff)) {
+                                       mppe_rekey(state, 0);
+                                       state->ccount =
+                                           (state->ccount +
+                                            256) % MPPE_CCOUNT_SPACE;
+                               }
+
+                               /* reset */
+                               state->discard = 0;
+                               state->ccount = ccount;
+                               /*
+                                * Another problem with RFC 3078 here.  It implies that the
+                                * peer need not send a Reset-Ack packet.  But RFC 1962
+                                * requires it.  Hopefully, M$ does send a Reset-Ack; even
+                                * though it isn't required for MPPE synchronization, it is
+                                * required to reset CCP state.
+                                */
+                       }
+               }
+               if (flushed)
+                       mppe_rekey(state, 0);
+       }
+
+       /*
+        * Fill in the first part of the PPP header.  The protocol field
+        * comes from the decrypted data.
+        */
+       obuf[0] = PPP_ADDRESS(ibuf);    /* +1 */
+       obuf[1] = PPP_CONTROL(ibuf);    /* +1 */
+       obuf += 2;
+       ibuf += PPP_HDRLEN + MPPE_OVHD;
+       isize -= PPP_HDRLEN + MPPE_OVHD;        /* -6 */
+       /* net osize: isize-4 */
+
+       /*
+        * Decrypt the first byte in order to check if it is
+        * a compressed or uncompressed protocol field.
+        */
+       sg_init_table(sg_in, 1);
+       sg_init_table(sg_out, 1);
+       setup_sg(sg_in, ibuf, 1);
+       setup_sg(sg_out, obuf, 1);
+       if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, 1) != 0) {
+               printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
+               return DECOMP_ERROR;
+       }
+
+       /*
+        * Do PFC decompression.
+        * This would be nicer if we were given the actual sk_buff
+        * instead of a char *.
+        */
+       if ((obuf[0] & 0x01) != 0) {
+               obuf[1] = obuf[0];
+               obuf[0] = 0;
+               obuf++;
+               osize++;
+       }
+
+       /* And finally, decrypt the rest of the packet. */
+       setup_sg(sg_in, ibuf + 1, isize - 1);
+       setup_sg(sg_out, obuf + 1, osize - 1);
+       if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, isize - 1)) {
+               printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
+               return DECOMP_ERROR;
+       }
+
+       state->stats.unc_bytes += osize;
+       state->stats.unc_packets++;
+       state->stats.comp_bytes += isize;
+       state->stats.comp_packets++;
+
+       /* good packet credit */
+       state->sanity_errors >>= 1;
+
+       return osize;
+}
+
+/*
+ * Incompressible data has arrived (this should never happen!).
+ * We should probably drop the link if the protocol is in the range
+ * of what should be encrypted.  At the least, we should drop this
+ * packet.  (How to do this?)
+ */
+static void mppe_incomp(void *arg, unsigned char *ibuf, int icnt)
+{
+       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
+
+       if (state->debug &&
+           (PPP_PROTOCOL(ibuf) >= 0x0021 && PPP_PROTOCOL(ibuf) <= 0x00fa))
+               printk(KERN_DEBUG
+                      "mppe_incomp[%d]: incompressible (unencrypted) data! "
+                      "(proto %04x)\n", state->unit, PPP_PROTOCOL(ibuf));
+
+       state->stats.inc_bytes += icnt;
+       state->stats.inc_packets++;
+       state->stats.unc_bytes += icnt;
+       state->stats.unc_packets++;
+}
+
+/*************************************************************
+ * Module interface table
+ *************************************************************/
+
+/*
+ * Procedures exported to if_ppp.c.
+ */
+static struct compressor ppp_mppe = {
+       .compress_proto = CI_MPPE,
+       .comp_alloc     = mppe_alloc,
+       .comp_free      = mppe_free,
+       .comp_init      = mppe_comp_init,
+       .comp_reset     = mppe_comp_reset,
+       .compress       = mppe_compress,
+       .comp_stat      = mppe_comp_stats,
+       .decomp_alloc   = mppe_alloc,
+       .decomp_free    = mppe_free,
+       .decomp_init    = mppe_decomp_init,
+       .decomp_reset   = mppe_decomp_reset,
+       .decompress     = mppe_decompress,
+       .incomp         = mppe_incomp,
+       .decomp_stat    = mppe_comp_stats,
+       .owner          = THIS_MODULE,
+       .comp_extra     = MPPE_PAD,
+};
+
+/*
+ * ppp_mppe_init()
+ *
+ * Prior to allowing load, try to load the arc4 and sha1 crypto
+ * libraries.  The actual use will be allocated later, but
+ * this way the module will fail to insmod if they aren't available.
+ */
+
+static int __init ppp_mppe_init(void)
+{
+       int answer;
+       if (!(crypto_has_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC) &&
+             crypto_has_hash("sha1", 0, CRYPTO_ALG_ASYNC)))
+               return -ENODEV;
+
+       sha_pad = kmalloc(sizeof(struct sha_pad), GFP_KERNEL);
+       if (!sha_pad)
+               return -ENOMEM;
+       sha_pad_init(sha_pad);
+
+       answer = ppp_register_compressor(&ppp_mppe);
+
+       if (answer == 0)
+               printk(KERN_INFO "PPP MPPE Compression module registered\n");
+       else
+               kfree(sha_pad);
+
+       return answer;
+}
+
+static void __exit ppp_mppe_cleanup(void)
+{
+       ppp_unregister_compressor(&ppp_mppe);
+       kfree(sha_pad);
+}
+
+module_init(ppp_mppe_init);
+module_exit(ppp_mppe_cleanup);
diff --git a/drivers/net/ppp/ppp_mppe.h b/drivers/net/ppp/ppp_mppe.h
new file mode 100644 (file)
index 0000000..7a14e05
--- /dev/null
@@ -0,0 +1,86 @@
+#define MPPE_PAD                4      /* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN       16      /* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40            0x01    /* 40 bit */
+#define MPPE_OPT_128           0x02    /* 128 bit */
+#define MPPE_OPT_STATEFUL      0x04    /* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56            0x08    /* 56 bit */
+#define MPPE_OPT_MPPC          0x10    /* MPPC compression */
+#define MPPE_OPT_D             0x20    /* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN       0x40    /* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit.  We could do a u_int32
+ * but then we have to do a htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT             0x01    /* MPPC */
+#define MPPE_D_BIT             0x10    /* Obsolete, usage unknown */
+#define MPPE_L_BIT             0x20    /* 40-bit */
+#define MPPE_S_BIT             0x40    /* 128-bit */
+#define MPPE_M_BIT             0x80    /* 56-bit, not supported */
+#define MPPE_H_BIT             0x01    /* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci)              \
+    do {                                       \
+       u_char *ptr = ci; /* u_char[4] */       \
+                                               \
+       /* H bit */                             \
+       if (opts & MPPE_OPT_STATEFUL)           \
+           *ptr++ = 0x0;                       \
+       else                                    \
+           *ptr++ = MPPE_H_BIT;                \
+       *ptr++ = 0;                             \
+       *ptr++ = 0;                             \
+                                               \
+       /* S,L bits */                          \
+       *ptr = 0;                               \
+       if (opts & MPPE_OPT_128)                \
+           *ptr |= MPPE_S_BIT;                 \
+       if (opts & MPPE_OPT_40)                 \
+           *ptr |= MPPE_L_BIT;                 \
+       /* M,D,C bits not supported */          \
+    } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts)              \
+    do {                                       \
+       u_char *ptr = ci; /* u_char[4] */       \
+                                               \
+       opts = 0;                               \
+                                               \
+       /* H bit */                             \
+       if (!(ptr[0] & MPPE_H_BIT))             \
+           opts |= MPPE_OPT_STATEFUL;          \
+                                               \
+       /* S,L bits */                          \
+       if (ptr[3] & MPPE_S_BIT)                \
+           opts |= MPPE_OPT_128;               \
+       if (ptr[3] & MPPE_L_BIT)                \
+           opts |= MPPE_OPT_40;                \
+                                               \
+       /* M,D,C bits */                        \
+       if (ptr[3] & MPPE_M_BIT)                \
+           opts |= MPPE_OPT_56;                \
+       if (ptr[3] & MPPE_D_BIT)                \
+           opts |= MPPE_OPT_D;                 \
+       if (ptr[3] & MPPE_C_BIT)                \
+           opts |= MPPE_OPT_MPPC;              \
+                                               \
+       /* Other bits */                        \
+       if (ptr[0] & ~MPPE_H_BIT)               \
+           opts |= MPPE_OPT_UNKNOWN;           \
+       if (ptr[1] || ptr[2])                   \
+           opts |= MPPE_OPT_UNKNOWN;           \
+       if (ptr[3] & ~MPPE_ALL_BITS)            \
+           opts |= MPPE_OPT_UNKNOWN;           \
+    } while (/* CONSTCOND */ 0)
diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c
new file mode 100644 (file)
index 0000000..736a39e
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * PPP synchronous tty channel driver for Linux.
+ *
+ * This is a ppp channel driver that can be used with tty device drivers
+ * that are frame oriented, such as synchronous HDLC devices.
+ *
+ * Complete PPP frames without encoding/decoding are exchanged between
+ * the channel driver and the device driver.
+ *
+ * The async map IOCTL codes are implemented to keep the user mode
+ * applications happy if they call them. Synchronous PPP does not use
+ * the async maps.
+ *
+ * Copyright 1999 Paul Mackerras.
+ *
+ * Also touched by the grubby hands of Paul Fulghum paulkf@microgate.com
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ *
+ * This driver provides the encapsulation and framing for sending
+ * and receiving PPP frames over sync serial lines.  It relies on
+ * the generic PPP layer to give it frames to send and to process
+ * received frames.  It implements the PPP line discipline.
+ *
+ * Part of the code in this driver was inspired by the old async-only
+ * PPP driver, written by Michael Callahan and Al Longyear, and
+ * subsequently hacked by Paul Mackerras.
+ *
+ * ==FILEVERSION 20040616==
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/tty.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <asm/uaccess.h>
+
+#define PPP_VERSION    "2.4.2"
+
+/* Structure for storing local state. */
+struct syncppp {
+       struct tty_struct *tty;
+       unsigned int    flags;
+       unsigned int    rbits;
+       int             mru;
+       spinlock_t      xmit_lock;
+       spinlock_t      recv_lock;
+       unsigned long   xmit_flags;
+       u32             xaccm[8];
+       u32             raccm;
+       unsigned int    bytes_sent;
+       unsigned int    bytes_rcvd;
+
+       struct sk_buff  *tpkt;
+       unsigned long   last_xmit;
+
+       struct sk_buff_head rqueue;
+
+       struct tasklet_struct tsk;
+
+       atomic_t        refcnt;
+       struct completion dead_cmp;
+       struct ppp_channel chan;        /* interface to generic ppp layer */
+};
+
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP    0
+#define XMIT_FULL      1
+
+/* Bits in rbits */
+#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
+
+#define PPPSYNC_MAX_RQLEN      32      /* arbitrary */
+
+/*
+ * Prototypes.
+ */
+static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
+static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
+static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
+                         unsigned long arg);
+static void ppp_sync_process(unsigned long arg);
+static int ppp_sync_push(struct syncppp *ap);
+static void ppp_sync_flush_output(struct syncppp *ap);
+static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
+                          char *flags, int count);
+
+static const struct ppp_channel_ops sync_ops = {
+       .start_xmit = ppp_sync_send,
+       .ioctl      = ppp_sync_ioctl,
+};
+
+/*
+ * Utility procedures to print a buffer in hex/ascii
+ */
+static void
+ppp_print_hex (register __u8 * out, const __u8 * in, int count)
+{
+       register __u8 next_ch;
+       static const char hex[] = "0123456789ABCDEF";
+
+       while (count-- > 0) {
+               next_ch = *in++;
+               *out++ = hex[(next_ch >> 4) & 0x0F];
+               *out++ = hex[next_ch & 0x0F];
+               ++out;
+       }
+}
+
+static void
+ppp_print_char (register __u8 * out, const __u8 * in, int count)
+{
+       register __u8 next_ch;
+
+       while (count-- > 0) {
+               next_ch = *in++;
+
+               if (next_ch < 0x20 || next_ch > 0x7e)
+                       *out++ = '.';
+               else {
+                       *out++ = next_ch;
+                       if (next_ch == '%')   /* printk/syslogd has a bug !! */
+                               *out++ = '%';
+               }
+       }
+       *out = '\0';
+}
+
+static void
+ppp_print_buffer (const char *name, const __u8 *buf, int count)
+{
+       __u8 line[44];
+
+       if (name != NULL)
+               printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count);
+
+       while (count > 8) {
+               memset (line, 32, 44);
+               ppp_print_hex (line, buf, 8);
+               ppp_print_char (&line[8 * 3], buf, 8);
+               printk(KERN_DEBUG "%s\n", line);
+               count -= 8;
+               buf += 8;
+       }
+
+       if (count > 0) {
+               memset (line, 32, 44);
+               ppp_print_hex (line, buf, count);
+               ppp_print_char (&line[8 * 3], buf, count);
+               printk(KERN_DEBUG "%s\n", line);
+       }
+}
+
+
+/*
+ * Routines implementing the synchronous PPP line discipline.
+ */
+
+/*
+ * We have a potential race on dereferencing tty->disc_data,
+ * because the tty layer provides no locking at all - thus one
+ * cpu could be running ppp_synctty_receive while another
+ * calls ppp_synctty_close, which zeroes tty->disc_data and
+ * frees the memory that ppp_synctty_receive is using.  The best
+ * way to fix this is to use a rwlock in the tty struct, but for now
+ * we use a single global rwlock for all ttys in ppp line discipline.
+ *
+ * FIXME: Fixed in tty_io nowadays.
+ */
+static DEFINE_RWLOCK(disc_data_lock);
+
+static struct syncppp *sp_get(struct tty_struct *tty)
+{
+       struct syncppp *ap;
+
+       read_lock(&disc_data_lock);
+       ap = tty->disc_data;
+       if (ap != NULL)
+               atomic_inc(&ap->refcnt);
+       read_unlock(&disc_data_lock);
+       return ap;
+}
+
+static void sp_put(struct syncppp *ap)
+{
+       if (atomic_dec_and_test(&ap->refcnt))
+               complete(&ap->dead_cmp);
+}
+
+/*
+ * Called when a tty is put into sync-PPP line discipline.
+ */
+static int
+ppp_sync_open(struct tty_struct *tty)
+{
+       struct syncppp *ap;
+       int err;
+       int speed;
+
+       if (tty->ops->write == NULL)
+               return -EOPNOTSUPP;
+
+       ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!ap)
+               goto out;
+
+       /* initialize the syncppp structure */
+       ap->tty = tty;
+       ap->mru = PPP_MRU;
+       spin_lock_init(&ap->xmit_lock);
+       spin_lock_init(&ap->recv_lock);
+       ap->xaccm[0] = ~0U;
+       ap->xaccm[3] = 0x60000000U;
+       ap->raccm = ~0U;
+
+       skb_queue_head_init(&ap->rqueue);
+       tasklet_init(&ap->tsk, ppp_sync_process, (unsigned long) ap);
+
+       atomic_set(&ap->refcnt, 1);
+       init_completion(&ap->dead_cmp);
+
+       ap->chan.private = ap;
+       ap->chan.ops = &sync_ops;
+       ap->chan.mtu = PPP_MRU;
+       ap->chan.hdrlen = 2;    /* for A/C bytes */
+       speed = tty_get_baud_rate(tty);
+       ap->chan.speed = speed;
+       err = ppp_register_channel(&ap->chan);
+       if (err)
+               goto out_free;
+
+       tty->disc_data = ap;
+       tty->receive_room = 65536;
+       return 0;
+
+ out_free:
+       kfree(ap);
+ out:
+       return err;
+}
+
+/*
+ * Called when the tty is put into another line discipline
+ * or it hangs up.  We have to wait for any cpu currently
+ * executing in any of the other ppp_synctty_* routines to
+ * finish before we can call ppp_unregister_channel and free
+ * the syncppp struct.  This routine must be called from
+ * process context, not interrupt or softirq context.
+ */
+static void
+ppp_sync_close(struct tty_struct *tty)
+{
+       struct syncppp *ap;
+
+       write_lock_irq(&disc_data_lock);
+       ap = tty->disc_data;
+       tty->disc_data = NULL;
+       write_unlock_irq(&disc_data_lock);
+       if (!ap)
+               return;
+
+       /*
+        * We have now ensured that nobody can start using ap from now
+        * on, but we have to wait for all existing users to finish.
+        * Note that ppp_unregister_channel ensures that no calls to
+        * our channel ops (i.e. ppp_sync_send/ioctl) are in progress
+        * by the time it returns.
+        */
+       if (!atomic_dec_and_test(&ap->refcnt))
+               wait_for_completion(&ap->dead_cmp);
+       tasklet_kill(&ap->tsk);
+
+       ppp_unregister_channel(&ap->chan);
+       skb_queue_purge(&ap->rqueue);
+       kfree_skb(ap->tpkt);
+       kfree(ap);
+}
+
+/*
+ * Called on tty hangup in process context.
+ *
+ * Wait for I/O to driver to complete and unregister PPP channel.
+ * This is already done by the close routine, so just call that.
+ */
+static int ppp_sync_hangup(struct tty_struct *tty)
+{
+       ppp_sync_close(tty);
+       return 0;
+}
+
+/*
+ * Read does nothing - no data is ever available this way.
+ * Pppd reads and writes packets via /dev/ppp instead.
+ */
+static ssize_t
+ppp_sync_read(struct tty_struct *tty, struct file *file,
+              unsigned char __user *buf, size_t count)
+{
+       return -EAGAIN;
+}
+
+/*
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
+ */
+static ssize_t
+ppp_sync_write(struct tty_struct *tty, struct file *file,
+               const unsigned char *buf, size_t count)
+{
+       return -EAGAIN;
+}
+
+static int
+ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
+                 unsigned int cmd, unsigned long arg)
+{
+       struct syncppp *ap = sp_get(tty);
+       int __user *p = (int __user *)arg;
+       int err, val;
+
+       if (!ap)
+               return -ENXIO;
+       err = -EFAULT;
+       switch (cmd) {
+       case PPPIOCGCHAN:
+               err = -EFAULT;
+               if (put_user(ppp_channel_index(&ap->chan), p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGUNIT:
+               err = -EFAULT;
+               if (put_user(ppp_unit_number(&ap->chan), p))
+                       break;
+               err = 0;
+               break;
+
+       case TCFLSH:
+               /* flush our buffers and the serial port's buffer */
+               if (arg == TCIOFLUSH || arg == TCOFLUSH)
+                       ppp_sync_flush_output(ap);
+               err = tty_perform_flush(tty, arg);
+               break;
+
+       case FIONREAD:
+               val = 0;
+               if (put_user(val, p))
+                       break;
+               err = 0;
+               break;
+
+       default:
+               err = tty_mode_ioctl(tty, file, cmd, arg);
+               break;
+       }
+
+       sp_put(ap);
+       return err;
+}
+
+/* No kernel lock - fine */
+static unsigned int
+ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+{
+       return 0;
+}
+
+/* May sleep, don't call from interrupt level or with interrupts disabled */
+static void
+ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
+                 char *cflags, int count)
+{
+       struct syncppp *ap = sp_get(tty);
+       unsigned long flags;
+
+       if (!ap)
+               return;
+       spin_lock_irqsave(&ap->recv_lock, flags);
+       ppp_sync_input(ap, buf, cflags, count);
+       spin_unlock_irqrestore(&ap->recv_lock, flags);
+       if (!skb_queue_empty(&ap->rqueue))
+               tasklet_schedule(&ap->tsk);
+       sp_put(ap);
+       tty_unthrottle(tty);
+}
+
+static void
+ppp_sync_wakeup(struct tty_struct *tty)
+{
+       struct syncppp *ap = sp_get(tty);
+
+       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+       if (!ap)
+               return;
+       set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+       tasklet_schedule(&ap->tsk);
+       sp_put(ap);
+}
+
+
+static struct tty_ldisc_ops ppp_sync_ldisc = {
+       .owner  = THIS_MODULE,
+       .magic  = TTY_LDISC_MAGIC,
+       .name   = "pppsync",
+       .open   = ppp_sync_open,
+       .close  = ppp_sync_close,
+       .hangup = ppp_sync_hangup,
+       .read   = ppp_sync_read,
+       .write  = ppp_sync_write,
+       .ioctl  = ppp_synctty_ioctl,
+       .poll   = ppp_sync_poll,
+       .receive_buf = ppp_sync_receive,
+       .write_wakeup = ppp_sync_wakeup,
+};
+
+static int __init
+ppp_sync_init(void)
+{
+       int err;
+
+       err = tty_register_ldisc(N_SYNC_PPP, &ppp_sync_ldisc);
+       if (err != 0)
+               printk(KERN_ERR "PPP_sync: error %d registering line disc.\n",
+                      err);
+       return err;
+}
+
+/*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+       struct syncppp *ap = chan->private;
+       int err, val;
+       u32 accm[8];
+       void __user *argp = (void __user *)arg;
+       u32 __user *p = argp;
+
+       err = -EFAULT;
+       switch (cmd) {
+       case PPPIOCGFLAGS:
+               val = ap->flags | ap->rbits;
+               if (put_user(val, (int __user *) argp))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSFLAGS:
+               if (get_user(val, (int __user *) argp))
+                       break;
+               ap->flags = val & ~SC_RCV_BITS;
+               spin_lock_irq(&ap->recv_lock);
+               ap->rbits = val & SC_RCV_BITS;
+               spin_unlock_irq(&ap->recv_lock);
+               err = 0;
+               break;
+
+       case PPPIOCGASYNCMAP:
+               if (put_user(ap->xaccm[0], p))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSASYNCMAP:
+               if (get_user(ap->xaccm[0], p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGRASYNCMAP:
+               if (put_user(ap->raccm, p))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSRASYNCMAP:
+               if (get_user(ap->raccm, p))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGXASYNCMAP:
+               if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSXASYNCMAP:
+               if (copy_from_user(accm, argp, sizeof(accm)))
+                       break;
+               accm[2] &= ~0x40000000U;        /* can't escape 0x5e */
+               accm[3] |= 0x60000000U;         /* must escape 0x7d, 0x7e */
+               memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+               err = 0;
+               break;
+
+       case PPPIOCGMRU:
+               if (put_user(ap->mru, (int __user *) argp))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSMRU:
+               if (get_user(val, (int __user *) argp))
+                       break;
+               if (val < PPP_MRU)
+                       val = PPP_MRU;
+               ap->mru = val;
+               err = 0;
+               break;
+
+       default:
+               err = -ENOTTY;
+       }
+       return err;
+}
+
+/*
+ * This is called at softirq level to deliver received packets
+ * to the ppp_generic code, and to tell the ppp_generic code
+ * if we can accept more output now.
+ */
+static void ppp_sync_process(unsigned long arg)
+{
+       struct syncppp *ap = (struct syncppp *) arg;
+       struct sk_buff *skb;
+
+       /* process received packets */
+       while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
+               if (skb->len == 0) {
+                       /* zero length buffers indicate error */
+                       ppp_input_error(&ap->chan, 0);
+                       kfree_skb(skb);
+               }
+               else
+                       ppp_input(&ap->chan, skb);
+       }
+
+       /* try to push more stuff out */
+       if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap))
+               ppp_output_wakeup(&ap->chan);
+}
+
+/*
+ * Procedures for encapsulation and framing.
+ */
+
+static struct sk_buff*
+ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
+{
+       int proto;
+       unsigned char *data;
+       int islcp;
+
+       data  = skb->data;
+       proto = get_unaligned_be16(data);
+
+       /* LCP packets with codes between 1 (configure-request)
+        * and 7 (code-reject) must be sent as though no options
+        * have been negotiated.
+        */
+       islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
+
+       /* compress protocol field if option enabled */
+       if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp)
+               skb_pull(skb,1);
+
+       /* prepend address/control fields if necessary */
+       if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
+               if (skb_headroom(skb) < 2) {
+                       struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
+                       if (npkt == NULL) {
+                               kfree_skb(skb);
+                               return NULL;
+                       }
+                       skb_reserve(npkt,2);
+                       skb_copy_from_linear_data(skb,
+                                     skb_put(npkt, skb->len), skb->len);
+                       kfree_skb(skb);
+                       skb = npkt;
+               }
+               skb_push(skb,2);
+               skb->data[0] = PPP_ALLSTATIONS;
+               skb->data[1] = PPP_UI;
+       }
+
+       ap->last_xmit = jiffies;
+
+       if (skb && ap->flags & SC_LOG_OUTPKT)
+               ppp_print_buffer ("send buffer", skb->data, skb->len);
+
+       return skb;
+}
+
+/*
+ * Transmit-side routines.
+ */
+
+/*
+ * Send a packet to the peer over an sync tty line.
+ * Returns 1 iff the packet was accepted.
+ * If the packet was not accepted, we will call ppp_output_wakeup
+ * at some later time.
+ */
+static int
+ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb)
+{
+       struct syncppp *ap = chan->private;
+
+       ppp_sync_push(ap);
+
+       if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
+               return 0;       /* already full */
+       skb = ppp_sync_txmunge(ap, skb);
+       if (skb != NULL)
+               ap->tpkt = skb;
+       else
+               clear_bit(XMIT_FULL, &ap->xmit_flags);
+
+       ppp_sync_push(ap);
+       return 1;
+}
+
+/*
+ * Push as much data as possible out to the tty.
+ */
+static int
+ppp_sync_push(struct syncppp *ap)
+{
+       int sent, done = 0;
+       struct tty_struct *tty = ap->tty;
+       int tty_stuffed = 0;
+
+       if (!spin_trylock_bh(&ap->xmit_lock))
+               return 0;
+       for (;;) {
+               if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
+                       tty_stuffed = 0;
+               if (!tty_stuffed && ap->tpkt) {
+                       set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+                       sent = tty->ops->write(tty, ap->tpkt->data, ap->tpkt->len);
+                       if (sent < 0)
+                               goto flush;     /* error, e.g. loss of CD */
+                       if (sent < ap->tpkt->len) {
+                               tty_stuffed = 1;
+                       } else {
+                               kfree_skb(ap->tpkt);
+                               ap->tpkt = NULL;
+                               clear_bit(XMIT_FULL, &ap->xmit_flags);
+                               done = 1;
+                       }
+                       continue;
+               }
+               /* haven't made any progress */
+               spin_unlock_bh(&ap->xmit_lock);
+               if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
+                     (!tty_stuffed && ap->tpkt)))
+                       break;
+               if (!spin_trylock_bh(&ap->xmit_lock))
+                       break;
+       }
+       return done;
+
+flush:
+       if (ap->tpkt) {
+               kfree_skb(ap->tpkt);
+               ap->tpkt = NULL;
+               clear_bit(XMIT_FULL, &ap->xmit_flags);
+               done = 1;
+       }
+       spin_unlock_bh(&ap->xmit_lock);
+       return done;
+}
+
+/*
+ * Flush output from our internal buffers.
+ * Called for the TCFLSH ioctl.
+ */
+static void
+ppp_sync_flush_output(struct syncppp *ap)
+{
+       int done = 0;
+
+       spin_lock_bh(&ap->xmit_lock);
+       if (ap->tpkt != NULL) {
+               kfree_skb(ap->tpkt);
+               ap->tpkt = NULL;
+               clear_bit(XMIT_FULL, &ap->xmit_flags);
+               done = 1;
+       }
+       spin_unlock_bh(&ap->xmit_lock);
+       if (done)
+               ppp_output_wakeup(&ap->chan);
+}
+
+/*
+ * Receive-side routines.
+ */
+
+/* called when the tty driver has data for us.
+ *
+ * Data is frame oriented: each call to ppp_sync_input is considered
+ * a whole frame. If the 1st flag byte is non-zero then the whole
+ * frame is considered to be in error and is tossed.
+ */
+static void
+ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
+               char *flags, int count)
+{
+       struct sk_buff *skb;
+       unsigned char *p;
+
+       if (count == 0)
+               return;
+
+       if (ap->flags & SC_LOG_INPKT)
+               ppp_print_buffer ("receive buffer", buf, count);
+
+       /* stuff the chars in the skb */
+       skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2);
+       if (!skb) {
+               printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
+               goto err;
+       }
+       /* Try to get the payload 4-byte aligned */
+       if (buf[0] != PPP_ALLSTATIONS)
+               skb_reserve(skb, 2 + (buf[0] & 1));
+
+       if (flags && *flags) {
+               /* error flag set, ignore frame */
+               goto err;
+       } else if (count > skb_tailroom(skb)) {
+               /* packet overflowed MRU */
+               goto err;
+       }
+
+       p = skb_put(skb, count);
+       memcpy(p, buf, count);
+
+       /* strip address/control field if present */
+       p = skb->data;
+       if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
+               /* chop off address/control */
+               if (skb->len < 3)
+                       goto err;
+               p = skb_pull(skb, 2);
+       }
+
+       /* decompress protocol field if compressed */
+       if (p[0] & 1) {
+               /* protocol is compressed */
+               skb_push(skb, 1)[0] = 0;
+       } else if (skb->len < 2)
+               goto err;
+
+       /* queue the frame to be processed */
+       skb_queue_tail(&ap->rqueue, skb);
+       return;
+
+err:
+       /* queue zero length packet as error indication */
+       if (skb || (skb = dev_alloc_skb(0))) {
+               skb_trim(skb, 0);
+               skb_queue_tail(&ap->rqueue, skb);
+       }
+}
+
+static void __exit
+ppp_sync_cleanup(void)
+{
+       if (tty_unregister_ldisc(N_SYNC_PPP) != 0)
+               printk(KERN_ERR "failed to unregister Sync PPP line discipline\n");
+}
+
+module_init(ppp_sync_init);
+module_exit(ppp_sync_cleanup);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_SYNC_PPP);
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
new file mode 100644 (file)
index 0000000..bc9a4bb
--- /dev/null
@@ -0,0 +1,1208 @@
+/** -*- linux-c -*- ***********************************************************
+ * Linux PPP over Ethernet (PPPoX/PPPoE) Sockets
+ *
+ * PPPoX --- Generic PPP encapsulation socket family
+ * PPPoE --- PPP over Ethernet (RFC 2516)
+ *
+ *
+ * Version:    0.7.0
+ *
+ * 070228 :    Fix to allow multiple sessions with same remote MAC and same
+ *             session id by including the local device ifindex in the
+ *             tuple identifying a session. This also ensures packets can't
+ *             be injected into a session from interfaces other than the one
+ *             specified by userspace. Florian Zumbiehl <florz@florz.de>
+ *             (Oh, BTW, this one is YYMMDD, in case you were wondering ...)
+ * 220102 :    Fix module use count on failure in pppoe_create, pppox_sk -acme
+ * 030700 :    Fixed connect logic to allow for disconnect.
+ * 270700 :    Fixed potential SMP problems; we must protect against
+ *             simultaneous invocation of ppp_input
+ *             and ppp_unregister_channel.
+ * 040800 :    Respect reference count mechanisms on net-devices.
+ * 200800 :    fix kfree(skb) in pppoe_rcv (acme)
+ *             Module reference count is decremented in the right spot now,
+ *             guards against sock_put not actually freeing the sk
+ *             in pppoe_release.
+ * 051000 :    Initialization cleanup.
+ * 111100 :    Fix recvmsg.
+ * 050101 :    Fix PADT procesing.
+ * 140501 :    Use pppoe_rcv_core to handle all backlog. (Alexey)
+ * 170701 :    Do not lock_sock with rwlock held. (DaveM)
+ *             Ignore discovery frames if user has socket
+ *             locked. (DaveM)
+ *             Ignore return value of dev_queue_xmit in __pppoe_xmit
+ *             or else we may kfree an SKB twice. (DaveM)
+ * 190701 :    When doing copies of skb's in __pppoe_xmit, always delete
+ *             the original skb that was passed in on success, never on
+ *             failure.  Delete the copy of the skb on failure to avoid
+ *             a memory leak.
+ * 081001 :    Misc. cleanup (licence string, non-blocking, prevent
+ *             reference of device on close).
+ * 121301 :    New ppp channels interface; cannot unregister a channel
+ *             from interrupts.  Thus, we mark the socket as a ZOMBIE
+ *             and do the unregistration later.
+ * 081002 :    seq_file support for proc stuff -acme
+ * 111602 :    Merge all 2.4 fixes into 2.5/2.6 tree.  Label 2.5/2.6
+ *             as version 0.7.  Spacing cleanup.
+ * Author:     Michal Ostrowski <mostrows@speakeasy.net>
+ * Contributors:
+ *             Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *             David S. Miller (davem@redhat.com)
+ *
+ * License:
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_ether.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/notifier.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <linux/nsproxy.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+
+#define PPPOE_HASH_BITS 4
+#define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS)
+#define PPPOE_HASH_MASK        (PPPOE_HASH_SIZE - 1)
+
+static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb);
+
+static const struct proto_ops pppoe_ops;
+static const struct ppp_channel_ops pppoe_chan_ops;
+
+/* per-net private data for this module */
+static int pppoe_net_id __read_mostly;
+struct pppoe_net {
+       /*
+        * we could use _single_ hash table for all
+        * nets by injecting net id into the hash but
+        * it would increase hash chains and add
+        * a few additional math comparations messy
+        * as well, moreover in case of SMP less locking
+        * controversy here
+        */
+       struct pppox_sock *hash_table[PPPOE_HASH_SIZE];
+       rwlock_t hash_lock;
+};
+
+/*
+ * PPPoE could be in the following stages:
+ * 1) Discovery stage (to obtain remote MAC and Session ID)
+ * 2) Session stage (MAC and SID are known)
+ *
+ * Ethernet frames have a special tag for this but
+ * we use simpler approach based on session id
+ */
+static inline bool stage_session(__be16 sid)
+{
+       return sid != 0;
+}
+
+static inline struct pppoe_net *pppoe_pernet(struct net *net)
+{
+       BUG_ON(!net);
+
+       return net_generic(net, pppoe_net_id);
+}
+
+static inline int cmp_2_addr(struct pppoe_addr *a, struct pppoe_addr *b)
+{
+       return a->sid == b->sid && !memcmp(a->remote, b->remote, ETH_ALEN);
+}
+
+static inline int cmp_addr(struct pppoe_addr *a, __be16 sid, char *addr)
+{
+       return a->sid == sid && !memcmp(a->remote, addr, ETH_ALEN);
+}
+
+#if 8 % PPPOE_HASH_BITS
+#error 8 must be a multiple of PPPOE_HASH_BITS
+#endif
+
+static int hash_item(__be16 sid, unsigned char *addr)
+{
+       unsigned char hash = 0;
+       unsigned int i;
+
+       for (i = 0; i < ETH_ALEN; i++)
+               hash ^= addr[i];
+       for (i = 0; i < sizeof(sid_t) * 8; i += 8)
+               hash ^= (__force __u32)sid >> i;
+       for (i = 8; (i >>= 1) >= PPPOE_HASH_BITS;)
+               hash ^= hash >> i;
+
+       return hash & PPPOE_HASH_MASK;
+}
+
+/**********************************************************************
+ *
+ *  Set/get/delete/rehash items  (internal versions)
+ *
+ **********************************************************************/
+static struct pppox_sock *__get_item(struct pppoe_net *pn, __be16 sid,
+                               unsigned char *addr, int ifindex)
+{
+       int hash = hash_item(sid, addr);
+       struct pppox_sock *ret;
+
+       ret = pn->hash_table[hash];
+       while (ret) {
+               if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
+                   ret->pppoe_ifindex == ifindex)
+                       return ret;
+
+               ret = ret->next;
+       }
+
+       return NULL;
+}
+
+static int __set_item(struct pppoe_net *pn, struct pppox_sock *po)
+{
+       int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
+       struct pppox_sock *ret;
+
+       ret = pn->hash_table[hash];
+       while (ret) {
+               if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) &&
+                   ret->pppoe_ifindex == po->pppoe_ifindex)
+                       return -EALREADY;
+
+               ret = ret->next;
+       }
+
+       po->next = pn->hash_table[hash];
+       pn->hash_table[hash] = po;
+
+       return 0;
+}
+
+static struct pppox_sock *__delete_item(struct pppoe_net *pn, __be16 sid,
+                                       char *addr, int ifindex)
+{
+       int hash = hash_item(sid, addr);
+       struct pppox_sock *ret, **src;
+
+       ret = pn->hash_table[hash];
+       src = &pn->hash_table[hash];
+
+       while (ret) {
+               if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
+                   ret->pppoe_ifindex == ifindex) {
+                       *src = ret->next;
+                       break;
+               }
+
+               src = &ret->next;
+               ret = ret->next;
+       }
+
+       return ret;
+}
+
+/**********************************************************************
+ *
+ *  Set/get/delete/rehash items
+ *
+ **********************************************************************/
+static inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid,
+                                       unsigned char *addr, int ifindex)
+{
+       struct pppox_sock *po;
+
+       read_lock_bh(&pn->hash_lock);
+       po = __get_item(pn, sid, addr, ifindex);
+       if (po)
+               sock_hold(sk_pppox(po));
+       read_unlock_bh(&pn->hash_lock);
+
+       return po;
+}
+
+static inline struct pppox_sock *get_item_by_addr(struct net *net,
+                                               struct sockaddr_pppox *sp)
+{
+       struct net_device *dev;
+       struct pppoe_net *pn;
+       struct pppox_sock *pppox_sock = NULL;
+
+       int ifindex;
+
+       rcu_read_lock();
+       dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev);
+       if (dev) {
+               ifindex = dev->ifindex;
+               pn = pppoe_pernet(net);
+               pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid,
+                               sp->sa_addr.pppoe.remote, ifindex);
+       }
+       rcu_read_unlock();
+       return pppox_sock;
+}
+
+static inline struct pppox_sock *delete_item(struct pppoe_net *pn, __be16 sid,
+                                       char *addr, int ifindex)
+{
+       struct pppox_sock *ret;
+
+       write_lock_bh(&pn->hash_lock);
+       ret = __delete_item(pn, sid, addr, ifindex);
+       write_unlock_bh(&pn->hash_lock);
+
+       return ret;
+}
+
+/***************************************************************************
+ *
+ *  Handler for device events.
+ *  Certain device events require that sockets be unconnected.
+ *
+ **************************************************************************/
+
+static void pppoe_flush_dev(struct net_device *dev)
+{
+       struct pppoe_net *pn;
+       int i;
+
+       pn = pppoe_pernet(dev_net(dev));
+       write_lock_bh(&pn->hash_lock);
+       for (i = 0; i < PPPOE_HASH_SIZE; i++) {
+               struct pppox_sock *po = pn->hash_table[i];
+               struct sock *sk;
+
+               while (po) {
+                       while (po && po->pppoe_dev != dev) {
+                               po = po->next;
+                       }
+
+                       if (!po)
+                               break;
+
+                       sk = sk_pppox(po);
+
+                       /* We always grab the socket lock, followed by the
+                        * hash_lock, in that order.  Since we should hold the
+                        * sock lock while doing any unbinding, we need to
+                        * release the lock we're holding.  Hold a reference to
+                        * the sock so it doesn't disappear as we're jumping
+                        * between locks.
+                        */
+
+                       sock_hold(sk);
+                       write_unlock_bh(&pn->hash_lock);
+                       lock_sock(sk);
+
+                       if (po->pppoe_dev == dev &&
+                           sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
+                               pppox_unbind_sock(sk);
+                               sk->sk_state = PPPOX_ZOMBIE;
+                               sk->sk_state_change(sk);
+                               po->pppoe_dev = NULL;
+                               dev_put(dev);
+                       }
+
+                       release_sock(sk);
+                       sock_put(sk);
+
+                       /* Restart the process from the start of the current
+                        * hash chain. We dropped locks so the world may have
+                        * change from underneath us.
+                        */
+
+                       BUG_ON(pppoe_pernet(dev_net(dev)) == NULL);
+                       write_lock_bh(&pn->hash_lock);
+                       po = pn->hash_table[i];
+               }
+       }
+       write_unlock_bh(&pn->hash_lock);
+}
+
+static int pppoe_device_event(struct notifier_block *this,
+                             unsigned long event, void *ptr)
+{
+       struct net_device *dev = (struct net_device *)ptr;
+
+       /* Only look at sockets that are using this specific device. */
+       switch (event) {
+       case NETDEV_CHANGEADDR:
+       case NETDEV_CHANGEMTU:
+               /* A change in mtu or address is a bad thing, requiring
+                * LCP re-negotiation.
+                */
+
+       case NETDEV_GOING_DOWN:
+       case NETDEV_DOWN:
+               /* Find every socket on this device and kill it. */
+               pppoe_flush_dev(dev);
+               break;
+
+       default:
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block pppoe_notifier = {
+       .notifier_call = pppoe_device_event,
+};
+
+/************************************************************************
+ *
+ * Do the real work of receiving a PPPoE Session frame.
+ *
+ ***********************************************************************/
+static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
+{
+       struct pppox_sock *po = pppox_sk(sk);
+       struct pppox_sock *relay_po;
+
+       /* Backlog receive. Semantics of backlog rcv preclude any code from
+        * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state
+        * can't change.
+        */
+
+       if (sk->sk_state & PPPOX_BOUND) {
+               ppp_input(&po->chan, skb);
+       } else if (sk->sk_state & PPPOX_RELAY) {
+               relay_po = get_item_by_addr(sock_net(sk),
+                                           &po->pppoe_relay);
+               if (relay_po == NULL)
+                       goto abort_kfree;
+
+               if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0)
+                       goto abort_put;
+
+               if (!__pppoe_xmit(sk_pppox(relay_po), skb))
+                       goto abort_put;
+       } else {
+               if (sock_queue_rcv_skb(sk, skb))
+                       goto abort_kfree;
+       }
+
+       return NET_RX_SUCCESS;
+
+abort_put:
+       sock_put(sk_pppox(relay_po));
+
+abort_kfree:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+/************************************************************************
+ *
+ * Receive wrapper called in BH context.
+ *
+ ***********************************************************************/
+static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
+                    struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct pppoe_hdr *ph;
+       struct pppox_sock *po;
+       struct pppoe_net *pn;
+       int len;
+
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb)
+               goto out;
+
+       if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
+               goto drop;
+
+       ph = pppoe_hdr(skb);
+       len = ntohs(ph->length);
+
+       skb_pull_rcsum(skb, sizeof(*ph));
+       if (skb->len < len)
+               goto drop;
+
+       if (pskb_trim_rcsum(skb, len))
+               goto drop;
+
+       pn = pppoe_pernet(dev_net(dev));
+
+       /* Note that get_item does a sock_hold(), so sk_pppox(po)
+        * is known to be safe.
+        */
+       po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
+       if (!po)
+               goto drop;
+
+       return sk_receive_skb(sk_pppox(po), skb, 0);
+
+drop:
+       kfree_skb(skb);
+out:
+       return NET_RX_DROP;
+}
+
+/************************************************************************
+ *
+ * Receive a PPPoE Discovery frame.
+ * This is solely for detection of PADT frames
+ *
+ ***********************************************************************/
+static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
+                         struct packet_type *pt, struct net_device *orig_dev)
+
+{
+       struct pppoe_hdr *ph;
+       struct pppox_sock *po;
+       struct pppoe_net *pn;
+
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb)
+               goto out;
+
+       if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
+               goto abort;
+
+       ph = pppoe_hdr(skb);
+       if (ph->code != PADT_CODE)
+               goto abort;
+
+       pn = pppoe_pernet(dev_net(dev));
+       po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
+       if (po) {
+               struct sock *sk = sk_pppox(po);
+
+               bh_lock_sock(sk);
+
+               /* If the user has locked the socket, just ignore
+                * the packet.  With the way two rcv protocols hook into
+                * one socket family type, we cannot (easily) distinguish
+                * what kind of SKB it is during backlog rcv.
+                */
+               if (sock_owned_by_user(sk) == 0) {
+                       /* We're no longer connect at the PPPOE layer,
+                        * and must wait for ppp channel to disconnect us.
+                        */
+                       sk->sk_state = PPPOX_ZOMBIE;
+               }
+
+               bh_unlock_sock(sk);
+               sock_put(sk);
+       }
+
+abort:
+       kfree_skb(skb);
+out:
+       return NET_RX_SUCCESS; /* Lies... :-) */
+}
+
+static struct packet_type pppoes_ptype __read_mostly = {
+       .type   = cpu_to_be16(ETH_P_PPP_SES),
+       .func   = pppoe_rcv,
+};
+
+static struct packet_type pppoed_ptype __read_mostly = {
+       .type   = cpu_to_be16(ETH_P_PPP_DISC),
+       .func   = pppoe_disc_rcv,
+};
+
+static struct proto pppoe_sk_proto __read_mostly = {
+       .name     = "PPPOE",
+       .owner    = THIS_MODULE,
+       .obj_size = sizeof(struct pppox_sock),
+};
+
+/***********************************************************************
+ *
+ * Initialize a new struct sock.
+ *
+ **********************************************************************/
+static int pppoe_create(struct net *net, struct socket *sock)
+{
+       struct sock *sk;
+
+       sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto);
+       if (!sk)
+               return -ENOMEM;
+
+       sock_init_data(sock, sk);
+
+       sock->state     = SS_UNCONNECTED;
+       sock->ops       = &pppoe_ops;
+
+       sk->sk_backlog_rcv      = pppoe_rcv_core;
+       sk->sk_state            = PPPOX_NONE;
+       sk->sk_type             = SOCK_STREAM;
+       sk->sk_family           = PF_PPPOX;
+       sk->sk_protocol         = PX_PROTO_OE;
+
+       return 0;
+}
+
+static int pppoe_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+       struct pppox_sock *po;
+       struct pppoe_net *pn;
+       struct net *net = NULL;
+
+       if (!sk)
+               return 0;
+
+       lock_sock(sk);
+       if (sock_flag(sk, SOCK_DEAD)) {
+               release_sock(sk);
+               return -EBADF;
+       }
+
+       po = pppox_sk(sk);
+
+       if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+               dev_put(po->pppoe_dev);
+               po->pppoe_dev = NULL;
+       }
+
+       pppox_unbind_sock(sk);
+
+       /* Signal the death of the socket. */
+       sk->sk_state = PPPOX_DEAD;
+
+       net = sock_net(sk);
+       pn = pppoe_pernet(net);
+
+       /*
+        * protect "po" from concurrent updates
+        * on pppoe_flush_dev
+        */
+       delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote,
+                   po->pppoe_ifindex);
+
+       sock_orphan(sk);
+       sock->sk = NULL;
+
+       skb_queue_purge(&sk->sk_receive_queue);
+       release_sock(sk);
+       sock_put(sk);
+
+       return 0;
+}
+
+static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
+                 int sockaddr_len, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct sockaddr_pppox *sp = (struct sockaddr_pppox *)uservaddr;
+       struct pppox_sock *po = pppox_sk(sk);
+       struct net_device *dev = NULL;
+       struct pppoe_net *pn;
+       struct net *net = NULL;
+       int error;
+
+       lock_sock(sk);
+
+       error = -EINVAL;
+       if (sp->sa_protocol != PX_PROTO_OE)
+               goto end;
+
+       /* Check for already bound sockets */
+       error = -EBUSY;
+       if ((sk->sk_state & PPPOX_CONNECTED) &&
+            stage_session(sp->sa_addr.pppoe.sid))
+               goto end;
+
+       /* Check for already disconnected sockets, on attempts to disconnect */
+       error = -EALREADY;
+       if ((sk->sk_state & PPPOX_DEAD) &&
+            !stage_session(sp->sa_addr.pppoe.sid))
+               goto end;
+
+       error = 0;
+
+       /* Delete the old binding */
+       if (stage_session(po->pppoe_pa.sid)) {
+               pppox_unbind_sock(sk);
+               pn = pppoe_pernet(sock_net(sk));
+               delete_item(pn, po->pppoe_pa.sid,
+                           po->pppoe_pa.remote, po->pppoe_ifindex);
+               if (po->pppoe_dev) {
+                       dev_put(po->pppoe_dev);
+                       po->pppoe_dev = NULL;
+               }
+
+               memset(sk_pppox(po) + 1, 0,
+                      sizeof(struct pppox_sock) - sizeof(struct sock));
+               sk->sk_state = PPPOX_NONE;
+       }
+
+       /* Re-bind in session stage only */
+       if (stage_session(sp->sa_addr.pppoe.sid)) {
+               error = -ENODEV;
+               net = sock_net(sk);
+               dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev);
+               if (!dev)
+                       goto err_put;
+
+               po->pppoe_dev = dev;
+               po->pppoe_ifindex = dev->ifindex;
+               pn = pppoe_pernet(net);
+               if (!(dev->flags & IFF_UP)) {
+                       goto err_put;
+               }
+
+               memcpy(&po->pppoe_pa,
+                      &sp->sa_addr.pppoe,
+                      sizeof(struct pppoe_addr));
+
+               write_lock_bh(&pn->hash_lock);
+               error = __set_item(pn, po);
+               write_unlock_bh(&pn->hash_lock);
+               if (error < 0)
+                       goto err_put;
+
+               po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
+                                  dev->hard_header_len);
+
+               po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr);
+               po->chan.private = sk;
+               po->chan.ops = &pppoe_chan_ops;
+
+               error = ppp_register_net_channel(dev_net(dev), &po->chan);
+               if (error) {
+                       delete_item(pn, po->pppoe_pa.sid,
+                                   po->pppoe_pa.remote, po->pppoe_ifindex);
+                       goto err_put;
+               }
+
+               sk->sk_state = PPPOX_CONNECTED;
+       }
+
+       po->num = sp->sa_addr.pppoe.sid;
+
+end:
+       release_sock(sk);
+       return error;
+err_put:
+       if (po->pppoe_dev) {
+               dev_put(po->pppoe_dev);
+               po->pppoe_dev = NULL;
+       }
+       goto end;
+}
+
+static int pppoe_getname(struct socket *sock, struct sockaddr *uaddr,
+                 int *usockaddr_len, int peer)
+{
+       int len = sizeof(struct sockaddr_pppox);
+       struct sockaddr_pppox sp;
+
+       sp.sa_family    = AF_PPPOX;
+       sp.sa_protocol  = PX_PROTO_OE;
+       memcpy(&sp.sa_addr.pppoe, &pppox_sk(sock->sk)->pppoe_pa,
+              sizeof(struct pppoe_addr));
+
+       memcpy(uaddr, &sp, len);
+
+       *usockaddr_len = len;
+
+       return 0;
+}
+
+static int pppoe_ioctl(struct socket *sock, unsigned int cmd,
+               unsigned long arg)
+{
+       struct sock *sk = sock->sk;
+       struct pppox_sock *po = pppox_sk(sk);
+       int val;
+       int err;
+
+       switch (cmd) {
+       case PPPIOCGMRU:
+               err = -ENXIO;
+               if (!(sk->sk_state & PPPOX_CONNECTED))
+                       break;
+
+               err = -EFAULT;
+               if (put_user(po->pppoe_dev->mtu -
+                            sizeof(struct pppoe_hdr) -
+                            PPP_HDRLEN,
+                            (int __user *)arg))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCSMRU:
+               err = -ENXIO;
+               if (!(sk->sk_state & PPPOX_CONNECTED))
+                       break;
+
+               err = -EFAULT;
+               if (get_user(val, (int __user *)arg))
+                       break;
+
+               if (val < (po->pppoe_dev->mtu
+                          - sizeof(struct pppoe_hdr)
+                          - PPP_HDRLEN))
+                       err = 0;
+               else
+                       err = -EINVAL;
+               break;
+
+       case PPPIOCSFLAGS:
+               err = -EFAULT;
+               if (get_user(val, (int __user *)arg))
+                       break;
+               err = 0;
+               break;
+
+       case PPPOEIOCSFWD:
+       {
+               struct pppox_sock *relay_po;
+
+               err = -EBUSY;
+               if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD))
+                       break;
+
+               err = -ENOTCONN;
+               if (!(sk->sk_state & PPPOX_CONNECTED))
+                       break;
+
+               /* PPPoE address from the user specifies an outbound
+                  PPPoE address which frames are forwarded to */
+               err = -EFAULT;
+               if (copy_from_user(&po->pppoe_relay,
+                                  (void __user *)arg,
+                                  sizeof(struct sockaddr_pppox)))
+                       break;
+
+               err = -EINVAL;
+               if (po->pppoe_relay.sa_family != AF_PPPOX ||
+                   po->pppoe_relay.sa_protocol != PX_PROTO_OE)
+                       break;
+
+               /* Check that the socket referenced by the address
+                  actually exists. */
+               relay_po = get_item_by_addr(sock_net(sk), &po->pppoe_relay);
+               if (!relay_po)
+                       break;
+
+               sock_put(sk_pppox(relay_po));
+               sk->sk_state |= PPPOX_RELAY;
+               err = 0;
+               break;
+       }
+
+       case PPPOEIOCDFWD:
+               err = -EALREADY;
+               if (!(sk->sk_state & PPPOX_RELAY))
+                       break;
+
+               sk->sk_state &= ~PPPOX_RELAY;
+               err = 0;
+               break;
+
+       default:
+               err = -ENOTTY;
+       }
+
+       return err;
+}
+
+static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock,
+                 struct msghdr *m, size_t total_len)
+{
+       struct sk_buff *skb;
+       struct sock *sk = sock->sk;
+       struct pppox_sock *po = pppox_sk(sk);
+       int error;
+       struct pppoe_hdr hdr;
+       struct pppoe_hdr *ph;
+       struct net_device *dev;
+       char *start;
+
+       lock_sock(sk);
+       if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) {
+               error = -ENOTCONN;
+               goto end;
+       }
+
+       hdr.ver = 1;
+       hdr.type = 1;
+       hdr.code = 0;
+       hdr.sid = po->num;
+
+       dev = po->pppoe_dev;
+
+       error = -EMSGSIZE;
+       if (total_len > (dev->mtu + dev->hard_header_len))
+               goto end;
+
+
+       skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32,
+                          0, GFP_KERNEL);
+       if (!skb) {
+               error = -ENOMEM;
+               goto end;
+       }
+
+       /* Reserve space for headers. */
+       skb_reserve(skb, dev->hard_header_len);
+       skb_reset_network_header(skb);
+
+       skb->dev = dev;
+
+       skb->priority = sk->sk_priority;
+       skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
+
+       ph = (struct pppoe_hdr *)skb_put(skb, total_len + sizeof(struct pppoe_hdr));
+       start = (char *)&ph->tag[0];
+
+       error = memcpy_fromiovec(start, m->msg_iov, total_len);
+       if (error < 0) {
+               kfree_skb(skb);
+               goto end;
+       }
+
+       error = total_len;
+       dev_hard_header(skb, dev, ETH_P_PPP_SES,
+                       po->pppoe_pa.remote, NULL, total_len);
+
+       memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
+
+       ph->length = htons(total_len);
+
+       dev_queue_xmit(skb);
+
+end:
+       release_sock(sk);
+       return error;
+}
+
+/************************************************************************
+ *
+ * xmit function for internal use.
+ *
+ ***********************************************************************/
+static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
+{
+       struct pppox_sock *po = pppox_sk(sk);
+       struct net_device *dev = po->pppoe_dev;
+       struct pppoe_hdr *ph;
+       int data_len = skb->len;
+
+       /* The higher-level PPP code (ppp_unregister_channel()) ensures the PPP
+        * xmit operations conclude prior to an unregistration call.  Thus
+        * sk->sk_state cannot change, so we don't need to do lock_sock().
+        * But, we also can't do a lock_sock since that introduces a potential
+        * deadlock as we'd reverse the lock ordering used when calling
+        * ppp_unregister_channel().
+        */
+
+       if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
+               goto abort;
+
+       if (!dev)
+               goto abort;
+
+       /* Copy the data if there is no space for the header or if it's
+        * read-only.
+        */
+       if (skb_cow_head(skb, sizeof(*ph) + dev->hard_header_len))
+               goto abort;
+
+       __skb_push(skb, sizeof(*ph));
+       skb_reset_network_header(skb);
+
+       ph = pppoe_hdr(skb);
+       ph->ver = 1;
+       ph->type = 1;
+       ph->code = 0;
+       ph->sid = po->num;
+       ph->length = htons(data_len);
+
+       skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
+       skb->dev = dev;
+
+       dev_hard_header(skb, dev, ETH_P_PPP_SES,
+                       po->pppoe_pa.remote, NULL, data_len);
+
+       dev_queue_xmit(skb);
+       return 1;
+
+abort:
+       kfree_skb(skb);
+       return 1;
+}
+
+/************************************************************************
+ *
+ * xmit function called by generic PPP driver
+ * sends PPP frame over PPPoE socket
+ *
+ ***********************************************************************/
+static int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+       struct sock *sk = (struct sock *)chan->private;
+       return __pppoe_xmit(sk, skb);
+}
+
+static const struct ppp_channel_ops pppoe_chan_ops = {
+       .start_xmit = pppoe_xmit,
+};
+
+static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
+                 struct msghdr *m, size_t total_len, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct sk_buff *skb;
+       int error = 0;
+
+       if (sk->sk_state & PPPOX_BOUND) {
+               error = -EIO;
+               goto end;
+       }
+
+       skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
+                               flags & MSG_DONTWAIT, &error);
+       if (error < 0)
+               goto end;
+
+       m->msg_namelen = 0;
+
+       if (skb) {
+               total_len = min_t(size_t, total_len, skb->len);
+               error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
+               if (error == 0)
+                       error = total_len;
+       }
+
+       kfree_skb(skb);
+end:
+       return error;
+}
+
+#ifdef CONFIG_PROC_FS
+static int pppoe_seq_show(struct seq_file *seq, void *v)
+{
+       struct pppox_sock *po;
+       char *dev_name;
+
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(seq, "Id       Address              Device\n");
+               goto out;
+       }
+
+       po = v;
+       dev_name = po->pppoe_pa.dev;
+
+       seq_printf(seq, "%08X %pM %8s\n",
+               po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
+out:
+       return 0;
+}
+
+static inline struct pppox_sock *pppoe_get_idx(struct pppoe_net *pn, loff_t pos)
+{
+       struct pppox_sock *po;
+       int i;
+
+       for (i = 0; i < PPPOE_HASH_SIZE; i++) {
+               po = pn->hash_table[i];
+               while (po) {
+                       if (!pos--)
+                               goto out;
+                       po = po->next;
+               }
+       }
+
+out:
+       return po;
+}
+
+static void *pppoe_seq_start(struct seq_file *seq, loff_t *pos)
+       __acquires(pn->hash_lock)
+{
+       struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
+       loff_t l = *pos;
+
+       read_lock_bh(&pn->hash_lock);
+       return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN;
+}
+
+static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
+       struct pppox_sock *po;
+
+       ++*pos;
+       if (v == SEQ_START_TOKEN) {
+               po = pppoe_get_idx(pn, 0);
+               goto out;
+       }
+       po = v;
+       if (po->next)
+               po = po->next;
+       else {
+               int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
+
+               po = NULL;
+               while (++hash < PPPOE_HASH_SIZE) {
+                       po = pn->hash_table[hash];
+                       if (po)
+                               break;
+               }
+       }
+
+out:
+       return po;
+}
+
+static void pppoe_seq_stop(struct seq_file *seq, void *v)
+       __releases(pn->hash_lock)
+{
+       struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
+       read_unlock_bh(&pn->hash_lock);
+}
+
+static const struct seq_operations pppoe_seq_ops = {
+       .start          = pppoe_seq_start,
+       .next           = pppoe_seq_next,
+       .stop           = pppoe_seq_stop,
+       .show           = pppoe_seq_show,
+};
+
+static int pppoe_seq_open(struct inode *inode, struct file *file)
+{
+       return seq_open_net(inode, file, &pppoe_seq_ops,
+                       sizeof(struct seq_net_private));
+}
+
+static const struct file_operations pppoe_seq_fops = {
+       .owner          = THIS_MODULE,
+       .open           = pppoe_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_net,
+};
+
+#endif /* CONFIG_PROC_FS */
+
+static const struct proto_ops pppoe_ops = {
+       .family         = AF_PPPOX,
+       .owner          = THIS_MODULE,
+       .release        = pppoe_release,
+       .bind           = sock_no_bind,
+       .connect        = pppoe_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .getname        = pppoe_getname,
+       .poll           = datagram_poll,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .sendmsg        = pppoe_sendmsg,
+       .recvmsg        = pppoe_recvmsg,
+       .mmap           = sock_no_mmap,
+       .ioctl          = pppox_ioctl,
+};
+
+static const struct pppox_proto pppoe_proto = {
+       .create = pppoe_create,
+       .ioctl  = pppoe_ioctl,
+       .owner  = THIS_MODULE,
+};
+
+static __net_init int pppoe_init_net(struct net *net)
+{
+       struct pppoe_net *pn = pppoe_pernet(net);
+       struct proc_dir_entry *pde;
+
+       rwlock_init(&pn->hash_lock);
+
+       pde = proc_net_fops_create(net, "pppoe", S_IRUGO, &pppoe_seq_fops);
+#ifdef CONFIG_PROC_FS
+       if (!pde)
+               return -ENOMEM;
+#endif
+
+       return 0;
+}
+
+static __net_exit void pppoe_exit_net(struct net *net)
+{
+       proc_net_remove(net, "pppoe");
+}
+
+static struct pernet_operations pppoe_net_ops = {
+       .init = pppoe_init_net,
+       .exit = pppoe_exit_net,
+       .id   = &pppoe_net_id,
+       .size = sizeof(struct pppoe_net),
+};
+
+static int __init pppoe_init(void)
+{
+       int err;
+
+       err = register_pernet_device(&pppoe_net_ops);
+       if (err)
+               goto out;
+
+       err = proto_register(&pppoe_sk_proto, 0);
+       if (err)
+               goto out_unregister_net_ops;
+
+       err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto);
+       if (err)
+               goto out_unregister_pppoe_proto;
+
+       dev_add_pack(&pppoes_ptype);
+       dev_add_pack(&pppoed_ptype);
+       register_netdevice_notifier(&pppoe_notifier);
+
+       return 0;
+
+out_unregister_pppoe_proto:
+       proto_unregister(&pppoe_sk_proto);
+out_unregister_net_ops:
+       unregister_pernet_device(&pppoe_net_ops);
+out:
+       return err;
+}
+
+static void __exit pppoe_exit(void)
+{
+       unregister_netdevice_notifier(&pppoe_notifier);
+       dev_remove_pack(&pppoed_ptype);
+       dev_remove_pack(&pppoes_ptype);
+       unregister_pppox_proto(PX_PROTO_OE);
+       proto_unregister(&pppoe_sk_proto);
+       unregister_pernet_device(&pppoe_net_ops);
+}
+
+module_init(pppoe_init);
+module_exit(pppoe_exit);
+
+MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
+MODULE_DESCRIPTION("PPP over Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_PPPOX);
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c
new file mode 100644 (file)
index 0000000..8c0d170
--- /dev/null
@@ -0,0 +1,149 @@
+/** -*- linux-c -*- ***********************************************************
+ * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets
+ *
+ * PPPoX --- Generic PPP encapsulation socket family
+ * PPPoE --- PPP over Ethernet (RFC 2516)
+ *
+ *
+ * Version:    0.5.2
+ *
+ * Author:     Michal Ostrowski <mostrows@speakeasy.net>
+ *
+ * 051000 :    Initialization cleanup
+ *
+ * License:
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/init.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+#include <linux/kmod.h>
+
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+
+static const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1];
+
+int register_pppox_proto(int proto_num, const struct pppox_proto *pp)
+{
+       if (proto_num < 0 || proto_num > PX_MAX_PROTO)
+               return -EINVAL;
+       if (pppox_protos[proto_num])
+               return -EALREADY;
+       pppox_protos[proto_num] = pp;
+       return 0;
+}
+
+void unregister_pppox_proto(int proto_num)
+{
+       if (proto_num >= 0 && proto_num <= PX_MAX_PROTO)
+               pppox_protos[proto_num] = NULL;
+}
+
+void pppox_unbind_sock(struct sock *sk)
+{
+       /* Clear connection to ppp device, if attached. */
+
+       if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
+               ppp_unregister_channel(&pppox_sk(sk)->chan);
+               sk->sk_state = PPPOX_DEAD;
+       }
+}
+
+EXPORT_SYMBOL(register_pppox_proto);
+EXPORT_SYMBOL(unregister_pppox_proto);
+EXPORT_SYMBOL(pppox_unbind_sock);
+
+int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       struct sock *sk = sock->sk;
+       struct pppox_sock *po = pppox_sk(sk);
+       int rc;
+
+       lock_sock(sk);
+
+       switch (cmd) {
+       case PPPIOCGCHAN: {
+               int index;
+               rc = -ENOTCONN;
+               if (!(sk->sk_state & PPPOX_CONNECTED))
+                       break;
+
+               rc = -EINVAL;
+               index = ppp_channel_index(&po->chan);
+               if (put_user(index , (int __user *) arg))
+                       break;
+
+               rc = 0;
+               sk->sk_state |= PPPOX_BOUND;
+               break;
+       }
+       default:
+               rc = pppox_protos[sk->sk_protocol]->ioctl ?
+                       pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY;
+       }
+
+       release_sock(sk);
+       return rc;
+}
+
+EXPORT_SYMBOL(pppox_ioctl);
+
+static int pppox_create(struct net *net, struct socket *sock, int protocol,
+                       int kern)
+{
+       int rc = -EPROTOTYPE;
+
+       if (protocol < 0 || protocol > PX_MAX_PROTO)
+               goto out;
+
+       rc = -EPROTONOSUPPORT;
+       if (!pppox_protos[protocol])
+               request_module("pppox-proto-%d", protocol);
+       if (!pppox_protos[protocol] ||
+           !try_module_get(pppox_protos[protocol]->owner))
+               goto out;
+
+       rc = pppox_protos[protocol]->create(net, sock);
+
+       module_put(pppox_protos[protocol]->owner);
+out:
+       return rc;
+}
+
+static const struct net_proto_family pppox_proto_family = {
+       .family = PF_PPPOX,
+       .create = pppox_create,
+       .owner  = THIS_MODULE,
+};
+
+static int __init pppox_init(void)
+{
+       return sock_register(&pppox_proto_family);
+}
+
+static void __exit pppox_exit(void)
+{
+       sock_unregister(PF_PPPOX);
+}
+
+module_init(pppox_init);
+module_exit(pppox_exit);
+
+MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
+MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
new file mode 100644 (file)
index 0000000..eae542a
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ *  Point-to-Point Tunneling Protocol for Linux
+ *
+ *     Authors: Dmitry Kozlov <xeb@mail.ru>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/ppp_channel.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_pppox.h>
+#include <linux/if_ppp.h>
+#include <linux/notifier.h>
+#include <linux/file.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+
+#include <net/sock.h>
+#include <net/protocol.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/route.h>
+#include <net/gre.h>
+
+#include <linux/uaccess.h>
+
+#define PPTP_DRIVER_VERSION "0.8.5"
+
+#define MAX_CALLID 65535
+
+static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1);
+static struct pppox_sock **callid_sock;
+
+static DEFINE_SPINLOCK(chan_lock);
+
+static struct proto pptp_sk_proto __read_mostly;
+static const struct ppp_channel_ops pptp_chan_ops;
+static const struct proto_ops pptp_ops;
+
+#define PPP_LCP_ECHOREQ 0x09
+#define PPP_LCP_ECHOREP 0x0A
+#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
+
+#define MISSING_WINDOW 20
+#define WRAPPED(curseq, lastseq)\
+       ((((curseq) & 0xffffff00) == 0) &&\
+       (((lastseq) & 0xffffff00) == 0xffffff00))
+
+#define PPTP_GRE_PROTO  0x880B
+#define PPTP_GRE_VER    0x1
+
+#define PPTP_GRE_FLAG_C        0x80
+#define PPTP_GRE_FLAG_R        0x40
+#define PPTP_GRE_FLAG_K        0x20
+#define PPTP_GRE_FLAG_S        0x10
+#define PPTP_GRE_FLAG_A        0x80
+
+#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
+#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
+#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
+#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
+#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)
+
+#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header))
+struct pptp_gre_header {
+       u8  flags;
+       u8  ver;
+       u16 protocol;
+       u16 payload_len;
+       u16 call_id;
+       u32 seq;
+       u32 ack;
+} __packed;
+
+static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
+{
+       struct pppox_sock *sock;
+       struct pptp_opt *opt;
+
+       rcu_read_lock();
+       sock = rcu_dereference(callid_sock[call_id]);
+       if (sock) {
+               opt = &sock->proto.pptp;
+               if (opt->dst_addr.sin_addr.s_addr != s_addr)
+                       sock = NULL;
+               else
+                       sock_hold(sk_pppox(sock));
+       }
+       rcu_read_unlock();
+
+       return sock;
+}
+
+static int lookup_chan_dst(u16 call_id, __be32 d_addr)
+{
+       struct pppox_sock *sock;
+       struct pptp_opt *opt;
+       int i;
+
+       rcu_read_lock();
+       for (i = find_next_bit(callid_bitmap, MAX_CALLID, 1); i < MAX_CALLID;
+            i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) {
+               sock = rcu_dereference(callid_sock[i]);
+               if (!sock)
+                       continue;
+               opt = &sock->proto.pptp;
+               if (opt->dst_addr.call_id == call_id &&
+                         opt->dst_addr.sin_addr.s_addr == d_addr)
+                       break;
+       }
+       rcu_read_unlock();
+
+       return i < MAX_CALLID;
+}
+
+static int add_chan(struct pppox_sock *sock)
+{
+       static int call_id;
+
+       spin_lock(&chan_lock);
+       if (!sock->proto.pptp.src_addr.call_id) {
+               call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1);
+               if (call_id == MAX_CALLID) {
+                       call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1);
+                       if (call_id == MAX_CALLID)
+                               goto out_err;
+               }
+               sock->proto.pptp.src_addr.call_id = call_id;
+       } else if (test_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap))
+               goto out_err;
+
+       set_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
+       rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], sock);
+       spin_unlock(&chan_lock);
+
+       return 0;
+
+out_err:
+       spin_unlock(&chan_lock);
+       return -1;
+}
+
+static void del_chan(struct pppox_sock *sock)
+{
+       spin_lock(&chan_lock);
+       clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
+       rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], NULL);
+       spin_unlock(&chan_lock);
+       synchronize_rcu();
+}
+
+static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+       struct sock *sk = (struct sock *) chan->private;
+       struct pppox_sock *po = pppox_sk(sk);
+       struct pptp_opt *opt = &po->proto.pptp;
+       struct pptp_gre_header *hdr;
+       unsigned int header_len = sizeof(*hdr);
+       struct flowi4 fl4;
+       int islcp;
+       int len;
+       unsigned char *data;
+       __u32 seq_recv;
+
+
+       struct rtable *rt;
+       struct net_device *tdev;
+       struct iphdr  *iph;
+       int    max_headroom;
+
+       if (sk_pppox(po)->sk_state & PPPOX_DEAD)
+               goto tx_error;
+
+       rt = ip_route_output_ports(&init_net, &fl4, NULL,
+                                  opt->dst_addr.sin_addr.s_addr,
+                                  opt->src_addr.sin_addr.s_addr,
+                                  0, 0, IPPROTO_GRE,
+                                  RT_TOS(0), 0);
+       if (IS_ERR(rt))
+               goto tx_error;
+
+       tdev = rt->dst.dev;
+
+       max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph) + sizeof(*hdr) + 2;
+
+       if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
+               struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+               if (!new_skb) {
+                       ip_rt_put(rt);
+                       goto tx_error;
+               }
+               if (skb->sk)
+                       skb_set_owner_w(new_skb, skb->sk);
+               kfree_skb(skb);
+               skb = new_skb;
+       }
+
+       data = skb->data;
+       islcp = ((data[0] << 8) + data[1]) == PPP_LCP && 1 <= data[2] && data[2] <= 7;
+
+       /* compress protocol field */
+       if ((opt->ppp_flags & SC_COMP_PROT) && data[0] == 0 && !islcp)
+               skb_pull(skb, 1);
+
+       /* Put in the address/control bytes if necessary */
+       if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) {
+               data = skb_push(skb, 2);
+               data[0] = PPP_ALLSTATIONS;
+               data[1] = PPP_UI;
+       }
+
+       len = skb->len;
+
+       seq_recv = opt->seq_recv;
+
+       if (opt->ack_sent == seq_recv)
+               header_len -= sizeof(hdr->ack);
+
+       /* Push down and install GRE header */
+       skb_push(skb, header_len);
+       hdr = (struct pptp_gre_header *)(skb->data);
+
+       hdr->flags       = PPTP_GRE_FLAG_K;
+       hdr->ver         = PPTP_GRE_VER;
+       hdr->protocol    = htons(PPTP_GRE_PROTO);
+       hdr->call_id     = htons(opt->dst_addr.call_id);
+
+       hdr->flags      |= PPTP_GRE_FLAG_S;
+       hdr->seq         = htonl(++opt->seq_sent);
+       if (opt->ack_sent != seq_recv)  {
+               /* send ack with this message */
+               hdr->ver |= PPTP_GRE_FLAG_A;
+               hdr->ack  = htonl(seq_recv);
+               opt->ack_sent = seq_recv;
+       }
+       hdr->payload_len = htons(len);
+
+       /*      Push down and install the IP header. */
+
+       skb_reset_transport_header(skb);
+       skb_push(skb, sizeof(*iph));
+       skb_reset_network_header(skb);
+       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED);
+
+       iph =   ip_hdr(skb);
+       iph->version =  4;
+       iph->ihl =      sizeof(struct iphdr) >> 2;
+       if (ip_dont_fragment(sk, &rt->dst))
+               iph->frag_off   =       htons(IP_DF);
+       else
+               iph->frag_off   =       0;
+       iph->protocol = IPPROTO_GRE;
+       iph->tos      = 0;
+       iph->daddr    = fl4.daddr;
+       iph->saddr    = fl4.saddr;
+       iph->ttl      = ip4_dst_hoplimit(&rt->dst);
+       iph->tot_len  = htons(skb->len);
+
+       skb_dst_drop(skb);
+       skb_dst_set(skb, &rt->dst);
+
+       nf_reset(skb);
+
+       skb->ip_summed = CHECKSUM_NONE;
+       ip_select_ident(iph, &rt->dst, NULL);
+       ip_send_check(iph);
+
+       ip_local_out(skb);
+
+tx_error:
+       return 1;
+}
+
+static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
+{
+       struct pppox_sock *po = pppox_sk(sk);
+       struct pptp_opt *opt = &po->proto.pptp;
+       int headersize, payload_len, seq;
+       __u8 *payload;
+       struct pptp_gre_header *header;
+
+       if (!(sk->sk_state & PPPOX_CONNECTED)) {
+               if (sock_queue_rcv_skb(sk, skb))
+                       goto drop;
+               return NET_RX_SUCCESS;
+       }
+
+       header = (struct pptp_gre_header *)(skb->data);
+
+       /* test if acknowledgement present */
+       if (PPTP_GRE_IS_A(header->ver)) {
+               __u32 ack = (PPTP_GRE_IS_S(header->flags)) ?
+                               header->ack : header->seq; /* ack in different place if S = 0 */
+
+               ack = ntohl(ack);
+
+               if (ack > opt->ack_recv)
+                       opt->ack_recv = ack;
+               /* also handle sequence number wrap-around  */
+               if (WRAPPED(ack, opt->ack_recv))
+                       opt->ack_recv = ack;
+       }
+
+       /* test if payload present */
+       if (!PPTP_GRE_IS_S(header->flags))
+               goto drop;
+
+       headersize  = sizeof(*header);
+       payload_len = ntohs(header->payload_len);
+       seq         = ntohl(header->seq);
+
+       /* no ack present? */
+       if (!PPTP_GRE_IS_A(header->ver))
+               headersize -= sizeof(header->ack);
+       /* check for incomplete packet (length smaller than expected) */
+       if (skb->len - headersize < payload_len)
+               goto drop;
+
+       payload = skb->data + headersize;
+       /* check for expected sequence number */
+       if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) {
+               if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) &&
+                               (PPP_PROTOCOL(payload) == PPP_LCP) &&
+                               ((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)))
+                       goto allow_packet;
+       } else {
+               opt->seq_recv = seq;
+allow_packet:
+               skb_pull(skb, headersize);
+
+               if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) {
+                       /* chop off address/control */
+                       if (skb->len < 3)
+                               goto drop;
+                       skb_pull(skb, 2);
+               }
+
+               if ((*skb->data) & 1) {
+                       /* protocol is compressed */
+                       skb_push(skb, 1)[0] = 0;
+               }
+
+               skb->ip_summed = CHECKSUM_NONE;
+               skb_set_network_header(skb, skb->head-skb->data);
+               ppp_input(&po->chan, skb);
+
+               return NET_RX_SUCCESS;
+       }
+drop:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+static int pptp_rcv(struct sk_buff *skb)
+{
+       struct pppox_sock *po;
+       struct pptp_gre_header *header;
+       struct iphdr *iph;
+
+       if (skb->pkt_type != PACKET_HOST)
+               goto drop;
+
+       if (!pskb_may_pull(skb, 12))
+               goto drop;
+
+       iph = ip_hdr(skb);
+
+       header = (struct pptp_gre_header *)skb->data;
+
+       if (ntohs(header->protocol) != PPTP_GRE_PROTO || /* PPTP-GRE protocol for PPTP */
+               PPTP_GRE_IS_C(header->flags) ||                /* flag C should be clear */
+               PPTP_GRE_IS_R(header->flags) ||                /* flag R should be clear */
+               !PPTP_GRE_IS_K(header->flags) ||               /* flag K should be set */
+               (header->flags&0xF) != 0)                      /* routing and recursion ctrl = 0 */
+               /* if invalid, discard this packet */
+               goto drop;
+
+       po = lookup_chan(htons(header->call_id), iph->saddr);
+       if (po) {
+               skb_dst_drop(skb);
+               nf_reset(skb);
+               return sk_receive_skb(sk_pppox(po), skb, 0);
+       }
+drop:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+static int pptp_bind(struct socket *sock, struct sockaddr *uservaddr,
+       int sockaddr_len)
+{
+       struct sock *sk = sock->sk;
+       struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
+       struct pppox_sock *po = pppox_sk(sk);
+       struct pptp_opt *opt = &po->proto.pptp;
+       int error = 0;
+
+       lock_sock(sk);
+
+       opt->src_addr = sp->sa_addr.pptp;
+       if (add_chan(po)) {
+               release_sock(sk);
+               error = -EBUSY;
+       }
+
+       release_sock(sk);
+       return error;
+}
+
+static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
+       int sockaddr_len, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
+       struct pppox_sock *po = pppox_sk(sk);
+       struct pptp_opt *opt = &po->proto.pptp;
+       struct rtable *rt;
+       struct flowi4 fl4;
+       int error = 0;
+
+       if (sp->sa_protocol != PX_PROTO_PPTP)
+               return -EINVAL;
+
+       if (lookup_chan_dst(sp->sa_addr.pptp.call_id, sp->sa_addr.pptp.sin_addr.s_addr))
+               return -EALREADY;
+
+       lock_sock(sk);
+       /* Check for already bound sockets */
+       if (sk->sk_state & PPPOX_CONNECTED) {
+               error = -EBUSY;
+               goto end;
+       }
+
+       /* Check for already disconnected sockets, on attempts to disconnect */
+       if (sk->sk_state & PPPOX_DEAD) {
+               error = -EALREADY;
+               goto end;
+       }
+
+       if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr) {
+               error = -EINVAL;
+               goto end;
+       }
+
+       po->chan.private = sk;
+       po->chan.ops = &pptp_chan_ops;
+
+       rt = ip_route_output_ports(&init_net, &fl4, sk,
+                                  opt->dst_addr.sin_addr.s_addr,
+                                  opt->src_addr.sin_addr.s_addr,
+                                  0, 0,
+                                  IPPROTO_GRE, RT_CONN_FLAGS(sk), 0);
+       if (IS_ERR(rt)) {
+               error = -EHOSTUNREACH;
+               goto end;
+       }
+       sk_setup_caps(sk, &rt->dst);
+
+       po->chan.mtu = dst_mtu(&rt->dst);
+       if (!po->chan.mtu)
+               po->chan.mtu = PPP_MTU;
+       ip_rt_put(rt);
+       po->chan.mtu -= PPTP_HEADER_OVERHEAD;
+
+       po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
+       error = ppp_register_channel(&po->chan);
+       if (error) {
+               pr_err("PPTP: failed to register PPP channel (%d)\n", error);
+               goto end;
+       }
+
+       opt->dst_addr = sp->sa_addr.pptp;
+       sk->sk_state = PPPOX_CONNECTED;
+
+ end:
+       release_sock(sk);
+       return error;
+}
+
+static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
+       int *usockaddr_len, int peer)
+{
+       int len = sizeof(struct sockaddr_pppox);
+       struct sockaddr_pppox sp;
+
+       sp.sa_family      = AF_PPPOX;
+       sp.sa_protocol  = PX_PROTO_PPTP;
+       sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
+
+       memcpy(uaddr, &sp, len);
+
+       *usockaddr_len = len;
+
+       return 0;
+}
+
+static int pptp_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+       struct pppox_sock *po;
+       struct pptp_opt *opt;
+       int error = 0;
+
+       if (!sk)
+               return 0;
+
+       lock_sock(sk);
+
+       if (sock_flag(sk, SOCK_DEAD)) {
+               release_sock(sk);
+               return -EBADF;
+       }
+
+       po = pppox_sk(sk);
+       opt = &po->proto.pptp;
+       del_chan(po);
+
+       pppox_unbind_sock(sk);
+       sk->sk_state = PPPOX_DEAD;
+
+       sock_orphan(sk);
+       sock->sk = NULL;
+
+       release_sock(sk);
+       sock_put(sk);
+
+       return error;
+}
+
+static void pptp_sock_destruct(struct sock *sk)
+{
+       if (!(sk->sk_state & PPPOX_DEAD)) {
+               del_chan(pppox_sk(sk));
+               pppox_unbind_sock(sk);
+       }
+       skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int pptp_create(struct net *net, struct socket *sock)
+{
+       int error = -ENOMEM;
+       struct sock *sk;
+       struct pppox_sock *po;
+       struct pptp_opt *opt;
+
+       sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pptp_sk_proto);
+       if (!sk)
+               goto out;
+
+       sock_init_data(sock, sk);
+
+       sock->state = SS_UNCONNECTED;
+       sock->ops   = &pptp_ops;
+
+       sk->sk_backlog_rcv = pptp_rcv_core;
+       sk->sk_state       = PPPOX_NONE;
+       sk->sk_type        = SOCK_STREAM;
+       sk->sk_family      = PF_PPPOX;
+       sk->sk_protocol    = PX_PROTO_PPTP;
+       sk->sk_destruct    = pptp_sock_destruct;
+
+       po = pppox_sk(sk);
+       opt = &po->proto.pptp;
+
+       opt->seq_sent = 0; opt->seq_recv = 0;
+       opt->ack_recv = 0; opt->ack_sent = 0;
+
+       error = 0;
+out:
+       return error;
+}
+
+static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
+       unsigned long arg)
+{
+       struct sock *sk = (struct sock *) chan->private;
+       struct pppox_sock *po = pppox_sk(sk);
+       struct pptp_opt *opt = &po->proto.pptp;
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       int err, val;
+
+       err = -EFAULT;
+       switch (cmd) {
+       case PPPIOCGFLAGS:
+               val = opt->ppp_flags;
+               if (put_user(val, p))
+                       break;
+               err = 0;
+               break;
+       case PPPIOCSFLAGS:
+               if (get_user(val, p))
+                       break;
+               opt->ppp_flags = val & ~SC_RCV_BITS;
+               err = 0;
+               break;
+       default:
+               err = -ENOTTY;
+       }
+
+       return err;
+}
+
+static const struct ppp_channel_ops pptp_chan_ops = {
+       .start_xmit = pptp_xmit,
+       .ioctl      = pptp_ppp_ioctl,
+};
+
+static struct proto pptp_sk_proto __read_mostly = {
+       .name     = "PPTP",
+       .owner    = THIS_MODULE,
+       .obj_size = sizeof(struct pppox_sock),
+};
+
+static const struct proto_ops pptp_ops = {
+       .family     = AF_PPPOX,
+       .owner      = THIS_MODULE,
+       .release    = pptp_release,
+       .bind       = pptp_bind,
+       .connect    = pptp_connect,
+       .socketpair = sock_no_socketpair,
+       .accept     = sock_no_accept,
+       .getname    = pptp_getname,
+       .poll       = sock_no_poll,
+       .listen     = sock_no_listen,
+       .shutdown   = sock_no_shutdown,
+       .setsockopt = sock_no_setsockopt,
+       .getsockopt = sock_no_getsockopt,
+       .sendmsg    = sock_no_sendmsg,
+       .recvmsg    = sock_no_recvmsg,
+       .mmap       = sock_no_mmap,
+       .ioctl      = pppox_ioctl,
+};
+
+static const struct pppox_proto pppox_pptp_proto = {
+       .create = pptp_create,
+       .owner  = THIS_MODULE,
+};
+
+static const struct gre_protocol gre_pptp_protocol = {
+       .handler = pptp_rcv,
+};
+
+static int __init pptp_init_module(void)
+{
+       int err = 0;
+       pr_info("PPTP driver version " PPTP_DRIVER_VERSION "\n");
+
+       callid_sock = vzalloc((MAX_CALLID + 1) * sizeof(void *));
+       if (!callid_sock) {
+               pr_err("PPTP: cann't allocate memory\n");
+               return -ENOMEM;
+       }
+
+       err = gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
+       if (err) {
+               pr_err("PPTP: can't add gre protocol\n");
+               goto out_mem_free;
+       }
+
+       err = proto_register(&pptp_sk_proto, 0);
+       if (err) {
+               pr_err("PPTP: can't register sk_proto\n");
+               goto out_gre_del_protocol;
+       }
+
+       err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
+       if (err) {
+               pr_err("PPTP: can't register pppox_proto\n");
+               goto out_unregister_sk_proto;
+       }
+
+       return 0;
+
+out_unregister_sk_proto:
+       proto_unregister(&pptp_sk_proto);
+out_gre_del_protocol:
+       gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
+out_mem_free:
+       vfree(callid_sock);
+
+       return err;
+}
+
+static void __exit pptp_exit_module(void)
+{
+       unregister_pppox_proto(PX_PROTO_PPTP);
+       proto_unregister(&pptp_sk_proto);
+       gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
+       vfree(callid_sock);
+}
+
+module_init(pptp_init_module);
+module_exit(pptp_exit_module);
+
+MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol");
+MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
deleted file mode 100644 (file)
index c6ba643..0000000
+++ /dev/null
@@ -1,1028 +0,0 @@
-/*
- * PPP async serial channel driver for Linux.
- *
- * Copyright 1999 Paul Mackerras.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- *
- * This driver provides the encapsulation and framing for sending
- * and receiving PPP frames over async serial lines.  It relies on
- * the generic PPP layer to give it frames to send and to process
- * received frames.  It implements the PPP line discipline.
- *
- * Part of the code in this driver was inspired by the old async-only
- * PPP driver, written by Michael Callahan and Al Longyear, and
- * subsequently hacked by Paul Mackerras.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/tty.h>
-#include <linux/netdevice.h>
-#include <linux/poll.h>
-#include <linux/crc-ccitt.h>
-#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
-#include <linux/ppp_channel.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/jiffies.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-#include <asm/uaccess.h>
-#include <asm/string.h>
-
-#define PPP_VERSION    "2.4.2"
-
-#define OBUFSIZE       4096
-
-/* Structure for storing local state. */
-struct asyncppp {
-       struct tty_struct *tty;
-       unsigned int    flags;
-       unsigned int    state;
-       unsigned int    rbits;
-       int             mru;
-       spinlock_t      xmit_lock;
-       spinlock_t      recv_lock;
-       unsigned long   xmit_flags;
-       u32             xaccm[8];
-       u32             raccm;
-       unsigned int    bytes_sent;
-       unsigned int    bytes_rcvd;
-
-       struct sk_buff  *tpkt;
-       int             tpkt_pos;
-       u16             tfcs;
-       unsigned char   *optr;
-       unsigned char   *olim;
-       unsigned long   last_xmit;
-
-       struct sk_buff  *rpkt;
-       int             lcp_fcs;
-       struct sk_buff_head rqueue;
-
-       struct tasklet_struct tsk;
-
-       atomic_t        refcnt;
-       struct semaphore dead_sem;
-       struct ppp_channel chan;        /* interface to generic ppp layer */
-       unsigned char   obuf[OBUFSIZE];
-};
-
-/* Bit numbers in xmit_flags */
-#define XMIT_WAKEUP    0
-#define XMIT_FULL      1
-#define XMIT_BUSY      2
-
-/* State bits */
-#define SC_TOSS                1
-#define SC_ESCAPE      2
-#define SC_PREV_ERROR  4
-
-/* Bits in rbits */
-#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-
-static int flag_time = HZ;
-module_param(flag_time, int, 0);
-MODULE_PARM_DESC(flag_time, "ppp_async: interval between flagged packets (in clock ticks)");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_LDISC(N_PPP);
-
-/*
- * Prototypes.
- */
-static int ppp_async_encode(struct asyncppp *ap);
-static int ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb);
-static int ppp_async_push(struct asyncppp *ap);
-static void ppp_async_flush_output(struct asyncppp *ap);
-static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
-                           char *flags, int count);
-static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
-                          unsigned long arg);
-static void ppp_async_process(unsigned long arg);
-
-static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
-                          int len, int inbound);
-
-static const struct ppp_channel_ops async_ops = {
-       .start_xmit = ppp_async_send,
-       .ioctl      = ppp_async_ioctl,
-};
-
-/*
- * Routines implementing the PPP line discipline.
- */
-
-/*
- * We have a potential race on dereferencing tty->disc_data,
- * because the tty layer provides no locking at all - thus one
- * cpu could be running ppp_asynctty_receive while another
- * calls ppp_asynctty_close, which zeroes tty->disc_data and
- * frees the memory that ppp_asynctty_receive is using.  The best
- * way to fix this is to use a rwlock in the tty struct, but for now
- * we use a single global rwlock for all ttys in ppp line discipline.
- *
- * FIXME: this is no longer true. The _close path for the ldisc is
- * now guaranteed to be sane.
- */
-static DEFINE_RWLOCK(disc_data_lock);
-
-static struct asyncppp *ap_get(struct tty_struct *tty)
-{
-       struct asyncppp *ap;
-
-       read_lock(&disc_data_lock);
-       ap = tty->disc_data;
-       if (ap != NULL)
-               atomic_inc(&ap->refcnt);
-       read_unlock(&disc_data_lock);
-       return ap;
-}
-
-static void ap_put(struct asyncppp *ap)
-{
-       if (atomic_dec_and_test(&ap->refcnt))
-               up(&ap->dead_sem);
-}
-
-/*
- * Called when a tty is put into PPP line discipline. Called in process
- * context.
- */
-static int
-ppp_asynctty_open(struct tty_struct *tty)
-{
-       struct asyncppp *ap;
-       int err;
-       int speed;
-
-       if (tty->ops->write == NULL)
-               return -EOPNOTSUPP;
-
-       err = -ENOMEM;
-       ap = kzalloc(sizeof(*ap), GFP_KERNEL);
-       if (!ap)
-               goto out;
-
-       /* initialize the asyncppp structure */
-       ap->tty = tty;
-       ap->mru = PPP_MRU;
-       spin_lock_init(&ap->xmit_lock);
-       spin_lock_init(&ap->recv_lock);
-       ap->xaccm[0] = ~0U;
-       ap->xaccm[3] = 0x60000000U;
-       ap->raccm = ~0U;
-       ap->optr = ap->obuf;
-       ap->olim = ap->obuf;
-       ap->lcp_fcs = -1;
-
-       skb_queue_head_init(&ap->rqueue);
-       tasklet_init(&ap->tsk, ppp_async_process, (unsigned long) ap);
-
-       atomic_set(&ap->refcnt, 1);
-       sema_init(&ap->dead_sem, 0);
-
-       ap->chan.private = ap;
-       ap->chan.ops = &async_ops;
-       ap->chan.mtu = PPP_MRU;
-       speed = tty_get_baud_rate(tty);
-       ap->chan.speed = speed;
-       err = ppp_register_channel(&ap->chan);
-       if (err)
-               goto out_free;
-
-       tty->disc_data = ap;
-       tty->receive_room = 65536;
-       return 0;
-
- out_free:
-       kfree(ap);
- out:
-       return err;
-}
-
-/*
- * Called when the tty is put into another line discipline
- * or it hangs up.  We have to wait for any cpu currently
- * executing in any of the other ppp_asynctty_* routines to
- * finish before we can call ppp_unregister_channel and free
- * the asyncppp struct.  This routine must be called from
- * process context, not interrupt or softirq context.
- */
-static void
-ppp_asynctty_close(struct tty_struct *tty)
-{
-       struct asyncppp *ap;
-
-       write_lock_irq(&disc_data_lock);
-       ap = tty->disc_data;
-       tty->disc_data = NULL;
-       write_unlock_irq(&disc_data_lock);
-       if (!ap)
-               return;
-
-       /*
-        * We have now ensured that nobody can start using ap from now
-        * on, but we have to wait for all existing users to finish.
-        * Note that ppp_unregister_channel ensures that no calls to
-        * our channel ops (i.e. ppp_async_send/ioctl) are in progress
-        * by the time it returns.
-        */
-       if (!atomic_dec_and_test(&ap->refcnt))
-               down(&ap->dead_sem);
-       tasklet_kill(&ap->tsk);
-
-       ppp_unregister_channel(&ap->chan);
-       kfree_skb(ap->rpkt);
-       skb_queue_purge(&ap->rqueue);
-       kfree_skb(ap->tpkt);
-       kfree(ap);
-}
-
-/*
- * Called on tty hangup in process context.
- *
- * Wait for I/O to driver to complete and unregister PPP channel.
- * This is already done by the close routine, so just call that.
- */
-static int ppp_asynctty_hangup(struct tty_struct *tty)
-{
-       ppp_asynctty_close(tty);
-       return 0;
-}
-
-/*
- * Read does nothing - no data is ever available this way.
- * Pppd reads and writes packets via /dev/ppp instead.
- */
-static ssize_t
-ppp_asynctty_read(struct tty_struct *tty, struct file *file,
-                 unsigned char __user *buf, size_t count)
-{
-       return -EAGAIN;
-}
-
-/*
- * Write on the tty does nothing, the packets all come in
- * from the ppp generic stuff.
- */
-static ssize_t
-ppp_asynctty_write(struct tty_struct *tty, struct file *file,
-                  const unsigned char *buf, size_t count)
-{
-       return -EAGAIN;
-}
-
-/*
- * Called in process context only. May be re-entered by multiple
- * ioctl calling threads.
- */
-
-static int
-ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
-                  unsigned int cmd, unsigned long arg)
-{
-       struct asyncppp *ap = ap_get(tty);
-       int err, val;
-       int __user *p = (int __user *)arg;
-
-       if (!ap)
-               return -ENXIO;
-       err = -EFAULT;
-       switch (cmd) {
-       case PPPIOCGCHAN:
-               err = -EFAULT;
-               if (put_user(ppp_channel_index(&ap->chan), p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGUNIT:
-               err = -EFAULT;
-               if (put_user(ppp_unit_number(&ap->chan), p))
-                       break;
-               err = 0;
-               break;
-
-       case TCFLSH:
-               /* flush our buffers and the serial port's buffer */
-               if (arg == TCIOFLUSH || arg == TCOFLUSH)
-                       ppp_async_flush_output(ap);
-               err = tty_perform_flush(tty, arg);
-               break;
-
-       case FIONREAD:
-               val = 0;
-               if (put_user(val, p))
-                       break;
-               err = 0;
-               break;
-
-       default:
-               /* Try the various mode ioctls */
-               err = tty_mode_ioctl(tty, file, cmd, arg);
-       }
-
-       ap_put(ap);
-       return err;
-}
-
-/* No kernel lock - fine */
-static unsigned int
-ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
-{
-       return 0;
-}
-
-/* May sleep, don't call from interrupt level or with interrupts disabled */
-static void
-ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
-                 char *cflags, int count)
-{
-       struct asyncppp *ap = ap_get(tty);
-       unsigned long flags;
-
-       if (!ap)
-               return;
-       spin_lock_irqsave(&ap->recv_lock, flags);
-       ppp_async_input(ap, buf, cflags, count);
-       spin_unlock_irqrestore(&ap->recv_lock, flags);
-       if (!skb_queue_empty(&ap->rqueue))
-               tasklet_schedule(&ap->tsk);
-       ap_put(ap);
-       tty_unthrottle(tty);
-}
-
-static void
-ppp_asynctty_wakeup(struct tty_struct *tty)
-{
-       struct asyncppp *ap = ap_get(tty);
-
-       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-       if (!ap)
-               return;
-       set_bit(XMIT_WAKEUP, &ap->xmit_flags);
-       tasklet_schedule(&ap->tsk);
-       ap_put(ap);
-}
-
-
-static struct tty_ldisc_ops ppp_ldisc = {
-       .owner  = THIS_MODULE,
-       .magic  = TTY_LDISC_MAGIC,
-       .name   = "ppp",
-       .open   = ppp_asynctty_open,
-       .close  = ppp_asynctty_close,
-       .hangup = ppp_asynctty_hangup,
-       .read   = ppp_asynctty_read,
-       .write  = ppp_asynctty_write,
-       .ioctl  = ppp_asynctty_ioctl,
-       .poll   = ppp_asynctty_poll,
-       .receive_buf = ppp_asynctty_receive,
-       .write_wakeup = ppp_asynctty_wakeup,
-};
-
-static int __init
-ppp_async_init(void)
-{
-       int err;
-
-       err = tty_register_ldisc(N_PPP, &ppp_ldisc);
-       if (err != 0)
-               printk(KERN_ERR "PPP_async: error %d registering line disc.\n",
-                      err);
-       return err;
-}
-
-/*
- * The following routines provide the PPP channel interface.
- */
-static int
-ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
-{
-       struct asyncppp *ap = chan->private;
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       int err, val;
-       u32 accm[8];
-
-       err = -EFAULT;
-       switch (cmd) {
-       case PPPIOCGFLAGS:
-               val = ap->flags | ap->rbits;
-               if (put_user(val, p))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSFLAGS:
-               if (get_user(val, p))
-                       break;
-               ap->flags = val & ~SC_RCV_BITS;
-               spin_lock_irq(&ap->recv_lock);
-               ap->rbits = val & SC_RCV_BITS;
-               spin_unlock_irq(&ap->recv_lock);
-               err = 0;
-               break;
-
-       case PPPIOCGASYNCMAP:
-               if (put_user(ap->xaccm[0], (u32 __user *)argp))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSASYNCMAP:
-               if (get_user(ap->xaccm[0], (u32 __user *)argp))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGRASYNCMAP:
-               if (put_user(ap->raccm, (u32 __user *)argp))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSRASYNCMAP:
-               if (get_user(ap->raccm, (u32 __user *)argp))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGXASYNCMAP:
-               if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSXASYNCMAP:
-               if (copy_from_user(accm, argp, sizeof(accm)))
-                       break;
-               accm[2] &= ~0x40000000U;        /* can't escape 0x5e */
-               accm[3] |= 0x60000000U;         /* must escape 0x7d, 0x7e */
-               memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
-               err = 0;
-               break;
-
-       case PPPIOCGMRU:
-               if (put_user(ap->mru, p))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSMRU:
-               if (get_user(val, p))
-                       break;
-               if (val < PPP_MRU)
-                       val = PPP_MRU;
-               ap->mru = val;
-               err = 0;
-               break;
-
-       default:
-               err = -ENOTTY;
-       }
-
-       return err;
-}
-
-/*
- * This is called at softirq level to deliver received packets
- * to the ppp_generic code, and to tell the ppp_generic code
- * if we can accept more output now.
- */
-static void ppp_async_process(unsigned long arg)
-{
-       struct asyncppp *ap = (struct asyncppp *) arg;
-       struct sk_buff *skb;
-
-       /* process received packets */
-       while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
-               if (skb->cb[0])
-                       ppp_input_error(&ap->chan, 0);
-               ppp_input(&ap->chan, skb);
-       }
-
-       /* try to push more stuff out */
-       if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_async_push(ap))
-               ppp_output_wakeup(&ap->chan);
-}
-
-/*
- * Procedures for encapsulation and framing.
- */
-
-/*
- * Procedure to encode the data for async serial transmission.
- * Does octet stuffing (escaping), puts the address/control bytes
- * on if A/C compression is disabled, and does protocol compression.
- * Assumes ap->tpkt != 0 on entry.
- * Returns 1 if we finished the current frame, 0 otherwise.
- */
-
-#define PUT_BYTE(ap, buf, c, islcp)    do {            \
-       if ((islcp && c < 0x20) || (ap->xaccm[c >> 5] & (1 << (c & 0x1f)))) {\
-               *buf++ = PPP_ESCAPE;                    \
-               *buf++ = c ^ PPP_TRANS;                 \
-       } else                                          \
-               *buf++ = c;                             \
-} while (0)
-
-static int
-ppp_async_encode(struct asyncppp *ap)
-{
-       int fcs, i, count, c, proto;
-       unsigned char *buf, *buflim;
-       unsigned char *data;
-       int islcp;
-
-       buf = ap->obuf;
-       ap->olim = buf;
-       ap->optr = buf;
-       i = ap->tpkt_pos;
-       data = ap->tpkt->data;
-       count = ap->tpkt->len;
-       fcs = ap->tfcs;
-       proto = get_unaligned_be16(data);
-
-       /*
-        * LCP packets with code values between 1 (configure-reqest)
-        * and 7 (code-reject) must be sent as though no options
-        * had been negotiated.
-        */
-       islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
-
-       if (i == 0) {
-               if (islcp)
-                       async_lcp_peek(ap, data, count, 0);
-
-               /*
-                * Start of a new packet - insert the leading FLAG
-                * character if necessary.
-                */
-               if (islcp || flag_time == 0 ||
-                   time_after_eq(jiffies, ap->last_xmit + flag_time))
-                       *buf++ = PPP_FLAG;
-               ap->last_xmit = jiffies;
-               fcs = PPP_INITFCS;
-
-               /*
-                * Put in the address/control bytes if necessary
-                */
-               if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
-                       PUT_BYTE(ap, buf, 0xff, islcp);
-                       fcs = PPP_FCS(fcs, 0xff);
-                       PUT_BYTE(ap, buf, 0x03, islcp);
-                       fcs = PPP_FCS(fcs, 0x03);
-               }
-       }
-
-       /*
-        * Once we put in the last byte, we need to put in the FCS
-        * and closing flag, so make sure there is at least 7 bytes
-        * of free space in the output buffer.
-        */
-       buflim = ap->obuf + OBUFSIZE - 6;
-       while (i < count && buf < buflim) {
-               c = data[i++];
-               if (i == 1 && c == 0 && (ap->flags & SC_COMP_PROT))
-                       continue;       /* compress protocol field */
-               fcs = PPP_FCS(fcs, c);
-               PUT_BYTE(ap, buf, c, islcp);
-       }
-
-       if (i < count) {
-               /*
-                * Remember where we are up to in this packet.
-                */
-               ap->olim = buf;
-               ap->tpkt_pos = i;
-               ap->tfcs = fcs;
-               return 0;
-       }
-
-       /*
-        * We have finished the packet.  Add the FCS and flag.
-        */
-       fcs = ~fcs;
-       c = fcs & 0xff;
-       PUT_BYTE(ap, buf, c, islcp);
-       c = (fcs >> 8) & 0xff;
-       PUT_BYTE(ap, buf, c, islcp);
-       *buf++ = PPP_FLAG;
-       ap->olim = buf;
-
-       kfree_skb(ap->tpkt);
-       ap->tpkt = NULL;
-       return 1;
-}
-
-/*
- * Transmit-side routines.
- */
-
-/*
- * Send a packet to the peer over an async tty line.
- * Returns 1 iff the packet was accepted.
- * If the packet was not accepted, we will call ppp_output_wakeup
- * at some later time.
- */
-static int
-ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb)
-{
-       struct asyncppp *ap = chan->private;
-
-       ppp_async_push(ap);
-
-       if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
-               return 0;       /* already full */
-       ap->tpkt = skb;
-       ap->tpkt_pos = 0;
-
-       ppp_async_push(ap);
-       return 1;
-}
-
-/*
- * Push as much data as possible out to the tty.
- */
-static int
-ppp_async_push(struct asyncppp *ap)
-{
-       int avail, sent, done = 0;
-       struct tty_struct *tty = ap->tty;
-       int tty_stuffed = 0;
-
-       /*
-        * We can get called recursively here if the tty write
-        * function calls our wakeup function.  This can happen
-        * for example on a pty with both the master and slave
-        * set to PPP line discipline.
-        * We use the XMIT_BUSY bit to detect this and get out,
-        * leaving the XMIT_WAKEUP bit set to tell the other
-        * instance that it may now be able to write more now.
-        */
-       if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags))
-               return 0;
-       spin_lock_bh(&ap->xmit_lock);
-       for (;;) {
-               if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
-                       tty_stuffed = 0;
-               if (!tty_stuffed && ap->optr < ap->olim) {
-                       avail = ap->olim - ap->optr;
-                       set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-                       sent = tty->ops->write(tty, ap->optr, avail);
-                       if (sent < 0)
-                               goto flush;     /* error, e.g. loss of CD */
-                       ap->optr += sent;
-                       if (sent < avail)
-                               tty_stuffed = 1;
-                       continue;
-               }
-               if (ap->optr >= ap->olim && ap->tpkt) {
-                       if (ppp_async_encode(ap)) {
-                               /* finished processing ap->tpkt */
-                               clear_bit(XMIT_FULL, &ap->xmit_flags);
-                               done = 1;
-                       }
-                       continue;
-               }
-               /*
-                * We haven't made any progress this time around.
-                * Clear XMIT_BUSY to let other callers in, but
-                * after doing so we have to check if anyone set
-                * XMIT_WAKEUP since we last checked it.  If they
-                * did, we should try again to set XMIT_BUSY and go
-                * around again in case XMIT_BUSY was still set when
-                * the other caller tried.
-                */
-               clear_bit(XMIT_BUSY, &ap->xmit_flags);
-               /* any more work to do? if not, exit the loop */
-               if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
-                     (!tty_stuffed && ap->tpkt)))
-                       break;
-               /* more work to do, see if we can do it now */
-               if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags))
-                       break;
-       }
-       spin_unlock_bh(&ap->xmit_lock);
-       return done;
-
-flush:
-       clear_bit(XMIT_BUSY, &ap->xmit_flags);
-       if (ap->tpkt) {
-               kfree_skb(ap->tpkt);
-               ap->tpkt = NULL;
-               clear_bit(XMIT_FULL, &ap->xmit_flags);
-               done = 1;
-       }
-       ap->optr = ap->olim;
-       spin_unlock_bh(&ap->xmit_lock);
-       return done;
-}
-
-/*
- * Flush output from our internal buffers.
- * Called for the TCFLSH ioctl. Can be entered in parallel
- * but this is covered by the xmit_lock.
- */
-static void
-ppp_async_flush_output(struct asyncppp *ap)
-{
-       int done = 0;
-
-       spin_lock_bh(&ap->xmit_lock);
-       ap->optr = ap->olim;
-       if (ap->tpkt != NULL) {
-               kfree_skb(ap->tpkt);
-               ap->tpkt = NULL;
-               clear_bit(XMIT_FULL, &ap->xmit_flags);
-               done = 1;
-       }
-       spin_unlock_bh(&ap->xmit_lock);
-       if (done)
-               ppp_output_wakeup(&ap->chan);
-}
-
-/*
- * Receive-side routines.
- */
-
-/* see how many ordinary chars there are at the start of buf */
-static inline int
-scan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count)
-{
-       int i, c;
-
-       for (i = 0; i < count; ++i) {
-               c = buf[i];
-               if (c == PPP_ESCAPE || c == PPP_FLAG ||
-                   (c < 0x20 && (ap->raccm & (1 << c)) != 0))
-                       break;
-       }
-       return i;
-}
-
-/* called when a flag is seen - do end-of-packet processing */
-static void
-process_input_packet(struct asyncppp *ap)
-{
-       struct sk_buff *skb;
-       unsigned char *p;
-       unsigned int len, fcs, proto;
-
-       skb = ap->rpkt;
-       if (ap->state & (SC_TOSS | SC_ESCAPE))
-               goto err;
-
-       if (skb == NULL)
-               return;         /* 0-length packet */
-
-       /* check the FCS */
-       p = skb->data;
-       len = skb->len;
-       if (len < 3)
-               goto err;       /* too short */
-       fcs = PPP_INITFCS;
-       for (; len > 0; --len)
-               fcs = PPP_FCS(fcs, *p++);
-       if (fcs != PPP_GOODFCS)
-               goto err;       /* bad FCS */
-       skb_trim(skb, skb->len - 2);
-
-       /* check for address/control and protocol compression */
-       p = skb->data;
-       if (p[0] == PPP_ALLSTATIONS) {
-               /* chop off address/control */
-               if (p[1] != PPP_UI || skb->len < 3)
-                       goto err;
-               p = skb_pull(skb, 2);
-       }
-       proto = p[0];
-       if (proto & 1) {
-               /* protocol is compressed */
-               skb_push(skb, 1)[0] = 0;
-       } else {
-               if (skb->len < 2)
-                       goto err;
-               proto = (proto << 8) + p[1];
-               if (proto == PPP_LCP)
-                       async_lcp_peek(ap, p, skb->len, 1);
-       }
-
-       /* queue the frame to be processed */
-       skb->cb[0] = ap->state;
-       skb_queue_tail(&ap->rqueue, skb);
-       ap->rpkt = NULL;
-       ap->state = 0;
-       return;
-
- err:
-       /* frame had an error, remember that, reset SC_TOSS & SC_ESCAPE */
-       ap->state = SC_PREV_ERROR;
-       if (skb) {
-               /* make skb appear as freshly allocated */
-               skb_trim(skb, 0);
-               skb_reserve(skb, - skb_headroom(skb));
-       }
-}
-
-/* Called when the tty driver has data for us. Runs parallel with the
-   other ldisc functions but will not be re-entered */
-
-static void
-ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
-               char *flags, int count)
-{
-       struct sk_buff *skb;
-       int c, i, j, n, s, f;
-       unsigned char *sp;
-
-       /* update bits used for 8-bit cleanness detection */
-       if (~ap->rbits & SC_RCV_BITS) {
-               s = 0;
-               for (i = 0; i < count; ++i) {
-                       c = buf[i];
-                       if (flags && flags[i] != 0)
-                               continue;
-                       s |= (c & 0x80)? SC_RCV_B7_1: SC_RCV_B7_0;
-                       c = ((c >> 4) ^ c) & 0xf;
-                       s |= (0x6996 & (1 << c))? SC_RCV_ODDP: SC_RCV_EVNP;
-               }
-               ap->rbits |= s;
-       }
-
-       while (count > 0) {
-               /* scan through and see how many chars we can do in bulk */
-               if ((ap->state & SC_ESCAPE) && buf[0] == PPP_ESCAPE)
-                       n = 1;
-               else
-                       n = scan_ordinary(ap, buf, count);
-
-               f = 0;
-               if (flags && (ap->state & SC_TOSS) == 0) {
-                       /* check the flags to see if any char had an error */
-                       for (j = 0; j < n; ++j)
-                               if ((f = flags[j]) != 0)
-                                       break;
-               }
-               if (f != 0) {
-                       /* start tossing */
-                       ap->state |= SC_TOSS;
-
-               } else if (n > 0 && (ap->state & SC_TOSS) == 0) {
-                       /* stuff the chars in the skb */
-                       skb = ap->rpkt;
-                       if (!skb) {
-                               skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2);
-                               if (!skb)
-                                       goto nomem;
-                               ap->rpkt = skb;
-                       }
-                       if (skb->len == 0) {
-                               /* Try to get the payload 4-byte aligned.
-                                * This should match the
-                                * PPP_ALLSTATIONS/PPP_UI/compressed tests in
-                                * process_input_packet, but we do not have
-                                * enough chars here to test buf[1] and buf[2].
-                                */
-                               if (buf[0] != PPP_ALLSTATIONS)
-                                       skb_reserve(skb, 2 + (buf[0] & 1));
-                       }
-                       if (n > skb_tailroom(skb)) {
-                               /* packet overflowed MRU */
-                               ap->state |= SC_TOSS;
-                       } else {
-                               sp = skb_put(skb, n);
-                               memcpy(sp, buf, n);
-                               if (ap->state & SC_ESCAPE) {
-                                       sp[0] ^= PPP_TRANS;
-                                       ap->state &= ~SC_ESCAPE;
-                               }
-                       }
-               }
-
-               if (n >= count)
-                       break;
-
-               c = buf[n];
-               if (flags != NULL && flags[n] != 0) {
-                       ap->state |= SC_TOSS;
-               } else if (c == PPP_FLAG) {
-                       process_input_packet(ap);
-               } else if (c == PPP_ESCAPE) {
-                       ap->state |= SC_ESCAPE;
-               } else if (I_IXON(ap->tty)) {
-                       if (c == START_CHAR(ap->tty))
-                               start_tty(ap->tty);
-                       else if (c == STOP_CHAR(ap->tty))
-                               stop_tty(ap->tty);
-               }
-               /* otherwise it's a char in the recv ACCM */
-               ++n;
-
-               buf += n;
-               if (flags)
-                       flags += n;
-               count -= n;
-       }
-       return;
-
- nomem:
-       printk(KERN_ERR "PPPasync: no memory (input pkt)\n");
-       ap->state |= SC_TOSS;
-}
-
-/*
- * We look at LCP frames going past so that we can notice
- * and react to the LCP configure-ack from the peer.
- * In the situation where the peer has been sent a configure-ack
- * already, LCP is up once it has sent its configure-ack
- * so the immediately following packet can be sent with the
- * configured LCP options.  This allows us to process the following
- * packet correctly without pppd needing to respond quickly.
- *
- * We only respond to the received configure-ack if we have just
- * sent a configure-request, and the configure-ack contains the
- * same data (this is checked using a 16-bit crc of the data).
- */
-#define CONFREQ                1       /* LCP code field values */
-#define CONFACK                2
-#define LCP_MRU                1       /* LCP option numbers */
-#define LCP_ASYNCMAP   2
-
-static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
-                          int len, int inbound)
-{
-       int dlen, fcs, i, code;
-       u32 val;
-
-       data += 2;              /* skip protocol bytes */
-       len -= 2;
-       if (len < 4)            /* 4 = code, ID, length */
-               return;
-       code = data[0];
-       if (code != CONFACK && code != CONFREQ)
-               return;
-       dlen = get_unaligned_be16(data + 2);
-       if (len < dlen)
-               return;         /* packet got truncated or length is bogus */
-
-       if (code == (inbound? CONFACK: CONFREQ)) {
-               /*
-                * sent confreq or received confack:
-                * calculate the crc of the data from the ID field on.
-                */
-               fcs = PPP_INITFCS;
-               for (i = 1; i < dlen; ++i)
-                       fcs = PPP_FCS(fcs, data[i]);
-
-               if (!inbound) {
-                       /* outbound confreq - remember the crc for later */
-                       ap->lcp_fcs = fcs;
-                       return;
-               }
-
-               /* received confack, check the crc */
-               fcs ^= ap->lcp_fcs;
-               ap->lcp_fcs = -1;
-               if (fcs != 0)
-                       return;
-       } else if (inbound)
-               return; /* not interested in received confreq */
-
-       /* process the options in the confack */
-       data += 4;
-       dlen -= 4;
-       /* data[0] is code, data[1] is length */
-       while (dlen >= 2 && dlen >= data[1] && data[1] >= 2) {
-               switch (data[0]) {
-               case LCP_MRU:
-                       val = get_unaligned_be16(data + 2);
-                       if (inbound)
-                               ap->mru = val;
-                       else
-                               ap->chan.mtu = val;
-                       break;
-               case LCP_ASYNCMAP:
-                       val = get_unaligned_be32(data + 2);
-                       if (inbound)
-                               ap->raccm = val;
-                       else
-                               ap->xaccm[0] = val;
-                       break;
-               }
-               dlen -= data[1];
-               data += data[1];
-       }
-}
-
-static void __exit ppp_async_cleanup(void)
-{
-       if (tty_unregister_ldisc(N_PPP) != 0)
-               printk(KERN_ERR "failed to unregister PPP line discipline\n");
-}
-
-module_init(ppp_async_init);
-module_exit(ppp_async_cleanup);
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
deleted file mode 100644 (file)
index 1dbdf82..0000000
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- *  ==FILEVERSION 980319==
- *
- * ppp_deflate.c - interface the zlib procedures for Deflate compression
- * and decompression (as used by gzip) to the PPP code.
- * This version is for use with Linux kernel 1.3.X.
- *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
- *
- * From: deflate.c,v 1.1 1996/01/18 03:17:48 paulus Exp
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/string.h>
-
-#include <linux/ppp_defs.h>
-#include <linux/ppp-comp.h>
-
-#include <linux/zlib.h>
-#include <asm/unaligned.h>
-
-/*
- * State for a Deflate (de)compressor.
- */
-struct ppp_deflate_state {
-    int                seqno;
-    int                w_size;
-    int                unit;
-    int                mru;
-    int                debug;
-    z_stream   strm;
-    struct compstat stats;
-};
-
-#define DEFLATE_OVHD   2               /* Deflate overhead/packet */
-
-static void    *z_comp_alloc(unsigned char *options, int opt_len);
-static void    *z_decomp_alloc(unsigned char *options, int opt_len);
-static void    z_comp_free(void *state);
-static void    z_decomp_free(void *state);
-static int     z_comp_init(void *state, unsigned char *options,
-                                int opt_len,
-                                int unit, int hdrlen, int debug);
-static int     z_decomp_init(void *state, unsigned char *options,
-                                  int opt_len,
-                                  int unit, int hdrlen, int mru, int debug);
-static int     z_compress(void *state, unsigned char *rptr,
-                               unsigned char *obuf,
-                               int isize, int osize);
-static void    z_incomp(void *state, unsigned char *ibuf, int icnt);
-static int     z_decompress(void *state, unsigned char *ibuf,
-                               int isize, unsigned char *obuf, int osize);
-static void    z_comp_reset(void *state);
-static void    z_decomp_reset(void *state);
-static void    z_comp_stats(void *state, struct compstat *stats);
-
-/**
- *     z_comp_free - free the memory used by a compressor
- *     @arg:   pointer to the private state for the compressor.
- */
-static void z_comp_free(void *arg)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       if (state) {
-               zlib_deflateEnd(&state->strm);
-               vfree(state->strm.workspace);
-               kfree(state);
-       }
-}
-
-/**
- *     z_comp_alloc - allocate space for a compressor.
- *     @options: pointer to CCP option data
- *     @opt_len: length of the CCP option at @options.
- *
- *     The @options pointer points to the a buffer containing the
- *     CCP option data for the compression being negotiated.  It is
- *     formatted according to RFC1979, and describes the window
- *     size that the peer is requesting that we use in compressing
- *     data to be sent to it.
- *
- *     Returns the pointer to the private state for the compressor,
- *     or NULL if we could not allocate enough memory.
- */
-static void *z_comp_alloc(unsigned char *options, int opt_len)
-{
-       struct ppp_deflate_state *state;
-       int w_size;
-
-       if (opt_len != CILEN_DEFLATE ||
-           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
-           options[1] != CILEN_DEFLATE ||
-           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
-           options[3] != DEFLATE_CHK_SEQUENCE)
-               return NULL;
-       w_size = DEFLATE_SIZE(options[2]);
-       if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
-               return NULL;
-
-       state = kzalloc(sizeof(*state),
-                                                    GFP_KERNEL);
-       if (state == NULL)
-               return NULL;
-
-       state->strm.next_in   = NULL;
-       state->w_size         = w_size;
-       state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8));
-       if (state->strm.workspace == NULL)
-               goto out_free;
-
-       if (zlib_deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION,
-                        DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY)
-           != Z_OK)
-               goto out_free;
-       return (void *) state;
-
-out_free:
-       z_comp_free(state);
-       return NULL;
-}
-
-/**
- *     z_comp_init - initialize a previously-allocated compressor.
- *     @arg:   pointer to the private state for the compressor
- *     @options: pointer to the CCP option data describing the
- *             compression that was negotiated with the peer
- *     @opt_len: length of the CCP option data at @options
- *     @unit:  PPP unit number for diagnostic messages
- *     @hdrlen: ignored (present for backwards compatibility)
- *     @debug: debug flag; if non-zero, debug messages are printed.
- *
- *     The CCP options described by @options must match the options
- *     specified when the compressor was allocated.  The compressor
- *     history is reset.  Returns 0 for failure (CCP options don't
- *     match) or 1 for success.
- */
-static int z_comp_init(void *arg, unsigned char *options, int opt_len,
-                      int unit, int hdrlen, int debug)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       if (opt_len < CILEN_DEFLATE ||
-           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
-           options[1] != CILEN_DEFLATE ||
-           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
-           DEFLATE_SIZE(options[2]) != state->w_size ||
-           options[3] != DEFLATE_CHK_SEQUENCE)
-               return 0;
-
-       state->seqno = 0;
-       state->unit  = unit;
-       state->debug = debug;
-
-       zlib_deflateReset(&state->strm);
-
-       return 1;
-}
-
-/**
- *     z_comp_reset - reset a previously-allocated compressor.
- *     @arg:   pointer to private state for the compressor.
- *
- *     This clears the history for the compressor and makes it
- *     ready to start emitting a new compressed stream.
- */
-static void z_comp_reset(void *arg)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       state->seqno = 0;
-       zlib_deflateReset(&state->strm);
-}
-
-/**
- *     z_compress - compress a PPP packet with Deflate compression.
- *     @arg:   pointer to private state for the compressor
- *     @rptr:  uncompressed packet (input)
- *     @obuf:  compressed packet (output)
- *     @isize: size of uncompressed packet
- *     @osize: space available at @obuf
- *
- *     Returns the length of the compressed packet, or 0 if the
- *     packet is incompressible.
- */
-static int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf,
-              int isize, int osize)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-       int r, proto, off, olen, oavail;
-       unsigned char *wptr;
-
-       /*
-        * Check that the protocol is in the range we handle.
-        */
-       proto = PPP_PROTOCOL(rptr);
-       if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
-               return 0;
-
-       /* Don't generate compressed packets which are larger than
-          the uncompressed packet. */
-       if (osize > isize)
-               osize = isize;
-
-       wptr = obuf;
-
-       /*
-        * Copy over the PPP header and store the 2-byte sequence number.
-        */
-       wptr[0] = PPP_ADDRESS(rptr);
-       wptr[1] = PPP_CONTROL(rptr);
-       put_unaligned_be16(PPP_COMP, wptr + 2);
-       wptr += PPP_HDRLEN;
-       put_unaligned_be16(state->seqno, wptr);
-       wptr += DEFLATE_OVHD;
-       olen = PPP_HDRLEN + DEFLATE_OVHD;
-       state->strm.next_out = wptr;
-       state->strm.avail_out = oavail = osize - olen;
-       ++state->seqno;
-
-       off = (proto > 0xff) ? 2 : 3;   /* skip 1st proto byte if 0 */
-       rptr += off;
-       state->strm.next_in = rptr;
-       state->strm.avail_in = (isize - off);
-
-       for (;;) {
-               r = zlib_deflate(&state->strm, Z_PACKET_FLUSH);
-               if (r != Z_OK) {
-                       if (state->debug)
-                               printk(KERN_ERR
-                                      "z_compress: deflate returned %d\n", r);
-                       break;
-               }
-               if (state->strm.avail_out == 0) {
-                       olen += oavail;
-                       state->strm.next_out = NULL;
-                       state->strm.avail_out = oavail = 1000000;
-               } else {
-                       break;          /* all done */
-               }
-       }
-       olen += oavail - state->strm.avail_out;
-
-       /*
-        * See if we managed to reduce the size of the packet.
-        */
-       if (olen < isize) {
-               state->stats.comp_bytes += olen;
-               state->stats.comp_packets++;
-       } else {
-               state->stats.inc_bytes += isize;
-               state->stats.inc_packets++;
-               olen = 0;
-       }
-       state->stats.unc_bytes += isize;
-       state->stats.unc_packets++;
-
-       return olen;
-}
-
-/**
- *     z_comp_stats - return compression statistics for a compressor
- *             or decompressor.
- *     @arg:   pointer to private space for the (de)compressor
- *     @stats: pointer to a struct compstat to receive the result.
- */
-static void z_comp_stats(void *arg, struct compstat *stats)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       *stats = state->stats;
-}
-
-/**
- *     z_decomp_free - Free the memory used by a decompressor.
- *     @arg:   pointer to private space for the decompressor.
- */
-static void z_decomp_free(void *arg)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       if (state) {
-               zlib_inflateEnd(&state->strm);
-               vfree(state->strm.workspace);
-               kfree(state);
-       }
-}
-
-/**
- *     z_decomp_alloc - allocate space for a decompressor.
- *     @options: pointer to CCP option data
- *     @opt_len: length of the CCP option at @options.
- *
- *     The @options pointer points to the a buffer containing the
- *     CCP option data for the compression being negotiated.  It is
- *     formatted according to RFC1979, and describes the window
- *     size that we are requesting the peer to use in compressing
- *     data to be sent to us.
- *
- *     Returns the pointer to the private state for the decompressor,
- *     or NULL if we could not allocate enough memory.
- */
-static void *z_decomp_alloc(unsigned char *options, int opt_len)
-{
-       struct ppp_deflate_state *state;
-       int w_size;
-
-       if (opt_len != CILEN_DEFLATE ||
-           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
-           options[1] != CILEN_DEFLATE ||
-           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
-           options[3] != DEFLATE_CHK_SEQUENCE)
-               return NULL;
-       w_size = DEFLATE_SIZE(options[2]);
-       if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
-               return NULL;
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (state == NULL)
-               return NULL;
-
-       state->w_size         = w_size;
-       state->strm.next_out  = NULL;
-       state->strm.workspace = vmalloc(zlib_inflate_workspacesize());
-       if (state->strm.workspace == NULL)
-               goto out_free;
-
-       if (zlib_inflateInit2(&state->strm, -w_size) != Z_OK)
-               goto out_free;
-       return (void *) state;
-
-out_free:
-       z_decomp_free(state);
-       return NULL;
-}
-
-/**
- *     z_decomp_init - initialize a previously-allocated decompressor.
- *     @arg:   pointer to the private state for the decompressor
- *     @options: pointer to the CCP option data describing the
- *             compression that was negotiated with the peer
- *     @opt_len: length of the CCP option data at @options
- *     @unit:  PPP unit number for diagnostic messages
- *     @hdrlen: ignored (present for backwards compatibility)
- *     @mru:   maximum length of decompressed packets
- *     @debug: debug flag; if non-zero, debug messages are printed.
- *
- *     The CCP options described by @options must match the options
- *     specified when the decompressor was allocated.  The decompressor
- *     history is reset.  Returns 0 for failure (CCP options don't
- *     match) or 1 for success.
- */
-static int z_decomp_init(void *arg, unsigned char *options, int opt_len,
-                        int unit, int hdrlen, int mru, int debug)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       if (opt_len < CILEN_DEFLATE ||
-           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
-           options[1] != CILEN_DEFLATE ||
-           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
-           DEFLATE_SIZE(options[2]) != state->w_size ||
-           options[3] != DEFLATE_CHK_SEQUENCE)
-               return 0;
-
-       state->seqno = 0;
-       state->unit  = unit;
-       state->debug = debug;
-       state->mru   = mru;
-
-       zlib_inflateReset(&state->strm);
-
-       return 1;
-}
-
-/**
- *     z_decomp_reset - reset a previously-allocated decompressor.
- *     @arg:   pointer to private state for the decompressor.
- *
- *     This clears the history for the decompressor and makes it
- *     ready to receive a new compressed stream.
- */
-static void z_decomp_reset(void *arg)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-
-       state->seqno = 0;
-       zlib_inflateReset(&state->strm);
-}
-
-/**
- *     z_decompress - decompress a Deflate-compressed packet.
- *     @arg:   pointer to private state for the decompressor
- *     @ibuf:  pointer to input (compressed) packet data
- *     @isize: length of input packet
- *     @obuf:  pointer to space for output (decompressed) packet
- *     @osize: amount of space available at @obuf
- *
- * Because of patent problems, we return DECOMP_ERROR for errors
- * found by inspecting the input data and for system problems, but
- * DECOMP_FATALERROR for any errors which could possibly be said to
- * be being detected "after" decompression.  For DECOMP_ERROR,
- * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
- * infringing a patent of Motorola's if we do, so we take CCP down
- * instead.
- *
- * Given that the frame has the correct sequence number and a good FCS,
- * errors such as invalid codes in the input most likely indicate a
- * bug, so we return DECOMP_FATALERROR for them in order to turn off
- * compression, even though they are detected by inspecting the input.
- */
-static int z_decompress(void *arg, unsigned char *ibuf, int isize,
-                unsigned char *obuf, int osize)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-       int olen, seq, r;
-       int decode_proto, overflow;
-       unsigned char overflow_buf[1];
-
-       if (isize <= PPP_HDRLEN + DEFLATE_OVHD) {
-               if (state->debug)
-                       printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n",
-                              state->unit, isize);
-               return DECOMP_ERROR;
-       }
-
-       /* Check the sequence number. */
-       seq = get_unaligned_be16(ibuf + PPP_HDRLEN);
-       if (seq != (state->seqno & 0xffff)) {
-               if (state->debug)
-                       printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
-                              state->unit, seq, state->seqno & 0xffff);
-               return DECOMP_ERROR;
-       }
-       ++state->seqno;
-
-       /*
-        * Fill in the first part of the PPP header.  The protocol field
-        * comes from the decompressed data.
-        */
-       obuf[0] = PPP_ADDRESS(ibuf);
-       obuf[1] = PPP_CONTROL(ibuf);
-       obuf[2] = 0;
-
-       /*
-        * Set up to call inflate.  We set avail_out to 1 initially so we can
-        * look at the first byte of the output and decide whether we have
-        * a 1-byte or 2-byte protocol field.
-        */
-       state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD;
-       state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD);
-       state->strm.next_out = obuf + 3;
-       state->strm.avail_out = 1;
-       decode_proto = 1;
-       overflow = 0;
-
-       /*
-        * Call inflate, supplying more input or output as needed.
-        */
-       for (;;) {
-               r = zlib_inflate(&state->strm, Z_PACKET_FLUSH);
-               if (r != Z_OK) {
-                       if (state->debug)
-                               printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n",
-                                      state->unit, r, (state->strm.msg? state->strm.msg: ""));
-                       return DECOMP_FATALERROR;
-               }
-               if (state->strm.avail_out != 0)
-                       break;          /* all done */
-               if (decode_proto) {
-                       state->strm.avail_out = osize - PPP_HDRLEN;
-                       if ((obuf[3] & 1) == 0) {
-                               /* 2-byte protocol field */
-                               obuf[2] = obuf[3];
-                               --state->strm.next_out;
-                               ++state->strm.avail_out;
-                       }
-                       decode_proto = 0;
-               } else if (!overflow) {
-                       /*
-                        * We've filled up the output buffer; the only way to
-                        * find out whether inflate has any more characters
-                        * left is to give it another byte of output space.
-                        */
-                       state->strm.next_out = overflow_buf;
-                       state->strm.avail_out = 1;
-                       overflow = 1;
-               } else {
-                       if (state->debug)
-                               printk(KERN_DEBUG "z_decompress%d: ran out of mru\n",
-                                      state->unit);
-                       return DECOMP_FATALERROR;
-               }
-       }
-
-       if (decode_proto) {
-               if (state->debug)
-                       printk(KERN_DEBUG "z_decompress%d: didn't get proto\n",
-                              state->unit);
-               return DECOMP_ERROR;
-       }
-
-       olen = osize + overflow - state->strm.avail_out;
-       state->stats.unc_bytes += olen;
-       state->stats.unc_packets++;
-       state->stats.comp_bytes += isize;
-       state->stats.comp_packets++;
-
-       return olen;
-}
-
-/**
- *     z_incomp - add incompressible input data to the history.
- *     @arg:   pointer to private state for the decompressor
- *     @ibuf:  pointer to input packet data
- *     @icnt:  length of input data.
- */
-static void z_incomp(void *arg, unsigned char *ibuf, int icnt)
-{
-       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-       int proto, r;
-
-       /*
-        * Check that the protocol is one we handle.
-        */
-       proto = PPP_PROTOCOL(ibuf);
-       if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
-               return;
-
-       ++state->seqno;
-
-       /*
-        * We start at the either the 1st or 2nd byte of the protocol field,
-        * depending on whether the protocol value is compressible.
-        */
-       state->strm.next_in = ibuf + 3;
-       state->strm.avail_in = icnt - 3;
-       if (proto > 0xff) {
-               --state->strm.next_in;
-               ++state->strm.avail_in;
-       }
-
-       r = zlib_inflateIncomp(&state->strm);
-       if (r != Z_OK) {
-               /* gak! */
-               if (state->debug) {
-                       printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n",
-                              state->unit, r, (state->strm.msg? state->strm.msg: ""));
-               }
-               return;
-       }
-
-       /*
-        * Update stats.
-        */
-       state->stats.inc_bytes += icnt;
-       state->stats.inc_packets++;
-       state->stats.unc_bytes += icnt;
-       state->stats.unc_packets++;
-}
-
-/*************************************************************
- * Module interface table
- *************************************************************/
-
-/* These are in ppp_generic.c */
-extern int  ppp_register_compressor   (struct compressor *cp);
-extern void ppp_unregister_compressor (struct compressor *cp);
-
-/*
- * Procedures exported to if_ppp.c.
- */
-static struct compressor ppp_deflate = {
-       .compress_proto =       CI_DEFLATE,
-       .comp_alloc =           z_comp_alloc,
-       .comp_free =            z_comp_free,
-       .comp_init =            z_comp_init,
-       .comp_reset =           z_comp_reset,
-       .compress =             z_compress,
-       .comp_stat =            z_comp_stats,
-       .decomp_alloc =         z_decomp_alloc,
-       .decomp_free =          z_decomp_free,
-       .decomp_init =          z_decomp_init,
-       .decomp_reset =         z_decomp_reset,
-       .decompress =           z_decompress,
-       .incomp =               z_incomp,
-       .decomp_stat =          z_comp_stats,
-       .owner =                THIS_MODULE
-};
-
-static struct compressor ppp_deflate_draft = {
-       .compress_proto =       CI_DEFLATE_DRAFT,
-       .comp_alloc =           z_comp_alloc,
-       .comp_free =            z_comp_free,
-       .comp_init =            z_comp_init,
-       .comp_reset =           z_comp_reset,
-       .compress =             z_compress,
-       .comp_stat =            z_comp_stats,
-       .decomp_alloc =         z_decomp_alloc,
-       .decomp_free =          z_decomp_free,
-       .decomp_init =          z_decomp_init,
-       .decomp_reset =         z_decomp_reset,
-       .decompress =           z_decompress,
-       .incomp =               z_incomp,
-       .decomp_stat =          z_comp_stats,
-       .owner =                THIS_MODULE
-};
-
-static int __init deflate_init(void)
-{
-        int answer = ppp_register_compressor(&ppp_deflate);
-        if (answer == 0)
-                printk(KERN_INFO
-                      "PPP Deflate Compression module registered\n");
-       ppp_register_compressor(&ppp_deflate_draft);
-        return answer;
-}
-
-static void __exit deflate_cleanup(void)
-{
-       ppp_unregister_compressor(&ppp_deflate);
-       ppp_unregister_compressor(&ppp_deflate_draft);
-}
-
-module_init(deflate_init);
-module_exit(deflate_cleanup);
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE));
-MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT));
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
deleted file mode 100644 (file)
index 10e5d98..0000000
+++ /dev/null
@@ -1,2954 +0,0 @@
-/*
- * Generic PPP layer for Linux.
- *
- * Copyright 1999-2002 Paul Mackerras.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- *
- * The generic PPP layer handles the PPP network interfaces, the
- * /dev/ppp device, packet and VJ compression, and multilink.
- * It talks to PPP `channels' via the interface defined in
- * include/linux/ppp_channel.h.  Channels provide the basic means for
- * sending and receiving PPP frames on some kind of communications
- * channel.
- *
- * Part of the code in this driver was inspired by the old async-only
- * PPP driver, written by Michael Callahan and Al Longyear, and
- * subsequently hacked by Paul Mackerras.
- *
- * ==FILEVERSION 20041108==
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/idr.h>
-#include <linux/netdevice.h>
-#include <linux/poll.h>
-#include <linux/ppp_defs.h>
-#include <linux/filter.h>
-#include <linux/if_ppp.h>
-#include <linux/ppp_channel.h>
-#include <linux/ppp-comp.h>
-#include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
-#include <linux/if_arp.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/spinlock.h>
-#include <linux/rwsem.h>
-#include <linux/stddef.h>
-#include <linux/device.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-#include <net/slhc_vj.h>
-#include <linux/atomic.h>
-
-#include <linux/nsproxy.h>
-#include <net/net_namespace.h>
-#include <net/netns/generic.h>
-
-#define PPP_VERSION    "2.4.2"
-
-/*
- * Network protocols we support.
- */
-#define NP_IP  0               /* Internet Protocol V4 */
-#define NP_IPV6        1               /* Internet Protocol V6 */
-#define NP_IPX 2               /* IPX protocol */
-#define NP_AT  3               /* Appletalk protocol */
-#define NP_MPLS_UC 4           /* MPLS unicast */
-#define NP_MPLS_MC 5           /* MPLS multicast */
-#define NUM_NP 6               /* Number of NPs. */
-
-#define MPHDRLEN       6       /* multilink protocol header length */
-#define MPHDRLEN_SSN   4       /* ditto with short sequence numbers */
-
-/*
- * An instance of /dev/ppp can be associated with either a ppp
- * interface unit or a ppp channel.  In both cases, file->private_data
- * points to one of these.
- */
-struct ppp_file {
-       enum {
-               INTERFACE=1, CHANNEL
-       }               kind;
-       struct sk_buff_head xq;         /* pppd transmit queue */
-       struct sk_buff_head rq;         /* receive queue for pppd */
-       wait_queue_head_t rwait;        /* for poll on reading /dev/ppp */
-       atomic_t        refcnt;         /* # refs (incl /dev/ppp attached) */
-       int             hdrlen;         /* space to leave for headers */
-       int             index;          /* interface unit / channel number */
-       int             dead;           /* unit/channel has been shut down */
-};
-
-#define PF_TO_X(pf, X)         container_of(pf, X, file)
-
-#define PF_TO_PPP(pf)          PF_TO_X(pf, struct ppp)
-#define PF_TO_CHANNEL(pf)      PF_TO_X(pf, struct channel)
-
-/*
- * Data structure describing one ppp unit.
- * A ppp unit corresponds to a ppp network interface device
- * and represents a multilink bundle.
- * It can have 0 or more ppp channels connected to it.
- */
-struct ppp {
-       struct ppp_file file;           /* stuff for read/write/poll 0 */
-       struct file     *owner;         /* file that owns this unit 48 */
-       struct list_head channels;      /* list of attached channels 4c */
-       int             n_channels;     /* how many channels are attached 54 */
-       spinlock_t      rlock;          /* lock for receive side 58 */
-       spinlock_t      wlock;          /* lock for transmit side 5c */
-       int             mru;            /* max receive unit 60 */
-       unsigned int    flags;          /* control bits 64 */
-       unsigned int    xstate;         /* transmit state bits 68 */
-       unsigned int    rstate;         /* receive state bits 6c */
-       int             debug;          /* debug flags 70 */
-       struct slcompress *vj;          /* state for VJ header compression */
-       enum NPmode     npmode[NUM_NP]; /* what to do with each net proto 78 */
-       struct sk_buff  *xmit_pending;  /* a packet ready to go out 88 */
-       struct compressor *xcomp;       /* transmit packet compressor 8c */
-       void            *xc_state;      /* its internal state 90 */
-       struct compressor *rcomp;       /* receive decompressor 94 */
-       void            *rc_state;      /* its internal state 98 */
-       unsigned long   last_xmit;      /* jiffies when last pkt sent 9c */
-       unsigned long   last_recv;      /* jiffies when last pkt rcvd a0 */
-       struct net_device *dev;         /* network interface device a4 */
-       int             closing;        /* is device closing down? a8 */
-#ifdef CONFIG_PPP_MULTILINK
-       int             nxchan;         /* next channel to send something on */
-       u32             nxseq;          /* next sequence number to send */
-       int             mrru;           /* MP: max reconst. receive unit */
-       u32             nextseq;        /* MP: seq no of next packet */
-       u32             minseq;         /* MP: min of most recent seqnos */
-       struct sk_buff_head mrq;        /* MP: receive reconstruction queue */
-#endif /* CONFIG_PPP_MULTILINK */
-#ifdef CONFIG_PPP_FILTER
-       struct sock_filter *pass_filter;        /* filter for packets to pass */
-       struct sock_filter *active_filter;/* filter for pkts to reset idle */
-       unsigned pass_len, active_len;
-#endif /* CONFIG_PPP_FILTER */
-       struct net      *ppp_net;       /* the net we belong to */
-};
-
-/*
- * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
- * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP,
- * SC_MUST_COMP
- * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
- * Bits in xstate: SC_COMP_RUN
- */
-#define SC_FLAG_BITS   (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
-                        |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \
-                        |SC_COMP_TCP|SC_REJ_COMP_TCP|SC_MUST_COMP)
-
-/*
- * Private data structure for each channel.
- * This includes the data structure used for multilink.
- */
-struct channel {
-       struct ppp_file file;           /* stuff for read/write/poll */
-       struct list_head list;          /* link in all/new_channels list */
-       struct ppp_channel *chan;       /* public channel data structure */
-       struct rw_semaphore chan_sem;   /* protects `chan' during chan ioctl */
-       spinlock_t      downl;          /* protects `chan', file.xq dequeue */
-       struct ppp      *ppp;           /* ppp unit we're connected to */
-       struct net      *chan_net;      /* the net channel belongs to */
-       struct list_head clist;         /* link in list of channels per unit */
-       rwlock_t        upl;            /* protects `ppp' */
-#ifdef CONFIG_PPP_MULTILINK
-       u8              avail;          /* flag used in multilink stuff */
-       u8              had_frag;       /* >= 1 fragments have been sent */
-       u32             lastseq;        /* MP: last sequence # received */
-       int             speed;          /* speed of the corresponding ppp channel*/
-#endif /* CONFIG_PPP_MULTILINK */
-};
-
-/*
- * SMP locking issues:
- * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
- * list and the ppp.n_channels field, you need to take both locks
- * before you modify them.
- * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
- * channel.downl.
- */
-
-static DEFINE_MUTEX(ppp_mutex);
-static atomic_t ppp_unit_count = ATOMIC_INIT(0);
-static atomic_t channel_count = ATOMIC_INIT(0);
-
-/* per-net private data for this module */
-static int ppp_net_id __read_mostly;
-struct ppp_net {
-       /* units to ppp mapping */
-       struct idr units_idr;
-
-       /*
-        * all_ppp_mutex protects the units_idr mapping.
-        * It also ensures that finding a ppp unit in the units_idr
-        * map and updating its file.refcnt field is atomic.
-        */
-       struct mutex all_ppp_mutex;
-
-       /* channels */
-       struct list_head all_channels;
-       struct list_head new_channels;
-       int last_channel_index;
-
-       /*
-        * all_channels_lock protects all_channels and
-        * last_channel_index, and the atomicity of find
-        * a channel and updating its file.refcnt field.
-        */
-       spinlock_t all_channels_lock;
-};
-
-/* Get the PPP protocol number from a skb */
-#define PPP_PROTO(skb) get_unaligned_be16((skb)->data)
-
-/* We limit the length of ppp->file.rq to this (arbitrary) value */
-#define PPP_MAX_RQLEN  32
-
-/*
- * Maximum number of multilink fragments queued up.
- * This has to be large enough to cope with the maximum latency of
- * the slowest channel relative to the others.  Strictly it should
- * depend on the number of channels and their characteristics.
- */
-#define PPP_MP_MAX_QLEN        128
-
-/* Multilink header bits. */
-#define B      0x80            /* this fragment begins a packet */
-#define E      0x40            /* this fragment ends a packet */
-
-/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
-#define seq_before(a, b)       ((s32)((a) - (b)) < 0)
-#define seq_after(a, b)                ((s32)((a) - (b)) > 0)
-
-/* Prototypes. */
-static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
-                       struct file *file, unsigned int cmd, unsigned long arg);
-static void ppp_xmit_process(struct ppp *ppp);
-static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
-static void ppp_push(struct ppp *ppp);
-static void ppp_channel_push(struct channel *pch);
-static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
-                             struct channel *pch);
-static void ppp_receive_error(struct ppp *ppp);
-static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
-static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
-                                           struct sk_buff *skb);
-#ifdef CONFIG_PPP_MULTILINK
-static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
-                               struct channel *pch);
-static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
-static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
-static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
-#endif /* CONFIG_PPP_MULTILINK */
-static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
-static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
-static void ppp_ccp_closed(struct ppp *ppp);
-static struct compressor *find_compressor(int type);
-static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
-static struct ppp *ppp_create_interface(struct net *net, int unit, int *retp);
-static void init_ppp_file(struct ppp_file *pf, int kind);
-static void ppp_shutdown_interface(struct ppp *ppp);
-static void ppp_destroy_interface(struct ppp *ppp);
-static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit);
-static struct channel *ppp_find_channel(struct ppp_net *pn, int unit);
-static int ppp_connect_channel(struct channel *pch, int unit);
-static int ppp_disconnect_channel(struct channel *pch);
-static void ppp_destroy_channel(struct channel *pch);
-static int unit_get(struct idr *p, void *ptr);
-static int unit_set(struct idr *p, void *ptr, int n);
-static void unit_put(struct idr *p, int n);
-static void *unit_find(struct idr *p, int n);
-
-static struct class *ppp_class;
-
-/* per net-namespace data */
-static inline struct ppp_net *ppp_pernet(struct net *net)
-{
-       BUG_ON(!net);
-
-       return net_generic(net, ppp_net_id);
-}
-
-/* Translates a PPP protocol number to a NP index (NP == network protocol) */
-static inline int proto_to_npindex(int proto)
-{
-       switch (proto) {
-       case PPP_IP:
-               return NP_IP;
-       case PPP_IPV6:
-               return NP_IPV6;
-       case PPP_IPX:
-               return NP_IPX;
-       case PPP_AT:
-               return NP_AT;
-       case PPP_MPLS_UC:
-               return NP_MPLS_UC;
-       case PPP_MPLS_MC:
-               return NP_MPLS_MC;
-       }
-       return -EINVAL;
-}
-
-/* Translates an NP index into a PPP protocol number */
-static const int npindex_to_proto[NUM_NP] = {
-       PPP_IP,
-       PPP_IPV6,
-       PPP_IPX,
-       PPP_AT,
-       PPP_MPLS_UC,
-       PPP_MPLS_MC,
-};
-
-/* Translates an ethertype into an NP index */
-static inline int ethertype_to_npindex(int ethertype)
-{
-       switch (ethertype) {
-       case ETH_P_IP:
-               return NP_IP;
-       case ETH_P_IPV6:
-               return NP_IPV6;
-       case ETH_P_IPX:
-               return NP_IPX;
-       case ETH_P_PPPTALK:
-       case ETH_P_ATALK:
-               return NP_AT;
-       case ETH_P_MPLS_UC:
-               return NP_MPLS_UC;
-       case ETH_P_MPLS_MC:
-               return NP_MPLS_MC;
-       }
-       return -1;
-}
-
-/* Translates an NP index into an ethertype */
-static const int npindex_to_ethertype[NUM_NP] = {
-       ETH_P_IP,
-       ETH_P_IPV6,
-       ETH_P_IPX,
-       ETH_P_PPPTALK,
-       ETH_P_MPLS_UC,
-       ETH_P_MPLS_MC,
-};
-
-/*
- * Locking shorthand.
- */
-#define ppp_xmit_lock(ppp)     spin_lock_bh(&(ppp)->wlock)
-#define ppp_xmit_unlock(ppp)   spin_unlock_bh(&(ppp)->wlock)
-#define ppp_recv_lock(ppp)     spin_lock_bh(&(ppp)->rlock)
-#define ppp_recv_unlock(ppp)   spin_unlock_bh(&(ppp)->rlock)
-#define ppp_lock(ppp)          do { ppp_xmit_lock(ppp); \
-                                    ppp_recv_lock(ppp); } while (0)
-#define ppp_unlock(ppp)                do { ppp_recv_unlock(ppp); \
-                                    ppp_xmit_unlock(ppp); } while (0)
-
-/*
- * /dev/ppp device routines.
- * The /dev/ppp device is used by pppd to control the ppp unit.
- * It supports the read, write, ioctl and poll functions.
- * Open instances of /dev/ppp can be in one of three states:
- * unattached, attached to a ppp unit, or attached to a ppp channel.
- */
-static int ppp_open(struct inode *inode, struct file *file)
-{
-       /*
-        * This could (should?) be enforced by the permissions on /dev/ppp.
-        */
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-       return 0;
-}
-
-static int ppp_release(struct inode *unused, struct file *file)
-{
-       struct ppp_file *pf = file->private_data;
-       struct ppp *ppp;
-
-       if (pf) {
-               file->private_data = NULL;
-               if (pf->kind == INTERFACE) {
-                       ppp = PF_TO_PPP(pf);
-                       if (file == ppp->owner)
-                               ppp_shutdown_interface(ppp);
-               }
-               if (atomic_dec_and_test(&pf->refcnt)) {
-                       switch (pf->kind) {
-                       case INTERFACE:
-                               ppp_destroy_interface(PF_TO_PPP(pf));
-                               break;
-                       case CHANNEL:
-                               ppp_destroy_channel(PF_TO_CHANNEL(pf));
-                               break;
-                       }
-               }
-       }
-       return 0;
-}
-
-static ssize_t ppp_read(struct file *file, char __user *buf,
-                       size_t count, loff_t *ppos)
-{
-       struct ppp_file *pf = file->private_data;
-       DECLARE_WAITQUEUE(wait, current);
-       ssize_t ret;
-       struct sk_buff *skb = NULL;
-       struct iovec iov;
-
-       ret = count;
-
-       if (!pf)
-               return -ENXIO;
-       add_wait_queue(&pf->rwait, &wait);
-       for (;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               skb = skb_dequeue(&pf->rq);
-               if (skb)
-                       break;
-               ret = 0;
-               if (pf->dead)
-                       break;
-               if (pf->kind == INTERFACE) {
-                       /*
-                        * Return 0 (EOF) on an interface that has no
-                        * channels connected, unless it is looping
-                        * network traffic (demand mode).
-                        */
-                       struct ppp *ppp = PF_TO_PPP(pf);
-                       if (ppp->n_channels == 0 &&
-                           (ppp->flags & SC_LOOP_TRAFFIC) == 0)
-                               break;
-               }
-               ret = -EAGAIN;
-               if (file->f_flags & O_NONBLOCK)
-                       break;
-               ret = -ERESTARTSYS;
-               if (signal_pending(current))
-                       break;
-               schedule();
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&pf->rwait, &wait);
-
-       if (!skb)
-               goto out;
-
-       ret = -EOVERFLOW;
-       if (skb->len > count)
-               goto outf;
-       ret = -EFAULT;
-       iov.iov_base = buf;
-       iov.iov_len = count;
-       if (skb_copy_datagram_iovec(skb, 0, &iov, skb->len))
-               goto outf;
-       ret = skb->len;
-
- outf:
-       kfree_skb(skb);
- out:
-       return ret;
-}
-
-static ssize_t ppp_write(struct file *file, const char __user *buf,
-                        size_t count, loff_t *ppos)
-{
-       struct ppp_file *pf = file->private_data;
-       struct sk_buff *skb;
-       ssize_t ret;
-
-       if (!pf)
-               return -ENXIO;
-       ret = -ENOMEM;
-       skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
-       if (!skb)
-               goto out;
-       skb_reserve(skb, pf->hdrlen);
-       ret = -EFAULT;
-       if (copy_from_user(skb_put(skb, count), buf, count)) {
-               kfree_skb(skb);
-               goto out;
-       }
-
-       skb_queue_tail(&pf->xq, skb);
-
-       switch (pf->kind) {
-       case INTERFACE:
-               ppp_xmit_process(PF_TO_PPP(pf));
-               break;
-       case CHANNEL:
-               ppp_channel_push(PF_TO_CHANNEL(pf));
-               break;
-       }
-
-       ret = count;
-
- out:
-       return ret;
-}
-
-/* No kernel lock - fine */
-static unsigned int ppp_poll(struct file *file, poll_table *wait)
-{
-       struct ppp_file *pf = file->private_data;
-       unsigned int mask;
-
-       if (!pf)
-               return 0;
-       poll_wait(file, &pf->rwait, wait);
-       mask = POLLOUT | POLLWRNORM;
-       if (skb_peek(&pf->rq))
-               mask |= POLLIN | POLLRDNORM;
-       if (pf->dead)
-               mask |= POLLHUP;
-       else if (pf->kind == INTERFACE) {
-               /* see comment in ppp_read */
-               struct ppp *ppp = PF_TO_PPP(pf);
-               if (ppp->n_channels == 0 &&
-                   (ppp->flags & SC_LOOP_TRAFFIC) == 0)
-                       mask |= POLLIN | POLLRDNORM;
-       }
-
-       return mask;
-}
-
-#ifdef CONFIG_PPP_FILTER
-static int get_filter(void __user *arg, struct sock_filter **p)
-{
-       struct sock_fprog uprog;
-       struct sock_filter *code = NULL;
-       int len, err;
-
-       if (copy_from_user(&uprog, arg, sizeof(uprog)))
-               return -EFAULT;
-
-       if (!uprog.len) {
-               *p = NULL;
-               return 0;
-       }
-
-       len = uprog.len * sizeof(struct sock_filter);
-       code = memdup_user(uprog.filter, len);
-       if (IS_ERR(code))
-               return PTR_ERR(code);
-
-       err = sk_chk_filter(code, uprog.len);
-       if (err) {
-               kfree(code);
-               return err;
-       }
-
-       *p = code;
-       return uprog.len;
-}
-#endif /* CONFIG_PPP_FILTER */
-
-static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       struct ppp_file *pf = file->private_data;
-       struct ppp *ppp;
-       int err = -EFAULT, val, val2, i;
-       struct ppp_idle idle;
-       struct npioctl npi;
-       int unit, cflags;
-       struct slcompress *vj;
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-
-       if (!pf)
-               return ppp_unattached_ioctl(current->nsproxy->net_ns,
-                                       pf, file, cmd, arg);
-
-       if (cmd == PPPIOCDETACH) {
-               /*
-                * We have to be careful here... if the file descriptor
-                * has been dup'd, we could have another process in the
-                * middle of a poll using the same file *, so we had
-                * better not free the interface data structures -
-                * instead we fail the ioctl.  Even in this case, we
-                * shut down the interface if we are the owner of it.
-                * Actually, we should get rid of PPPIOCDETACH, userland
-                * (i.e. pppd) could achieve the same effect by closing
-                * this fd and reopening /dev/ppp.
-                */
-               err = -EINVAL;
-               mutex_lock(&ppp_mutex);
-               if (pf->kind == INTERFACE) {
-                       ppp = PF_TO_PPP(pf);
-                       if (file == ppp->owner)
-                               ppp_shutdown_interface(ppp);
-               }
-               if (atomic_long_read(&file->f_count) <= 2) {
-                       ppp_release(NULL, file);
-                       err = 0;
-               } else
-                       pr_warn("PPPIOCDETACH file->f_count=%ld\n",
-                               atomic_long_read(&file->f_count));
-               mutex_unlock(&ppp_mutex);
-               return err;
-       }
-
-       if (pf->kind == CHANNEL) {
-               struct channel *pch;
-               struct ppp_channel *chan;
-
-               mutex_lock(&ppp_mutex);
-               pch = PF_TO_CHANNEL(pf);
-
-               switch (cmd) {
-               case PPPIOCCONNECT:
-                       if (get_user(unit, p))
-                               break;
-                       err = ppp_connect_channel(pch, unit);
-                       break;
-
-               case PPPIOCDISCONN:
-                       err = ppp_disconnect_channel(pch);
-                       break;
-
-               default:
-                       down_read(&pch->chan_sem);
-                       chan = pch->chan;
-                       err = -ENOTTY;
-                       if (chan && chan->ops->ioctl)
-                               err = chan->ops->ioctl(chan, cmd, arg);
-                       up_read(&pch->chan_sem);
-               }
-               mutex_unlock(&ppp_mutex);
-               return err;
-       }
-
-       if (pf->kind != INTERFACE) {
-               /* can't happen */
-               pr_err("PPP: not interface or channel??\n");
-               return -EINVAL;
-       }
-
-       mutex_lock(&ppp_mutex);
-       ppp = PF_TO_PPP(pf);
-       switch (cmd) {
-       case PPPIOCSMRU:
-               if (get_user(val, p))
-                       break;
-               ppp->mru = val;
-               err = 0;
-               break;
-
-       case PPPIOCSFLAGS:
-               if (get_user(val, p))
-                       break;
-               ppp_lock(ppp);
-               cflags = ppp->flags & ~val;
-               ppp->flags = val & SC_FLAG_BITS;
-               ppp_unlock(ppp);
-               if (cflags & SC_CCP_OPEN)
-                       ppp_ccp_closed(ppp);
-               err = 0;
-               break;
-
-       case PPPIOCGFLAGS:
-               val = ppp->flags | ppp->xstate | ppp->rstate;
-               if (put_user(val, p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCSCOMPRESS:
-               err = ppp_set_compress(ppp, arg);
-               break;
-
-       case PPPIOCGUNIT:
-               if (put_user(ppp->file.index, p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCSDEBUG:
-               if (get_user(val, p))
-                       break;
-               ppp->debug = val;
-               err = 0;
-               break;
-
-       case PPPIOCGDEBUG:
-               if (put_user(ppp->debug, p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGIDLE:
-               idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-               idle.recv_idle = (jiffies - ppp->last_recv) / HZ;
-               if (copy_to_user(argp, &idle, sizeof(idle)))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCSMAXCID:
-               if (get_user(val, p))
-                       break;
-               val2 = 15;
-               if ((val >> 16) != 0) {
-                       val2 = val >> 16;
-                       val &= 0xffff;
-               }
-               vj = slhc_init(val2+1, val+1);
-               if (!vj) {
-                       netdev_err(ppp->dev,
-                                  "PPP: no memory (VJ compressor)\n");
-                       err = -ENOMEM;
-                       break;
-               }
-               ppp_lock(ppp);
-               if (ppp->vj)
-                       slhc_free(ppp->vj);
-               ppp->vj = vj;
-               ppp_unlock(ppp);
-               err = 0;
-               break;
-
-       case PPPIOCGNPMODE:
-       case PPPIOCSNPMODE:
-               if (copy_from_user(&npi, argp, sizeof(npi)))
-                       break;
-               err = proto_to_npindex(npi.protocol);
-               if (err < 0)
-                       break;
-               i = err;
-               if (cmd == PPPIOCGNPMODE) {
-                       err = -EFAULT;
-                       npi.mode = ppp->npmode[i];
-                       if (copy_to_user(argp, &npi, sizeof(npi)))
-                               break;
-               } else {
-                       ppp->npmode[i] = npi.mode;
-                       /* we may be able to transmit more packets now (??) */
-                       netif_wake_queue(ppp->dev);
-               }
-               err = 0;
-               break;
-
-#ifdef CONFIG_PPP_FILTER
-       case PPPIOCSPASS:
-       {
-               struct sock_filter *code;
-               err = get_filter(argp, &code);
-               if (err >= 0) {
-                       ppp_lock(ppp);
-                       kfree(ppp->pass_filter);
-                       ppp->pass_filter = code;
-                       ppp->pass_len = err;
-                       ppp_unlock(ppp);
-                       err = 0;
-               }
-               break;
-       }
-       case PPPIOCSACTIVE:
-       {
-               struct sock_filter *code;
-               err = get_filter(argp, &code);
-               if (err >= 0) {
-                       ppp_lock(ppp);
-                       kfree(ppp->active_filter);
-                       ppp->active_filter = code;
-                       ppp->active_len = err;
-                       ppp_unlock(ppp);
-                       err = 0;
-               }
-               break;
-       }
-#endif /* CONFIG_PPP_FILTER */
-
-#ifdef CONFIG_PPP_MULTILINK
-       case PPPIOCSMRRU:
-               if (get_user(val, p))
-                       break;
-               ppp_recv_lock(ppp);
-               ppp->mrru = val;
-               ppp_recv_unlock(ppp);
-               err = 0;
-               break;
-#endif /* CONFIG_PPP_MULTILINK */
-
-       default:
-               err = -ENOTTY;
-       }
-       mutex_unlock(&ppp_mutex);
-       return err;
-}
-
-static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
-                       struct file *file, unsigned int cmd, unsigned long arg)
-{
-       int unit, err = -EFAULT;
-       struct ppp *ppp;
-       struct channel *chan;
-       struct ppp_net *pn;
-       int __user *p = (int __user *)arg;
-
-       mutex_lock(&ppp_mutex);
-       switch (cmd) {
-       case PPPIOCNEWUNIT:
-               /* Create a new ppp unit */
-               if (get_user(unit, p))
-                       break;
-               ppp = ppp_create_interface(net, unit, &err);
-               if (!ppp)
-                       break;
-               file->private_data = &ppp->file;
-               ppp->owner = file;
-               err = -EFAULT;
-               if (put_user(ppp->file.index, p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCATTACH:
-               /* Attach to an existing ppp unit */
-               if (get_user(unit, p))
-                       break;
-               err = -ENXIO;
-               pn = ppp_pernet(net);
-               mutex_lock(&pn->all_ppp_mutex);
-               ppp = ppp_find_unit(pn, unit);
-               if (ppp) {
-                       atomic_inc(&ppp->file.refcnt);
-                       file->private_data = &ppp->file;
-                       err = 0;
-               }
-               mutex_unlock(&pn->all_ppp_mutex);
-               break;
-
-       case PPPIOCATTCHAN:
-               if (get_user(unit, p))
-                       break;
-               err = -ENXIO;
-               pn = ppp_pernet(net);
-               spin_lock_bh(&pn->all_channels_lock);
-               chan = ppp_find_channel(pn, unit);
-               if (chan) {
-                       atomic_inc(&chan->file.refcnt);
-                       file->private_data = &chan->file;
-                       err = 0;
-               }
-               spin_unlock_bh(&pn->all_channels_lock);
-               break;
-
-       default:
-               err = -ENOTTY;
-       }
-       mutex_unlock(&ppp_mutex);
-       return err;
-}
-
-static const struct file_operations ppp_device_fops = {
-       .owner          = THIS_MODULE,
-       .read           = ppp_read,
-       .write          = ppp_write,
-       .poll           = ppp_poll,
-       .unlocked_ioctl = ppp_ioctl,
-       .open           = ppp_open,
-       .release        = ppp_release,
-       .llseek         = noop_llseek,
-};
-
-static __net_init int ppp_init_net(struct net *net)
-{
-       struct ppp_net *pn = net_generic(net, ppp_net_id);
-
-       idr_init(&pn->units_idr);
-       mutex_init(&pn->all_ppp_mutex);
-
-       INIT_LIST_HEAD(&pn->all_channels);
-       INIT_LIST_HEAD(&pn->new_channels);
-
-       spin_lock_init(&pn->all_channels_lock);
-
-       return 0;
-}
-
-static __net_exit void ppp_exit_net(struct net *net)
-{
-       struct ppp_net *pn = net_generic(net, ppp_net_id);
-
-       idr_destroy(&pn->units_idr);
-}
-
-static struct pernet_operations ppp_net_ops = {
-       .init = ppp_init_net,
-       .exit = ppp_exit_net,
-       .id   = &ppp_net_id,
-       .size = sizeof(struct ppp_net),
-};
-
-#define PPP_MAJOR      108
-
-/* Called at boot time if ppp is compiled into the kernel,
-   or at module load time (from init_module) if compiled as a module. */
-static int __init ppp_init(void)
-{
-       int err;
-
-       pr_info("PPP generic driver version " PPP_VERSION "\n");
-
-       err = register_pernet_device(&ppp_net_ops);
-       if (err) {
-               pr_err("failed to register PPP pernet device (%d)\n", err);
-               goto out;
-       }
-
-       err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
-       if (err) {
-               pr_err("failed to register PPP device (%d)\n", err);
-               goto out_net;
-       }
-
-       ppp_class = class_create(THIS_MODULE, "ppp");
-       if (IS_ERR(ppp_class)) {
-               err = PTR_ERR(ppp_class);
-               goto out_chrdev;
-       }
-
-       /* not a big deal if we fail here :-) */
-       device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
-
-       return 0;
-
-out_chrdev:
-       unregister_chrdev(PPP_MAJOR, "ppp");
-out_net:
-       unregister_pernet_device(&ppp_net_ops);
-out:
-       return err;
-}
-
-/*
- * Network interface unit routines.
- */
-static netdev_tx_t
-ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       struct ppp *ppp = netdev_priv(dev);
-       int npi, proto;
-       unsigned char *pp;
-
-       npi = ethertype_to_npindex(ntohs(skb->protocol));
-       if (npi < 0)
-               goto outf;
-
-       /* Drop, accept or reject the packet */
-       switch (ppp->npmode[npi]) {
-       case NPMODE_PASS:
-               break;
-       case NPMODE_QUEUE:
-               /* it would be nice to have a way to tell the network
-                  system to queue this one up for later. */
-               goto outf;
-       case NPMODE_DROP:
-       case NPMODE_ERROR:
-               goto outf;
-       }
-
-       /* Put the 2-byte PPP protocol number on the front,
-          making sure there is room for the address and control fields. */
-       if (skb_cow_head(skb, PPP_HDRLEN))
-               goto outf;
-
-       pp = skb_push(skb, 2);
-       proto = npindex_to_proto[npi];
-       put_unaligned_be16(proto, pp);
-
-       netif_stop_queue(dev);
-       skb_queue_tail(&ppp->file.xq, skb);
-       ppp_xmit_process(ppp);
-       return NETDEV_TX_OK;
-
- outf:
-       kfree_skb(skb);
-       ++dev->stats.tx_dropped;
-       return NETDEV_TX_OK;
-}
-
-static int
-ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-       struct ppp *ppp = netdev_priv(dev);
-       int err = -EFAULT;
-       void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data;
-       struct ppp_stats stats;
-       struct ppp_comp_stats cstats;
-       char *vers;
-
-       switch (cmd) {
-       case SIOCGPPPSTATS:
-               ppp_get_stats(ppp, &stats);
-               if (copy_to_user(addr, &stats, sizeof(stats)))
-                       break;
-               err = 0;
-               break;
-
-       case SIOCGPPPCSTATS:
-               memset(&cstats, 0, sizeof(cstats));
-               if (ppp->xc_state)
-                       ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c);
-               if (ppp->rc_state)
-                       ppp->rcomp->decomp_stat(ppp->rc_state, &cstats.d);
-               if (copy_to_user(addr, &cstats, sizeof(cstats)))
-                       break;
-               err = 0;
-               break;
-
-       case SIOCGPPPVER:
-               vers = PPP_VERSION;
-               if (copy_to_user(addr, vers, strlen(vers) + 1))
-                       break;
-               err = 0;
-               break;
-
-       default:
-               err = -EINVAL;
-       }
-
-       return err;
-}
-
-static const struct net_device_ops ppp_netdev_ops = {
-       .ndo_start_xmit = ppp_start_xmit,
-       .ndo_do_ioctl   = ppp_net_ioctl,
-};
-
-static void ppp_setup(struct net_device *dev)
-{
-       dev->netdev_ops = &ppp_netdev_ops;
-       dev->hard_header_len = PPP_HDRLEN;
-       dev->mtu = PPP_MTU;
-       dev->addr_len = 0;
-       dev->tx_queue_len = 3;
-       dev->type = ARPHRD_PPP;
-       dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
-       dev->features |= NETIF_F_NETNS_LOCAL;
-       dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
-}
-
-/*
- * Transmit-side routines.
- */
-
-/*
- * Called to do any work queued up on the transmit side
- * that can now be done.
- */
-static void
-ppp_xmit_process(struct ppp *ppp)
-{
-       struct sk_buff *skb;
-
-       ppp_xmit_lock(ppp);
-       if (!ppp->closing) {
-               ppp_push(ppp);
-               while (!ppp->xmit_pending &&
-                      (skb = skb_dequeue(&ppp->file.xq)))
-                       ppp_send_frame(ppp, skb);
-               /* If there's no work left to do, tell the core net
-                  code that we can accept some more. */
-               if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq))
-                       netif_wake_queue(ppp->dev);
-       }
-       ppp_xmit_unlock(ppp);
-}
-
-static inline struct sk_buff *
-pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
-{
-       struct sk_buff *new_skb;
-       int len;
-       int new_skb_size = ppp->dev->mtu +
-               ppp->xcomp->comp_extra + ppp->dev->hard_header_len;
-       int compressor_skb_size = ppp->dev->mtu +
-               ppp->xcomp->comp_extra + PPP_HDRLEN;
-       new_skb = alloc_skb(new_skb_size, GFP_ATOMIC);
-       if (!new_skb) {
-               if (net_ratelimit())
-                       netdev_err(ppp->dev, "PPP: no memory (comp pkt)\n");
-               return NULL;
-       }
-       if (ppp->dev->hard_header_len > PPP_HDRLEN)
-               skb_reserve(new_skb,
-                           ppp->dev->hard_header_len - PPP_HDRLEN);
-
-       /* compressor still expects A/C bytes in hdr */
-       len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2,
-                                  new_skb->data, skb->len + 2,
-                                  compressor_skb_size);
-       if (len > 0 && (ppp->flags & SC_CCP_UP)) {
-               kfree_skb(skb);
-               skb = new_skb;
-               skb_put(skb, len);
-               skb_pull(skb, 2);       /* pull off A/C bytes */
-       } else if (len == 0) {
-               /* didn't compress, or CCP not up yet */
-               kfree_skb(new_skb);
-               new_skb = skb;
-       } else {
-               /*
-                * (len < 0)
-                * MPPE requires that we do not send unencrypted
-                * frames.  The compressor will return -1 if we
-                * should drop the frame.  We cannot simply test
-                * the compress_proto because MPPE and MPPC share
-                * the same number.
-                */
-               if (net_ratelimit())
-                       netdev_err(ppp->dev, "ppp: compressor dropped pkt\n");
-               kfree_skb(skb);
-               kfree_skb(new_skb);
-               new_skb = NULL;
-       }
-       return new_skb;
-}
-
-/*
- * Compress and send a frame.
- * The caller should have locked the xmit path,
- * and xmit_pending should be 0.
- */
-static void
-ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
-{
-       int proto = PPP_PROTO(skb);
-       struct sk_buff *new_skb;
-       int len;
-       unsigned char *cp;
-
-       if (proto < 0x8000) {
-#ifdef CONFIG_PPP_FILTER
-               /* check if we should pass this packet */
-               /* the filter instructions are constructed assuming
-                  a four-byte PPP header on each packet */
-               *skb_push(skb, 2) = 1;
-               if (ppp->pass_filter &&
-                   sk_run_filter(skb, ppp->pass_filter) == 0) {
-                       if (ppp->debug & 1)
-                               netdev_printk(KERN_DEBUG, ppp->dev,
-                                             "PPP: outbound frame "
-                                             "not passed\n");
-                       kfree_skb(skb);
-                       return;
-               }
-               /* if this packet passes the active filter, record the time */
-               if (!(ppp->active_filter &&
-                     sk_run_filter(skb, ppp->active_filter) == 0))
-                       ppp->last_xmit = jiffies;
-               skb_pull(skb, 2);
-#else
-               /* for data packets, record the time */
-               ppp->last_xmit = jiffies;
-#endif /* CONFIG_PPP_FILTER */
-       }
-
-       ++ppp->dev->stats.tx_packets;
-       ppp->dev->stats.tx_bytes += skb->len - 2;
-
-       switch (proto) {
-       case PPP_IP:
-               if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0)
-                       break;
-               /* try to do VJ TCP header compression */
-               new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2,
-                                   GFP_ATOMIC);
-               if (!new_skb) {
-                       netdev_err(ppp->dev, "PPP: no memory (VJ comp pkt)\n");
-                       goto drop;
-               }
-               skb_reserve(new_skb, ppp->dev->hard_header_len - 2);
-               cp = skb->data + 2;
-               len = slhc_compress(ppp->vj, cp, skb->len - 2,
-                                   new_skb->data + 2, &cp,
-                                   !(ppp->flags & SC_NO_TCP_CCID));
-               if (cp == skb->data + 2) {
-                       /* didn't compress */
-                       kfree_skb(new_skb);
-               } else {
-                       if (cp[0] & SL_TYPE_COMPRESSED_TCP) {
-                               proto = PPP_VJC_COMP;
-                               cp[0] &= ~SL_TYPE_COMPRESSED_TCP;
-                       } else {
-                               proto = PPP_VJC_UNCOMP;
-                               cp[0] = skb->data[2];
-                       }
-                       kfree_skb(skb);
-                       skb = new_skb;
-                       cp = skb_put(skb, len + 2);
-                       cp[0] = 0;
-                       cp[1] = proto;
-               }
-               break;
-
-       case PPP_CCP:
-               /* peek at outbound CCP frames */
-               ppp_ccp_peek(ppp, skb, 0);
-               break;
-       }
-
-       /* try to do packet compression */
-       if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state &&
-           proto != PPP_LCP && proto != PPP_CCP) {
-               if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) {
-                       if (net_ratelimit())
-                               netdev_err(ppp->dev,
-                                          "ppp: compression required but "
-                                          "down - pkt dropped.\n");
-                       goto drop;
-               }
-               skb = pad_compress_skb(ppp, skb);
-               if (!skb)
-                       goto drop;
-       }
-
-       /*
-        * If we are waiting for traffic (demand dialling),
-        * queue it up for pppd to receive.
-        */
-       if (ppp->flags & SC_LOOP_TRAFFIC) {
-               if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
-                       goto drop;
-               skb_queue_tail(&ppp->file.rq, skb);
-               wake_up_interruptible(&ppp->file.rwait);
-               return;
-       }
-
-       ppp->xmit_pending = skb;
-       ppp_push(ppp);
-       return;
-
- drop:
-       kfree_skb(skb);
-       ++ppp->dev->stats.tx_errors;
-}
-
-/*
- * Try to send the frame in xmit_pending.
- * The caller should have the xmit path locked.
- */
-static void
-ppp_push(struct ppp *ppp)
-{
-       struct list_head *list;
-       struct channel *pch;
-       struct sk_buff *skb = ppp->xmit_pending;
-
-       if (!skb)
-               return;
-
-       list = &ppp->channels;
-       if (list_empty(list)) {
-               /* nowhere to send the packet, just drop it */
-               ppp->xmit_pending = NULL;
-               kfree_skb(skb);
-               return;
-       }
-
-       if ((ppp->flags & SC_MULTILINK) == 0) {
-               /* not doing multilink: send it down the first channel */
-               list = list->next;
-               pch = list_entry(list, struct channel, clist);
-
-               spin_lock_bh(&pch->downl);
-               if (pch->chan) {
-                       if (pch->chan->ops->start_xmit(pch->chan, skb))
-                               ppp->xmit_pending = NULL;
-               } else {
-                       /* channel got unregistered */
-                       kfree_skb(skb);
-                       ppp->xmit_pending = NULL;
-               }
-               spin_unlock_bh(&pch->downl);
-               return;
-       }
-
-#ifdef CONFIG_PPP_MULTILINK
-       /* Multilink: fragment the packet over as many links
-          as can take the packet at the moment. */
-       if (!ppp_mp_explode(ppp, skb))
-               return;
-#endif /* CONFIG_PPP_MULTILINK */
-
-       ppp->xmit_pending = NULL;
-       kfree_skb(skb);
-}
-
-#ifdef CONFIG_PPP_MULTILINK
-static bool mp_protocol_compress __read_mostly = true;
-module_param(mp_protocol_compress, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(mp_protocol_compress,
-                "compress protocol id in multilink fragments");
-
-/*
- * Divide a packet to be transmitted into fragments and
- * send them out the individual links.
- */
-static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
-{
-       int len, totlen;
-       int i, bits, hdrlen, mtu;
-       int flen;
-       int navail, nfree, nzero;
-       int nbigger;
-       int totspeed;
-       int totfree;
-       unsigned char *p, *q;
-       struct list_head *list;
-       struct channel *pch;
-       struct sk_buff *frag;
-       struct ppp_channel *chan;
-
-       totspeed = 0; /*total bitrate of the bundle*/
-       nfree = 0; /* # channels which have no packet already queued */
-       navail = 0; /* total # of usable channels (not deregistered) */
-       nzero = 0; /* number of channels with zero speed associated*/
-       totfree = 0; /*total # of channels available and
-                                 *having no queued packets before
-                                 *starting the fragmentation*/
-
-       hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
-       i = 0;
-       list_for_each_entry(pch, &ppp->channels, clist) {
-               if (pch->chan) {
-                       pch->avail = 1;
-                       navail++;
-                       pch->speed = pch->chan->speed;
-               } else {
-                       pch->avail = 0;
-               }
-               if (pch->avail) {
-                       if (skb_queue_empty(&pch->file.xq) ||
-                               !pch->had_frag) {
-                                       if (pch->speed == 0)
-                                               nzero++;
-                                       else
-                                               totspeed += pch->speed;
-
-                                       pch->avail = 2;
-                                       ++nfree;
-                                       ++totfree;
-                               }
-                       if (!pch->had_frag && i < ppp->nxchan)
-                               ppp->nxchan = i;
-               }
-               ++i;
-       }
-       /*
-        * Don't start sending this packet unless at least half of
-        * the channels are free.  This gives much better TCP
-        * performance if we have a lot of channels.
-        */
-       if (nfree == 0 || nfree < navail / 2)
-               return 0; /* can't take now, leave it in xmit_pending */
-
-       /* Do protocol field compression */
-       p = skb->data;
-       len = skb->len;
-       if (*p == 0 && mp_protocol_compress) {
-               ++p;
-               --len;
-       }
-
-       totlen = len;
-       nbigger = len % nfree;
-
-       /* skip to the channel after the one we last used
-          and start at that one */
-       list = &ppp->channels;
-       for (i = 0; i < ppp->nxchan; ++i) {
-               list = list->next;
-               if (list == &ppp->channels) {
-                       i = 0;
-                       break;
-               }
-       }
-
-       /* create a fragment for each channel */
-       bits = B;
-       while (len > 0) {
-               list = list->next;
-               if (list == &ppp->channels) {
-                       i = 0;
-                       continue;
-               }
-               pch = list_entry(list, struct channel, clist);
-               ++i;
-               if (!pch->avail)
-                       continue;
-
-               /*
-                * Skip this channel if it has a fragment pending already and
-                * we haven't given a fragment to all of the free channels.
-                */
-               if (pch->avail == 1) {
-                       if (nfree > 0)
-                               continue;
-               } else {
-                       pch->avail = 1;
-               }
-
-               /* check the channel's mtu and whether it is still attached. */
-               spin_lock_bh(&pch->downl);
-               if (pch->chan == NULL) {
-                       /* can't use this channel, it's being deregistered */
-                       if (pch->speed == 0)
-                               nzero--;
-                       else
-                               totspeed -= pch->speed;
-
-                       spin_unlock_bh(&pch->downl);
-                       pch->avail = 0;
-                       totlen = len;
-                       totfree--;
-                       nfree--;
-                       if (--navail == 0)
-                               break;
-                       continue;
-               }
-
-               /*
-               *if the channel speed is not set divide
-               *the packet evenly among the free channels;
-               *otherwise divide it according to the speed
-               *of the channel we are going to transmit on
-               */
-               flen = len;
-               if (nfree > 0) {
-                       if (pch->speed == 0) {
-                               flen = len/nfree;
-                               if (nbigger > 0) {
-                                       flen++;
-                                       nbigger--;
-                               }
-                       } else {
-                               flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) /
-                                       ((totspeed*totfree)/pch->speed)) - hdrlen;
-                               if (nbigger > 0) {
-                                       flen += ((totfree - nzero)*pch->speed)/totspeed;
-                                       nbigger -= ((totfree - nzero)*pch->speed)/
-                                                       totspeed;
-                               }
-                       }
-                       nfree--;
-               }
-
-               /*
-                *check if we are on the last channel or
-                *we exceded the length of the data to
-                *fragment
-                */
-               if ((nfree <= 0) || (flen > len))
-                       flen = len;
-               /*
-                *it is not worth to tx on slow channels:
-                *in that case from the resulting flen according to the
-                *above formula will be equal or less than zero.
-                *Skip the channel in this case
-                */
-               if (flen <= 0) {
-                       pch->avail = 2;
-                       spin_unlock_bh(&pch->downl);
-                       continue;
-               }
-
-               mtu = pch->chan->mtu - hdrlen;
-               if (mtu < 4)
-                       mtu = 4;
-               if (flen > mtu)
-                       flen = mtu;
-               if (flen == len)
-                       bits |= E;
-               frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC);
-               if (!frag)
-                       goto noskb;
-               q = skb_put(frag, flen + hdrlen);
-
-               /* make the MP header */
-               put_unaligned_be16(PPP_MP, q);
-               if (ppp->flags & SC_MP_XSHORTSEQ) {
-                       q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
-                       q[3] = ppp->nxseq;
-               } else {
-                       q[2] = bits;
-                       q[3] = ppp->nxseq >> 16;
-                       q[4] = ppp->nxseq >> 8;
-                       q[5] = ppp->nxseq;
-               }
-
-               memcpy(q + hdrlen, p, flen);
-
-               /* try to send it down the channel */
-               chan = pch->chan;
-               if (!skb_queue_empty(&pch->file.xq) ||
-                       !chan->ops->start_xmit(chan, frag))
-                       skb_queue_tail(&pch->file.xq, frag);
-               pch->had_frag = 1;
-               p += flen;
-               len -= flen;
-               ++ppp->nxseq;
-               bits = 0;
-               spin_unlock_bh(&pch->downl);
-       }
-       ppp->nxchan = i;
-
-       return 1;
-
- noskb:
-       spin_unlock_bh(&pch->downl);
-       if (ppp->debug & 1)
-               netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
-       ++ppp->dev->stats.tx_errors;
-       ++ppp->nxseq;
-       return 1;       /* abandon the frame */
-}
-#endif /* CONFIG_PPP_MULTILINK */
-
-/*
- * Try to send data out on a channel.
- */
-static void
-ppp_channel_push(struct channel *pch)
-{
-       struct sk_buff *skb;
-       struct ppp *ppp;
-
-       spin_lock_bh(&pch->downl);
-       if (pch->chan) {
-               while (!skb_queue_empty(&pch->file.xq)) {
-                       skb = skb_dequeue(&pch->file.xq);
-                       if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
-                               /* put the packet back and try again later */
-                               skb_queue_head(&pch->file.xq, skb);
-                               break;
-                       }
-               }
-       } else {
-               /* channel got deregistered */
-               skb_queue_purge(&pch->file.xq);
-       }
-       spin_unlock_bh(&pch->downl);
-       /* see if there is anything from the attached unit to be sent */
-       if (skb_queue_empty(&pch->file.xq)) {
-               read_lock_bh(&pch->upl);
-               ppp = pch->ppp;
-               if (ppp)
-                       ppp_xmit_process(ppp);
-               read_unlock_bh(&pch->upl);
-       }
-}
-
-/*
- * Receive-side routines.
- */
-
-struct ppp_mp_skb_parm {
-       u32             sequence;
-       u8              BEbits;
-};
-#define PPP_MP_CB(skb) ((struct ppp_mp_skb_parm *)((skb)->cb))
-
-static inline void
-ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
-{
-       ppp_recv_lock(ppp);
-       if (!ppp->closing)
-               ppp_receive_frame(ppp, skb, pch);
-       else
-               kfree_skb(skb);
-       ppp_recv_unlock(ppp);
-}
-
-void
-ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
-{
-       struct channel *pch = chan->ppp;
-       int proto;
-
-       if (!pch) {
-               kfree_skb(skb);
-               return;
-       }
-
-       read_lock_bh(&pch->upl);
-       if (!pskb_may_pull(skb, 2)) {
-               kfree_skb(skb);
-               if (pch->ppp) {
-                       ++pch->ppp->dev->stats.rx_length_errors;
-                       ppp_receive_error(pch->ppp);
-               }
-               goto done;
-       }
-
-       proto = PPP_PROTO(skb);
-       if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
-               /* put it on the channel queue */
-               skb_queue_tail(&pch->file.rq, skb);
-               /* drop old frames if queue too long */
-               while (pch->file.rq.qlen > PPP_MAX_RQLEN &&
-                      (skb = skb_dequeue(&pch->file.rq)))
-                       kfree_skb(skb);
-               wake_up_interruptible(&pch->file.rwait);
-       } else {
-               ppp_do_recv(pch->ppp, skb, pch);
-       }
-
-done:
-       read_unlock_bh(&pch->upl);
-}
-
-/* Put a 0-length skb in the receive queue as an error indication */
-void
-ppp_input_error(struct ppp_channel *chan, int code)
-{
-       struct channel *pch = chan->ppp;
-       struct sk_buff *skb;
-
-       if (!pch)
-               return;
-
-       read_lock_bh(&pch->upl);
-       if (pch->ppp) {
-               skb = alloc_skb(0, GFP_ATOMIC);
-               if (skb) {
-                       skb->len = 0;           /* probably unnecessary */
-                       skb->cb[0] = code;
-                       ppp_do_recv(pch->ppp, skb, pch);
-               }
-       }
-       read_unlock_bh(&pch->upl);
-}
-
-/*
- * We come in here to process a received frame.
- * The receive side of the ppp unit is locked.
- */
-static void
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
-{
-       /* note: a 0-length skb is used as an error indication */
-       if (skb->len > 0) {
-#ifdef CONFIG_PPP_MULTILINK
-               /* XXX do channel-level decompression here */
-               if (PPP_PROTO(skb) == PPP_MP)
-                       ppp_receive_mp_frame(ppp, skb, pch);
-               else
-#endif /* CONFIG_PPP_MULTILINK */
-                       ppp_receive_nonmp_frame(ppp, skb);
-       } else {
-               kfree_skb(skb);
-               ppp_receive_error(ppp);
-       }
-}
-
-static void
-ppp_receive_error(struct ppp *ppp)
-{
-       ++ppp->dev->stats.rx_errors;
-       if (ppp->vj)
-               slhc_toss(ppp->vj);
-}
-
-static void
-ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
-{
-       struct sk_buff *ns;
-       int proto, len, npi;
-
-       /*
-        * Decompress the frame, if compressed.
-        * Note that some decompressors need to see uncompressed frames
-        * that come in as well as compressed frames.
-        */
-       if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) &&
-           (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
-               skb = ppp_decompress_frame(ppp, skb);
-
-       if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
-               goto err;
-
-       proto = PPP_PROTO(skb);
-       switch (proto) {
-       case PPP_VJC_COMP:
-               /* decompress VJ compressed packets */
-               if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
-                       goto err;
-
-               if (skb_tailroom(skb) < 124 || skb_cloned(skb)) {
-                       /* copy to a new sk_buff with more tailroom */
-                       ns = dev_alloc_skb(skb->len + 128);
-                       if (!ns) {
-                               netdev_err(ppp->dev, "PPP: no memory "
-                                          "(VJ decomp)\n");
-                               goto err;
-                       }
-                       skb_reserve(ns, 2);
-                       skb_copy_bits(skb, 0, skb_put(ns, skb->len), skb->len);
-                       kfree_skb(skb);
-                       skb = ns;
-               }
-               else
-                       skb->ip_summed = CHECKSUM_NONE;
-
-               len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
-               if (len <= 0) {
-                       netdev_printk(KERN_DEBUG, ppp->dev,
-                                     "PPP: VJ decompression error\n");
-                       goto err;
-               }
-               len += 2;
-               if (len > skb->len)
-                       skb_put(skb, len - skb->len);
-               else if (len < skb->len)
-                       skb_trim(skb, len);
-               proto = PPP_IP;
-               break;
-
-       case PPP_VJC_UNCOMP:
-               if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
-                       goto err;
-
-               /* Until we fix the decompressor need to make sure
-                * data portion is linear.
-                */
-               if (!pskb_may_pull(skb, skb->len))
-                       goto err;
-
-               if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) {
-                       netdev_err(ppp->dev, "PPP: VJ uncompressed error\n");
-                       goto err;
-               }
-               proto = PPP_IP;
-               break;
-
-       case PPP_CCP:
-               ppp_ccp_peek(ppp, skb, 1);
-               break;
-       }
-
-       ++ppp->dev->stats.rx_packets;
-       ppp->dev->stats.rx_bytes += skb->len - 2;
-
-       npi = proto_to_npindex(proto);
-       if (npi < 0) {
-               /* control or unknown frame - pass it to pppd */
-               skb_queue_tail(&ppp->file.rq, skb);
-               /* limit queue length by dropping old frames */
-               while (ppp->file.rq.qlen > PPP_MAX_RQLEN &&
-                      (skb = skb_dequeue(&ppp->file.rq)))
-                       kfree_skb(skb);
-               /* wake up any process polling or blocking on read */
-               wake_up_interruptible(&ppp->file.rwait);
-
-       } else {
-               /* network protocol frame - give it to the kernel */
-
-#ifdef CONFIG_PPP_FILTER
-               /* check if the packet passes the pass and active filters */
-               /* the filter instructions are constructed assuming
-                  a four-byte PPP header on each packet */
-               if (ppp->pass_filter || ppp->active_filter) {
-                       if (skb_cloned(skb) &&
-                           pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
-                               goto err;
-
-                       *skb_push(skb, 2) = 0;
-                       if (ppp->pass_filter &&
-                           sk_run_filter(skb, ppp->pass_filter) == 0) {
-                               if (ppp->debug & 1)
-                                       netdev_printk(KERN_DEBUG, ppp->dev,
-                                                     "PPP: inbound frame "
-                                                     "not passed\n");
-                               kfree_skb(skb);
-                               return;
-                       }
-                       if (!(ppp->active_filter &&
-                             sk_run_filter(skb, ppp->active_filter) == 0))
-                               ppp->last_recv = jiffies;
-                       __skb_pull(skb, 2);
-               } else
-#endif /* CONFIG_PPP_FILTER */
-                       ppp->last_recv = jiffies;
-
-               if ((ppp->dev->flags & IFF_UP) == 0 ||
-                   ppp->npmode[npi] != NPMODE_PASS) {
-                       kfree_skb(skb);
-               } else {
-                       /* chop off protocol */
-                       skb_pull_rcsum(skb, 2);
-                       skb->dev = ppp->dev;
-                       skb->protocol = htons(npindex_to_ethertype[npi]);
-                       skb_reset_mac_header(skb);
-                       netif_rx(skb);
-               }
-       }
-       return;
-
- err:
-       kfree_skb(skb);
-       ppp_receive_error(ppp);
-}
-
-static struct sk_buff *
-ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
-{
-       int proto = PPP_PROTO(skb);
-       struct sk_buff *ns;
-       int len;
-
-       /* Until we fix all the decompressor's need to make sure
-        * data portion is linear.
-        */
-       if (!pskb_may_pull(skb, skb->len))
-               goto err;
-
-       if (proto == PPP_COMP) {
-               int obuff_size;
-
-               switch(ppp->rcomp->compress_proto) {
-               case CI_MPPE:
-                       obuff_size = ppp->mru + PPP_HDRLEN + 1;
-                       break;
-               default:
-                       obuff_size = ppp->mru + PPP_HDRLEN;
-                       break;
-               }
-
-               ns = dev_alloc_skb(obuff_size);
-               if (!ns) {
-                       netdev_err(ppp->dev, "ppp_decompress_frame: "
-                                  "no memory\n");
-                       goto err;
-               }
-               /* the decompressor still expects the A/C bytes in the hdr */
-               len = ppp->rcomp->decompress(ppp->rc_state, skb->data - 2,
-                               skb->len + 2, ns->data, obuff_size);
-               if (len < 0) {
-                       /* Pass the compressed frame to pppd as an
-                          error indication. */
-                       if (len == DECOMP_FATALERROR)
-                               ppp->rstate |= SC_DC_FERROR;
-                       kfree_skb(ns);
-                       goto err;
-               }
-
-               kfree_skb(skb);
-               skb = ns;
-               skb_put(skb, len);
-               skb_pull(skb, 2);       /* pull off the A/C bytes */
-
-       } else {
-               /* Uncompressed frame - pass to decompressor so it
-                  can update its dictionary if necessary. */
-               if (ppp->rcomp->incomp)
-                       ppp->rcomp->incomp(ppp->rc_state, skb->data - 2,
-                                          skb->len + 2);
-       }
-
-       return skb;
-
- err:
-       ppp->rstate |= SC_DC_ERROR;
-       ppp_receive_error(ppp);
-       return skb;
-}
-
-#ifdef CONFIG_PPP_MULTILINK
-/*
- * Receive a multilink frame.
- * We put it on the reconstruction queue and then pull off
- * as many completed frames as we can.
- */
-static void
-ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
-{
-       u32 mask, seq;
-       struct channel *ch;
-       int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
-
-       if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0)
-               goto err;               /* no good, throw it away */
-
-       /* Decode sequence number and begin/end bits */
-       if (ppp->flags & SC_MP_SHORTSEQ) {
-               seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
-               mask = 0xfff;
-       } else {
-               seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
-               mask = 0xffffff;
-       }
-       PPP_MP_CB(skb)->BEbits = skb->data[2];
-       skb_pull(skb, mphdrlen);        /* pull off PPP and MP headers */
-
-       /*
-        * Do protocol ID decompression on the first fragment of each packet.
-        */
-       if ((PPP_MP_CB(skb)->BEbits & B) && (skb->data[0] & 1))
-               *skb_push(skb, 1) = 0;
-
-       /*
-        * Expand sequence number to 32 bits, making it as close
-        * as possible to ppp->minseq.
-        */
-       seq |= ppp->minseq & ~mask;
-       if ((int)(ppp->minseq - seq) > (int)(mask >> 1))
-               seq += mask + 1;
-       else if ((int)(seq - ppp->minseq) > (int)(mask >> 1))
-               seq -= mask + 1;        /* should never happen */
-       PPP_MP_CB(skb)->sequence = seq;
-       pch->lastseq = seq;
-
-       /*
-        * If this packet comes before the next one we were expecting,
-        * drop it.
-        */
-       if (seq_before(seq, ppp->nextseq)) {
-               kfree_skb(skb);
-               ++ppp->dev->stats.rx_dropped;
-               ppp_receive_error(ppp);
-               return;
-       }
-
-       /*
-        * Reevaluate minseq, the minimum over all channels of the
-        * last sequence number received on each channel.  Because of
-        * the increasing sequence number rule, we know that any fragment
-        * before `minseq' which hasn't arrived is never going to arrive.
-        * The list of channels can't change because we have the receive
-        * side of the ppp unit locked.
-        */
-       list_for_each_entry(ch, &ppp->channels, clist) {
-               if (seq_before(ch->lastseq, seq))
-                       seq = ch->lastseq;
-       }
-       if (seq_before(ppp->minseq, seq))
-               ppp->minseq = seq;
-
-       /* Put the fragment on the reconstruction queue */
-       ppp_mp_insert(ppp, skb);
-
-       /* If the queue is getting long, don't wait any longer for packets
-          before the start of the queue. */
-       if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN) {
-               struct sk_buff *mskb = skb_peek(&ppp->mrq);
-               if (seq_before(ppp->minseq, PPP_MP_CB(mskb)->sequence))
-                       ppp->minseq = PPP_MP_CB(mskb)->sequence;
-       }
-
-       /* Pull completed packets off the queue and receive them. */
-       while ((skb = ppp_mp_reconstruct(ppp))) {
-               if (pskb_may_pull(skb, 2))
-                       ppp_receive_nonmp_frame(ppp, skb);
-               else {
-                       ++ppp->dev->stats.rx_length_errors;
-                       kfree_skb(skb);
-                       ppp_receive_error(ppp);
-               }
-       }
-
-       return;
-
- err:
-       kfree_skb(skb);
-       ppp_receive_error(ppp);
-}
-
-/*
- * Insert a fragment on the MP reconstruction queue.
- * The queue is ordered by increasing sequence number.
- */
-static void
-ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
-{
-       struct sk_buff *p;
-       struct sk_buff_head *list = &ppp->mrq;
-       u32 seq = PPP_MP_CB(skb)->sequence;
-
-       /* N.B. we don't need to lock the list lock because we have the
-          ppp unit receive-side lock. */
-       skb_queue_walk(list, p) {
-               if (seq_before(seq, PPP_MP_CB(p)->sequence))
-                       break;
-       }
-       __skb_queue_before(list, p, skb);
-}
-
-/*
- * Reconstruct a packet from the MP fragment queue.
- * We go through increasing sequence numbers until we find a
- * complete packet, or we get to the sequence number for a fragment
- * which hasn't arrived but might still do so.
- */
-static struct sk_buff *
-ppp_mp_reconstruct(struct ppp *ppp)
-{
-       u32 seq = ppp->nextseq;
-       u32 minseq = ppp->minseq;
-       struct sk_buff_head *list = &ppp->mrq;
-       struct sk_buff *p, *tmp;
-       struct sk_buff *head, *tail;
-       struct sk_buff *skb = NULL;
-       int lost = 0, len = 0;
-
-       if (ppp->mrru == 0)     /* do nothing until mrru is set */
-               return NULL;
-       head = list->next;
-       tail = NULL;
-       skb_queue_walk_safe(list, p, tmp) {
-       again:
-               if (seq_before(PPP_MP_CB(p)->sequence, seq)) {
-                       /* this can't happen, anyway ignore the skb */
-                       netdev_err(ppp->dev, "ppp_mp_reconstruct bad "
-                                  "seq %u < %u\n",
-                                  PPP_MP_CB(p)->sequence, seq);
-                       __skb_unlink(p, list);
-                       kfree_skb(p);
-                       continue;
-               }
-               if (PPP_MP_CB(p)->sequence != seq) {
-                       /* Fragment `seq' is missing.  If it is after
-                          minseq, it might arrive later, so stop here. */
-                       if (seq_after(seq, minseq))
-                               break;
-                       /* Fragment `seq' is lost, keep going. */
-                       lost = 1;
-                       seq = seq_before(minseq, PPP_MP_CB(p)->sequence)?
-                               minseq + 1: PPP_MP_CB(p)->sequence;
-                       goto again;
-               }
-
-               /*
-                * At this point we know that all the fragments from
-                * ppp->nextseq to seq are either present or lost.
-                * Also, there are no complete packets in the queue
-                * that have no missing fragments and end before this
-                * fragment.
-                */
-
-               /* B bit set indicates this fragment starts a packet */
-               if (PPP_MP_CB(p)->BEbits & B) {
-                       head = p;
-                       lost = 0;
-                       len = 0;
-               }
-
-               len += p->len;
-
-               /* Got a complete packet yet? */
-               if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) &&
-                   (PPP_MP_CB(head)->BEbits & B)) {
-                       if (len > ppp->mrru + 2) {
-                               ++ppp->dev->stats.rx_length_errors;
-                               netdev_printk(KERN_DEBUG, ppp->dev,
-                                             "PPP: reconstructed packet"
-                                             " is too long (%d)\n", len);
-                       } else {
-                               tail = p;
-                               break;
-                       }
-                       ppp->nextseq = seq + 1;
-               }
-
-               /*
-                * If this is the ending fragment of a packet,
-                * and we haven't found a complete valid packet yet,
-                * we can discard up to and including this fragment.
-                */
-               if (PPP_MP_CB(p)->BEbits & E) {
-                       struct sk_buff *tmp2;
-
-                       skb_queue_reverse_walk_from_safe(list, p, tmp2) {
-                               __skb_unlink(p, list);
-                               kfree_skb(p);
-                       }
-                       head = skb_peek(list);
-                       if (!head)
-                               break;
-               }
-               ++seq;
-       }
-
-       /* If we have a complete packet, copy it all into one skb. */
-       if (tail != NULL) {
-               /* If we have discarded any fragments,
-                  signal a receive error. */
-               if (PPP_MP_CB(head)->sequence != ppp->nextseq) {
-                       if (ppp->debug & 1)
-                               netdev_printk(KERN_DEBUG, ppp->dev,
-                                             "  missed pkts %u..%u\n",
-                                             ppp->nextseq,
-                                             PPP_MP_CB(head)->sequence-1);
-                       ++ppp->dev->stats.rx_dropped;
-                       ppp_receive_error(ppp);
-               }
-
-               skb = head;
-               if (head != tail) {
-                       struct sk_buff **fragpp = &skb_shinfo(skb)->frag_list;
-                       p = skb_queue_next(list, head);
-                       __skb_unlink(skb, list);
-                       skb_queue_walk_from_safe(list, p, tmp) {
-                               __skb_unlink(p, list);
-                               *fragpp = p;
-                               p->next = NULL;
-                               fragpp = &p->next;
-
-                               skb->len += p->len;
-                               skb->data_len += p->len;
-                               skb->truesize += p->len;
-
-                               if (p == tail)
-                                       break;
-                       }
-               } else {
-                       __skb_unlink(skb, list);
-               }
-
-               ppp->nextseq = PPP_MP_CB(tail)->sequence + 1;
-       }
-
-       return skb;
-}
-#endif /* CONFIG_PPP_MULTILINK */
-
-/*
- * Channel interface.
- */
-
-/* Create a new, unattached ppp channel. */
-int ppp_register_channel(struct ppp_channel *chan)
-{
-       return ppp_register_net_channel(current->nsproxy->net_ns, chan);
-}
-
-/* Create a new, unattached ppp channel for specified net. */
-int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
-{
-       struct channel *pch;
-       struct ppp_net *pn;
-
-       pch = kzalloc(sizeof(struct channel), GFP_KERNEL);
-       if (!pch)
-               return -ENOMEM;
-
-       pn = ppp_pernet(net);
-
-       pch->ppp = NULL;
-       pch->chan = chan;
-       pch->chan_net = net;
-       chan->ppp = pch;
-       init_ppp_file(&pch->file, CHANNEL);
-       pch->file.hdrlen = chan->hdrlen;
-#ifdef CONFIG_PPP_MULTILINK
-       pch->lastseq = -1;
-#endif /* CONFIG_PPP_MULTILINK */
-       init_rwsem(&pch->chan_sem);
-       spin_lock_init(&pch->downl);
-       rwlock_init(&pch->upl);
-
-       spin_lock_bh(&pn->all_channels_lock);
-       pch->file.index = ++pn->last_channel_index;
-       list_add(&pch->list, &pn->new_channels);
-       atomic_inc(&channel_count);
-       spin_unlock_bh(&pn->all_channels_lock);
-
-       return 0;
-}
-
-/*
- * Return the index of a channel.
- */
-int ppp_channel_index(struct ppp_channel *chan)
-{
-       struct channel *pch = chan->ppp;
-
-       if (pch)
-               return pch->file.index;
-       return -1;
-}
-
-/*
- * Return the PPP unit number to which a channel is connected.
- */
-int ppp_unit_number(struct ppp_channel *chan)
-{
-       struct channel *pch = chan->ppp;
-       int unit = -1;
-
-       if (pch) {
-               read_lock_bh(&pch->upl);
-               if (pch->ppp)
-                       unit = pch->ppp->file.index;
-               read_unlock_bh(&pch->upl);
-       }
-       return unit;
-}
-
-/*
- * Return the PPP device interface name of a channel.
- */
-char *ppp_dev_name(struct ppp_channel *chan)
-{
-       struct channel *pch = chan->ppp;
-       char *name = NULL;
-
-       if (pch) {
-               read_lock_bh(&pch->upl);
-               if (pch->ppp && pch->ppp->dev)
-                       name = pch->ppp->dev->name;
-               read_unlock_bh(&pch->upl);
-       }
-       return name;
-}
-
-
-/*
- * Disconnect a channel from the generic layer.
- * This must be called in process context.
- */
-void
-ppp_unregister_channel(struct ppp_channel *chan)
-{
-       struct channel *pch = chan->ppp;
-       struct ppp_net *pn;
-
-       if (!pch)
-               return;         /* should never happen */
-
-       chan->ppp = NULL;
-
-       /*
-        * This ensures that we have returned from any calls into the
-        * the channel's start_xmit or ioctl routine before we proceed.
-        */
-       down_write(&pch->chan_sem);
-       spin_lock_bh(&pch->downl);
-       pch->chan = NULL;
-       spin_unlock_bh(&pch->downl);
-       up_write(&pch->chan_sem);
-       ppp_disconnect_channel(pch);
-
-       pn = ppp_pernet(pch->chan_net);
-       spin_lock_bh(&pn->all_channels_lock);
-       list_del(&pch->list);
-       spin_unlock_bh(&pn->all_channels_lock);
-
-       pch->file.dead = 1;
-       wake_up_interruptible(&pch->file.rwait);
-       if (atomic_dec_and_test(&pch->file.refcnt))
-               ppp_destroy_channel(pch);
-}
-
-/*
- * Callback from a channel when it can accept more to transmit.
- * This should be called at BH/softirq level, not interrupt level.
- */
-void
-ppp_output_wakeup(struct ppp_channel *chan)
-{
-       struct channel *pch = chan->ppp;
-
-       if (!pch)
-               return;
-       ppp_channel_push(pch);
-}
-
-/*
- * Compression control.
- */
-
-/* Process the PPPIOCSCOMPRESS ioctl. */
-static int
-ppp_set_compress(struct ppp *ppp, unsigned long arg)
-{
-       int err;
-       struct compressor *cp, *ocomp;
-       struct ppp_option_data data;
-       void *state, *ostate;
-       unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
-
-       err = -EFAULT;
-       if (copy_from_user(&data, (void __user *) arg, sizeof(data)) ||
-           (data.length <= CCP_MAX_OPTION_LENGTH &&
-            copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
-               goto out;
-       err = -EINVAL;
-       if (data.length > CCP_MAX_OPTION_LENGTH ||
-           ccp_option[1] < 2 || ccp_option[1] > data.length)
-               goto out;
-
-       cp = try_then_request_module(
-               find_compressor(ccp_option[0]),
-               "ppp-compress-%d", ccp_option[0]);
-       if (!cp)
-               goto out;
-
-       err = -ENOBUFS;
-       if (data.transmit) {
-               state = cp->comp_alloc(ccp_option, data.length);
-               if (state) {
-                       ppp_xmit_lock(ppp);
-                       ppp->xstate &= ~SC_COMP_RUN;
-                       ocomp = ppp->xcomp;
-                       ostate = ppp->xc_state;
-                       ppp->xcomp = cp;
-                       ppp->xc_state = state;
-                       ppp_xmit_unlock(ppp);
-                       if (ostate) {
-                               ocomp->comp_free(ostate);
-                               module_put(ocomp->owner);
-                       }
-                       err = 0;
-               } else
-                       module_put(cp->owner);
-
-       } else {
-               state = cp->decomp_alloc(ccp_option, data.length);
-               if (state) {
-                       ppp_recv_lock(ppp);
-                       ppp->rstate &= ~SC_DECOMP_RUN;
-                       ocomp = ppp->rcomp;
-                       ostate = ppp->rc_state;
-                       ppp->rcomp = cp;
-                       ppp->rc_state = state;
-                       ppp_recv_unlock(ppp);
-                       if (ostate) {
-                               ocomp->decomp_free(ostate);
-                               module_put(ocomp->owner);
-                       }
-                       err = 0;
-               } else
-                       module_put(cp->owner);
-       }
-
- out:
-       return err;
-}
-
-/*
- * Look at a CCP packet and update our state accordingly.
- * We assume the caller has the xmit or recv path locked.
- */
-static void
-ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
-{
-       unsigned char *dp;
-       int len;
-
-       if (!pskb_may_pull(skb, CCP_HDRLEN + 2))
-               return; /* no header */
-       dp = skb->data + 2;
-
-       switch (CCP_CODE(dp)) {
-       case CCP_CONFREQ:
-
-               /* A ConfReq starts negotiation of compression
-                * in one direction of transmission,
-                * and hence brings it down...but which way?
-                *
-                * Remember:
-                * A ConfReq indicates what the sender would like to receive
-                */
-               if(inbound)
-                       /* He is proposing what I should send */
-                       ppp->xstate &= ~SC_COMP_RUN;
-               else
-                       /* I am proposing to what he should send */
-                       ppp->rstate &= ~SC_DECOMP_RUN;
-
-               break;
-
-       case CCP_TERMREQ:
-       case CCP_TERMACK:
-               /*
-                * CCP is going down, both directions of transmission
-                */
-               ppp->rstate &= ~SC_DECOMP_RUN;
-               ppp->xstate &= ~SC_COMP_RUN;
-               break;
-
-       case CCP_CONFACK:
-               if ((ppp->flags & (SC_CCP_OPEN | SC_CCP_UP)) != SC_CCP_OPEN)
-                       break;
-               len = CCP_LENGTH(dp);
-               if (!pskb_may_pull(skb, len + 2))
-                       return;         /* too short */
-               dp += CCP_HDRLEN;
-               len -= CCP_HDRLEN;
-               if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp))
-                       break;
-               if (inbound) {
-                       /* we will start receiving compressed packets */
-                       if (!ppp->rc_state)
-                               break;
-                       if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
-                                       ppp->file.index, 0, ppp->mru, ppp->debug)) {
-                               ppp->rstate |= SC_DECOMP_RUN;
-                               ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
-                       }
-               } else {
-                       /* we will soon start sending compressed packets */
-                       if (!ppp->xc_state)
-                               break;
-                       if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
-                                       ppp->file.index, 0, ppp->debug))
-                               ppp->xstate |= SC_COMP_RUN;
-               }
-               break;
-
-       case CCP_RESETACK:
-               /* reset the [de]compressor */
-               if ((ppp->flags & SC_CCP_UP) == 0)
-                       break;
-               if (inbound) {
-                       if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)) {
-                               ppp->rcomp->decomp_reset(ppp->rc_state);
-                               ppp->rstate &= ~SC_DC_ERROR;
-                       }
-               } else {
-                       if (ppp->xc_state && (ppp->xstate & SC_COMP_RUN))
-                               ppp->xcomp->comp_reset(ppp->xc_state);
-               }
-               break;
-       }
-}
-
-/* Free up compression resources. */
-static void
-ppp_ccp_closed(struct ppp *ppp)
-{
-       void *xstate, *rstate;
-       struct compressor *xcomp, *rcomp;
-
-       ppp_lock(ppp);
-       ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
-       ppp->xstate = 0;
-       xcomp = ppp->xcomp;
-       xstate = ppp->xc_state;
-       ppp->xc_state = NULL;
-       ppp->rstate = 0;
-       rcomp = ppp->rcomp;
-       rstate = ppp->rc_state;
-       ppp->rc_state = NULL;
-       ppp_unlock(ppp);
-
-       if (xstate) {
-               xcomp->comp_free(xstate);
-               module_put(xcomp->owner);
-       }
-       if (rstate) {
-               rcomp->decomp_free(rstate);
-               module_put(rcomp->owner);
-       }
-}
-
-/* List of compressors. */
-static LIST_HEAD(compressor_list);
-static DEFINE_SPINLOCK(compressor_list_lock);
-
-struct compressor_entry {
-       struct list_head list;
-       struct compressor *comp;
-};
-
-static struct compressor_entry *
-find_comp_entry(int proto)
-{
-       struct compressor_entry *ce;
-
-       list_for_each_entry(ce, &compressor_list, list) {
-               if (ce->comp->compress_proto == proto)
-                       return ce;
-       }
-       return NULL;
-}
-
-/* Register a compressor */
-int
-ppp_register_compressor(struct compressor *cp)
-{
-       struct compressor_entry *ce;
-       int ret;
-       spin_lock(&compressor_list_lock);
-       ret = -EEXIST;
-       if (find_comp_entry(cp->compress_proto))
-               goto out;
-       ret = -ENOMEM;
-       ce = kmalloc(sizeof(struct compressor_entry), GFP_ATOMIC);
-       if (!ce)
-               goto out;
-       ret = 0;
-       ce->comp = cp;
-       list_add(&ce->list, &compressor_list);
- out:
-       spin_unlock(&compressor_list_lock);
-       return ret;
-}
-
-/* Unregister a compressor */
-void
-ppp_unregister_compressor(struct compressor *cp)
-{
-       struct compressor_entry *ce;
-
-       spin_lock(&compressor_list_lock);
-       ce = find_comp_entry(cp->compress_proto);
-       if (ce && ce->comp == cp) {
-               list_del(&ce->list);
-               kfree(ce);
-       }
-       spin_unlock(&compressor_list_lock);
-}
-
-/* Find a compressor. */
-static struct compressor *
-find_compressor(int type)
-{
-       struct compressor_entry *ce;
-       struct compressor *cp = NULL;
-
-       spin_lock(&compressor_list_lock);
-       ce = find_comp_entry(type);
-       if (ce) {
-               cp = ce->comp;
-               if (!try_module_get(cp->owner))
-                       cp = NULL;
-       }
-       spin_unlock(&compressor_list_lock);
-       return cp;
-}
-
-/*
- * Miscelleneous stuff.
- */
-
-static void
-ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
-{
-       struct slcompress *vj = ppp->vj;
-
-       memset(st, 0, sizeof(*st));
-       st->p.ppp_ipackets = ppp->dev->stats.rx_packets;
-       st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
-       st->p.ppp_ibytes = ppp->dev->stats.rx_bytes;
-       st->p.ppp_opackets = ppp->dev->stats.tx_packets;
-       st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
-       st->p.ppp_obytes = ppp->dev->stats.tx_bytes;
-       if (!vj)
-               return;
-       st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
-       st->vj.vjs_compressed = vj->sls_o_compressed;
-       st->vj.vjs_searches = vj->sls_o_searches;
-       st->vj.vjs_misses = vj->sls_o_misses;
-       st->vj.vjs_errorin = vj->sls_i_error;
-       st->vj.vjs_tossed = vj->sls_i_tossed;
-       st->vj.vjs_uncompressedin = vj->sls_i_uncompressed;
-       st->vj.vjs_compressedin = vj->sls_i_compressed;
-}
-
-/*
- * Stuff for handling the lists of ppp units and channels
- * and for initialization.
- */
-
-/*
- * Create a new ppp interface unit.  Fails if it can't allocate memory
- * or if there is already a unit with the requested number.
- * unit == -1 means allocate a new number.
- */
-static struct ppp *
-ppp_create_interface(struct net *net, int unit, int *retp)
-{
-       struct ppp *ppp;
-       struct ppp_net *pn;
-       struct net_device *dev = NULL;
-       int ret = -ENOMEM;
-       int i;
-
-       dev = alloc_netdev(sizeof(struct ppp), "", ppp_setup);
-       if (!dev)
-               goto out1;
-
-       pn = ppp_pernet(net);
-
-       ppp = netdev_priv(dev);
-       ppp->dev = dev;
-       ppp->mru = PPP_MRU;
-       init_ppp_file(&ppp->file, INTERFACE);
-       ppp->file.hdrlen = PPP_HDRLEN - 2;      /* don't count proto bytes */
-       for (i = 0; i < NUM_NP; ++i)
-               ppp->npmode[i] = NPMODE_PASS;
-       INIT_LIST_HEAD(&ppp->channels);
-       spin_lock_init(&ppp->rlock);
-       spin_lock_init(&ppp->wlock);
-#ifdef CONFIG_PPP_MULTILINK
-       ppp->minseq = -1;
-       skb_queue_head_init(&ppp->mrq);
-#endif /* CONFIG_PPP_MULTILINK */
-
-       /*
-        * drum roll: don't forget to set
-        * the net device is belong to
-        */
-       dev_net_set(dev, net);
-
-       mutex_lock(&pn->all_ppp_mutex);
-
-       if (unit < 0) {
-               unit = unit_get(&pn->units_idr, ppp);
-               if (unit < 0) {
-                       ret = unit;
-                       goto out2;
-               }
-       } else {
-               ret = -EEXIST;
-               if (unit_find(&pn->units_idr, unit))
-                       goto out2; /* unit already exists */
-               /*
-                * if caller need a specified unit number
-                * lets try to satisfy him, otherwise --
-                * he should better ask us for new unit number
-                *
-                * NOTE: yes I know that returning EEXIST it's not
-                * fair but at least pppd will ask us to allocate
-                * new unit in this case so user is happy :)
-                */
-               unit = unit_set(&pn->units_idr, ppp, unit);
-               if (unit < 0)
-                       goto out2;
-       }
-
-       /* Initialize the new ppp unit */
-       ppp->file.index = unit;
-       sprintf(dev->name, "ppp%d", unit);
-
-       ret = register_netdev(dev);
-       if (ret != 0) {
-               unit_put(&pn->units_idr, unit);
-               netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n",
-                          dev->name, ret);
-               goto out2;
-       }
-
-       ppp->ppp_net = net;
-
-       atomic_inc(&ppp_unit_count);
-       mutex_unlock(&pn->all_ppp_mutex);
-
-       *retp = 0;
-       return ppp;
-
-out2:
-       mutex_unlock(&pn->all_ppp_mutex);
-       free_netdev(dev);
-out1:
-       *retp = ret;
-       return NULL;
-}
-
-/*
- * Initialize a ppp_file structure.
- */
-static void
-init_ppp_file(struct ppp_file *pf, int kind)
-{
-       pf->kind = kind;
-       skb_queue_head_init(&pf->xq);
-       skb_queue_head_init(&pf->rq);
-       atomic_set(&pf->refcnt, 1);
-       init_waitqueue_head(&pf->rwait);
-}
-
-/*
- * Take down a ppp interface unit - called when the owning file
- * (the one that created the unit) is closed or detached.
- */
-static void ppp_shutdown_interface(struct ppp *ppp)
-{
-       struct ppp_net *pn;
-
-       pn = ppp_pernet(ppp->ppp_net);
-       mutex_lock(&pn->all_ppp_mutex);
-
-       /* This will call dev_close() for us. */
-       ppp_lock(ppp);
-       if (!ppp->closing) {
-               ppp->closing = 1;
-               ppp_unlock(ppp);
-               unregister_netdev(ppp->dev);
-               unit_put(&pn->units_idr, ppp->file.index);
-       } else
-               ppp_unlock(ppp);
-
-       ppp->file.dead = 1;
-       ppp->owner = NULL;
-       wake_up_interruptible(&ppp->file.rwait);
-
-       mutex_unlock(&pn->all_ppp_mutex);
-}
-
-/*
- * Free the memory used by a ppp unit.  This is only called once
- * there are no channels connected to the unit and no file structs
- * that reference the unit.
- */
-static void ppp_destroy_interface(struct ppp *ppp)
-{
-       atomic_dec(&ppp_unit_count);
-
-       if (!ppp->file.dead || ppp->n_channels) {
-               /* "can't happen" */
-               netdev_err(ppp->dev, "ppp: destroying ppp struct %p "
-                          "but dead=%d n_channels=%d !\n",
-                          ppp, ppp->file.dead, ppp->n_channels);
-               return;
-       }
-
-       ppp_ccp_closed(ppp);
-       if (ppp->vj) {
-               slhc_free(ppp->vj);
-               ppp->vj = NULL;
-       }
-       skb_queue_purge(&ppp->file.xq);
-       skb_queue_purge(&ppp->file.rq);
-#ifdef CONFIG_PPP_MULTILINK
-       skb_queue_purge(&ppp->mrq);
-#endif /* CONFIG_PPP_MULTILINK */
-#ifdef CONFIG_PPP_FILTER
-       kfree(ppp->pass_filter);
-       ppp->pass_filter = NULL;
-       kfree(ppp->active_filter);
-       ppp->active_filter = NULL;
-#endif /* CONFIG_PPP_FILTER */
-
-       kfree_skb(ppp->xmit_pending);
-
-       free_netdev(ppp->dev);
-}
-
-/*
- * Locate an existing ppp unit.
- * The caller should have locked the all_ppp_mutex.
- */
-static struct ppp *
-ppp_find_unit(struct ppp_net *pn, int unit)
-{
-       return unit_find(&pn->units_idr, unit);
-}
-
-/*
- * Locate an existing ppp channel.
- * The caller should have locked the all_channels_lock.
- * First we look in the new_channels list, then in the
- * all_channels list.  If found in the new_channels list,
- * we move it to the all_channels list.  This is for speed
- * when we have a lot of channels in use.
- */
-static struct channel *
-ppp_find_channel(struct ppp_net *pn, int unit)
-{
-       struct channel *pch;
-
-       list_for_each_entry(pch, &pn->new_channels, list) {
-               if (pch->file.index == unit) {
-                       list_move(&pch->list, &pn->all_channels);
-                       return pch;
-               }
-       }
-
-       list_for_each_entry(pch, &pn->all_channels, list) {
-               if (pch->file.index == unit)
-                       return pch;
-       }
-
-       return NULL;
-}
-
-/*
- * Connect a PPP channel to a PPP interface unit.
- */
-static int
-ppp_connect_channel(struct channel *pch, int unit)
-{
-       struct ppp *ppp;
-       struct ppp_net *pn;
-       int ret = -ENXIO;
-       int hdrlen;
-
-       pn = ppp_pernet(pch->chan_net);
-
-       mutex_lock(&pn->all_ppp_mutex);
-       ppp = ppp_find_unit(pn, unit);
-       if (!ppp)
-               goto out;
-       write_lock_bh(&pch->upl);
-       ret = -EINVAL;
-       if (pch->ppp)
-               goto outl;
-
-       ppp_lock(ppp);
-       if (pch->file.hdrlen > ppp->file.hdrlen)
-               ppp->file.hdrlen = pch->file.hdrlen;
-       hdrlen = pch->file.hdrlen + 2;  /* for protocol bytes */
-       if (hdrlen > ppp->dev->hard_header_len)
-               ppp->dev->hard_header_len = hdrlen;
-       list_add_tail(&pch->clist, &ppp->channels);
-       ++ppp->n_channels;
-       pch->ppp = ppp;
-       atomic_inc(&ppp->file.refcnt);
-       ppp_unlock(ppp);
-       ret = 0;
-
- outl:
-       write_unlock_bh(&pch->upl);
- out:
-       mutex_unlock(&pn->all_ppp_mutex);
-       return ret;
-}
-
-/*
- * Disconnect a channel from its ppp unit.
- */
-static int
-ppp_disconnect_channel(struct channel *pch)
-{
-       struct ppp *ppp;
-       int err = -EINVAL;
-
-       write_lock_bh(&pch->upl);
-       ppp = pch->ppp;
-       pch->ppp = NULL;
-       write_unlock_bh(&pch->upl);
-       if (ppp) {
-               /* remove it from the ppp unit's list */
-               ppp_lock(ppp);
-               list_del(&pch->clist);
-               if (--ppp->n_channels == 0)
-                       wake_up_interruptible(&ppp->file.rwait);
-               ppp_unlock(ppp);
-               if (atomic_dec_and_test(&ppp->file.refcnt))
-                       ppp_destroy_interface(ppp);
-               err = 0;
-       }
-       return err;
-}
-
-/*
- * Free up the resources used by a ppp channel.
- */
-static void ppp_destroy_channel(struct channel *pch)
-{
-       atomic_dec(&channel_count);
-
-       if (!pch->file.dead) {
-               /* "can't happen" */
-               pr_err("ppp: destroying undead channel %p !\n", pch);
-               return;
-       }
-       skb_queue_purge(&pch->file.xq);
-       skb_queue_purge(&pch->file.rq);
-       kfree(pch);
-}
-
-static void __exit ppp_cleanup(void)
-{
-       /* should never happen */
-       if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
-               pr_err("PPP: removing module but units remain!\n");
-       unregister_chrdev(PPP_MAJOR, "ppp");
-       device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
-       class_destroy(ppp_class);
-       unregister_pernet_device(&ppp_net_ops);
-}
-
-/*
- * Units handling. Caller must protect concurrent access
- * by holding all_ppp_mutex
- */
-
-static int __unit_alloc(struct idr *p, void *ptr, int n)
-{
-       int unit, err;
-
-again:
-       if (!idr_pre_get(p, GFP_KERNEL)) {
-               pr_err("PPP: No free memory for idr\n");
-               return -ENOMEM;
-       }
-
-       err = idr_get_new_above(p, ptr, n, &unit);
-       if (err < 0) {
-               if (err == -EAGAIN)
-                       goto again;
-               return err;
-       }
-
-       return unit;
-}
-
-/* associate pointer with specified number */
-static int unit_set(struct idr *p, void *ptr, int n)
-{
-       int unit;
-
-       unit = __unit_alloc(p, ptr, n);
-       if (unit < 0)
-               return unit;
-       else if (unit != n) {
-               idr_remove(p, unit);
-               return -EINVAL;
-       }
-
-       return unit;
-}
-
-/* get new free unit number and associate pointer with it */
-static int unit_get(struct idr *p, void *ptr)
-{
-       return __unit_alloc(p, ptr, 0);
-}
-
-/* put unit number back to a pool */
-static void unit_put(struct idr *p, int n)
-{
-       idr_remove(p, n);
-}
-
-/* get pointer associated with the number */
-static void *unit_find(struct idr *p, int n)
-{
-       return idr_find(p, n);
-}
-
-/* Module/initialization stuff */
-
-module_init(ppp_init);
-module_exit(ppp_cleanup);
-
-EXPORT_SYMBOL(ppp_register_net_channel);
-EXPORT_SYMBOL(ppp_register_channel);
-EXPORT_SYMBOL(ppp_unregister_channel);
-EXPORT_SYMBOL(ppp_channel_index);
-EXPORT_SYMBOL(ppp_unit_number);
-EXPORT_SYMBOL(ppp_dev_name);
-EXPORT_SYMBOL(ppp_input);
-EXPORT_SYMBOL(ppp_input_error);
-EXPORT_SYMBOL(ppp_output_wakeup);
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0);
-MODULE_ALIAS("devname:ppp");
diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp_mppe.c
deleted file mode 100644 (file)
index 9a1849a..0000000
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
- * ppp_mppe.c - interface MPPE to the PPP code.
- * This version is for use with Linux kernel 2.6.14+
- *
- * By Frank Cusack <fcusack@fcusack.com>.
- * Copyright (c) 2002,2003,2004 Google, Inc.
- * All rights reserved.
- *
- * License:
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied.
- *
- * ALTERNATIVELY, provided that this notice is retained in full, this product
- * may be distributed under the terms of the GNU General Public License (GPL),
- * in which case the provisions of the GPL apply INSTEAD OF those given above.
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *
- * Changelog:
- *      08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
- *                 Only need extra skb padding on transmit, not receive.
- *      06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
- *                 Use Linux kernel 2.6 arc4 and sha1 routines rather than
- *                 providing our own.
- *      2/15/04 - TS: added #include <version.h> and testing for Kernel
- *                    version before using
- *                    MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
- *                    deprecated in 2.6
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/crypto.h>
-#include <linux/mm.h>
-#include <linux/ppp_defs.h>
-#include <linux/ppp-comp.h>
-#include <linux/scatterlist.h>
-#include <asm/unaligned.h>
-
-#include "ppp_mppe.h"
-
-MODULE_AUTHOR("Frank Cusack <fcusack@fcusack.com>");
-MODULE_DESCRIPTION("Point-to-Point Protocol Microsoft Point-to-Point Encryption support");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("ppp-compress-" __stringify(CI_MPPE));
-MODULE_VERSION("1.0.2");
-
-static unsigned int
-setup_sg(struct scatterlist *sg, const void *address, unsigned int length)
-{
-       sg_set_buf(sg, address, length);
-       return length;
-}
-
-#define SHA1_PAD_SIZE 40
-
-/*
- * kernel crypto API needs its arguments to be in kmalloc'd memory, not in the module
- * static data area.  That means sha_pad needs to be kmalloc'd.
- */
-
-struct sha_pad {
-       unsigned char sha_pad1[SHA1_PAD_SIZE];
-       unsigned char sha_pad2[SHA1_PAD_SIZE];
-};
-static struct sha_pad *sha_pad;
-
-static inline void sha_pad_init(struct sha_pad *shapad)
-{
-       memset(shapad->sha_pad1, 0x00, sizeof(shapad->sha_pad1));
-       memset(shapad->sha_pad2, 0xF2, sizeof(shapad->sha_pad2));
-}
-
-/*
- * State for an MPPE (de)compressor.
- */
-struct ppp_mppe_state {
-       struct crypto_blkcipher *arc4;
-       struct crypto_hash *sha1;
-       unsigned char *sha1_digest;
-       unsigned char master_key[MPPE_MAX_KEY_LEN];
-       unsigned char session_key[MPPE_MAX_KEY_LEN];
-       unsigned keylen;        /* key length in bytes             */
-       /* NB: 128-bit == 16, 40-bit == 8! */
-       /* If we want to support 56-bit,   */
-       /* the unit has to change to bits  */
-       unsigned char bits;     /* MPPE control bits */
-       unsigned ccount;        /* 12-bit coherency count (seqno)  */
-       unsigned stateful;      /* stateful mode flag */
-       int discard;            /* stateful mode packet loss flag */
-       int sanity_errors;      /* take down LCP if too many */
-       int unit;
-       int debug;
-       struct compstat stats;
-};
-
-/* struct ppp_mppe_state.bits definitions */
-#define MPPE_BIT_A     0x80    /* Encryption table were (re)inititalized */
-#define MPPE_BIT_B     0x40    /* MPPC only (not implemented) */
-#define MPPE_BIT_C     0x20    /* MPPC only (not implemented) */
-#define MPPE_BIT_D     0x10    /* This is an encrypted frame */
-
-#define MPPE_BIT_FLUSHED       MPPE_BIT_A
-#define MPPE_BIT_ENCRYPTED     MPPE_BIT_D
-
-#define MPPE_BITS(p) ((p)[4] & 0xf0)
-#define MPPE_CCOUNT(p) ((((p)[4] & 0x0f) << 8) + (p)[5])
-#define MPPE_CCOUNT_SPACE 0x1000       /* The size of the ccount space */
-
-#define MPPE_OVHD      2       /* MPPE overhead/packet */
-#define SANITY_MAX     1600    /* Max bogon factor we will tolerate */
-
-/*
- * Key Derivation, from RFC 3078, RFC 3079.
- * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
- */
-static void get_new_key_from_sha(struct ppp_mppe_state * state)
-{
-       struct hash_desc desc;
-       struct scatterlist sg[4];
-       unsigned int nbytes;
-
-       sg_init_table(sg, 4);
-
-       nbytes = setup_sg(&sg[0], state->master_key, state->keylen);
-       nbytes += setup_sg(&sg[1], sha_pad->sha_pad1,
-                          sizeof(sha_pad->sha_pad1));
-       nbytes += setup_sg(&sg[2], state->session_key, state->keylen);
-       nbytes += setup_sg(&sg[3], sha_pad->sha_pad2,
-                          sizeof(sha_pad->sha_pad2));
-
-       desc.tfm = state->sha1;
-       desc.flags = 0;
-
-       crypto_hash_digest(&desc, sg, nbytes, state->sha1_digest);
-}
-
-/*
- * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
- * Well, not what's written there, but rather what they meant.
- */
-static void mppe_rekey(struct ppp_mppe_state * state, int initial_key)
-{
-       struct scatterlist sg_in[1], sg_out[1];
-       struct blkcipher_desc desc = { .tfm = state->arc4 };
-
-       get_new_key_from_sha(state);
-       if (!initial_key) {
-               crypto_blkcipher_setkey(state->arc4, state->sha1_digest,
-                                       state->keylen);
-               sg_init_table(sg_in, 1);
-               sg_init_table(sg_out, 1);
-               setup_sg(sg_in, state->sha1_digest, state->keylen);
-               setup_sg(sg_out, state->session_key, state->keylen);
-               if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
-                                            state->keylen) != 0) {
-                   printk(KERN_WARNING "mppe_rekey: cipher_encrypt failed\n");
-               }
-       } else {
-               memcpy(state->session_key, state->sha1_digest, state->keylen);
-       }
-       if (state->keylen == 8) {
-               /* See RFC 3078 */
-               state->session_key[0] = 0xd1;
-               state->session_key[1] = 0x26;
-               state->session_key[2] = 0x9e;
-       }
-       crypto_blkcipher_setkey(state->arc4, state->session_key, state->keylen);
-}
-
-/*
- * Allocate space for a (de)compressor.
- */
-static void *mppe_alloc(unsigned char *options, int optlen)
-{
-       struct ppp_mppe_state *state;
-       unsigned int digestsize;
-
-       if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
-           options[0] != CI_MPPE || options[1] != CILEN_MPPE)
-               goto out;
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (state == NULL)
-               goto out;
-
-
-       state->arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(state->arc4)) {
-               state->arc4 = NULL;
-               goto out_free;
-       }
-
-       state->sha1 = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(state->sha1)) {
-               state->sha1 = NULL;
-               goto out_free;
-       }
-
-       digestsize = crypto_hash_digestsize(state->sha1);
-       if (digestsize < MPPE_MAX_KEY_LEN)
-               goto out_free;
-
-       state->sha1_digest = kmalloc(digestsize, GFP_KERNEL);
-       if (!state->sha1_digest)
-               goto out_free;
-
-       /* Save keys. */
-       memcpy(state->master_key, &options[CILEN_MPPE],
-              sizeof(state->master_key));
-       memcpy(state->session_key, state->master_key,
-              sizeof(state->master_key));
-
-       /*
-        * We defer initial key generation until mppe_init(), as mppe_alloc()
-        * is called frequently during negotiation.
-        */
-
-       return (void *)state;
-
-       out_free:
-           if (state->sha1_digest)
-               kfree(state->sha1_digest);
-           if (state->sha1)
-               crypto_free_hash(state->sha1);
-           if (state->arc4)
-               crypto_free_blkcipher(state->arc4);
-           kfree(state);
-       out:
-       return NULL;
-}
-
-/*
- * Deallocate space for a (de)compressor.
- */
-static void mppe_free(void *arg)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-       if (state) {
-           if (state->sha1_digest)
-               kfree(state->sha1_digest);
-           if (state->sha1)
-               crypto_free_hash(state->sha1);
-           if (state->arc4)
-               crypto_free_blkcipher(state->arc4);
-           kfree(state);
-       }
-}
-
-/*
- * Initialize (de)compressor state.
- */
-static int
-mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
-         const char *debugstr)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-       unsigned char mppe_opts;
-
-       if (optlen != CILEN_MPPE ||
-           options[0] != CI_MPPE || options[1] != CILEN_MPPE)
-               return 0;
-
-       MPPE_CI_TO_OPTS(&options[2], mppe_opts);
-       if (mppe_opts & MPPE_OPT_128)
-               state->keylen = 16;
-       else if (mppe_opts & MPPE_OPT_40)
-               state->keylen = 8;
-       else {
-               printk(KERN_WARNING "%s[%d]: unknown key length\n", debugstr,
-                      unit);
-               return 0;
-       }
-       if (mppe_opts & MPPE_OPT_STATEFUL)
-               state->stateful = 1;
-
-       /* Generate the initial session key. */
-       mppe_rekey(state, 1);
-
-       if (debug) {
-               int i;
-               char mkey[sizeof(state->master_key) * 2 + 1];
-               char skey[sizeof(state->session_key) * 2 + 1];
-
-               printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n",
-                      debugstr, unit, (state->keylen == 16) ? 128 : 40,
-                      (state->stateful) ? "stateful" : "stateless");
-
-               for (i = 0; i < sizeof(state->master_key); i++)
-                       sprintf(mkey + i * 2, "%02x", state->master_key[i]);
-               for (i = 0; i < sizeof(state->session_key); i++)
-                       sprintf(skey + i * 2, "%02x", state->session_key[i]);
-               printk(KERN_DEBUG
-                      "%s[%d]: keys: master: %s initial session: %s\n",
-                      debugstr, unit, mkey, skey);
-       }
-
-       /*
-        * Initialize the coherency count.  The initial value is not specified
-        * in RFC 3078, but we can make a reasonable assumption that it will
-        * start at 0.  Setting it to the max here makes the comp/decomp code
-        * do the right thing (determined through experiment).
-        */
-       state->ccount = MPPE_CCOUNT_SPACE - 1;
-
-       /*
-        * Note that even though we have initialized the key table, we don't
-        * set the FLUSHED bit.  This is contrary to RFC 3078, sec. 3.1.
-        */
-       state->bits = MPPE_BIT_ENCRYPTED;
-
-       state->unit = unit;
-       state->debug = debug;
-
-       return 1;
-}
-
-static int
-mppe_comp_init(void *arg, unsigned char *options, int optlen, int unit,
-              int hdrlen, int debug)
-{
-       /* ARGSUSED */
-       return mppe_init(arg, options, optlen, unit, debug, "mppe_comp_init");
-}
-
-/*
- * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
- * tell the compressor to rekey.  Note that we MUST NOT rekey for
- * every CCP Reset-Request; we only rekey on the next xmit packet.
- * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
- * So, rekeying for every CCP Reset-Request is broken as the peer will not
- * know how many times we've rekeyed.  (If we rekey and THEN get another
- * CCP Reset-Request, we must rekey again.)
- */
-static void mppe_comp_reset(void *arg)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-
-       state->bits |= MPPE_BIT_FLUSHED;
-}
-
-/*
- * Compress (encrypt) a packet.
- * It's strange to call this a compressor, since the output is always
- * MPPE_OVHD + 2 bytes larger than the input.
- */
-static int
-mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
-             int isize, int osize)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-       struct blkcipher_desc desc = { .tfm = state->arc4 };
-       int proto;
-       struct scatterlist sg_in[1], sg_out[1];
-
-       /*
-        * Check that the protocol is in the range we handle.
-        */
-       proto = PPP_PROTOCOL(ibuf);
-       if (proto < 0x0021 || proto > 0x00fa)
-               return 0;
-
-       /* Make sure we have enough room to generate an encrypted packet. */
-       if (osize < isize + MPPE_OVHD + 2) {
-               /* Drop the packet if we should encrypt it, but can't. */
-               printk(KERN_DEBUG "mppe_compress[%d]: osize too small! "
-                      "(have: %d need: %d)\n", state->unit,
-                      osize, osize + MPPE_OVHD + 2);
-               return -1;
-       }
-
-       osize = isize + MPPE_OVHD + 2;
-
-       /*
-        * Copy over the PPP header and set control bits.
-        */
-       obuf[0] = PPP_ADDRESS(ibuf);
-       obuf[1] = PPP_CONTROL(ibuf);
-       put_unaligned_be16(PPP_COMP, obuf + 2);
-       obuf += PPP_HDRLEN;
-
-       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
-       if (state->debug >= 7)
-               printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
-                      state->ccount);
-       put_unaligned_be16(state->ccount, obuf);
-
-       if (!state->stateful || /* stateless mode     */
-           ((state->ccount & 0xff) == 0xff) || /* "flag" packet      */
-           (state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request  */
-               /* We must rekey */
-               if (state->debug && state->stateful)
-                       printk(KERN_DEBUG "mppe_compress[%d]: rekeying\n",
-                              state->unit);
-               mppe_rekey(state, 0);
-               state->bits |= MPPE_BIT_FLUSHED;
-       }
-       obuf[0] |= state->bits;
-       state->bits &= ~MPPE_BIT_FLUSHED;       /* reset for next xmit */
-
-       obuf += MPPE_OVHD;
-       ibuf += 2;              /* skip to proto field */
-       isize -= 2;
-
-       /* Encrypt packet */
-       sg_init_table(sg_in, 1);
-       sg_init_table(sg_out, 1);
-       setup_sg(sg_in, ibuf, isize);
-       setup_sg(sg_out, obuf, osize);
-       if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in, isize) != 0) {
-               printk(KERN_DEBUG "crypto_cypher_encrypt failed\n");
-               return -1;
-       }
-
-       state->stats.unc_bytes += isize;
-       state->stats.unc_packets++;
-       state->stats.comp_bytes += osize;
-       state->stats.comp_packets++;
-
-       return osize;
-}
-
-/*
- * Since every frame grows by MPPE_OVHD + 2 bytes, this is always going
- * to look bad ... and the longer the link is up the worse it will get.
- */
-static void mppe_comp_stats(void *arg, struct compstat *stats)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-
-       *stats = state->stats;
-}
-
-static int
-mppe_decomp_init(void *arg, unsigned char *options, int optlen, int unit,
-                int hdrlen, int mru, int debug)
-{
-       /* ARGSUSED */
-       return mppe_init(arg, options, optlen, unit, debug, "mppe_decomp_init");
-}
-
-/*
- * We received a CCP Reset-Ack.  Just ignore it.
- */
-static void mppe_decomp_reset(void *arg)
-{
-       /* ARGSUSED */
-       return;
-}
-
-/*
- * Decompress (decrypt) an MPPE packet.
- */
-static int
-mppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
-               int osize)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-       struct blkcipher_desc desc = { .tfm = state->arc4 };
-       unsigned ccount;
-       int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
-       int sanity = 0;
-       struct scatterlist sg_in[1], sg_out[1];
-
-       if (isize <= PPP_HDRLEN + MPPE_OVHD) {
-               if (state->debug)
-                       printk(KERN_DEBUG
-                              "mppe_decompress[%d]: short pkt (%d)\n",
-                              state->unit, isize);
-               return DECOMP_ERROR;
-       }
-
-       /*
-        * Make sure we have enough room to decrypt the packet.
-        * Note that for our test we only subtract 1 byte whereas in
-        * mppe_compress() we added 2 bytes (+MPPE_OVHD);
-        * this is to account for possible PFC.
-        */
-       if (osize < isize - MPPE_OVHD - 1) {
-               printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! "
-                      "(have: %d need: %d)\n", state->unit,
-                      osize, isize - MPPE_OVHD - 1);
-               return DECOMP_ERROR;
-       }
-       osize = isize - MPPE_OVHD - 2;  /* assume no PFC */
-
-       ccount = MPPE_CCOUNT(ibuf);
-       if (state->debug >= 7)
-               printk(KERN_DEBUG "mppe_decompress[%d]: ccount %d\n",
-                      state->unit, ccount);
-
-       /* sanity checks -- terminate with extreme prejudice */
-       if (!(MPPE_BITS(ibuf) & MPPE_BIT_ENCRYPTED)) {
-               printk(KERN_DEBUG
-                      "mppe_decompress[%d]: ENCRYPTED bit not set!\n",
-                      state->unit);
-               state->sanity_errors += 100;
-               sanity = 1;
-       }
-       if (!state->stateful && !flushed) {
-               printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
-                      "stateless mode!\n", state->unit);
-               state->sanity_errors += 100;
-               sanity = 1;
-       }
-       if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
-               printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set on "
-                      "flag packet!\n", state->unit);
-               state->sanity_errors += 100;
-               sanity = 1;
-       }
-
-       if (sanity) {
-               if (state->sanity_errors < SANITY_MAX)
-                       return DECOMP_ERROR;
-               else
-                       /*
-                        * Take LCP down if the peer is sending too many bogons.
-                        * We don't want to do this for a single or just a few
-                        * instances since it could just be due to packet corruption.
-                        */
-                       return DECOMP_FATALERROR;
-       }
-
-       /*
-        * Check the coherency count.
-        */
-
-       if (!state->stateful) {
-               /* RFC 3078, sec 8.1.  Rekey for every packet. */
-               while (state->ccount != ccount) {
-                       mppe_rekey(state, 0);
-                       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
-               }
-       } else {
-               /* RFC 3078, sec 8.2. */
-               if (!state->discard) {
-                       /* normal state */
-                       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
-                       if (ccount != state->ccount) {
-                               /*
-                                * (ccount > state->ccount)
-                                * Packet loss detected, enter the discard state.
-                                * Signal the peer to rekey (by sending a CCP Reset-Request).
-                                */
-                               state->discard = 1;
-                               return DECOMP_ERROR;
-                       }
-               } else {
-                       /* discard state */
-                       if (!flushed) {
-                               /* ccp.c will be silent (no additional CCP Reset-Requests). */
-                               return DECOMP_ERROR;
-                       } else {
-                               /* Rekey for every missed "flag" packet. */
-                               while ((ccount & ~0xff) !=
-                                      (state->ccount & ~0xff)) {
-                                       mppe_rekey(state, 0);
-                                       state->ccount =
-                                           (state->ccount +
-                                            256) % MPPE_CCOUNT_SPACE;
-                               }
-
-                               /* reset */
-                               state->discard = 0;
-                               state->ccount = ccount;
-                               /*
-                                * Another problem with RFC 3078 here.  It implies that the
-                                * peer need not send a Reset-Ack packet.  But RFC 1962
-                                * requires it.  Hopefully, M$ does send a Reset-Ack; even
-                                * though it isn't required for MPPE synchronization, it is
-                                * required to reset CCP state.
-                                */
-                       }
-               }
-               if (flushed)
-                       mppe_rekey(state, 0);
-       }
-
-       /*
-        * Fill in the first part of the PPP header.  The protocol field
-        * comes from the decrypted data.
-        */
-       obuf[0] = PPP_ADDRESS(ibuf);    /* +1 */
-       obuf[1] = PPP_CONTROL(ibuf);    /* +1 */
-       obuf += 2;
-       ibuf += PPP_HDRLEN + MPPE_OVHD;
-       isize -= PPP_HDRLEN + MPPE_OVHD;        /* -6 */
-       /* net osize: isize-4 */
-
-       /*
-        * Decrypt the first byte in order to check if it is
-        * a compressed or uncompressed protocol field.
-        */
-       sg_init_table(sg_in, 1);
-       sg_init_table(sg_out, 1);
-       setup_sg(sg_in, ibuf, 1);
-       setup_sg(sg_out, obuf, 1);
-       if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, 1) != 0) {
-               printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
-               return DECOMP_ERROR;
-       }
-
-       /*
-        * Do PFC decompression.
-        * This would be nicer if we were given the actual sk_buff
-        * instead of a char *.
-        */
-       if ((obuf[0] & 0x01) != 0) {
-               obuf[1] = obuf[0];
-               obuf[0] = 0;
-               obuf++;
-               osize++;
-       }
-
-       /* And finally, decrypt the rest of the packet. */
-       setup_sg(sg_in, ibuf + 1, isize - 1);
-       setup_sg(sg_out, obuf + 1, osize - 1);
-       if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, isize - 1)) {
-               printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
-               return DECOMP_ERROR;
-       }
-
-       state->stats.unc_bytes += osize;
-       state->stats.unc_packets++;
-       state->stats.comp_bytes += isize;
-       state->stats.comp_packets++;
-
-       /* good packet credit */
-       state->sanity_errors >>= 1;
-
-       return osize;
-}
-
-/*
- * Incompressible data has arrived (this should never happen!).
- * We should probably drop the link if the protocol is in the range
- * of what should be encrypted.  At the least, we should drop this
- * packet.  (How to do this?)
- */
-static void mppe_incomp(void *arg, unsigned char *ibuf, int icnt)
-{
-       struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
-
-       if (state->debug &&
-           (PPP_PROTOCOL(ibuf) >= 0x0021 && PPP_PROTOCOL(ibuf) <= 0x00fa))
-               printk(KERN_DEBUG
-                      "mppe_incomp[%d]: incompressible (unencrypted) data! "
-                      "(proto %04x)\n", state->unit, PPP_PROTOCOL(ibuf));
-
-       state->stats.inc_bytes += icnt;
-       state->stats.inc_packets++;
-       state->stats.unc_bytes += icnt;
-       state->stats.unc_packets++;
-}
-
-/*************************************************************
- * Module interface table
- *************************************************************/
-
-/*
- * Procedures exported to if_ppp.c.
- */
-static struct compressor ppp_mppe = {
-       .compress_proto = CI_MPPE,
-       .comp_alloc     = mppe_alloc,
-       .comp_free      = mppe_free,
-       .comp_init      = mppe_comp_init,
-       .comp_reset     = mppe_comp_reset,
-       .compress       = mppe_compress,
-       .comp_stat      = mppe_comp_stats,
-       .decomp_alloc   = mppe_alloc,
-       .decomp_free    = mppe_free,
-       .decomp_init    = mppe_decomp_init,
-       .decomp_reset   = mppe_decomp_reset,
-       .decompress     = mppe_decompress,
-       .incomp         = mppe_incomp,
-       .decomp_stat    = mppe_comp_stats,
-       .owner          = THIS_MODULE,
-       .comp_extra     = MPPE_PAD,
-};
-
-/*
- * ppp_mppe_init()
- *
- * Prior to allowing load, try to load the arc4 and sha1 crypto
- * libraries.  The actual use will be allocated later, but
- * this way the module will fail to insmod if they aren't available.
- */
-
-static int __init ppp_mppe_init(void)
-{
-       int answer;
-       if (!(crypto_has_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC) &&
-             crypto_has_hash("sha1", 0, CRYPTO_ALG_ASYNC)))
-               return -ENODEV;
-
-       sha_pad = kmalloc(sizeof(struct sha_pad), GFP_KERNEL);
-       if (!sha_pad)
-               return -ENOMEM;
-       sha_pad_init(sha_pad);
-
-       answer = ppp_register_compressor(&ppp_mppe);
-
-       if (answer == 0)
-               printk(KERN_INFO "PPP MPPE Compression module registered\n");
-       else
-               kfree(sha_pad);
-
-       return answer;
-}
-
-static void __exit ppp_mppe_cleanup(void)
-{
-       ppp_unregister_compressor(&ppp_mppe);
-       kfree(sha_pad);
-}
-
-module_init(ppp_mppe_init);
-module_exit(ppp_mppe_cleanup);
diff --git a/drivers/net/ppp_mppe.h b/drivers/net/ppp_mppe.h
deleted file mode 100644 (file)
index 7a14e05..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#define MPPE_PAD                4      /* MPPE growth per frame */
-#define MPPE_MAX_KEY_LEN       16      /* largest key length (128-bit) */
-
-/* option bits for ccp_options.mppe */
-#define MPPE_OPT_40            0x01    /* 40 bit */
-#define MPPE_OPT_128           0x02    /* 128 bit */
-#define MPPE_OPT_STATEFUL      0x04    /* stateful mode */
-/* unsupported opts */
-#define MPPE_OPT_56            0x08    /* 56 bit */
-#define MPPE_OPT_MPPC          0x10    /* MPPC compression */
-#define MPPE_OPT_D             0x20    /* Unknown */
-#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
-#define MPPE_OPT_UNKNOWN       0x40    /* Bits !defined in RFC 3078 were set */
-
-/*
- * This is not nice ... the alternative is a bitfield struct though.
- * And unfortunately, we cannot share the same bits for the option
- * names above since C and H are the same bit.  We could do a u_int32
- * but then we have to do a htonl() all the time and/or we still need
- * to know which octet is which.
- */
-#define MPPE_C_BIT             0x01    /* MPPC */
-#define MPPE_D_BIT             0x10    /* Obsolete, usage unknown */
-#define MPPE_L_BIT             0x20    /* 40-bit */
-#define MPPE_S_BIT             0x40    /* 128-bit */
-#define MPPE_M_BIT             0x80    /* 56-bit, not supported */
-#define MPPE_H_BIT             0x01    /* Stateless (in a different byte) */
-
-/* Does not include H bit; used for least significant octet only. */
-#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
-
-/* Build a CI from mppe opts (see RFC 3078) */
-#define MPPE_OPTS_TO_CI(opts, ci)              \
-    do {                                       \
-       u_char *ptr = ci; /* u_char[4] */       \
-                                               \
-       /* H bit */                             \
-       if (opts & MPPE_OPT_STATEFUL)           \
-           *ptr++ = 0x0;                       \
-       else                                    \
-           *ptr++ = MPPE_H_BIT;                \
-       *ptr++ = 0;                             \
-       *ptr++ = 0;                             \
-                                               \
-       /* S,L bits */                          \
-       *ptr = 0;                               \
-       if (opts & MPPE_OPT_128)                \
-           *ptr |= MPPE_S_BIT;                 \
-       if (opts & MPPE_OPT_40)                 \
-           *ptr |= MPPE_L_BIT;                 \
-       /* M,D,C bits not supported */          \
-    } while (/* CONSTCOND */ 0)
-
-/* The reverse of the above */
-#define MPPE_CI_TO_OPTS(ci, opts)              \
-    do {                                       \
-       u_char *ptr = ci; /* u_char[4] */       \
-                                               \
-       opts = 0;                               \
-                                               \
-       /* H bit */                             \
-       if (!(ptr[0] & MPPE_H_BIT))             \
-           opts |= MPPE_OPT_STATEFUL;          \
-                                               \
-       /* S,L bits */                          \
-       if (ptr[3] & MPPE_S_BIT)                \
-           opts |= MPPE_OPT_128;               \
-       if (ptr[3] & MPPE_L_BIT)                \
-           opts |= MPPE_OPT_40;                \
-                                               \
-       /* M,D,C bits */                        \
-       if (ptr[3] & MPPE_M_BIT)                \
-           opts |= MPPE_OPT_56;                \
-       if (ptr[3] & MPPE_D_BIT)                \
-           opts |= MPPE_OPT_D;                 \
-       if (ptr[3] & MPPE_C_BIT)                \
-           opts |= MPPE_OPT_MPPC;              \
-                                               \
-       /* Other bits */                        \
-       if (ptr[0] & ~MPPE_H_BIT)               \
-           opts |= MPPE_OPT_UNKNOWN;           \
-       if (ptr[1] || ptr[2])                   \
-           opts |= MPPE_OPT_UNKNOWN;           \
-       if (ptr[3] & ~MPPE_ALL_BITS)            \
-           opts |= MPPE_OPT_UNKNOWN;           \
-    } while (/* CONSTCOND */ 0)
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
deleted file mode 100644 (file)
index 736a39e..0000000
+++ /dev/null
@@ -1,790 +0,0 @@
-/*
- * PPP synchronous tty channel driver for Linux.
- *
- * This is a ppp channel driver that can be used with tty device drivers
- * that are frame oriented, such as synchronous HDLC devices.
- *
- * Complete PPP frames without encoding/decoding are exchanged between
- * the channel driver and the device driver.
- *
- * The async map IOCTL codes are implemented to keep the user mode
- * applications happy if they call them. Synchronous PPP does not use
- * the async maps.
- *
- * Copyright 1999 Paul Mackerras.
- *
- * Also touched by the grubby hands of Paul Fulghum paulkf@microgate.com
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- *
- * This driver provides the encapsulation and framing for sending
- * and receiving PPP frames over sync serial lines.  It relies on
- * the generic PPP layer to give it frames to send and to process
- * received frames.  It implements the PPP line discipline.
- *
- * Part of the code in this driver was inspired by the old async-only
- * PPP driver, written by Michael Callahan and Al Longyear, and
- * subsequently hacked by Paul Mackerras.
- *
- * ==FILEVERSION 20040616==
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/tty.h>
-#include <linux/netdevice.h>
-#include <linux/poll.h>
-#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
-#include <linux/ppp_channel.h>
-#include <linux/spinlock.h>
-#include <linux/completion.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-#include <asm/uaccess.h>
-
-#define PPP_VERSION    "2.4.2"
-
-/* Structure for storing local state. */
-struct syncppp {
-       struct tty_struct *tty;
-       unsigned int    flags;
-       unsigned int    rbits;
-       int             mru;
-       spinlock_t      xmit_lock;
-       spinlock_t      recv_lock;
-       unsigned long   xmit_flags;
-       u32             xaccm[8];
-       u32             raccm;
-       unsigned int    bytes_sent;
-       unsigned int    bytes_rcvd;
-
-       struct sk_buff  *tpkt;
-       unsigned long   last_xmit;
-
-       struct sk_buff_head rqueue;
-
-       struct tasklet_struct tsk;
-
-       atomic_t        refcnt;
-       struct completion dead_cmp;
-       struct ppp_channel chan;        /* interface to generic ppp layer */
-};
-
-/* Bit numbers in xmit_flags */
-#define XMIT_WAKEUP    0
-#define XMIT_FULL      1
-
-/* Bits in rbits */
-#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-
-#define PPPSYNC_MAX_RQLEN      32      /* arbitrary */
-
-/*
- * Prototypes.
- */
-static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
-static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
-static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
-                         unsigned long arg);
-static void ppp_sync_process(unsigned long arg);
-static int ppp_sync_push(struct syncppp *ap);
-static void ppp_sync_flush_output(struct syncppp *ap);
-static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
-                          char *flags, int count);
-
-static const struct ppp_channel_ops sync_ops = {
-       .start_xmit = ppp_sync_send,
-       .ioctl      = ppp_sync_ioctl,
-};
-
-/*
- * Utility procedures to print a buffer in hex/ascii
- */
-static void
-ppp_print_hex (register __u8 * out, const __u8 * in, int count)
-{
-       register __u8 next_ch;
-       static const char hex[] = "0123456789ABCDEF";
-
-       while (count-- > 0) {
-               next_ch = *in++;
-               *out++ = hex[(next_ch >> 4) & 0x0F];
-               *out++ = hex[next_ch & 0x0F];
-               ++out;
-       }
-}
-
-static void
-ppp_print_char (register __u8 * out, const __u8 * in, int count)
-{
-       register __u8 next_ch;
-
-       while (count-- > 0) {
-               next_ch = *in++;
-
-               if (next_ch < 0x20 || next_ch > 0x7e)
-                       *out++ = '.';
-               else {
-                       *out++ = next_ch;
-                       if (next_ch == '%')   /* printk/syslogd has a bug !! */
-                               *out++ = '%';
-               }
-       }
-       *out = '\0';
-}
-
-static void
-ppp_print_buffer (const char *name, const __u8 *buf, int count)
-{
-       __u8 line[44];
-
-       if (name != NULL)
-               printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count);
-
-       while (count > 8) {
-               memset (line, 32, 44);
-               ppp_print_hex (line, buf, 8);
-               ppp_print_char (&line[8 * 3], buf, 8);
-               printk(KERN_DEBUG "%s\n", line);
-               count -= 8;
-               buf += 8;
-       }
-
-       if (count > 0) {
-               memset (line, 32, 44);
-               ppp_print_hex (line, buf, count);
-               ppp_print_char (&line[8 * 3], buf, count);
-               printk(KERN_DEBUG "%s\n", line);
-       }
-}
-
-
-/*
- * Routines implementing the synchronous PPP line discipline.
- */
-
-/*
- * We have a potential race on dereferencing tty->disc_data,
- * because the tty layer provides no locking at all - thus one
- * cpu could be running ppp_synctty_receive while another
- * calls ppp_synctty_close, which zeroes tty->disc_data and
- * frees the memory that ppp_synctty_receive is using.  The best
- * way to fix this is to use a rwlock in the tty struct, but for now
- * we use a single global rwlock for all ttys in ppp line discipline.
- *
- * FIXME: Fixed in tty_io nowadays.
- */
-static DEFINE_RWLOCK(disc_data_lock);
-
-static struct syncppp *sp_get(struct tty_struct *tty)
-{
-       struct syncppp *ap;
-
-       read_lock(&disc_data_lock);
-       ap = tty->disc_data;
-       if (ap != NULL)
-               atomic_inc(&ap->refcnt);
-       read_unlock(&disc_data_lock);
-       return ap;
-}
-
-static void sp_put(struct syncppp *ap)
-{
-       if (atomic_dec_and_test(&ap->refcnt))
-               complete(&ap->dead_cmp);
-}
-
-/*
- * Called when a tty is put into sync-PPP line discipline.
- */
-static int
-ppp_sync_open(struct tty_struct *tty)
-{
-       struct syncppp *ap;
-       int err;
-       int speed;
-
-       if (tty->ops->write == NULL)
-               return -EOPNOTSUPP;
-
-       ap = kzalloc(sizeof(*ap), GFP_KERNEL);
-       err = -ENOMEM;
-       if (!ap)
-               goto out;
-
-       /* initialize the syncppp structure */
-       ap->tty = tty;
-       ap->mru = PPP_MRU;
-       spin_lock_init(&ap->xmit_lock);
-       spin_lock_init(&ap->recv_lock);
-       ap->xaccm[0] = ~0U;
-       ap->xaccm[3] = 0x60000000U;
-       ap->raccm = ~0U;
-
-       skb_queue_head_init(&ap->rqueue);
-       tasklet_init(&ap->tsk, ppp_sync_process, (unsigned long) ap);
-
-       atomic_set(&ap->refcnt, 1);
-       init_completion(&ap->dead_cmp);
-
-       ap->chan.private = ap;
-       ap->chan.ops = &sync_ops;
-       ap->chan.mtu = PPP_MRU;
-       ap->chan.hdrlen = 2;    /* for A/C bytes */
-       speed = tty_get_baud_rate(tty);
-       ap->chan.speed = speed;
-       err = ppp_register_channel(&ap->chan);
-       if (err)
-               goto out_free;
-
-       tty->disc_data = ap;
-       tty->receive_room = 65536;
-       return 0;
-
- out_free:
-       kfree(ap);
- out:
-       return err;
-}
-
-/*
- * Called when the tty is put into another line discipline
- * or it hangs up.  We have to wait for any cpu currently
- * executing in any of the other ppp_synctty_* routines to
- * finish before we can call ppp_unregister_channel and free
- * the syncppp struct.  This routine must be called from
- * process context, not interrupt or softirq context.
- */
-static void
-ppp_sync_close(struct tty_struct *tty)
-{
-       struct syncppp *ap;
-
-       write_lock_irq(&disc_data_lock);
-       ap = tty->disc_data;
-       tty->disc_data = NULL;
-       write_unlock_irq(&disc_data_lock);
-       if (!ap)
-               return;
-
-       /*
-        * We have now ensured that nobody can start using ap from now
-        * on, but we have to wait for all existing users to finish.
-        * Note that ppp_unregister_channel ensures that no calls to
-        * our channel ops (i.e. ppp_sync_send/ioctl) are in progress
-        * by the time it returns.
-        */
-       if (!atomic_dec_and_test(&ap->refcnt))
-               wait_for_completion(&ap->dead_cmp);
-       tasklet_kill(&ap->tsk);
-
-       ppp_unregister_channel(&ap->chan);
-       skb_queue_purge(&ap->rqueue);
-       kfree_skb(ap->tpkt);
-       kfree(ap);
-}
-
-/*
- * Called on tty hangup in process context.
- *
- * Wait for I/O to driver to complete and unregister PPP channel.
- * This is already done by the close routine, so just call that.
- */
-static int ppp_sync_hangup(struct tty_struct *tty)
-{
-       ppp_sync_close(tty);
-       return 0;
-}
-
-/*
- * Read does nothing - no data is ever available this way.
- * Pppd reads and writes packets via /dev/ppp instead.
- */
-static ssize_t
-ppp_sync_read(struct tty_struct *tty, struct file *file,
-              unsigned char __user *buf, size_t count)
-{
-       return -EAGAIN;
-}
-
-/*
- * Write on the tty does nothing, the packets all come in
- * from the ppp generic stuff.
- */
-static ssize_t
-ppp_sync_write(struct tty_struct *tty, struct file *file,
-               const unsigned char *buf, size_t count)
-{
-       return -EAGAIN;
-}
-
-static int
-ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
-                 unsigned int cmd, unsigned long arg)
-{
-       struct syncppp *ap = sp_get(tty);
-       int __user *p = (int __user *)arg;
-       int err, val;
-
-       if (!ap)
-               return -ENXIO;
-       err = -EFAULT;
-       switch (cmd) {
-       case PPPIOCGCHAN:
-               err = -EFAULT;
-               if (put_user(ppp_channel_index(&ap->chan), p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGUNIT:
-               err = -EFAULT;
-               if (put_user(ppp_unit_number(&ap->chan), p))
-                       break;
-               err = 0;
-               break;
-
-       case TCFLSH:
-               /* flush our buffers and the serial port's buffer */
-               if (arg == TCIOFLUSH || arg == TCOFLUSH)
-                       ppp_sync_flush_output(ap);
-               err = tty_perform_flush(tty, arg);
-               break;
-
-       case FIONREAD:
-               val = 0;
-               if (put_user(val, p))
-                       break;
-               err = 0;
-               break;
-
-       default:
-               err = tty_mode_ioctl(tty, file, cmd, arg);
-               break;
-       }
-
-       sp_put(ap);
-       return err;
-}
-
-/* No kernel lock - fine */
-static unsigned int
-ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
-{
-       return 0;
-}
-
-/* May sleep, don't call from interrupt level or with interrupts disabled */
-static void
-ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
-                 char *cflags, int count)
-{
-       struct syncppp *ap = sp_get(tty);
-       unsigned long flags;
-
-       if (!ap)
-               return;
-       spin_lock_irqsave(&ap->recv_lock, flags);
-       ppp_sync_input(ap, buf, cflags, count);
-       spin_unlock_irqrestore(&ap->recv_lock, flags);
-       if (!skb_queue_empty(&ap->rqueue))
-               tasklet_schedule(&ap->tsk);
-       sp_put(ap);
-       tty_unthrottle(tty);
-}
-
-static void
-ppp_sync_wakeup(struct tty_struct *tty)
-{
-       struct syncppp *ap = sp_get(tty);
-
-       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-       if (!ap)
-               return;
-       set_bit(XMIT_WAKEUP, &ap->xmit_flags);
-       tasklet_schedule(&ap->tsk);
-       sp_put(ap);
-}
-
-
-static struct tty_ldisc_ops ppp_sync_ldisc = {
-       .owner  = THIS_MODULE,
-       .magic  = TTY_LDISC_MAGIC,
-       .name   = "pppsync",
-       .open   = ppp_sync_open,
-       .close  = ppp_sync_close,
-       .hangup = ppp_sync_hangup,
-       .read   = ppp_sync_read,
-       .write  = ppp_sync_write,
-       .ioctl  = ppp_synctty_ioctl,
-       .poll   = ppp_sync_poll,
-       .receive_buf = ppp_sync_receive,
-       .write_wakeup = ppp_sync_wakeup,
-};
-
-static int __init
-ppp_sync_init(void)
-{
-       int err;
-
-       err = tty_register_ldisc(N_SYNC_PPP, &ppp_sync_ldisc);
-       if (err != 0)
-               printk(KERN_ERR "PPP_sync: error %d registering line disc.\n",
-                      err);
-       return err;
-}
-
-/*
- * The following routines provide the PPP channel interface.
- */
-static int
-ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
-{
-       struct syncppp *ap = chan->private;
-       int err, val;
-       u32 accm[8];
-       void __user *argp = (void __user *)arg;
-       u32 __user *p = argp;
-
-       err = -EFAULT;
-       switch (cmd) {
-       case PPPIOCGFLAGS:
-               val = ap->flags | ap->rbits;
-               if (put_user(val, (int __user *) argp))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSFLAGS:
-               if (get_user(val, (int __user *) argp))
-                       break;
-               ap->flags = val & ~SC_RCV_BITS;
-               spin_lock_irq(&ap->recv_lock);
-               ap->rbits = val & SC_RCV_BITS;
-               spin_unlock_irq(&ap->recv_lock);
-               err = 0;
-               break;
-
-       case PPPIOCGASYNCMAP:
-               if (put_user(ap->xaccm[0], p))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSASYNCMAP:
-               if (get_user(ap->xaccm[0], p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGRASYNCMAP:
-               if (put_user(ap->raccm, p))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSRASYNCMAP:
-               if (get_user(ap->raccm, p))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCGXASYNCMAP:
-               if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSXASYNCMAP:
-               if (copy_from_user(accm, argp, sizeof(accm)))
-                       break;
-               accm[2] &= ~0x40000000U;        /* can't escape 0x5e */
-               accm[3] |= 0x60000000U;         /* must escape 0x7d, 0x7e */
-               memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
-               err = 0;
-               break;
-
-       case PPPIOCGMRU:
-               if (put_user(ap->mru, (int __user *) argp))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSMRU:
-               if (get_user(val, (int __user *) argp))
-                       break;
-               if (val < PPP_MRU)
-                       val = PPP_MRU;
-               ap->mru = val;
-               err = 0;
-               break;
-
-       default:
-               err = -ENOTTY;
-       }
-       return err;
-}
-
-/*
- * This is called at softirq level to deliver received packets
- * to the ppp_generic code, and to tell the ppp_generic code
- * if we can accept more output now.
- */
-static void ppp_sync_process(unsigned long arg)
-{
-       struct syncppp *ap = (struct syncppp *) arg;
-       struct sk_buff *skb;
-
-       /* process received packets */
-       while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
-               if (skb->len == 0) {
-                       /* zero length buffers indicate error */
-                       ppp_input_error(&ap->chan, 0);
-                       kfree_skb(skb);
-               }
-               else
-                       ppp_input(&ap->chan, skb);
-       }
-
-       /* try to push more stuff out */
-       if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap))
-               ppp_output_wakeup(&ap->chan);
-}
-
-/*
- * Procedures for encapsulation and framing.
- */
-
-static struct sk_buff*
-ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
-{
-       int proto;
-       unsigned char *data;
-       int islcp;
-
-       data  = skb->data;
-       proto = get_unaligned_be16(data);
-
-       /* LCP packets with codes between 1 (configure-request)
-        * and 7 (code-reject) must be sent as though no options
-        * have been negotiated.
-        */
-       islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
-
-       /* compress protocol field if option enabled */
-       if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp)
-               skb_pull(skb,1);
-
-       /* prepend address/control fields if necessary */
-       if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
-               if (skb_headroom(skb) < 2) {
-                       struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
-                       if (npkt == NULL) {
-                               kfree_skb(skb);
-                               return NULL;
-                       }
-                       skb_reserve(npkt,2);
-                       skb_copy_from_linear_data(skb,
-                                     skb_put(npkt, skb->len), skb->len);
-                       kfree_skb(skb);
-                       skb = npkt;
-               }
-               skb_push(skb,2);
-               skb->data[0] = PPP_ALLSTATIONS;
-               skb->data[1] = PPP_UI;
-       }
-
-       ap->last_xmit = jiffies;
-
-       if (skb && ap->flags & SC_LOG_OUTPKT)
-               ppp_print_buffer ("send buffer", skb->data, skb->len);
-
-       return skb;
-}
-
-/*
- * Transmit-side routines.
- */
-
-/*
- * Send a packet to the peer over an sync tty line.
- * Returns 1 iff the packet was accepted.
- * If the packet was not accepted, we will call ppp_output_wakeup
- * at some later time.
- */
-static int
-ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb)
-{
-       struct syncppp *ap = chan->private;
-
-       ppp_sync_push(ap);
-
-       if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
-               return 0;       /* already full */
-       skb = ppp_sync_txmunge(ap, skb);
-       if (skb != NULL)
-               ap->tpkt = skb;
-       else
-               clear_bit(XMIT_FULL, &ap->xmit_flags);
-
-       ppp_sync_push(ap);
-       return 1;
-}
-
-/*
- * Push as much data as possible out to the tty.
- */
-static int
-ppp_sync_push(struct syncppp *ap)
-{
-       int sent, done = 0;
-       struct tty_struct *tty = ap->tty;
-       int tty_stuffed = 0;
-
-       if (!spin_trylock_bh(&ap->xmit_lock))
-               return 0;
-       for (;;) {
-               if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
-                       tty_stuffed = 0;
-               if (!tty_stuffed && ap->tpkt) {
-                       set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-                       sent = tty->ops->write(tty, ap->tpkt->data, ap->tpkt->len);
-                       if (sent < 0)
-                               goto flush;     /* error, e.g. loss of CD */
-                       if (sent < ap->tpkt->len) {
-                               tty_stuffed = 1;
-                       } else {
-                               kfree_skb(ap->tpkt);
-                               ap->tpkt = NULL;
-                               clear_bit(XMIT_FULL, &ap->xmit_flags);
-                               done = 1;
-                       }
-                       continue;
-               }
-               /* haven't made any progress */
-               spin_unlock_bh(&ap->xmit_lock);
-               if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
-                     (!tty_stuffed && ap->tpkt)))
-                       break;
-               if (!spin_trylock_bh(&ap->xmit_lock))
-                       break;
-       }
-       return done;
-
-flush:
-       if (ap->tpkt) {
-               kfree_skb(ap->tpkt);
-               ap->tpkt = NULL;
-               clear_bit(XMIT_FULL, &ap->xmit_flags);
-               done = 1;
-       }
-       spin_unlock_bh(&ap->xmit_lock);
-       return done;
-}
-
-/*
- * Flush output from our internal buffers.
- * Called for the TCFLSH ioctl.
- */
-static void
-ppp_sync_flush_output(struct syncppp *ap)
-{
-       int done = 0;
-
-       spin_lock_bh(&ap->xmit_lock);
-       if (ap->tpkt != NULL) {
-               kfree_skb(ap->tpkt);
-               ap->tpkt = NULL;
-               clear_bit(XMIT_FULL, &ap->xmit_flags);
-               done = 1;
-       }
-       spin_unlock_bh(&ap->xmit_lock);
-       if (done)
-               ppp_output_wakeup(&ap->chan);
-}
-
-/*
- * Receive-side routines.
- */
-
-/* called when the tty driver has data for us.
- *
- * Data is frame oriented: each call to ppp_sync_input is considered
- * a whole frame. If the 1st flag byte is non-zero then the whole
- * frame is considered to be in error and is tossed.
- */
-static void
-ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
-               char *flags, int count)
-{
-       struct sk_buff *skb;
-       unsigned char *p;
-
-       if (count == 0)
-               return;
-
-       if (ap->flags & SC_LOG_INPKT)
-               ppp_print_buffer ("receive buffer", buf, count);
-
-       /* stuff the chars in the skb */
-       skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2);
-       if (!skb) {
-               printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
-               goto err;
-       }
-       /* Try to get the payload 4-byte aligned */
-       if (buf[0] != PPP_ALLSTATIONS)
-               skb_reserve(skb, 2 + (buf[0] & 1));
-
-       if (flags && *flags) {
-               /* error flag set, ignore frame */
-               goto err;
-       } else if (count > skb_tailroom(skb)) {
-               /* packet overflowed MRU */
-               goto err;
-       }
-
-       p = skb_put(skb, count);
-       memcpy(p, buf, count);
-
-       /* strip address/control field if present */
-       p = skb->data;
-       if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
-               /* chop off address/control */
-               if (skb->len < 3)
-                       goto err;
-               p = skb_pull(skb, 2);
-       }
-
-       /* decompress protocol field if compressed */
-       if (p[0] & 1) {
-               /* protocol is compressed */
-               skb_push(skb, 1)[0] = 0;
-       } else if (skb->len < 2)
-               goto err;
-
-       /* queue the frame to be processed */
-       skb_queue_tail(&ap->rqueue, skb);
-       return;
-
-err:
-       /* queue zero length packet as error indication */
-       if (skb || (skb = dev_alloc_skb(0))) {
-               skb_trim(skb, 0);
-               skb_queue_tail(&ap->rqueue, skb);
-       }
-}
-
-static void __exit
-ppp_sync_cleanup(void)
-{
-       if (tty_unregister_ldisc(N_SYNC_PPP) != 0)
-               printk(KERN_ERR "failed to unregister Sync PPP line discipline\n");
-}
-
-module_init(ppp_sync_init);
-module_exit(ppp_sync_cleanup);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_LDISC(N_SYNC_PPP);
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
deleted file mode 100644 (file)
index bc9a4bb..0000000
+++ /dev/null
@@ -1,1208 +0,0 @@
-/** -*- linux-c -*- ***********************************************************
- * Linux PPP over Ethernet (PPPoX/PPPoE) Sockets
- *
- * PPPoX --- Generic PPP encapsulation socket family
- * PPPoE --- PPP over Ethernet (RFC 2516)
- *
- *
- * Version:    0.7.0
- *
- * 070228 :    Fix to allow multiple sessions with same remote MAC and same
- *             session id by including the local device ifindex in the
- *             tuple identifying a session. This also ensures packets can't
- *             be injected into a session from interfaces other than the one
- *             specified by userspace. Florian Zumbiehl <florz@florz.de>
- *             (Oh, BTW, this one is YYMMDD, in case you were wondering ...)
- * 220102 :    Fix module use count on failure in pppoe_create, pppox_sk -acme
- * 030700 :    Fixed connect logic to allow for disconnect.
- * 270700 :    Fixed potential SMP problems; we must protect against
- *             simultaneous invocation of ppp_input
- *             and ppp_unregister_channel.
- * 040800 :    Respect reference count mechanisms on net-devices.
- * 200800 :    fix kfree(skb) in pppoe_rcv (acme)
- *             Module reference count is decremented in the right spot now,
- *             guards against sock_put not actually freeing the sk
- *             in pppoe_release.
- * 051000 :    Initialization cleanup.
- * 111100 :    Fix recvmsg.
- * 050101 :    Fix PADT procesing.
- * 140501 :    Use pppoe_rcv_core to handle all backlog. (Alexey)
- * 170701 :    Do not lock_sock with rwlock held. (DaveM)
- *             Ignore discovery frames if user has socket
- *             locked. (DaveM)
- *             Ignore return value of dev_queue_xmit in __pppoe_xmit
- *             or else we may kfree an SKB twice. (DaveM)
- * 190701 :    When doing copies of skb's in __pppoe_xmit, always delete
- *             the original skb that was passed in on success, never on
- *             failure.  Delete the copy of the skb on failure to avoid
- *             a memory leak.
- * 081001 :    Misc. cleanup (licence string, non-blocking, prevent
- *             reference of device on close).
- * 121301 :    New ppp channels interface; cannot unregister a channel
- *             from interrupts.  Thus, we mark the socket as a ZOMBIE
- *             and do the unregistration later.
- * 081002 :    seq_file support for proc stuff -acme
- * 111602 :    Merge all 2.4 fixes into 2.5/2.6 tree.  Label 2.5/2.6
- *             as version 0.7.  Spacing cleanup.
- * Author:     Michal Ostrowski <mostrows@speakeasy.net>
- * Contributors:
- *             Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *             David S. Miller (davem@redhat.com)
- *
- * License:
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/net.h>
-#include <linux/inetdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/if_ether.h>
-#include <linux/if_pppox.h>
-#include <linux/ppp_channel.h>
-#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
-#include <linux/notifier.h>
-#include <linux/file.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-
-#include <linux/nsproxy.h>
-#include <net/net_namespace.h>
-#include <net/netns/generic.h>
-#include <net/sock.h>
-
-#include <asm/uaccess.h>
-
-#define PPPOE_HASH_BITS 4
-#define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS)
-#define PPPOE_HASH_MASK        (PPPOE_HASH_SIZE - 1)
-
-static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb);
-
-static const struct proto_ops pppoe_ops;
-static const struct ppp_channel_ops pppoe_chan_ops;
-
-/* per-net private data for this module */
-static int pppoe_net_id __read_mostly;
-struct pppoe_net {
-       /*
-        * we could use _single_ hash table for all
-        * nets by injecting net id into the hash but
-        * it would increase hash chains and add
-        * a few additional math comparations messy
-        * as well, moreover in case of SMP less locking
-        * controversy here
-        */
-       struct pppox_sock *hash_table[PPPOE_HASH_SIZE];
-       rwlock_t hash_lock;
-};
-
-/*
- * PPPoE could be in the following stages:
- * 1) Discovery stage (to obtain remote MAC and Session ID)
- * 2) Session stage (MAC and SID are known)
- *
- * Ethernet frames have a special tag for this but
- * we use simpler approach based on session id
- */
-static inline bool stage_session(__be16 sid)
-{
-       return sid != 0;
-}
-
-static inline struct pppoe_net *pppoe_pernet(struct net *net)
-{
-       BUG_ON(!net);
-
-       return net_generic(net, pppoe_net_id);
-}
-
-static inline int cmp_2_addr(struct pppoe_addr *a, struct pppoe_addr *b)
-{
-       return a->sid == b->sid && !memcmp(a->remote, b->remote, ETH_ALEN);
-}
-
-static inline int cmp_addr(struct pppoe_addr *a, __be16 sid, char *addr)
-{
-       return a->sid == sid && !memcmp(a->remote, addr, ETH_ALEN);
-}
-
-#if 8 % PPPOE_HASH_BITS
-#error 8 must be a multiple of PPPOE_HASH_BITS
-#endif
-
-static int hash_item(__be16 sid, unsigned char *addr)
-{
-       unsigned char hash = 0;
-       unsigned int i;
-
-       for (i = 0; i < ETH_ALEN; i++)
-               hash ^= addr[i];
-       for (i = 0; i < sizeof(sid_t) * 8; i += 8)
-               hash ^= (__force __u32)sid >> i;
-       for (i = 8; (i >>= 1) >= PPPOE_HASH_BITS;)
-               hash ^= hash >> i;
-
-       return hash & PPPOE_HASH_MASK;
-}
-
-/**********************************************************************
- *
- *  Set/get/delete/rehash items  (internal versions)
- *
- **********************************************************************/
-static struct pppox_sock *__get_item(struct pppoe_net *pn, __be16 sid,
-                               unsigned char *addr, int ifindex)
-{
-       int hash = hash_item(sid, addr);
-       struct pppox_sock *ret;
-
-       ret = pn->hash_table[hash];
-       while (ret) {
-               if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
-                   ret->pppoe_ifindex == ifindex)
-                       return ret;
-
-               ret = ret->next;
-       }
-
-       return NULL;
-}
-
-static int __set_item(struct pppoe_net *pn, struct pppox_sock *po)
-{
-       int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
-       struct pppox_sock *ret;
-
-       ret = pn->hash_table[hash];
-       while (ret) {
-               if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) &&
-                   ret->pppoe_ifindex == po->pppoe_ifindex)
-                       return -EALREADY;
-
-               ret = ret->next;
-       }
-
-       po->next = pn->hash_table[hash];
-       pn->hash_table[hash] = po;
-
-       return 0;
-}
-
-static struct pppox_sock *__delete_item(struct pppoe_net *pn, __be16 sid,
-                                       char *addr, int ifindex)
-{
-       int hash = hash_item(sid, addr);
-       struct pppox_sock *ret, **src;
-
-       ret = pn->hash_table[hash];
-       src = &pn->hash_table[hash];
-
-       while (ret) {
-               if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
-                   ret->pppoe_ifindex == ifindex) {
-                       *src = ret->next;
-                       break;
-               }
-
-               src = &ret->next;
-               ret = ret->next;
-       }
-
-       return ret;
-}
-
-/**********************************************************************
- *
- *  Set/get/delete/rehash items
- *
- **********************************************************************/
-static inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid,
-                                       unsigned char *addr, int ifindex)
-{
-       struct pppox_sock *po;
-
-       read_lock_bh(&pn->hash_lock);
-       po = __get_item(pn, sid, addr, ifindex);
-       if (po)
-               sock_hold(sk_pppox(po));
-       read_unlock_bh(&pn->hash_lock);
-
-       return po;
-}
-
-static inline struct pppox_sock *get_item_by_addr(struct net *net,
-                                               struct sockaddr_pppox *sp)
-{
-       struct net_device *dev;
-       struct pppoe_net *pn;
-       struct pppox_sock *pppox_sock = NULL;
-
-       int ifindex;
-
-       rcu_read_lock();
-       dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev);
-       if (dev) {
-               ifindex = dev->ifindex;
-               pn = pppoe_pernet(net);
-               pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid,
-                               sp->sa_addr.pppoe.remote, ifindex);
-       }
-       rcu_read_unlock();
-       return pppox_sock;
-}
-
-static inline struct pppox_sock *delete_item(struct pppoe_net *pn, __be16 sid,
-                                       char *addr, int ifindex)
-{
-       struct pppox_sock *ret;
-
-       write_lock_bh(&pn->hash_lock);
-       ret = __delete_item(pn, sid, addr, ifindex);
-       write_unlock_bh(&pn->hash_lock);
-
-       return ret;
-}
-
-/***************************************************************************
- *
- *  Handler for device events.
- *  Certain device events require that sockets be unconnected.
- *
- **************************************************************************/
-
-static void pppoe_flush_dev(struct net_device *dev)
-{
-       struct pppoe_net *pn;
-       int i;
-
-       pn = pppoe_pernet(dev_net(dev));
-       write_lock_bh(&pn->hash_lock);
-       for (i = 0; i < PPPOE_HASH_SIZE; i++) {
-               struct pppox_sock *po = pn->hash_table[i];
-               struct sock *sk;
-
-               while (po) {
-                       while (po && po->pppoe_dev != dev) {
-                               po = po->next;
-                       }
-
-                       if (!po)
-                               break;
-
-                       sk = sk_pppox(po);
-
-                       /* We always grab the socket lock, followed by the
-                        * hash_lock, in that order.  Since we should hold the
-                        * sock lock while doing any unbinding, we need to
-                        * release the lock we're holding.  Hold a reference to
-                        * the sock so it doesn't disappear as we're jumping
-                        * between locks.
-                        */
-
-                       sock_hold(sk);
-                       write_unlock_bh(&pn->hash_lock);
-                       lock_sock(sk);
-
-                       if (po->pppoe_dev == dev &&
-                           sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
-                               pppox_unbind_sock(sk);
-                               sk->sk_state = PPPOX_ZOMBIE;
-                               sk->sk_state_change(sk);
-                               po->pppoe_dev = NULL;
-                               dev_put(dev);
-                       }
-
-                       release_sock(sk);
-                       sock_put(sk);
-
-                       /* Restart the process from the start of the current
-                        * hash chain. We dropped locks so the world may have
-                        * change from underneath us.
-                        */
-
-                       BUG_ON(pppoe_pernet(dev_net(dev)) == NULL);
-                       write_lock_bh(&pn->hash_lock);
-                       po = pn->hash_table[i];
-               }
-       }
-       write_unlock_bh(&pn->hash_lock);
-}
-
-static int pppoe_device_event(struct notifier_block *this,
-                             unsigned long event, void *ptr)
-{
-       struct net_device *dev = (struct net_device *)ptr;
-
-       /* Only look at sockets that are using this specific device. */
-       switch (event) {
-       case NETDEV_CHANGEADDR:
-       case NETDEV_CHANGEMTU:
-               /* A change in mtu or address is a bad thing, requiring
-                * LCP re-negotiation.
-                */
-
-       case NETDEV_GOING_DOWN:
-       case NETDEV_DOWN:
-               /* Find every socket on this device and kill it. */
-               pppoe_flush_dev(dev);
-               break;
-
-       default:
-               break;
-       }
-
-       return NOTIFY_DONE;
-}
-
-static struct notifier_block pppoe_notifier = {
-       .notifier_call = pppoe_device_event,
-};
-
-/************************************************************************
- *
- * Do the real work of receiving a PPPoE Session frame.
- *
- ***********************************************************************/
-static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
-{
-       struct pppox_sock *po = pppox_sk(sk);
-       struct pppox_sock *relay_po;
-
-       /* Backlog receive. Semantics of backlog rcv preclude any code from
-        * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state
-        * can't change.
-        */
-
-       if (sk->sk_state & PPPOX_BOUND) {
-               ppp_input(&po->chan, skb);
-       } else if (sk->sk_state & PPPOX_RELAY) {
-               relay_po = get_item_by_addr(sock_net(sk),
-                                           &po->pppoe_relay);
-               if (relay_po == NULL)
-                       goto abort_kfree;
-
-               if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0)
-                       goto abort_put;
-
-               if (!__pppoe_xmit(sk_pppox(relay_po), skb))
-                       goto abort_put;
-       } else {
-               if (sock_queue_rcv_skb(sk, skb))
-                       goto abort_kfree;
-       }
-
-       return NET_RX_SUCCESS;
-
-abort_put:
-       sock_put(sk_pppox(relay_po));
-
-abort_kfree:
-       kfree_skb(skb);
-       return NET_RX_DROP;
-}
-
-/************************************************************************
- *
- * Receive wrapper called in BH context.
- *
- ***********************************************************************/
-static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
-                    struct packet_type *pt, struct net_device *orig_dev)
-{
-       struct pppoe_hdr *ph;
-       struct pppox_sock *po;
-       struct pppoe_net *pn;
-       int len;
-
-       skb = skb_share_check(skb, GFP_ATOMIC);
-       if (!skb)
-               goto out;
-
-       if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
-               goto drop;
-
-       ph = pppoe_hdr(skb);
-       len = ntohs(ph->length);
-
-       skb_pull_rcsum(skb, sizeof(*ph));
-       if (skb->len < len)
-               goto drop;
-
-       if (pskb_trim_rcsum(skb, len))
-               goto drop;
-
-       pn = pppoe_pernet(dev_net(dev));
-
-       /* Note that get_item does a sock_hold(), so sk_pppox(po)
-        * is known to be safe.
-        */
-       po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
-       if (!po)
-               goto drop;
-
-       return sk_receive_skb(sk_pppox(po), skb, 0);
-
-drop:
-       kfree_skb(skb);
-out:
-       return NET_RX_DROP;
-}
-
-/************************************************************************
- *
- * Receive a PPPoE Discovery frame.
- * This is solely for detection of PADT frames
- *
- ***********************************************************************/
-static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
-                         struct packet_type *pt, struct net_device *orig_dev)
-
-{
-       struct pppoe_hdr *ph;
-       struct pppox_sock *po;
-       struct pppoe_net *pn;
-
-       skb = skb_share_check(skb, GFP_ATOMIC);
-       if (!skb)
-               goto out;
-
-       if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
-               goto abort;
-
-       ph = pppoe_hdr(skb);
-       if (ph->code != PADT_CODE)
-               goto abort;
-
-       pn = pppoe_pernet(dev_net(dev));
-       po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
-       if (po) {
-               struct sock *sk = sk_pppox(po);
-
-               bh_lock_sock(sk);
-
-               /* If the user has locked the socket, just ignore
-                * the packet.  With the way two rcv protocols hook into
-                * one socket family type, we cannot (easily) distinguish
-                * what kind of SKB it is during backlog rcv.
-                */
-               if (sock_owned_by_user(sk) == 0) {
-                       /* We're no longer connect at the PPPOE layer,
-                        * and must wait for ppp channel to disconnect us.
-                        */
-                       sk->sk_state = PPPOX_ZOMBIE;
-               }
-
-               bh_unlock_sock(sk);
-               sock_put(sk);
-       }
-
-abort:
-       kfree_skb(skb);
-out:
-       return NET_RX_SUCCESS; /* Lies... :-) */
-}
-
-static struct packet_type pppoes_ptype __read_mostly = {
-       .type   = cpu_to_be16(ETH_P_PPP_SES),
-       .func   = pppoe_rcv,
-};
-
-static struct packet_type pppoed_ptype __read_mostly = {
-       .type   = cpu_to_be16(ETH_P_PPP_DISC),
-       .func   = pppoe_disc_rcv,
-};
-
-static struct proto pppoe_sk_proto __read_mostly = {
-       .name     = "PPPOE",
-       .owner    = THIS_MODULE,
-       .obj_size = sizeof(struct pppox_sock),
-};
-
-/***********************************************************************
- *
- * Initialize a new struct sock.
- *
- **********************************************************************/
-static int pppoe_create(struct net *net, struct socket *sock)
-{
-       struct sock *sk;
-
-       sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto);
-       if (!sk)
-               return -ENOMEM;
-
-       sock_init_data(sock, sk);
-
-       sock->state     = SS_UNCONNECTED;
-       sock->ops       = &pppoe_ops;
-
-       sk->sk_backlog_rcv      = pppoe_rcv_core;
-       sk->sk_state            = PPPOX_NONE;
-       sk->sk_type             = SOCK_STREAM;
-       sk->sk_family           = PF_PPPOX;
-       sk->sk_protocol         = PX_PROTO_OE;
-
-       return 0;
-}
-
-static int pppoe_release(struct socket *sock)
-{
-       struct sock *sk = sock->sk;
-       struct pppox_sock *po;
-       struct pppoe_net *pn;
-       struct net *net = NULL;
-
-       if (!sk)
-               return 0;
-
-       lock_sock(sk);
-       if (sock_flag(sk, SOCK_DEAD)) {
-               release_sock(sk);
-               return -EBADF;
-       }
-
-       po = pppox_sk(sk);
-
-       if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
-               dev_put(po->pppoe_dev);
-               po->pppoe_dev = NULL;
-       }
-
-       pppox_unbind_sock(sk);
-
-       /* Signal the death of the socket. */
-       sk->sk_state = PPPOX_DEAD;
-
-       net = sock_net(sk);
-       pn = pppoe_pernet(net);
-
-       /*
-        * protect "po" from concurrent updates
-        * on pppoe_flush_dev
-        */
-       delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote,
-                   po->pppoe_ifindex);
-
-       sock_orphan(sk);
-       sock->sk = NULL;
-
-       skb_queue_purge(&sk->sk_receive_queue);
-       release_sock(sk);
-       sock_put(sk);
-
-       return 0;
-}
-
-static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
-                 int sockaddr_len, int flags)
-{
-       struct sock *sk = sock->sk;
-       struct sockaddr_pppox *sp = (struct sockaddr_pppox *)uservaddr;
-       struct pppox_sock *po = pppox_sk(sk);
-       struct net_device *dev = NULL;
-       struct pppoe_net *pn;
-       struct net *net = NULL;
-       int error;
-
-       lock_sock(sk);
-
-       error = -EINVAL;
-       if (sp->sa_protocol != PX_PROTO_OE)
-               goto end;
-
-       /* Check for already bound sockets */
-       error = -EBUSY;
-       if ((sk->sk_state & PPPOX_CONNECTED) &&
-            stage_session(sp->sa_addr.pppoe.sid))
-               goto end;
-
-       /* Check for already disconnected sockets, on attempts to disconnect */
-       error = -EALREADY;
-       if ((sk->sk_state & PPPOX_DEAD) &&
-            !stage_session(sp->sa_addr.pppoe.sid))
-               goto end;
-
-       error = 0;
-
-       /* Delete the old binding */
-       if (stage_session(po->pppoe_pa.sid)) {
-               pppox_unbind_sock(sk);
-               pn = pppoe_pernet(sock_net(sk));
-               delete_item(pn, po->pppoe_pa.sid,
-                           po->pppoe_pa.remote, po->pppoe_ifindex);
-               if (po->pppoe_dev) {
-                       dev_put(po->pppoe_dev);
-                       po->pppoe_dev = NULL;
-               }
-
-               memset(sk_pppox(po) + 1, 0,
-                      sizeof(struct pppox_sock) - sizeof(struct sock));
-               sk->sk_state = PPPOX_NONE;
-       }
-
-       /* Re-bind in session stage only */
-       if (stage_session(sp->sa_addr.pppoe.sid)) {
-               error = -ENODEV;
-               net = sock_net(sk);
-               dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev);
-               if (!dev)
-                       goto err_put;
-
-               po->pppoe_dev = dev;
-               po->pppoe_ifindex = dev->ifindex;
-               pn = pppoe_pernet(net);
-               if (!(dev->flags & IFF_UP)) {
-                       goto err_put;
-               }
-
-               memcpy(&po->pppoe_pa,
-                      &sp->sa_addr.pppoe,
-                      sizeof(struct pppoe_addr));
-
-               write_lock_bh(&pn->hash_lock);
-               error = __set_item(pn, po);
-               write_unlock_bh(&pn->hash_lock);
-               if (error < 0)
-                       goto err_put;
-
-               po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
-                                  dev->hard_header_len);
-
-               po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr);
-               po->chan.private = sk;
-               po->chan.ops = &pppoe_chan_ops;
-
-               error = ppp_register_net_channel(dev_net(dev), &po->chan);
-               if (error) {
-                       delete_item(pn, po->pppoe_pa.sid,
-                                   po->pppoe_pa.remote, po->pppoe_ifindex);
-                       goto err_put;
-               }
-
-               sk->sk_state = PPPOX_CONNECTED;
-       }
-
-       po->num = sp->sa_addr.pppoe.sid;
-
-end:
-       release_sock(sk);
-       return error;
-err_put:
-       if (po->pppoe_dev) {
-               dev_put(po->pppoe_dev);
-               po->pppoe_dev = NULL;
-       }
-       goto end;
-}
-
-static int pppoe_getname(struct socket *sock, struct sockaddr *uaddr,
-                 int *usockaddr_len, int peer)
-{
-       int len = sizeof(struct sockaddr_pppox);
-       struct sockaddr_pppox sp;
-
-       sp.sa_family    = AF_PPPOX;
-       sp.sa_protocol  = PX_PROTO_OE;
-       memcpy(&sp.sa_addr.pppoe, &pppox_sk(sock->sk)->pppoe_pa,
-              sizeof(struct pppoe_addr));
-
-       memcpy(uaddr, &sp, len);
-
-       *usockaddr_len = len;
-
-       return 0;
-}
-
-static int pppoe_ioctl(struct socket *sock, unsigned int cmd,
-               unsigned long arg)
-{
-       struct sock *sk = sock->sk;
-       struct pppox_sock *po = pppox_sk(sk);
-       int val;
-       int err;
-
-       switch (cmd) {
-       case PPPIOCGMRU:
-               err = -ENXIO;
-               if (!(sk->sk_state & PPPOX_CONNECTED))
-                       break;
-
-               err = -EFAULT;
-               if (put_user(po->pppoe_dev->mtu -
-                            sizeof(struct pppoe_hdr) -
-                            PPP_HDRLEN,
-                            (int __user *)arg))
-                       break;
-               err = 0;
-               break;
-
-       case PPPIOCSMRU:
-               err = -ENXIO;
-               if (!(sk->sk_state & PPPOX_CONNECTED))
-                       break;
-
-               err = -EFAULT;
-               if (get_user(val, (int __user *)arg))
-                       break;
-
-               if (val < (po->pppoe_dev->mtu
-                          - sizeof(struct pppoe_hdr)
-                          - PPP_HDRLEN))
-                       err = 0;
-               else
-                       err = -EINVAL;
-               break;
-
-       case PPPIOCSFLAGS:
-               err = -EFAULT;
-               if (get_user(val, (int __user *)arg))
-                       break;
-               err = 0;
-               break;
-
-       case PPPOEIOCSFWD:
-       {
-               struct pppox_sock *relay_po;
-
-               err = -EBUSY;
-               if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD))
-                       break;
-
-               err = -ENOTCONN;
-               if (!(sk->sk_state & PPPOX_CONNECTED))
-                       break;
-
-               /* PPPoE address from the user specifies an outbound
-                  PPPoE address which frames are forwarded to */
-               err = -EFAULT;
-               if (copy_from_user(&po->pppoe_relay,
-                                  (void __user *)arg,
-                                  sizeof(struct sockaddr_pppox)))
-                       break;
-
-               err = -EINVAL;
-               if (po->pppoe_relay.sa_family != AF_PPPOX ||
-                   po->pppoe_relay.sa_protocol != PX_PROTO_OE)
-                       break;
-
-               /* Check that the socket referenced by the address
-                  actually exists. */
-               relay_po = get_item_by_addr(sock_net(sk), &po->pppoe_relay);
-               if (!relay_po)
-                       break;
-
-               sock_put(sk_pppox(relay_po));
-               sk->sk_state |= PPPOX_RELAY;
-               err = 0;
-               break;
-       }
-
-       case PPPOEIOCDFWD:
-               err = -EALREADY;
-               if (!(sk->sk_state & PPPOX_RELAY))
-                       break;
-
-               sk->sk_state &= ~PPPOX_RELAY;
-               err = 0;
-               break;
-
-       default:
-               err = -ENOTTY;
-       }
-
-       return err;
-}
-
-static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock,
-                 struct msghdr *m, size_t total_len)
-{
-       struct sk_buff *skb;
-       struct sock *sk = sock->sk;
-       struct pppox_sock *po = pppox_sk(sk);
-       int error;
-       struct pppoe_hdr hdr;
-       struct pppoe_hdr *ph;
-       struct net_device *dev;
-       char *start;
-
-       lock_sock(sk);
-       if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) {
-               error = -ENOTCONN;
-               goto end;
-       }
-
-       hdr.ver = 1;
-       hdr.type = 1;
-       hdr.code = 0;
-       hdr.sid = po->num;
-
-       dev = po->pppoe_dev;
-
-       error = -EMSGSIZE;
-       if (total_len > (dev->mtu + dev->hard_header_len))
-               goto end;
-
-
-       skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32,
-                          0, GFP_KERNEL);
-       if (!skb) {
-               error = -ENOMEM;
-               goto end;
-       }
-
-       /* Reserve space for headers. */
-       skb_reserve(skb, dev->hard_header_len);
-       skb_reset_network_header(skb);
-
-       skb->dev = dev;
-
-       skb->priority = sk->sk_priority;
-       skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
-
-       ph = (struct pppoe_hdr *)skb_put(skb, total_len + sizeof(struct pppoe_hdr));
-       start = (char *)&ph->tag[0];
-
-       error = memcpy_fromiovec(start, m->msg_iov, total_len);
-       if (error < 0) {
-               kfree_skb(skb);
-               goto end;
-       }
-
-       error = total_len;
-       dev_hard_header(skb, dev, ETH_P_PPP_SES,
-                       po->pppoe_pa.remote, NULL, total_len);
-
-       memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
-
-       ph->length = htons(total_len);
-
-       dev_queue_xmit(skb);
-
-end:
-       release_sock(sk);
-       return error;
-}
-
-/************************************************************************
- *
- * xmit function for internal use.
- *
- ***********************************************************************/
-static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
-{
-       struct pppox_sock *po = pppox_sk(sk);
-       struct net_device *dev = po->pppoe_dev;
-       struct pppoe_hdr *ph;
-       int data_len = skb->len;
-
-       /* The higher-level PPP code (ppp_unregister_channel()) ensures the PPP
-        * xmit operations conclude prior to an unregistration call.  Thus
-        * sk->sk_state cannot change, so we don't need to do lock_sock().
-        * But, we also can't do a lock_sock since that introduces a potential
-        * deadlock as we'd reverse the lock ordering used when calling
-        * ppp_unregister_channel().
-        */
-
-       if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
-               goto abort;
-
-       if (!dev)
-               goto abort;
-
-       /* Copy the data if there is no space for the header or if it's
-        * read-only.
-        */
-       if (skb_cow_head(skb, sizeof(*ph) + dev->hard_header_len))
-               goto abort;
-
-       __skb_push(skb, sizeof(*ph));
-       skb_reset_network_header(skb);
-
-       ph = pppoe_hdr(skb);
-       ph->ver = 1;
-       ph->type = 1;
-       ph->code = 0;
-       ph->sid = po->num;
-       ph->length = htons(data_len);
-
-       skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
-       skb->dev = dev;
-
-       dev_hard_header(skb, dev, ETH_P_PPP_SES,
-                       po->pppoe_pa.remote, NULL, data_len);
-
-       dev_queue_xmit(skb);
-       return 1;
-
-abort:
-       kfree_skb(skb);
-       return 1;
-}
-
-/************************************************************************
- *
- * xmit function called by generic PPP driver
- * sends PPP frame over PPPoE socket
- *
- ***********************************************************************/
-static int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb)
-{
-       struct sock *sk = (struct sock *)chan->private;
-       return __pppoe_xmit(sk, skb);
-}
-
-static const struct ppp_channel_ops pppoe_chan_ops = {
-       .start_xmit = pppoe_xmit,
-};
-
-static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
-                 struct msghdr *m, size_t total_len, int flags)
-{
-       struct sock *sk = sock->sk;
-       struct sk_buff *skb;
-       int error = 0;
-
-       if (sk->sk_state & PPPOX_BOUND) {
-               error = -EIO;
-               goto end;
-       }
-
-       skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
-                               flags & MSG_DONTWAIT, &error);
-       if (error < 0)
-               goto end;
-
-       m->msg_namelen = 0;
-
-       if (skb) {
-               total_len = min_t(size_t, total_len, skb->len);
-               error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
-               if (error == 0)
-                       error = total_len;
-       }
-
-       kfree_skb(skb);
-end:
-       return error;
-}
-
-#ifdef CONFIG_PROC_FS
-static int pppoe_seq_show(struct seq_file *seq, void *v)
-{
-       struct pppox_sock *po;
-       char *dev_name;
-
-       if (v == SEQ_START_TOKEN) {
-               seq_puts(seq, "Id       Address              Device\n");
-               goto out;
-       }
-
-       po = v;
-       dev_name = po->pppoe_pa.dev;
-
-       seq_printf(seq, "%08X %pM %8s\n",
-               po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
-out:
-       return 0;
-}
-
-static inline struct pppox_sock *pppoe_get_idx(struct pppoe_net *pn, loff_t pos)
-{
-       struct pppox_sock *po;
-       int i;
-
-       for (i = 0; i < PPPOE_HASH_SIZE; i++) {
-               po = pn->hash_table[i];
-               while (po) {
-                       if (!pos--)
-                               goto out;
-                       po = po->next;
-               }
-       }
-
-out:
-       return po;
-}
-
-static void *pppoe_seq_start(struct seq_file *seq, loff_t *pos)
-       __acquires(pn->hash_lock)
-{
-       struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
-       loff_t l = *pos;
-
-       read_lock_bh(&pn->hash_lock);
-       return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN;
-}
-
-static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
-       struct pppox_sock *po;
-
-       ++*pos;
-       if (v == SEQ_START_TOKEN) {
-               po = pppoe_get_idx(pn, 0);
-               goto out;
-       }
-       po = v;
-       if (po->next)
-               po = po->next;
-       else {
-               int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
-
-               po = NULL;
-               while (++hash < PPPOE_HASH_SIZE) {
-                       po = pn->hash_table[hash];
-                       if (po)
-                               break;
-               }
-       }
-
-out:
-       return po;
-}
-
-static void pppoe_seq_stop(struct seq_file *seq, void *v)
-       __releases(pn->hash_lock)
-{
-       struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
-       read_unlock_bh(&pn->hash_lock);
-}
-
-static const struct seq_operations pppoe_seq_ops = {
-       .start          = pppoe_seq_start,
-       .next           = pppoe_seq_next,
-       .stop           = pppoe_seq_stop,
-       .show           = pppoe_seq_show,
-};
-
-static int pppoe_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open_net(inode, file, &pppoe_seq_ops,
-                       sizeof(struct seq_net_private));
-}
-
-static const struct file_operations pppoe_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = pppoe_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release_net,
-};
-
-#endif /* CONFIG_PROC_FS */
-
-static const struct proto_ops pppoe_ops = {
-       .family         = AF_PPPOX,
-       .owner          = THIS_MODULE,
-       .release        = pppoe_release,
-       .bind           = sock_no_bind,
-       .connect        = pppoe_connect,
-       .socketpair     = sock_no_socketpair,
-       .accept         = sock_no_accept,
-       .getname        = pppoe_getname,
-       .poll           = datagram_poll,
-       .listen         = sock_no_listen,
-       .shutdown       = sock_no_shutdown,
-       .setsockopt     = sock_no_setsockopt,
-       .getsockopt     = sock_no_getsockopt,
-       .sendmsg        = pppoe_sendmsg,
-       .recvmsg        = pppoe_recvmsg,
-       .mmap           = sock_no_mmap,
-       .ioctl          = pppox_ioctl,
-};
-
-static const struct pppox_proto pppoe_proto = {
-       .create = pppoe_create,
-       .ioctl  = pppoe_ioctl,
-       .owner  = THIS_MODULE,
-};
-
-static __net_init int pppoe_init_net(struct net *net)
-{
-       struct pppoe_net *pn = pppoe_pernet(net);
-       struct proc_dir_entry *pde;
-
-       rwlock_init(&pn->hash_lock);
-
-       pde = proc_net_fops_create(net, "pppoe", S_IRUGO, &pppoe_seq_fops);
-#ifdef CONFIG_PROC_FS
-       if (!pde)
-               return -ENOMEM;
-#endif
-
-       return 0;
-}
-
-static __net_exit void pppoe_exit_net(struct net *net)
-{
-       proc_net_remove(net, "pppoe");
-}
-
-static struct pernet_operations pppoe_net_ops = {
-       .init = pppoe_init_net,
-       .exit = pppoe_exit_net,
-       .id   = &pppoe_net_id,
-       .size = sizeof(struct pppoe_net),
-};
-
-static int __init pppoe_init(void)
-{
-       int err;
-
-       err = register_pernet_device(&pppoe_net_ops);
-       if (err)
-               goto out;
-
-       err = proto_register(&pppoe_sk_proto, 0);
-       if (err)
-               goto out_unregister_net_ops;
-
-       err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto);
-       if (err)
-               goto out_unregister_pppoe_proto;
-
-       dev_add_pack(&pppoes_ptype);
-       dev_add_pack(&pppoed_ptype);
-       register_netdevice_notifier(&pppoe_notifier);
-
-       return 0;
-
-out_unregister_pppoe_proto:
-       proto_unregister(&pppoe_sk_proto);
-out_unregister_net_ops:
-       unregister_pernet_device(&pppoe_net_ops);
-out:
-       return err;
-}
-
-static void __exit pppoe_exit(void)
-{
-       unregister_netdevice_notifier(&pppoe_notifier);
-       dev_remove_pack(&pppoed_ptype);
-       dev_remove_pack(&pppoes_ptype);
-       unregister_pppox_proto(PX_PROTO_OE);
-       proto_unregister(&pppoe_sk_proto);
-       unregister_pernet_device(&pppoe_net_ops);
-}
-
-module_init(pppoe_init);
-module_exit(pppoe_exit);
-
-MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
-MODULE_DESCRIPTION("PPP over Ethernet driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_PPPOX);
diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c
deleted file mode 100644 (file)
index 8c0d170..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/** -*- linux-c -*- ***********************************************************
- * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets
- *
- * PPPoX --- Generic PPP encapsulation socket family
- * PPPoE --- PPP over Ethernet (RFC 2516)
- *
- *
- * Version:    0.5.2
- *
- * Author:     Michal Ostrowski <mostrows@speakeasy.net>
- *
- * 051000 :    Initialization cleanup
- *
- * License:
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/net.h>
-#include <linux/init.h>
-#include <linux/if_pppox.h>
-#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
-#include <linux/ppp_channel.h>
-#include <linux/kmod.h>
-
-#include <net/sock.h>
-
-#include <asm/uaccess.h>
-
-static const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1];
-
-int register_pppox_proto(int proto_num, const struct pppox_proto *pp)
-{
-       if (proto_num < 0 || proto_num > PX_MAX_PROTO)
-               return -EINVAL;
-       if (pppox_protos[proto_num])
-               return -EALREADY;
-       pppox_protos[proto_num] = pp;
-       return 0;
-}
-
-void unregister_pppox_proto(int proto_num)
-{
-       if (proto_num >= 0 && proto_num <= PX_MAX_PROTO)
-               pppox_protos[proto_num] = NULL;
-}
-
-void pppox_unbind_sock(struct sock *sk)
-{
-       /* Clear connection to ppp device, if attached. */
-
-       if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
-               ppp_unregister_channel(&pppox_sk(sk)->chan);
-               sk->sk_state = PPPOX_DEAD;
-       }
-}
-
-EXPORT_SYMBOL(register_pppox_proto);
-EXPORT_SYMBOL(unregister_pppox_proto);
-EXPORT_SYMBOL(pppox_unbind_sock);
-
-int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-       struct sock *sk = sock->sk;
-       struct pppox_sock *po = pppox_sk(sk);
-       int rc;
-
-       lock_sock(sk);
-
-       switch (cmd) {
-       case PPPIOCGCHAN: {
-               int index;
-               rc = -ENOTCONN;
-               if (!(sk->sk_state & PPPOX_CONNECTED))
-                       break;
-
-               rc = -EINVAL;
-               index = ppp_channel_index(&po->chan);
-               if (put_user(index , (int __user *) arg))
-                       break;
-
-               rc = 0;
-               sk->sk_state |= PPPOX_BOUND;
-               break;
-       }
-       default:
-               rc = pppox_protos[sk->sk_protocol]->ioctl ?
-                       pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY;
-       }
-
-       release_sock(sk);
-       return rc;
-}
-
-EXPORT_SYMBOL(pppox_ioctl);
-
-static int pppox_create(struct net *net, struct socket *sock, int protocol,
-                       int kern)
-{
-       int rc = -EPROTOTYPE;
-
-       if (protocol < 0 || protocol > PX_MAX_PROTO)
-               goto out;
-
-       rc = -EPROTONOSUPPORT;
-       if (!pppox_protos[protocol])
-               request_module("pppox-proto-%d", protocol);
-       if (!pppox_protos[protocol] ||
-           !try_module_get(pppox_protos[protocol]->owner))
-               goto out;
-
-       rc = pppox_protos[protocol]->create(net, sock);
-
-       module_put(pppox_protos[protocol]->owner);
-out:
-       return rc;
-}
-
-static const struct net_proto_family pppox_proto_family = {
-       .family = PF_PPPOX,
-       .create = pppox_create,
-       .owner  = THIS_MODULE,
-};
-
-static int __init pppox_init(void)
-{
-       return sock_register(&pppox_proto_family);
-}
-
-static void __exit pppox_exit(void)
-{
-       sock_unregister(PF_PPPOX);
-}
-
-module_init(pppox_init);
-module_exit(pppox_exit);
-
-MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
-MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/pptp.c b/drivers/net/pptp.c
deleted file mode 100644 (file)
index eae542a..0000000
+++ /dev/null
@@ -1,717 +0,0 @@
-/*
- *  Point-to-Point Tunneling Protocol for Linux
- *
- *     Authors: Dmitry Kozlov <xeb@mail.ru>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License
- *     as published by the Free Software Foundation; either version
- *     2 of the License, or (at your option) any later version.
- *
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/net.h>
-#include <linux/skbuff.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/ppp_channel.h>
-#include <linux/ppp_defs.h>
-#include <linux/if_pppox.h>
-#include <linux/if_ppp.h>
-#include <linux/notifier.h>
-#include <linux/file.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/rcupdate.h>
-#include <linux/spinlock.h>
-
-#include <net/sock.h>
-#include <net/protocol.h>
-#include <net/ip.h>
-#include <net/icmp.h>
-#include <net/route.h>
-#include <net/gre.h>
-
-#include <linux/uaccess.h>
-
-#define PPTP_DRIVER_VERSION "0.8.5"
-
-#define MAX_CALLID 65535
-
-static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1);
-static struct pppox_sock **callid_sock;
-
-static DEFINE_SPINLOCK(chan_lock);
-
-static struct proto pptp_sk_proto __read_mostly;
-static const struct ppp_channel_ops pptp_chan_ops;
-static const struct proto_ops pptp_ops;
-
-#define PPP_LCP_ECHOREQ 0x09
-#define PPP_LCP_ECHOREP 0x0A
-#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-
-#define MISSING_WINDOW 20
-#define WRAPPED(curseq, lastseq)\
-       ((((curseq) & 0xffffff00) == 0) &&\
-       (((lastseq) & 0xffffff00) == 0xffffff00))
-
-#define PPTP_GRE_PROTO  0x880B
-#define PPTP_GRE_VER    0x1
-
-#define PPTP_GRE_FLAG_C        0x80
-#define PPTP_GRE_FLAG_R        0x40
-#define PPTP_GRE_FLAG_K        0x20
-#define PPTP_GRE_FLAG_S        0x10
-#define PPTP_GRE_FLAG_A        0x80
-
-#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
-#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
-#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
-#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
-#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)
-
-#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header))
-struct pptp_gre_header {
-       u8  flags;
-       u8  ver;
-       u16 protocol;
-       u16 payload_len;
-       u16 call_id;
-       u32 seq;
-       u32 ack;
-} __packed;
-
-static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
-{
-       struct pppox_sock *sock;
-       struct pptp_opt *opt;
-
-       rcu_read_lock();
-       sock = rcu_dereference(callid_sock[call_id]);
-       if (sock) {
-               opt = &sock->proto.pptp;
-               if (opt->dst_addr.sin_addr.s_addr != s_addr)
-                       sock = NULL;
-               else
-                       sock_hold(sk_pppox(sock));
-       }
-       rcu_read_unlock();
-
-       return sock;
-}
-
-static int lookup_chan_dst(u16 call_id, __be32 d_addr)
-{
-       struct pppox_sock *sock;
-       struct pptp_opt *opt;
-       int i;
-
-       rcu_read_lock();
-       for (i = find_next_bit(callid_bitmap, MAX_CALLID, 1); i < MAX_CALLID;
-            i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) {
-               sock = rcu_dereference(callid_sock[i]);
-               if (!sock)
-                       continue;
-               opt = &sock->proto.pptp;
-               if (opt->dst_addr.call_id == call_id &&
-                         opt->dst_addr.sin_addr.s_addr == d_addr)
-                       break;
-       }
-       rcu_read_unlock();
-
-       return i < MAX_CALLID;
-}
-
-static int add_chan(struct pppox_sock *sock)
-{
-       static int call_id;
-
-       spin_lock(&chan_lock);
-       if (!sock->proto.pptp.src_addr.call_id) {
-               call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1);
-               if (call_id == MAX_CALLID) {
-                       call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1);
-                       if (call_id == MAX_CALLID)
-                               goto out_err;
-               }
-               sock->proto.pptp.src_addr.call_id = call_id;
-       } else if (test_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap))
-               goto out_err;
-
-       set_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
-       rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], sock);
-       spin_unlock(&chan_lock);
-
-       return 0;
-
-out_err:
-       spin_unlock(&chan_lock);
-       return -1;
-}
-
-static void del_chan(struct pppox_sock *sock)
-{
-       spin_lock(&chan_lock);
-       clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
-       rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], NULL);
-       spin_unlock(&chan_lock);
-       synchronize_rcu();
-}
-
-static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
-{
-       struct sock *sk = (struct sock *) chan->private;
-       struct pppox_sock *po = pppox_sk(sk);
-       struct pptp_opt *opt = &po->proto.pptp;
-       struct pptp_gre_header *hdr;
-       unsigned int header_len = sizeof(*hdr);
-       struct flowi4 fl4;
-       int islcp;
-       int len;
-       unsigned char *data;
-       __u32 seq_recv;
-
-
-       struct rtable *rt;
-       struct net_device *tdev;
-       struct iphdr  *iph;
-       int    max_headroom;
-
-       if (sk_pppox(po)->sk_state & PPPOX_DEAD)
-               goto tx_error;
-
-       rt = ip_route_output_ports(&init_net, &fl4, NULL,
-                                  opt->dst_addr.sin_addr.s_addr,
-                                  opt->src_addr.sin_addr.s_addr,
-                                  0, 0, IPPROTO_GRE,
-                                  RT_TOS(0), 0);
-       if (IS_ERR(rt))
-               goto tx_error;
-
-       tdev = rt->dst.dev;
-
-       max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph) + sizeof(*hdr) + 2;
-
-       if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
-               struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
-               if (!new_skb) {
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
-               if (skb->sk)
-                       skb_set_owner_w(new_skb, skb->sk);
-               kfree_skb(skb);
-               skb = new_skb;
-       }
-
-       data = skb->data;
-       islcp = ((data[0] << 8) + data[1]) == PPP_LCP && 1 <= data[2] && data[2] <= 7;
-
-       /* compress protocol field */
-       if ((opt->ppp_flags & SC_COMP_PROT) && data[0] == 0 && !islcp)
-               skb_pull(skb, 1);
-
-       /* Put in the address/control bytes if necessary */
-       if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) {
-               data = skb_push(skb, 2);
-               data[0] = PPP_ALLSTATIONS;
-               data[1] = PPP_UI;
-       }
-
-       len = skb->len;
-
-       seq_recv = opt->seq_recv;
-
-       if (opt->ack_sent == seq_recv)
-               header_len -= sizeof(hdr->ack);
-
-       /* Push down and install GRE header */
-       skb_push(skb, header_len);
-       hdr = (struct pptp_gre_header *)(skb->data);
-
-       hdr->flags       = PPTP_GRE_FLAG_K;
-       hdr->ver         = PPTP_GRE_VER;
-       hdr->protocol    = htons(PPTP_GRE_PROTO);
-       hdr->call_id     = htons(opt->dst_addr.call_id);
-
-       hdr->flags      |= PPTP_GRE_FLAG_S;
-       hdr->seq         = htonl(++opt->seq_sent);
-       if (opt->ack_sent != seq_recv)  {
-               /* send ack with this message */
-               hdr->ver |= PPTP_GRE_FLAG_A;
-               hdr->ack  = htonl(seq_recv);
-               opt->ack_sent = seq_recv;
-       }
-       hdr->payload_len = htons(len);
-
-       /*      Push down and install the IP header. */
-
-       skb_reset_transport_header(skb);
-       skb_push(skb, sizeof(*iph));
-       skb_reset_network_header(skb);
-       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED);
-
-       iph =   ip_hdr(skb);
-       iph->version =  4;
-       iph->ihl =      sizeof(struct iphdr) >> 2;
-       if (ip_dont_fragment(sk, &rt->dst))
-               iph->frag_off   =       htons(IP_DF);
-       else
-               iph->frag_off   =       0;
-       iph->protocol = IPPROTO_GRE;
-       iph->tos      = 0;
-       iph->daddr    = fl4.daddr;
-       iph->saddr    = fl4.saddr;
-       iph->ttl      = ip4_dst_hoplimit(&rt->dst);
-       iph->tot_len  = htons(skb->len);
-
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
-       nf_reset(skb);
-
-       skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(iph, &rt->dst, NULL);
-       ip_send_check(iph);
-
-       ip_local_out(skb);
-
-tx_error:
-       return 1;
-}
-
-static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
-{
-       struct pppox_sock *po = pppox_sk(sk);
-       struct pptp_opt *opt = &po->proto.pptp;
-       int headersize, payload_len, seq;
-       __u8 *payload;
-       struct pptp_gre_header *header;
-
-       if (!(sk->sk_state & PPPOX_CONNECTED)) {
-               if (sock_queue_rcv_skb(sk, skb))
-                       goto drop;
-               return NET_RX_SUCCESS;
-       }
-
-       header = (struct pptp_gre_header *)(skb->data);
-
-       /* test if acknowledgement present */
-       if (PPTP_GRE_IS_A(header->ver)) {
-               __u32 ack = (PPTP_GRE_IS_S(header->flags)) ?
-                               header->ack : header->seq; /* ack in different place if S = 0 */
-
-               ack = ntohl(ack);
-
-               if (ack > opt->ack_recv)
-                       opt->ack_recv = ack;
-               /* also handle sequence number wrap-around  */
-               if (WRAPPED(ack, opt->ack_recv))
-                       opt->ack_recv = ack;
-       }
-
-       /* test if payload present */
-       if (!PPTP_GRE_IS_S(header->flags))
-               goto drop;
-
-       headersize  = sizeof(*header);
-       payload_len = ntohs(header->payload_len);
-       seq         = ntohl(header->seq);
-
-       /* no ack present? */
-       if (!PPTP_GRE_IS_A(header->ver))
-               headersize -= sizeof(header->ack);
-       /* check for incomplete packet (length smaller than expected) */
-       if (skb->len - headersize < payload_len)
-               goto drop;
-
-       payload = skb->data + headersize;
-       /* check for expected sequence number */
-       if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) {
-               if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) &&
-                               (PPP_PROTOCOL(payload) == PPP_LCP) &&
-                               ((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)))
-                       goto allow_packet;
-       } else {
-               opt->seq_recv = seq;
-allow_packet:
-               skb_pull(skb, headersize);
-
-               if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) {
-                       /* chop off address/control */
-                       if (skb->len < 3)
-                               goto drop;
-                       skb_pull(skb, 2);
-               }
-
-               if ((*skb->data) & 1) {
-                       /* protocol is compressed */
-                       skb_push(skb, 1)[0] = 0;
-               }
-
-               skb->ip_summed = CHECKSUM_NONE;
-               skb_set_network_header(skb, skb->head-skb->data);
-               ppp_input(&po->chan, skb);
-
-               return NET_RX_SUCCESS;
-       }
-drop:
-       kfree_skb(skb);
-       return NET_RX_DROP;
-}
-
-static int pptp_rcv(struct sk_buff *skb)
-{
-       struct pppox_sock *po;
-       struct pptp_gre_header *header;
-       struct iphdr *iph;
-
-       if (skb->pkt_type != PACKET_HOST)
-               goto drop;
-
-       if (!pskb_may_pull(skb, 12))
-               goto drop;
-
-       iph = ip_hdr(skb);
-
-       header = (struct pptp_gre_header *)skb->data;
-
-       if (ntohs(header->protocol) != PPTP_GRE_PROTO || /* PPTP-GRE protocol for PPTP */
-               PPTP_GRE_IS_C(header->flags) ||                /* flag C should be clear */
-               PPTP_GRE_IS_R(header->flags) ||                /* flag R should be clear */
-               !PPTP_GRE_IS_K(header->flags) ||               /* flag K should be set */
-               (header->flags&0xF) != 0)                      /* routing and recursion ctrl = 0 */
-               /* if invalid, discard this packet */
-               goto drop;
-
-       po = lookup_chan(htons(header->call_id), iph->saddr);
-       if (po) {
-               skb_dst_drop(skb);
-               nf_reset(skb);
-               return sk_receive_skb(sk_pppox(po), skb, 0);
-       }
-drop:
-       kfree_skb(skb);
-       return NET_RX_DROP;
-}
-
-static int pptp_bind(struct socket *sock, struct sockaddr *uservaddr,
-       int sockaddr_len)
-{
-       struct sock *sk = sock->sk;
-       struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
-       struct pppox_sock *po = pppox_sk(sk);
-       struct pptp_opt *opt = &po->proto.pptp;
-       int error = 0;
-
-       lock_sock(sk);
-
-       opt->src_addr = sp->sa_addr.pptp;
-       if (add_chan(po)) {
-               release_sock(sk);
-               error = -EBUSY;
-       }
-
-       release_sock(sk);
-       return error;
-}
-
-static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
-       int sockaddr_len, int flags)
-{
-       struct sock *sk = sock->sk;
-       struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
-       struct pppox_sock *po = pppox_sk(sk);
-       struct pptp_opt *opt = &po->proto.pptp;
-       struct rtable *rt;
-       struct flowi4 fl4;
-       int error = 0;
-
-       if (sp->sa_protocol != PX_PROTO_PPTP)
-               return -EINVAL;
-
-       if (lookup_chan_dst(sp->sa_addr.pptp.call_id, sp->sa_addr.pptp.sin_addr.s_addr))
-               return -EALREADY;
-
-       lock_sock(sk);
-       /* Check for already bound sockets */
-       if (sk->sk_state & PPPOX_CONNECTED) {
-               error = -EBUSY;
-               goto end;
-       }
-
-       /* Check for already disconnected sockets, on attempts to disconnect */
-       if (sk->sk_state & PPPOX_DEAD) {
-               error = -EALREADY;
-               goto end;
-       }
-
-       if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr) {
-               error = -EINVAL;
-               goto end;
-       }
-
-       po->chan.private = sk;
-       po->chan.ops = &pptp_chan_ops;
-
-       rt = ip_route_output_ports(&init_net, &fl4, sk,
-                                  opt->dst_addr.sin_addr.s_addr,
-                                  opt->src_addr.sin_addr.s_addr,
-                                  0, 0,
-                                  IPPROTO_GRE, RT_CONN_FLAGS(sk), 0);
-       if (IS_ERR(rt)) {
-               error = -EHOSTUNREACH;
-               goto end;
-       }
-       sk_setup_caps(sk, &rt->dst);
-
-       po->chan.mtu = dst_mtu(&rt->dst);
-       if (!po->chan.mtu)
-               po->chan.mtu = PPP_MTU;
-       ip_rt_put(rt);
-       po->chan.mtu -= PPTP_HEADER_OVERHEAD;
-
-       po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
-       error = ppp_register_channel(&po->chan);
-       if (error) {
-               pr_err("PPTP: failed to register PPP channel (%d)\n", error);
-               goto end;
-       }
-
-       opt->dst_addr = sp->sa_addr.pptp;
-       sk->sk_state = PPPOX_CONNECTED;
-
- end:
-       release_sock(sk);
-       return error;
-}
-
-static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
-       int *usockaddr_len, int peer)
-{
-       int len = sizeof(struct sockaddr_pppox);
-       struct sockaddr_pppox sp;
-
-       sp.sa_family      = AF_PPPOX;
-       sp.sa_protocol  = PX_PROTO_PPTP;
-       sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
-
-       memcpy(uaddr, &sp, len);
-
-       *usockaddr_len = len;
-
-       return 0;
-}
-
-static int pptp_release(struct socket *sock)
-{
-       struct sock *sk = sock->sk;
-       struct pppox_sock *po;
-       struct pptp_opt *opt;
-       int error = 0;
-
-       if (!sk)
-               return 0;
-
-       lock_sock(sk);
-
-       if (sock_flag(sk, SOCK_DEAD)) {
-               release_sock(sk);
-               return -EBADF;
-       }
-
-       po = pppox_sk(sk);
-       opt = &po->proto.pptp;
-       del_chan(po);
-
-       pppox_unbind_sock(sk);
-       sk->sk_state = PPPOX_DEAD;
-
-       sock_orphan(sk);
-       sock->sk = NULL;
-
-       release_sock(sk);
-       sock_put(sk);
-
-       return error;
-}
-
-static void pptp_sock_destruct(struct sock *sk)
-{
-       if (!(sk->sk_state & PPPOX_DEAD)) {
-               del_chan(pppox_sk(sk));
-               pppox_unbind_sock(sk);
-       }
-       skb_queue_purge(&sk->sk_receive_queue);
-}
-
-static int pptp_create(struct net *net, struct socket *sock)
-{
-       int error = -ENOMEM;
-       struct sock *sk;
-       struct pppox_sock *po;
-       struct pptp_opt *opt;
-
-       sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pptp_sk_proto);
-       if (!sk)
-               goto out;
-
-       sock_init_data(sock, sk);
-
-       sock->state = SS_UNCONNECTED;
-       sock->ops   = &pptp_ops;
-
-       sk->sk_backlog_rcv = pptp_rcv_core;
-       sk->sk_state       = PPPOX_NONE;
-       sk->sk_type        = SOCK_STREAM;
-       sk->sk_family      = PF_PPPOX;
-       sk->sk_protocol    = PX_PROTO_PPTP;
-       sk->sk_destruct    = pptp_sock_destruct;
-
-       po = pppox_sk(sk);
-       opt = &po->proto.pptp;
-
-       opt->seq_sent = 0; opt->seq_recv = 0;
-       opt->ack_recv = 0; opt->ack_sent = 0;
-
-       error = 0;
-out:
-       return error;
-}
-
-static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
-       unsigned long arg)
-{
-       struct sock *sk = (struct sock *) chan->private;
-       struct pppox_sock *po = pppox_sk(sk);
-       struct pptp_opt *opt = &po->proto.pptp;
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       int err, val;
-
-       err = -EFAULT;
-       switch (cmd) {
-       case PPPIOCGFLAGS:
-               val = opt->ppp_flags;
-               if (put_user(val, p))
-                       break;
-               err = 0;
-               break;
-       case PPPIOCSFLAGS:
-               if (get_user(val, p))
-                       break;
-               opt->ppp_flags = val & ~SC_RCV_BITS;
-               err = 0;
-               break;
-       default:
-               err = -ENOTTY;
-       }
-
-       return err;
-}
-
-static const struct ppp_channel_ops pptp_chan_ops = {
-       .start_xmit = pptp_xmit,
-       .ioctl      = pptp_ppp_ioctl,
-};
-
-static struct proto pptp_sk_proto __read_mostly = {
-       .name     = "PPTP",
-       .owner    = THIS_MODULE,
-       .obj_size = sizeof(struct pppox_sock),
-};
-
-static const struct proto_ops pptp_ops = {
-       .family     = AF_PPPOX,
-       .owner      = THIS_MODULE,
-       .release    = pptp_release,
-       .bind       = pptp_bind,
-       .connect    = pptp_connect,
-       .socketpair = sock_no_socketpair,
-       .accept     = sock_no_accept,
-       .getname    = pptp_getname,
-       .poll       = sock_no_poll,
-       .listen     = sock_no_listen,
-       .shutdown   = sock_no_shutdown,
-       .setsockopt = sock_no_setsockopt,
-       .getsockopt = sock_no_getsockopt,
-       .sendmsg    = sock_no_sendmsg,
-       .recvmsg    = sock_no_recvmsg,
-       .mmap       = sock_no_mmap,
-       .ioctl      = pppox_ioctl,
-};
-
-static const struct pppox_proto pppox_pptp_proto = {
-       .create = pptp_create,
-       .owner  = THIS_MODULE,
-};
-
-static const struct gre_protocol gre_pptp_protocol = {
-       .handler = pptp_rcv,
-};
-
-static int __init pptp_init_module(void)
-{
-       int err = 0;
-       pr_info("PPTP driver version " PPTP_DRIVER_VERSION "\n");
-
-       callid_sock = vzalloc((MAX_CALLID + 1) * sizeof(void *));
-       if (!callid_sock) {
-               pr_err("PPTP: cann't allocate memory\n");
-               return -ENOMEM;
-       }
-
-       err = gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
-       if (err) {
-               pr_err("PPTP: can't add gre protocol\n");
-               goto out_mem_free;
-       }
-
-       err = proto_register(&pptp_sk_proto, 0);
-       if (err) {
-               pr_err("PPTP: can't register sk_proto\n");
-               goto out_gre_del_protocol;
-       }
-
-       err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
-       if (err) {
-               pr_err("PPTP: can't register pppox_proto\n");
-               goto out_unregister_sk_proto;
-       }
-
-       return 0;
-
-out_unregister_sk_proto:
-       proto_unregister(&pptp_sk_proto);
-out_gre_del_protocol:
-       gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
-out_mem_free:
-       vfree(callid_sock);
-
-       return err;
-}
-
-static void __exit pptp_exit_module(void)
-{
-       unregister_pppox_proto(PX_PROTO_PPTP);
-       proto_unregister(&pptp_sk_proto);
-       gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
-       vfree(callid_sock);
-}
-
-module_init(pptp_init_module);
-module_exit(pptp_exit_module);
-
-MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol");
-MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
-MODULE_LICENSE("GPL");