From 224cf5ad14c038b13c119dff29422f178a306f54 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Sun, 31 Jul 2011 02:38:19 -0700 Subject: ppp: Move the PPP drivers Move the PPP drivers into drivers/net/ppp/ and make the necessary Kconfig and Makefile changes. CC: Paul Mackerras CC: Frank Cusack CC: Michal Ostrowski CC: Michal Ostrowski CC: Dmitry Kozlov Signed-off-by: Jeff Kirsher --- MAINTAINERS | 8 +- drivers/net/Kconfig | 170 +-- drivers/net/Makefile | 18 +- drivers/net/bsd_comp.c | 1170 ---------------- drivers/net/ppp/Kconfig | 175 +++ drivers/net/ppp/Makefile | 13 + drivers/net/ppp/bsd_comp.c | 1170 ++++++++++++++++ drivers/net/ppp/ppp_async.c | 1028 ++++++++++++++ drivers/net/ppp/ppp_deflate.c | 653 +++++++++ drivers/net/ppp/ppp_generic.c | 2954 +++++++++++++++++++++++++++++++++++++++++ drivers/net/ppp/ppp_mppe.c | 740 +++++++++++ drivers/net/ppp/ppp_mppe.h | 86 ++ drivers/net/ppp/ppp_synctty.c | 790 +++++++++++ drivers/net/ppp/pppoe.c | 1208 +++++++++++++++++ drivers/net/ppp/pppox.c | 149 +++ drivers/net/ppp/pptp.c | 717 ++++++++++ drivers/net/ppp_async.c | 1028 -------------- drivers/net/ppp_deflate.c | 653 --------- drivers/net/ppp_generic.c | 2954 ----------------------------------------- drivers/net/ppp_mppe.c | 740 ----------- drivers/net/ppp_mppe.h | 86 -- drivers/net/ppp_synctty.c | 790 ----------- drivers/net/pppoe.c | 1208 ----------------- drivers/net/pppox.c | 149 --- drivers/net/pptp.c | 717 ---------- 25 files changed, 9698 insertions(+), 9676 deletions(-) delete mode 100644 drivers/net/bsd_comp.c create mode 100644 drivers/net/ppp/Kconfig create mode 100644 drivers/net/ppp/Makefile create mode 100644 drivers/net/ppp/bsd_comp.c create mode 100644 drivers/net/ppp/ppp_async.c create mode 100644 drivers/net/ppp/ppp_deflate.c create mode 100644 drivers/net/ppp/ppp_generic.c create mode 100644 drivers/net/ppp/ppp_mppe.c create mode 100644 drivers/net/ppp/ppp_mppe.h create mode 100644 drivers/net/ppp/ppp_synctty.c create mode 100644 drivers/net/ppp/pppoe.c create mode 100644 drivers/net/ppp/pppox.c create mode 100644 drivers/net/ppp/pptp.c delete mode 100644 drivers/net/ppp_async.c delete mode 100644 drivers/net/ppp_deflate.c delete mode 100644 drivers/net/ppp_generic.c delete mode 100644 drivers/net/ppp_mppe.c delete mode 100644 drivers/net/ppp_mppe.h delete mode 100644 drivers/net/ppp_synctty.c delete mode 100644 drivers/net/pppoe.c delete mode 100644 drivers/net/pppox.c delete mode 100644 drivers/net/pptp.c diff --git a/MAINTAINERS b/MAINTAINERS index 2777088..c5ec925 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5066,7 +5066,7 @@ PPP PROTOCOL DRIVERS AND COMPRESSORS M: Paul Mackerras 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 @@ -5077,8 +5077,8 @@ F: include/linux/atmppp.h PPP OVER ETHERNET M: Michal Ostrowski 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 @@ -5099,7 +5099,7 @@ PPTP DRIVER M: Dmitry Kozlov 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 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7bdc22b..c5e2a38 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -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 - . Make sure that you have - the version of pppd recommended in . - 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 () - 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--- diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3087b27..a397f1e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -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 index a9b759a..0000000 --- a/drivers/net/bsd_comp.c +++ /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 -#include -#include -#include -#include - -#include - -#undef PACKETPTR -#define PACKETPTR 1 -#include -#undef PACKETPTR - -#include - -/* - * 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) - -/* - * 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 index 0000000..872df3e --- /dev/null +++ b/drivers/net/ppp/Kconfig @@ -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 + . Make sure that you have + the version of pppd recommended in . + 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 () + 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 index 0000000..a6b6297 --- /dev/null +++ b/drivers/net/ppp/Makefile @@ -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 index 0000000..a9b759a --- /dev/null +++ b/drivers/net/ppp/bsd_comp.c @@ -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 +#include +#include +#include +#include + +#include + +#undef PACKETPTR +#define PACKETPTR 1 +#include +#undef PACKETPTR + +#include + +/* + * 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) + +/* + * 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 index 0000000..c6ba643 --- /dev/null +++ b/drivers/net/ppp/ppp_async.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..1dbdf82 --- /dev/null +++ b/drivers/net/ppp/ppp_deflate.c @@ -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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * 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 index 0000000..10e5d98 --- /dev/null +++ b/drivers/net/ppp/ppp_generic.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 index 0000000..9a1849a --- /dev/null +++ b/drivers/net/ppp/ppp_mppe.c @@ -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 . + * 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 + * Only need extra skb padding on transmit, not receive. + * 06/18/04 - Matt Domsch , Oleg Makarenko + * Use Linux kernel 2.6 arc4 and sha1 routines rather than + * providing our own. + * 2/15/04 - TS: added #include and testing for Kernel + * version before using + * MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are + * deprecated in 2.6 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ppp_mppe.h" + +MODULE_AUTHOR("Frank Cusack "); +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 index 0000000..7a14e05 --- /dev/null +++ b/drivers/net/ppp/ppp_mppe.h @@ -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 index 0000000..736a39e --- /dev/null +++ b/drivers/net/ppp/ppp_synctty.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..bc9a4bb --- /dev/null +++ b/drivers/net/ppp/pppoe.c @@ -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 + * (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 + * Contributors: + * Arnaldo Carvalho de Melo + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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 "); +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 index 0000000..8c0d170 --- /dev/null +++ b/drivers/net/ppp/pppox.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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 "); +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 index 0000000..eae542a --- /dev/null +++ b/drivers/net/ppp/pptp.c @@ -0,0 +1,717 @@ +/* + * Point-to-Point Tunneling Protocol for Linux + * + * Authors: Dmitry Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#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 index c6ba643..0000000 --- a/drivers/net/ppp_async.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index 1dbdf82..0000000 --- a/drivers/net/ppp_deflate.c +++ /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 -#include -#include -#include -#include - -#include -#include - -#include -#include - -/* - * 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 index 10e5d98..0000000 --- a/drivers/net/ppp_generic.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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 index 9a1849a..0000000 --- a/drivers/net/ppp_mppe.c +++ /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 . - * 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 - * Only need extra skb padding on transmit, not receive. - * 06/18/04 - Matt Domsch , Oleg Makarenko - * Use Linux kernel 2.6 arc4 and sha1 routines rather than - * providing our own. - * 2/15/04 - TS: added #include and testing for Kernel - * version before using - * MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are - * deprecated in 2.6 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ppp_mppe.h" - -MODULE_AUTHOR("Frank Cusack "); -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 index 7a14e05..0000000 --- a/drivers/net/ppp_mppe.h +++ /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 index 736a39e..0000000 --- a/drivers/net/ppp_synctty.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index bc9a4bb..0000000 --- a/drivers/net/pppoe.c +++ /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 - * (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 - * Contributors: - * Arnaldo Carvalho de Melo - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#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 "); -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 index 8c0d170..0000000 --- a/drivers/net/pppox.c +++ /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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -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 "); -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 index eae542a..0000000 --- a/drivers/net/pptp.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * Point-to-Point Tunneling Protocol for Linux - * - * Authors: Dmitry Kozlov - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#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"); -- cgit v1.1