ppp: Move the PPP drivers
Move the PPP drivers into drivers/net/ppp/ and make the necessary Kconfig and Makefile changes. CC: Paul Mackerras <paulus@samba.org> CC: Frank Cusack <fcusack@fcusack.com> CC: Michal Ostrowski <mostrows@speakeasy.net> CC: Michal Ostrowski <mostrows@earthlink.net> CC: Dmitry Kozlov <xeb@mail.ru> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Цей коміт міститься в:
175
drivers/net/ppp/Kconfig
Звичайний файл
175
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
|
||||
<http://www.tldp.org/docs.html#howto>. Make sure that you have
|
||||
the version of pppd recommended in <file:Documentation/Changes>.
|
||||
The PPP option enlarges your kernel by about 16 KB.
|
||||
|
||||
There are actually two versions of PPP: the traditional PPP for
|
||||
asynchronous lines, such as regular analog phone lines, and
|
||||
synchronous PPP which can be used over digital ISDN lines for
|
||||
example. If you want to use PPP over phone lines or other
|
||||
asynchronous serial lines, you need to say Y (or M) here and also to
|
||||
the next option, "PPP support for async serial ports". For PPP over
|
||||
synchronous lines, you should say Y (or M) here and to "Support
|
||||
synchronous PPP", below.
|
||||
|
||||
If you said Y to "Version information on all symbols" above, then
|
||||
you cannot compile the PPP driver into the kernel; you can then only
|
||||
compile it as a module. To compile this driver as a module, choose M
|
||||
here. The module will be called ppp_generic.
|
||||
|
||||
if PPP
|
||||
|
||||
config PPP_BSDCOMP
|
||||
tristate "PPP BSD-Compress compression"
|
||||
depends on PPP
|
||||
---help---
|
||||
Support for the BSD-Compress compression method for PPP, which uses
|
||||
the LZW compression method to compress each PPP packet before it is
|
||||
sent over the wire. The machine at the other end of the PPP link
|
||||
(usually your ISP) has to support the BSD-Compress compression
|
||||
method as well for this to be useful. Even if they don't support it,
|
||||
it is safe to say Y here.
|
||||
|
||||
The PPP Deflate compression method ("PPP Deflate compression",
|
||||
above) is preferable to BSD-Compress, because it compresses better
|
||||
and is patent-free.
|
||||
|
||||
Note that the BSD compression code will always be compiled as a
|
||||
module; it is called bsd_comp and will show up in the directory
|
||||
modules once you have said "make modules". If unsure, say N.
|
||||
|
||||
config PPP_DEFLATE
|
||||
tristate "PPP Deflate compression"
|
||||
depends on PPP
|
||||
select ZLIB_INFLATE
|
||||
select ZLIB_DEFLATE
|
||||
---help---
|
||||
Support for the Deflate compression method for PPP, which uses the
|
||||
Deflate algorithm (the same algorithm that gzip uses) to compress
|
||||
each PPP packet before it is sent over the wire. The machine at the
|
||||
other end of the PPP link (usually your ISP) has to support the
|
||||
Deflate compression method as well for this to be useful. Even if
|
||||
they don't support it, it is safe to say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PPP_FILTER
|
||||
bool "PPP filtering"
|
||||
depends on PPP
|
||||
---help---
|
||||
Say Y here if you want to be able to filter the packets passing over
|
||||
PPP interfaces. This allows you to control which packets count as
|
||||
activity (i.e. which packets will reset the idle timer or bring up
|
||||
a demand-dialed link) and which packets are to be dropped entirely.
|
||||
You need to say Y here if you wish to use the pass-filter and
|
||||
active-filter options to pppd.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PPP_MPPE
|
||||
tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)"
|
||||
depends on PPP && EXPERIMENTAL
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_ARC4
|
||||
select CRYPTO_ECB
|
||||
---help---
|
||||
Support for the MPPE Encryption protocol, as employed by the
|
||||
Microsoft Point-to-Point Tunneling Protocol.
|
||||
|
||||
See http://pptpclient.sourceforge.net/ for information on
|
||||
configuring PPTP clients and servers to utilize this method.
|
||||
|
||||
config PPP_MULTILINK
|
||||
bool "PPP multilink support (EXPERIMENTAL)"
|
||||
depends on PPP && EXPERIMENTAL
|
||||
---help---
|
||||
PPP multilink is a protocol (defined in RFC 1990) which allows you
|
||||
to combine several (logical or physical) lines into one logical PPP
|
||||
connection, so that you can utilize your full bandwidth.
|
||||
|
||||
This has to be supported at the other end as well and you need a
|
||||
version of the pppd daemon which understands the multilink protocol.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PPPOATM
|
||||
tristate "PPP over ATM"
|
||||
depends on ATM && PPP
|
||||
---help---
|
||||
Support PPP (Point to Point Protocol) encapsulated in ATM frames.
|
||||
This implementation does not yet comply with section 8 of RFC2364,
|
||||
which can lead to bad results if the ATM peer loses state and
|
||||
changes its encapsulation unilaterally.
|
||||
|
||||
config PPPOE
|
||||
tristate "PPP over Ethernet (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && PPP
|
||||
---help---
|
||||
Support for PPP over Ethernet.
|
||||
|
||||
This driver requires the latest version of pppd from the CVS
|
||||
repository at cvs.samba.org. Alternatively, see the
|
||||
RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
|
||||
which contains instruction on how to use this driver (under
|
||||
the heading "Kernel mode PPPoE").
|
||||
|
||||
config PPTP
|
||||
tristate "PPP over IPv4 (PPTP) (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && PPP && NET_IPGRE_DEMUX
|
||||
---help---
|
||||
Support for PPP over IPv4.(Point-to-Point Tunneling Protocol)
|
||||
|
||||
This driver requires pppd plugin to work in client mode or
|
||||
modified pptpd (poptop) to work in server mode.
|
||||
See http://accel-pptp.sourceforge.net/ for information how to
|
||||
utilize this module.
|
||||
|
||||
config PPPOL2TP
|
||||
tristate "PPP over L2TP (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && L2TP && PPP
|
||||
---help---
|
||||
Support for PPP-over-L2TP socket family. L2TP is a protocol
|
||||
used by ISPs and enterprises to tunnel PPP traffic over UDP
|
||||
tunnels. L2TP is replacing PPTP for VPN uses.
|
||||
|
||||
config PPP_ASYNC
|
||||
tristate "PPP support for async serial ports"
|
||||
depends on PPP
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
Say Y (or M) here if you want to be able to use PPP over standard
|
||||
asynchronous serial ports, such as COM1 or COM2 on a PC. If you use
|
||||
a modem (not a synchronous or ISDN modem) to contact your ISP, you
|
||||
need this option.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config PPP_SYNC_TTY
|
||||
tristate "PPP support for sync tty ports"
|
||||
depends on PPP
|
||||
---help---
|
||||
Say Y (or M) here if you want to be able to use PPP over synchronous
|
||||
(HDLC) tty devices, such as the SyncLink adapter. These devices
|
||||
are often used for high-speed leased lines like T1/E1.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
endif # PPP
|
13
drivers/net/ppp/Makefile
Звичайний файл
13
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
|
1170
drivers/net/ppp/bsd_comp.c
Звичайний файл
1170
drivers/net/ppp/bsd_comp.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
1028
drivers/net/ppp/ppp_async.c
Звичайний файл
1028
drivers/net/ppp/ppp_async.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
653
drivers/net/ppp/ppp_deflate.c
Звичайний файл
653
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 <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/ppp-comp.h>
|
||||
|
||||
#include <linux/zlib.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/*
|
||||
* State for a Deflate (de)compressor.
|
||||
*/
|
||||
struct ppp_deflate_state {
|
||||
int seqno;
|
||||
int w_size;
|
||||
int unit;
|
||||
int mru;
|
||||
int debug;
|
||||
z_stream strm;
|
||||
struct compstat stats;
|
||||
};
|
||||
|
||||
#define DEFLATE_OVHD 2 /* Deflate overhead/packet */
|
||||
|
||||
static void *z_comp_alloc(unsigned char *options, int opt_len);
|
||||
static void *z_decomp_alloc(unsigned char *options, int opt_len);
|
||||
static void z_comp_free(void *state);
|
||||
static void z_decomp_free(void *state);
|
||||
static int z_comp_init(void *state, unsigned char *options,
|
||||
int opt_len,
|
||||
int unit, int hdrlen, int debug);
|
||||
static int z_decomp_init(void *state, unsigned char *options,
|
||||
int opt_len,
|
||||
int unit, int hdrlen, int mru, int debug);
|
||||
static int z_compress(void *state, unsigned char *rptr,
|
||||
unsigned char *obuf,
|
||||
int isize, int osize);
|
||||
static void z_incomp(void *state, unsigned char *ibuf, int icnt);
|
||||
static int z_decompress(void *state, unsigned char *ibuf,
|
||||
int isize, unsigned char *obuf, int osize);
|
||||
static void z_comp_reset(void *state);
|
||||
static void z_decomp_reset(void *state);
|
||||
static void z_comp_stats(void *state, struct compstat *stats);
|
||||
|
||||
/**
|
||||
* z_comp_free - free the memory used by a compressor
|
||||
* @arg: pointer to the private state for the compressor.
|
||||
*/
|
||||
static void z_comp_free(void *arg)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
if (state) {
|
||||
zlib_deflateEnd(&state->strm);
|
||||
vfree(state->strm.workspace);
|
||||
kfree(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* z_comp_alloc - allocate space for a compressor.
|
||||
* @options: pointer to CCP option data
|
||||
* @opt_len: length of the CCP option at @options.
|
||||
*
|
||||
* The @options pointer points to the a buffer containing the
|
||||
* CCP option data for the compression being negotiated. It is
|
||||
* formatted according to RFC1979, and describes the window
|
||||
* size that the peer is requesting that we use in compressing
|
||||
* data to be sent to it.
|
||||
*
|
||||
* Returns the pointer to the private state for the compressor,
|
||||
* or NULL if we could not allocate enough memory.
|
||||
*/
|
||||
static void *z_comp_alloc(unsigned char *options, int opt_len)
|
||||
{
|
||||
struct ppp_deflate_state *state;
|
||||
int w_size;
|
||||
|
||||
if (opt_len != CILEN_DEFLATE ||
|
||||
(options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
|
||||
options[1] != CILEN_DEFLATE ||
|
||||
DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
|
||||
options[3] != DEFLATE_CHK_SEQUENCE)
|
||||
return NULL;
|
||||
w_size = DEFLATE_SIZE(options[2]);
|
||||
if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
|
||||
return NULL;
|
||||
|
||||
state = kzalloc(sizeof(*state),
|
||||
GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
return NULL;
|
||||
|
||||
state->strm.next_in = NULL;
|
||||
state->w_size = w_size;
|
||||
state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8));
|
||||
if (state->strm.workspace == NULL)
|
||||
goto out_free;
|
||||
|
||||
if (zlib_deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION,
|
||||
DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY)
|
||||
!= Z_OK)
|
||||
goto out_free;
|
||||
return (void *) state;
|
||||
|
||||
out_free:
|
||||
z_comp_free(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_comp_init - initialize a previously-allocated compressor.
|
||||
* @arg: pointer to the private state for the compressor
|
||||
* @options: pointer to the CCP option data describing the
|
||||
* compression that was negotiated with the peer
|
||||
* @opt_len: length of the CCP option data at @options
|
||||
* @unit: PPP unit number for diagnostic messages
|
||||
* @hdrlen: ignored (present for backwards compatibility)
|
||||
* @debug: debug flag; if non-zero, debug messages are printed.
|
||||
*
|
||||
* The CCP options described by @options must match the options
|
||||
* specified when the compressor was allocated. The compressor
|
||||
* history is reset. Returns 0 for failure (CCP options don't
|
||||
* match) or 1 for success.
|
||||
*/
|
||||
static int z_comp_init(void *arg, unsigned char *options, int opt_len,
|
||||
int unit, int hdrlen, int debug)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
if (opt_len < CILEN_DEFLATE ||
|
||||
(options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
|
||||
options[1] != CILEN_DEFLATE ||
|
||||
DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
|
||||
DEFLATE_SIZE(options[2]) != state->w_size ||
|
||||
options[3] != DEFLATE_CHK_SEQUENCE)
|
||||
return 0;
|
||||
|
||||
state->seqno = 0;
|
||||
state->unit = unit;
|
||||
state->debug = debug;
|
||||
|
||||
zlib_deflateReset(&state->strm);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_comp_reset - reset a previously-allocated compressor.
|
||||
* @arg: pointer to private state for the compressor.
|
||||
*
|
||||
* This clears the history for the compressor and makes it
|
||||
* ready to start emitting a new compressed stream.
|
||||
*/
|
||||
static void z_comp_reset(void *arg)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
state->seqno = 0;
|
||||
zlib_deflateReset(&state->strm);
|
||||
}
|
||||
|
||||
/**
|
||||
* z_compress - compress a PPP packet with Deflate compression.
|
||||
* @arg: pointer to private state for the compressor
|
||||
* @rptr: uncompressed packet (input)
|
||||
* @obuf: compressed packet (output)
|
||||
* @isize: size of uncompressed packet
|
||||
* @osize: space available at @obuf
|
||||
*
|
||||
* Returns the length of the compressed packet, or 0 if the
|
||||
* packet is incompressible.
|
||||
*/
|
||||
static int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf,
|
||||
int isize, int osize)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
int r, proto, off, olen, oavail;
|
||||
unsigned char *wptr;
|
||||
|
||||
/*
|
||||
* Check that the protocol is in the range we handle.
|
||||
*/
|
||||
proto = PPP_PROTOCOL(rptr);
|
||||
if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
|
||||
return 0;
|
||||
|
||||
/* Don't generate compressed packets which are larger than
|
||||
the uncompressed packet. */
|
||||
if (osize > isize)
|
||||
osize = isize;
|
||||
|
||||
wptr = obuf;
|
||||
|
||||
/*
|
||||
* Copy over the PPP header and store the 2-byte sequence number.
|
||||
*/
|
||||
wptr[0] = PPP_ADDRESS(rptr);
|
||||
wptr[1] = PPP_CONTROL(rptr);
|
||||
put_unaligned_be16(PPP_COMP, wptr + 2);
|
||||
wptr += PPP_HDRLEN;
|
||||
put_unaligned_be16(state->seqno, wptr);
|
||||
wptr += DEFLATE_OVHD;
|
||||
olen = PPP_HDRLEN + DEFLATE_OVHD;
|
||||
state->strm.next_out = wptr;
|
||||
state->strm.avail_out = oavail = osize - olen;
|
||||
++state->seqno;
|
||||
|
||||
off = (proto > 0xff) ? 2 : 3; /* skip 1st proto byte if 0 */
|
||||
rptr += off;
|
||||
state->strm.next_in = rptr;
|
||||
state->strm.avail_in = (isize - off);
|
||||
|
||||
for (;;) {
|
||||
r = zlib_deflate(&state->strm, Z_PACKET_FLUSH);
|
||||
if (r != Z_OK) {
|
||||
if (state->debug)
|
||||
printk(KERN_ERR
|
||||
"z_compress: deflate returned %d\n", r);
|
||||
break;
|
||||
}
|
||||
if (state->strm.avail_out == 0) {
|
||||
olen += oavail;
|
||||
state->strm.next_out = NULL;
|
||||
state->strm.avail_out = oavail = 1000000;
|
||||
} else {
|
||||
break; /* all done */
|
||||
}
|
||||
}
|
||||
olen += oavail - state->strm.avail_out;
|
||||
|
||||
/*
|
||||
* See if we managed to reduce the size of the packet.
|
||||
*/
|
||||
if (olen < isize) {
|
||||
state->stats.comp_bytes += olen;
|
||||
state->stats.comp_packets++;
|
||||
} else {
|
||||
state->stats.inc_bytes += isize;
|
||||
state->stats.inc_packets++;
|
||||
olen = 0;
|
||||
}
|
||||
state->stats.unc_bytes += isize;
|
||||
state->stats.unc_packets++;
|
||||
|
||||
return olen;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_comp_stats - return compression statistics for a compressor
|
||||
* or decompressor.
|
||||
* @arg: pointer to private space for the (de)compressor
|
||||
* @stats: pointer to a struct compstat to receive the result.
|
||||
*/
|
||||
static void z_comp_stats(void *arg, struct compstat *stats)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
*stats = state->stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_decomp_free - Free the memory used by a decompressor.
|
||||
* @arg: pointer to private space for the decompressor.
|
||||
*/
|
||||
static void z_decomp_free(void *arg)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
if (state) {
|
||||
zlib_inflateEnd(&state->strm);
|
||||
vfree(state->strm.workspace);
|
||||
kfree(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* z_decomp_alloc - allocate space for a decompressor.
|
||||
* @options: pointer to CCP option data
|
||||
* @opt_len: length of the CCP option at @options.
|
||||
*
|
||||
* The @options pointer points to the a buffer containing the
|
||||
* CCP option data for the compression being negotiated. It is
|
||||
* formatted according to RFC1979, and describes the window
|
||||
* size that we are requesting the peer to use in compressing
|
||||
* data to be sent to us.
|
||||
*
|
||||
* Returns the pointer to the private state for the decompressor,
|
||||
* or NULL if we could not allocate enough memory.
|
||||
*/
|
||||
static void *z_decomp_alloc(unsigned char *options, int opt_len)
|
||||
{
|
||||
struct ppp_deflate_state *state;
|
||||
int w_size;
|
||||
|
||||
if (opt_len != CILEN_DEFLATE ||
|
||||
(options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
|
||||
options[1] != CILEN_DEFLATE ||
|
||||
DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
|
||||
options[3] != DEFLATE_CHK_SEQUENCE)
|
||||
return NULL;
|
||||
w_size = DEFLATE_SIZE(options[2]);
|
||||
if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
|
||||
return NULL;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
return NULL;
|
||||
|
||||
state->w_size = w_size;
|
||||
state->strm.next_out = NULL;
|
||||
state->strm.workspace = vmalloc(zlib_inflate_workspacesize());
|
||||
if (state->strm.workspace == NULL)
|
||||
goto out_free;
|
||||
|
||||
if (zlib_inflateInit2(&state->strm, -w_size) != Z_OK)
|
||||
goto out_free;
|
||||
return (void *) state;
|
||||
|
||||
out_free:
|
||||
z_decomp_free(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_decomp_init - initialize a previously-allocated decompressor.
|
||||
* @arg: pointer to the private state for the decompressor
|
||||
* @options: pointer to the CCP option data describing the
|
||||
* compression that was negotiated with the peer
|
||||
* @opt_len: length of the CCP option data at @options
|
||||
* @unit: PPP unit number for diagnostic messages
|
||||
* @hdrlen: ignored (present for backwards compatibility)
|
||||
* @mru: maximum length of decompressed packets
|
||||
* @debug: debug flag; if non-zero, debug messages are printed.
|
||||
*
|
||||
* The CCP options described by @options must match the options
|
||||
* specified when the decompressor was allocated. The decompressor
|
||||
* history is reset. Returns 0 for failure (CCP options don't
|
||||
* match) or 1 for success.
|
||||
*/
|
||||
static int z_decomp_init(void *arg, unsigned char *options, int opt_len,
|
||||
int unit, int hdrlen, int mru, int debug)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
if (opt_len < CILEN_DEFLATE ||
|
||||
(options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
|
||||
options[1] != CILEN_DEFLATE ||
|
||||
DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
|
||||
DEFLATE_SIZE(options[2]) != state->w_size ||
|
||||
options[3] != DEFLATE_CHK_SEQUENCE)
|
||||
return 0;
|
||||
|
||||
state->seqno = 0;
|
||||
state->unit = unit;
|
||||
state->debug = debug;
|
||||
state->mru = mru;
|
||||
|
||||
zlib_inflateReset(&state->strm);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_decomp_reset - reset a previously-allocated decompressor.
|
||||
* @arg: pointer to private state for the decompressor.
|
||||
*
|
||||
* This clears the history for the decompressor and makes it
|
||||
* ready to receive a new compressed stream.
|
||||
*/
|
||||
static void z_decomp_reset(void *arg)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
|
||||
state->seqno = 0;
|
||||
zlib_inflateReset(&state->strm);
|
||||
}
|
||||
|
||||
/**
|
||||
* z_decompress - decompress a Deflate-compressed packet.
|
||||
* @arg: pointer to private state for the decompressor
|
||||
* @ibuf: pointer to input (compressed) packet data
|
||||
* @isize: length of input packet
|
||||
* @obuf: pointer to space for output (decompressed) packet
|
||||
* @osize: amount of space available at @obuf
|
||||
*
|
||||
* Because of patent problems, we return DECOMP_ERROR for errors
|
||||
* found by inspecting the input data and for system problems, but
|
||||
* DECOMP_FATALERROR for any errors which could possibly be said to
|
||||
* be being detected "after" decompression. For DECOMP_ERROR,
|
||||
* we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
|
||||
* infringing a patent of Motorola's if we do, so we take CCP down
|
||||
* instead.
|
||||
*
|
||||
* Given that the frame has the correct sequence number and a good FCS,
|
||||
* errors such as invalid codes in the input most likely indicate a
|
||||
* bug, so we return DECOMP_FATALERROR for them in order to turn off
|
||||
* compression, even though they are detected by inspecting the input.
|
||||
*/
|
||||
static int z_decompress(void *arg, unsigned char *ibuf, int isize,
|
||||
unsigned char *obuf, int osize)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
int olen, seq, r;
|
||||
int decode_proto, overflow;
|
||||
unsigned char overflow_buf[1];
|
||||
|
||||
if (isize <= PPP_HDRLEN + DEFLATE_OVHD) {
|
||||
if (state->debug)
|
||||
printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n",
|
||||
state->unit, isize);
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
|
||||
/* Check the sequence number. */
|
||||
seq = get_unaligned_be16(ibuf + PPP_HDRLEN);
|
||||
if (seq != (state->seqno & 0xffff)) {
|
||||
if (state->debug)
|
||||
printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
|
||||
state->unit, seq, state->seqno & 0xffff);
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
++state->seqno;
|
||||
|
||||
/*
|
||||
* Fill in the first part of the PPP header. The protocol field
|
||||
* comes from the decompressed data.
|
||||
*/
|
||||
obuf[0] = PPP_ADDRESS(ibuf);
|
||||
obuf[1] = PPP_CONTROL(ibuf);
|
||||
obuf[2] = 0;
|
||||
|
||||
/*
|
||||
* Set up to call inflate. We set avail_out to 1 initially so we can
|
||||
* look at the first byte of the output and decide whether we have
|
||||
* a 1-byte or 2-byte protocol field.
|
||||
*/
|
||||
state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD;
|
||||
state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD);
|
||||
state->strm.next_out = obuf + 3;
|
||||
state->strm.avail_out = 1;
|
||||
decode_proto = 1;
|
||||
overflow = 0;
|
||||
|
||||
/*
|
||||
* Call inflate, supplying more input or output as needed.
|
||||
*/
|
||||
for (;;) {
|
||||
r = zlib_inflate(&state->strm, Z_PACKET_FLUSH);
|
||||
if (r != Z_OK) {
|
||||
if (state->debug)
|
||||
printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n",
|
||||
state->unit, r, (state->strm.msg? state->strm.msg: ""));
|
||||
return DECOMP_FATALERROR;
|
||||
}
|
||||
if (state->strm.avail_out != 0)
|
||||
break; /* all done */
|
||||
if (decode_proto) {
|
||||
state->strm.avail_out = osize - PPP_HDRLEN;
|
||||
if ((obuf[3] & 1) == 0) {
|
||||
/* 2-byte protocol field */
|
||||
obuf[2] = obuf[3];
|
||||
--state->strm.next_out;
|
||||
++state->strm.avail_out;
|
||||
}
|
||||
decode_proto = 0;
|
||||
} else if (!overflow) {
|
||||
/*
|
||||
* We've filled up the output buffer; the only way to
|
||||
* find out whether inflate has any more characters
|
||||
* left is to give it another byte of output space.
|
||||
*/
|
||||
state->strm.next_out = overflow_buf;
|
||||
state->strm.avail_out = 1;
|
||||
overflow = 1;
|
||||
} else {
|
||||
if (state->debug)
|
||||
printk(KERN_DEBUG "z_decompress%d: ran out of mru\n",
|
||||
state->unit);
|
||||
return DECOMP_FATALERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_proto) {
|
||||
if (state->debug)
|
||||
printk(KERN_DEBUG "z_decompress%d: didn't get proto\n",
|
||||
state->unit);
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
|
||||
olen = osize + overflow - state->strm.avail_out;
|
||||
state->stats.unc_bytes += olen;
|
||||
state->stats.unc_packets++;
|
||||
state->stats.comp_bytes += isize;
|
||||
state->stats.comp_packets++;
|
||||
|
||||
return olen;
|
||||
}
|
||||
|
||||
/**
|
||||
* z_incomp - add incompressible input data to the history.
|
||||
* @arg: pointer to private state for the decompressor
|
||||
* @ibuf: pointer to input packet data
|
||||
* @icnt: length of input data.
|
||||
*/
|
||||
static void z_incomp(void *arg, unsigned char *ibuf, int icnt)
|
||||
{
|
||||
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
|
||||
int proto, r;
|
||||
|
||||
/*
|
||||
* Check that the protocol is one we handle.
|
||||
*/
|
||||
proto = PPP_PROTOCOL(ibuf);
|
||||
if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
|
||||
return;
|
||||
|
||||
++state->seqno;
|
||||
|
||||
/*
|
||||
* We start at the either the 1st or 2nd byte of the protocol field,
|
||||
* depending on whether the protocol value is compressible.
|
||||
*/
|
||||
state->strm.next_in = ibuf + 3;
|
||||
state->strm.avail_in = icnt - 3;
|
||||
if (proto > 0xff) {
|
||||
--state->strm.next_in;
|
||||
++state->strm.avail_in;
|
||||
}
|
||||
|
||||
r = zlib_inflateIncomp(&state->strm);
|
||||
if (r != Z_OK) {
|
||||
/* gak! */
|
||||
if (state->debug) {
|
||||
printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n",
|
||||
state->unit, r, (state->strm.msg? state->strm.msg: ""));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update stats.
|
||||
*/
|
||||
state->stats.inc_bytes += icnt;
|
||||
state->stats.inc_packets++;
|
||||
state->stats.unc_bytes += icnt;
|
||||
state->stats.unc_packets++;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Module interface table
|
||||
*************************************************************/
|
||||
|
||||
/* These are in ppp_generic.c */
|
||||
extern int ppp_register_compressor (struct compressor *cp);
|
||||
extern void ppp_unregister_compressor (struct compressor *cp);
|
||||
|
||||
/*
|
||||
* Procedures exported to if_ppp.c.
|
||||
*/
|
||||
static struct compressor ppp_deflate = {
|
||||
.compress_proto = CI_DEFLATE,
|
||||
.comp_alloc = z_comp_alloc,
|
||||
.comp_free = z_comp_free,
|
||||
.comp_init = z_comp_init,
|
||||
.comp_reset = z_comp_reset,
|
||||
.compress = z_compress,
|
||||
.comp_stat = z_comp_stats,
|
||||
.decomp_alloc = z_decomp_alloc,
|
||||
.decomp_free = z_decomp_free,
|
||||
.decomp_init = z_decomp_init,
|
||||
.decomp_reset = z_decomp_reset,
|
||||
.decompress = z_decompress,
|
||||
.incomp = z_incomp,
|
||||
.decomp_stat = z_comp_stats,
|
||||
.owner = THIS_MODULE
|
||||
};
|
||||
|
||||
static struct compressor ppp_deflate_draft = {
|
||||
.compress_proto = CI_DEFLATE_DRAFT,
|
||||
.comp_alloc = z_comp_alloc,
|
||||
.comp_free = z_comp_free,
|
||||
.comp_init = z_comp_init,
|
||||
.comp_reset = z_comp_reset,
|
||||
.compress = z_compress,
|
||||
.comp_stat = z_comp_stats,
|
||||
.decomp_alloc = z_decomp_alloc,
|
||||
.decomp_free = z_decomp_free,
|
||||
.decomp_init = z_decomp_init,
|
||||
.decomp_reset = z_decomp_reset,
|
||||
.decompress = z_decompress,
|
||||
.incomp = z_incomp,
|
||||
.decomp_stat = z_comp_stats,
|
||||
.owner = THIS_MODULE
|
||||
};
|
||||
|
||||
static int __init deflate_init(void)
|
||||
{
|
||||
int answer = ppp_register_compressor(&ppp_deflate);
|
||||
if (answer == 0)
|
||||
printk(KERN_INFO
|
||||
"PPP Deflate Compression module registered\n");
|
||||
ppp_register_compressor(&ppp_deflate_draft);
|
||||
return answer;
|
||||
}
|
||||
|
||||
static void __exit deflate_cleanup(void)
|
||||
{
|
||||
ppp_unregister_compressor(&ppp_deflate);
|
||||
ppp_unregister_compressor(&ppp_deflate_draft);
|
||||
}
|
||||
|
||||
module_init(deflate_init);
|
||||
module_exit(deflate_cleanup);
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE));
|
||||
MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT));
|
2954
drivers/net/ppp/ppp_generic.c
Звичайний файл
2954
drivers/net/ppp/ppp_generic.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
740
drivers/net/ppp/ppp_mppe.c
Звичайний файл
740
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 <fcusack@fcusack.com>.
|
||||
* Copyright (c) 2002,2003,2004 Google, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License:
|
||||
* Permission to use, copy, modify, and distribute this software and its
|
||||
* documentation is hereby granted, provided that the above copyright
|
||||
* notice appears in all copies. This software is provided without any
|
||||
* warranty, express or implied.
|
||||
*
|
||||
* ALTERNATIVELY, provided that this notice is retained in full, this product
|
||||
* may be distributed under the terms of the GNU General Public License (GPL),
|
||||
* in which case the provisions of the GPL apply INSTEAD OF those given above.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*
|
||||
* Changelog:
|
||||
* 08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
|
||||
* Only need extra skb padding on transmit, not receive.
|
||||
* 06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
|
||||
* Use Linux kernel 2.6 arc4 and sha1 routines rather than
|
||||
* providing our own.
|
||||
* 2/15/04 - TS: added #include <version.h> and testing for Kernel
|
||||
* version before using
|
||||
* MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
|
||||
* deprecated in 2.6
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/ppp-comp.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "ppp_mppe.h"
|
||||
|
||||
MODULE_AUTHOR("Frank Cusack <fcusack@fcusack.com>");
|
||||
MODULE_DESCRIPTION("Point-to-Point Protocol Microsoft Point-to-Point Encryption support");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS("ppp-compress-" __stringify(CI_MPPE));
|
||||
MODULE_VERSION("1.0.2");
|
||||
|
||||
static unsigned int
|
||||
setup_sg(struct scatterlist *sg, const void *address, unsigned int length)
|
||||
{
|
||||
sg_set_buf(sg, address, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
#define SHA1_PAD_SIZE 40
|
||||
|
||||
/*
|
||||
* kernel crypto API needs its arguments to be in kmalloc'd memory, not in the module
|
||||
* static data area. That means sha_pad needs to be kmalloc'd.
|
||||
*/
|
||||
|
||||
struct sha_pad {
|
||||
unsigned char sha_pad1[SHA1_PAD_SIZE];
|
||||
unsigned char sha_pad2[SHA1_PAD_SIZE];
|
||||
};
|
||||
static struct sha_pad *sha_pad;
|
||||
|
||||
static inline void sha_pad_init(struct sha_pad *shapad)
|
||||
{
|
||||
memset(shapad->sha_pad1, 0x00, sizeof(shapad->sha_pad1));
|
||||
memset(shapad->sha_pad2, 0xF2, sizeof(shapad->sha_pad2));
|
||||
}
|
||||
|
||||
/*
|
||||
* State for an MPPE (de)compressor.
|
||||
*/
|
||||
struct ppp_mppe_state {
|
||||
struct crypto_blkcipher *arc4;
|
||||
struct crypto_hash *sha1;
|
||||
unsigned char *sha1_digest;
|
||||
unsigned char master_key[MPPE_MAX_KEY_LEN];
|
||||
unsigned char session_key[MPPE_MAX_KEY_LEN];
|
||||
unsigned keylen; /* key length in bytes */
|
||||
/* NB: 128-bit == 16, 40-bit == 8! */
|
||||
/* If we want to support 56-bit, */
|
||||
/* the unit has to change to bits */
|
||||
unsigned char bits; /* MPPE control bits */
|
||||
unsigned ccount; /* 12-bit coherency count (seqno) */
|
||||
unsigned stateful; /* stateful mode flag */
|
||||
int discard; /* stateful mode packet loss flag */
|
||||
int sanity_errors; /* take down LCP if too many */
|
||||
int unit;
|
||||
int debug;
|
||||
struct compstat stats;
|
||||
};
|
||||
|
||||
/* struct ppp_mppe_state.bits definitions */
|
||||
#define MPPE_BIT_A 0x80 /* Encryption table were (re)inititalized */
|
||||
#define MPPE_BIT_B 0x40 /* MPPC only (not implemented) */
|
||||
#define MPPE_BIT_C 0x20 /* MPPC only (not implemented) */
|
||||
#define MPPE_BIT_D 0x10 /* This is an encrypted frame */
|
||||
|
||||
#define MPPE_BIT_FLUSHED MPPE_BIT_A
|
||||
#define MPPE_BIT_ENCRYPTED MPPE_BIT_D
|
||||
|
||||
#define MPPE_BITS(p) ((p)[4] & 0xf0)
|
||||
#define MPPE_CCOUNT(p) ((((p)[4] & 0x0f) << 8) + (p)[5])
|
||||
#define MPPE_CCOUNT_SPACE 0x1000 /* The size of the ccount space */
|
||||
|
||||
#define MPPE_OVHD 2 /* MPPE overhead/packet */
|
||||
#define SANITY_MAX 1600 /* Max bogon factor we will tolerate */
|
||||
|
||||
/*
|
||||
* Key Derivation, from RFC 3078, RFC 3079.
|
||||
* Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
|
||||
*/
|
||||
static void get_new_key_from_sha(struct ppp_mppe_state * state)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[4];
|
||||
unsigned int nbytes;
|
||||
|
||||
sg_init_table(sg, 4);
|
||||
|
||||
nbytes = setup_sg(&sg[0], state->master_key, state->keylen);
|
||||
nbytes += setup_sg(&sg[1], sha_pad->sha_pad1,
|
||||
sizeof(sha_pad->sha_pad1));
|
||||
nbytes += setup_sg(&sg[2], state->session_key, state->keylen);
|
||||
nbytes += setup_sg(&sg[3], sha_pad->sha_pad2,
|
||||
sizeof(sha_pad->sha_pad2));
|
||||
|
||||
desc.tfm = state->sha1;
|
||||
desc.flags = 0;
|
||||
|
||||
crypto_hash_digest(&desc, sg, nbytes, state->sha1_digest);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
|
||||
* Well, not what's written there, but rather what they meant.
|
||||
*/
|
||||
static void mppe_rekey(struct ppp_mppe_state * state, int initial_key)
|
||||
{
|
||||
struct scatterlist sg_in[1], sg_out[1];
|
||||
struct blkcipher_desc desc = { .tfm = state->arc4 };
|
||||
|
||||
get_new_key_from_sha(state);
|
||||
if (!initial_key) {
|
||||
crypto_blkcipher_setkey(state->arc4, state->sha1_digest,
|
||||
state->keylen);
|
||||
sg_init_table(sg_in, 1);
|
||||
sg_init_table(sg_out, 1);
|
||||
setup_sg(sg_in, state->sha1_digest, state->keylen);
|
||||
setup_sg(sg_out, state->session_key, state->keylen);
|
||||
if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
|
||||
state->keylen) != 0) {
|
||||
printk(KERN_WARNING "mppe_rekey: cipher_encrypt failed\n");
|
||||
}
|
||||
} else {
|
||||
memcpy(state->session_key, state->sha1_digest, state->keylen);
|
||||
}
|
||||
if (state->keylen == 8) {
|
||||
/* See RFC 3078 */
|
||||
state->session_key[0] = 0xd1;
|
||||
state->session_key[1] = 0x26;
|
||||
state->session_key[2] = 0x9e;
|
||||
}
|
||||
crypto_blkcipher_setkey(state->arc4, state->session_key, state->keylen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate space for a (de)compressor.
|
||||
*/
|
||||
static void *mppe_alloc(unsigned char *options, int optlen)
|
||||
{
|
||||
struct ppp_mppe_state *state;
|
||||
unsigned int digestsize;
|
||||
|
||||
if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
|
||||
options[0] != CI_MPPE || options[1] != CILEN_MPPE)
|
||||
goto out;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
goto out;
|
||||
|
||||
|
||||
state->arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(state->arc4)) {
|
||||
state->arc4 = NULL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
state->sha1 = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(state->sha1)) {
|
||||
state->sha1 = NULL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
digestsize = crypto_hash_digestsize(state->sha1);
|
||||
if (digestsize < MPPE_MAX_KEY_LEN)
|
||||
goto out_free;
|
||||
|
||||
state->sha1_digest = kmalloc(digestsize, GFP_KERNEL);
|
||||
if (!state->sha1_digest)
|
||||
goto out_free;
|
||||
|
||||
/* Save keys. */
|
||||
memcpy(state->master_key, &options[CILEN_MPPE],
|
||||
sizeof(state->master_key));
|
||||
memcpy(state->session_key, state->master_key,
|
||||
sizeof(state->master_key));
|
||||
|
||||
/*
|
||||
* We defer initial key generation until mppe_init(), as mppe_alloc()
|
||||
* is called frequently during negotiation.
|
||||
*/
|
||||
|
||||
return (void *)state;
|
||||
|
||||
out_free:
|
||||
if (state->sha1_digest)
|
||||
kfree(state->sha1_digest);
|
||||
if (state->sha1)
|
||||
crypto_free_hash(state->sha1);
|
||||
if (state->arc4)
|
||||
crypto_free_blkcipher(state->arc4);
|
||||
kfree(state);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deallocate space for a (de)compressor.
|
||||
*/
|
||||
static void mppe_free(void *arg)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
if (state) {
|
||||
if (state->sha1_digest)
|
||||
kfree(state->sha1_digest);
|
||||
if (state->sha1)
|
||||
crypto_free_hash(state->sha1);
|
||||
if (state->arc4)
|
||||
crypto_free_blkcipher(state->arc4);
|
||||
kfree(state);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize (de)compressor state.
|
||||
*/
|
||||
static int
|
||||
mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
|
||||
const char *debugstr)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
unsigned char mppe_opts;
|
||||
|
||||
if (optlen != CILEN_MPPE ||
|
||||
options[0] != CI_MPPE || options[1] != CILEN_MPPE)
|
||||
return 0;
|
||||
|
||||
MPPE_CI_TO_OPTS(&options[2], mppe_opts);
|
||||
if (mppe_opts & MPPE_OPT_128)
|
||||
state->keylen = 16;
|
||||
else if (mppe_opts & MPPE_OPT_40)
|
||||
state->keylen = 8;
|
||||
else {
|
||||
printk(KERN_WARNING "%s[%d]: unknown key length\n", debugstr,
|
||||
unit);
|
||||
return 0;
|
||||
}
|
||||
if (mppe_opts & MPPE_OPT_STATEFUL)
|
||||
state->stateful = 1;
|
||||
|
||||
/* Generate the initial session key. */
|
||||
mppe_rekey(state, 1);
|
||||
|
||||
if (debug) {
|
||||
int i;
|
||||
char mkey[sizeof(state->master_key) * 2 + 1];
|
||||
char skey[sizeof(state->session_key) * 2 + 1];
|
||||
|
||||
printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n",
|
||||
debugstr, unit, (state->keylen == 16) ? 128 : 40,
|
||||
(state->stateful) ? "stateful" : "stateless");
|
||||
|
||||
for (i = 0; i < sizeof(state->master_key); i++)
|
||||
sprintf(mkey + i * 2, "%02x", state->master_key[i]);
|
||||
for (i = 0; i < sizeof(state->session_key); i++)
|
||||
sprintf(skey + i * 2, "%02x", state->session_key[i]);
|
||||
printk(KERN_DEBUG
|
||||
"%s[%d]: keys: master: %s initial session: %s\n",
|
||||
debugstr, unit, mkey, skey);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the coherency count. The initial value is not specified
|
||||
* in RFC 3078, but we can make a reasonable assumption that it will
|
||||
* start at 0. Setting it to the max here makes the comp/decomp code
|
||||
* do the right thing (determined through experiment).
|
||||
*/
|
||||
state->ccount = MPPE_CCOUNT_SPACE - 1;
|
||||
|
||||
/*
|
||||
* Note that even though we have initialized the key table, we don't
|
||||
* set the FLUSHED bit. This is contrary to RFC 3078, sec. 3.1.
|
||||
*/
|
||||
state->bits = MPPE_BIT_ENCRYPTED;
|
||||
|
||||
state->unit = unit;
|
||||
state->debug = debug;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
mppe_comp_init(void *arg, unsigned char *options, int optlen, int unit,
|
||||
int hdrlen, int debug)
|
||||
{
|
||||
/* ARGSUSED */
|
||||
return mppe_init(arg, options, optlen, unit, debug, "mppe_comp_init");
|
||||
}
|
||||
|
||||
/*
|
||||
* We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
|
||||
* tell the compressor to rekey. Note that we MUST NOT rekey for
|
||||
* every CCP Reset-Request; we only rekey on the next xmit packet.
|
||||
* We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
|
||||
* So, rekeying for every CCP Reset-Request is broken as the peer will not
|
||||
* know how many times we've rekeyed. (If we rekey and THEN get another
|
||||
* CCP Reset-Request, we must rekey again.)
|
||||
*/
|
||||
static void mppe_comp_reset(void *arg)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
|
||||
state->bits |= MPPE_BIT_FLUSHED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compress (encrypt) a packet.
|
||||
* It's strange to call this a compressor, since the output is always
|
||||
* MPPE_OVHD + 2 bytes larger than the input.
|
||||
*/
|
||||
static int
|
||||
mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
|
||||
int isize, int osize)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
struct blkcipher_desc desc = { .tfm = state->arc4 };
|
||||
int proto;
|
||||
struct scatterlist sg_in[1], sg_out[1];
|
||||
|
||||
/*
|
||||
* Check that the protocol is in the range we handle.
|
||||
*/
|
||||
proto = PPP_PROTOCOL(ibuf);
|
||||
if (proto < 0x0021 || proto > 0x00fa)
|
||||
return 0;
|
||||
|
||||
/* Make sure we have enough room to generate an encrypted packet. */
|
||||
if (osize < isize + MPPE_OVHD + 2) {
|
||||
/* Drop the packet if we should encrypt it, but can't. */
|
||||
printk(KERN_DEBUG "mppe_compress[%d]: osize too small! "
|
||||
"(have: %d need: %d)\n", state->unit,
|
||||
osize, osize + MPPE_OVHD + 2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
osize = isize + MPPE_OVHD + 2;
|
||||
|
||||
/*
|
||||
* Copy over the PPP header and set control bits.
|
||||
*/
|
||||
obuf[0] = PPP_ADDRESS(ibuf);
|
||||
obuf[1] = PPP_CONTROL(ibuf);
|
||||
put_unaligned_be16(PPP_COMP, obuf + 2);
|
||||
obuf += PPP_HDRLEN;
|
||||
|
||||
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
|
||||
if (state->debug >= 7)
|
||||
printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
|
||||
state->ccount);
|
||||
put_unaligned_be16(state->ccount, obuf);
|
||||
|
||||
if (!state->stateful || /* stateless mode */
|
||||
((state->ccount & 0xff) == 0xff) || /* "flag" packet */
|
||||
(state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request */
|
||||
/* We must rekey */
|
||||
if (state->debug && state->stateful)
|
||||
printk(KERN_DEBUG "mppe_compress[%d]: rekeying\n",
|
||||
state->unit);
|
||||
mppe_rekey(state, 0);
|
||||
state->bits |= MPPE_BIT_FLUSHED;
|
||||
}
|
||||
obuf[0] |= state->bits;
|
||||
state->bits &= ~MPPE_BIT_FLUSHED; /* reset for next xmit */
|
||||
|
||||
obuf += MPPE_OVHD;
|
||||
ibuf += 2; /* skip to proto field */
|
||||
isize -= 2;
|
||||
|
||||
/* Encrypt packet */
|
||||
sg_init_table(sg_in, 1);
|
||||
sg_init_table(sg_out, 1);
|
||||
setup_sg(sg_in, ibuf, isize);
|
||||
setup_sg(sg_out, obuf, osize);
|
||||
if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in, isize) != 0) {
|
||||
printk(KERN_DEBUG "crypto_cypher_encrypt failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->stats.unc_bytes += isize;
|
||||
state->stats.unc_packets++;
|
||||
state->stats.comp_bytes += osize;
|
||||
state->stats.comp_packets++;
|
||||
|
||||
return osize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since every frame grows by MPPE_OVHD + 2 bytes, this is always going
|
||||
* to look bad ... and the longer the link is up the worse it will get.
|
||||
*/
|
||||
static void mppe_comp_stats(void *arg, struct compstat *stats)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
|
||||
*stats = state->stats;
|
||||
}
|
||||
|
||||
static int
|
||||
mppe_decomp_init(void *arg, unsigned char *options, int optlen, int unit,
|
||||
int hdrlen, int mru, int debug)
|
||||
{
|
||||
/* ARGSUSED */
|
||||
return mppe_init(arg, options, optlen, unit, debug, "mppe_decomp_init");
|
||||
}
|
||||
|
||||
/*
|
||||
* We received a CCP Reset-Ack. Just ignore it.
|
||||
*/
|
||||
static void mppe_decomp_reset(void *arg)
|
||||
{
|
||||
/* ARGSUSED */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decompress (decrypt) an MPPE packet.
|
||||
*/
|
||||
static int
|
||||
mppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
|
||||
int osize)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
struct blkcipher_desc desc = { .tfm = state->arc4 };
|
||||
unsigned ccount;
|
||||
int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
|
||||
int sanity = 0;
|
||||
struct scatterlist sg_in[1], sg_out[1];
|
||||
|
||||
if (isize <= PPP_HDRLEN + MPPE_OVHD) {
|
||||
if (state->debug)
|
||||
printk(KERN_DEBUG
|
||||
"mppe_decompress[%d]: short pkt (%d)\n",
|
||||
state->unit, isize);
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we have enough room to decrypt the packet.
|
||||
* Note that for our test we only subtract 1 byte whereas in
|
||||
* mppe_compress() we added 2 bytes (+MPPE_OVHD);
|
||||
* this is to account for possible PFC.
|
||||
*/
|
||||
if (osize < isize - MPPE_OVHD - 1) {
|
||||
printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! "
|
||||
"(have: %d need: %d)\n", state->unit,
|
||||
osize, isize - MPPE_OVHD - 1);
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
osize = isize - MPPE_OVHD - 2; /* assume no PFC */
|
||||
|
||||
ccount = MPPE_CCOUNT(ibuf);
|
||||
if (state->debug >= 7)
|
||||
printk(KERN_DEBUG "mppe_decompress[%d]: ccount %d\n",
|
||||
state->unit, ccount);
|
||||
|
||||
/* sanity checks -- terminate with extreme prejudice */
|
||||
if (!(MPPE_BITS(ibuf) & MPPE_BIT_ENCRYPTED)) {
|
||||
printk(KERN_DEBUG
|
||||
"mppe_decompress[%d]: ENCRYPTED bit not set!\n",
|
||||
state->unit);
|
||||
state->sanity_errors += 100;
|
||||
sanity = 1;
|
||||
}
|
||||
if (!state->stateful && !flushed) {
|
||||
printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
|
||||
"stateless mode!\n", state->unit);
|
||||
state->sanity_errors += 100;
|
||||
sanity = 1;
|
||||
}
|
||||
if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
|
||||
printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set on "
|
||||
"flag packet!\n", state->unit);
|
||||
state->sanity_errors += 100;
|
||||
sanity = 1;
|
||||
}
|
||||
|
||||
if (sanity) {
|
||||
if (state->sanity_errors < SANITY_MAX)
|
||||
return DECOMP_ERROR;
|
||||
else
|
||||
/*
|
||||
* Take LCP down if the peer is sending too many bogons.
|
||||
* We don't want to do this for a single or just a few
|
||||
* instances since it could just be due to packet corruption.
|
||||
*/
|
||||
return DECOMP_FATALERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the coherency count.
|
||||
*/
|
||||
|
||||
if (!state->stateful) {
|
||||
/* RFC 3078, sec 8.1. Rekey for every packet. */
|
||||
while (state->ccount != ccount) {
|
||||
mppe_rekey(state, 0);
|
||||
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
|
||||
}
|
||||
} else {
|
||||
/* RFC 3078, sec 8.2. */
|
||||
if (!state->discard) {
|
||||
/* normal state */
|
||||
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
|
||||
if (ccount != state->ccount) {
|
||||
/*
|
||||
* (ccount > state->ccount)
|
||||
* Packet loss detected, enter the discard state.
|
||||
* Signal the peer to rekey (by sending a CCP Reset-Request).
|
||||
*/
|
||||
state->discard = 1;
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* discard state */
|
||||
if (!flushed) {
|
||||
/* ccp.c will be silent (no additional CCP Reset-Requests). */
|
||||
return DECOMP_ERROR;
|
||||
} else {
|
||||
/* Rekey for every missed "flag" packet. */
|
||||
while ((ccount & ~0xff) !=
|
||||
(state->ccount & ~0xff)) {
|
||||
mppe_rekey(state, 0);
|
||||
state->ccount =
|
||||
(state->ccount +
|
||||
256) % MPPE_CCOUNT_SPACE;
|
||||
}
|
||||
|
||||
/* reset */
|
||||
state->discard = 0;
|
||||
state->ccount = ccount;
|
||||
/*
|
||||
* Another problem with RFC 3078 here. It implies that the
|
||||
* peer need not send a Reset-Ack packet. But RFC 1962
|
||||
* requires it. Hopefully, M$ does send a Reset-Ack; even
|
||||
* though it isn't required for MPPE synchronization, it is
|
||||
* required to reset CCP state.
|
||||
*/
|
||||
}
|
||||
}
|
||||
if (flushed)
|
||||
mppe_rekey(state, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the first part of the PPP header. The protocol field
|
||||
* comes from the decrypted data.
|
||||
*/
|
||||
obuf[0] = PPP_ADDRESS(ibuf); /* +1 */
|
||||
obuf[1] = PPP_CONTROL(ibuf); /* +1 */
|
||||
obuf += 2;
|
||||
ibuf += PPP_HDRLEN + MPPE_OVHD;
|
||||
isize -= PPP_HDRLEN + MPPE_OVHD; /* -6 */
|
||||
/* net osize: isize-4 */
|
||||
|
||||
/*
|
||||
* Decrypt the first byte in order to check if it is
|
||||
* a compressed or uncompressed protocol field.
|
||||
*/
|
||||
sg_init_table(sg_in, 1);
|
||||
sg_init_table(sg_out, 1);
|
||||
setup_sg(sg_in, ibuf, 1);
|
||||
setup_sg(sg_out, obuf, 1);
|
||||
if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, 1) != 0) {
|
||||
printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do PFC decompression.
|
||||
* This would be nicer if we were given the actual sk_buff
|
||||
* instead of a char *.
|
||||
*/
|
||||
if ((obuf[0] & 0x01) != 0) {
|
||||
obuf[1] = obuf[0];
|
||||
obuf[0] = 0;
|
||||
obuf++;
|
||||
osize++;
|
||||
}
|
||||
|
||||
/* And finally, decrypt the rest of the packet. */
|
||||
setup_sg(sg_in, ibuf + 1, isize - 1);
|
||||
setup_sg(sg_out, obuf + 1, osize - 1);
|
||||
if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, isize - 1)) {
|
||||
printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
|
||||
return DECOMP_ERROR;
|
||||
}
|
||||
|
||||
state->stats.unc_bytes += osize;
|
||||
state->stats.unc_packets++;
|
||||
state->stats.comp_bytes += isize;
|
||||
state->stats.comp_packets++;
|
||||
|
||||
/* good packet credit */
|
||||
state->sanity_errors >>= 1;
|
||||
|
||||
return osize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Incompressible data has arrived (this should never happen!).
|
||||
* We should probably drop the link if the protocol is in the range
|
||||
* of what should be encrypted. At the least, we should drop this
|
||||
* packet. (How to do this?)
|
||||
*/
|
||||
static void mppe_incomp(void *arg, unsigned char *ibuf, int icnt)
|
||||
{
|
||||
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
|
||||
|
||||
if (state->debug &&
|
||||
(PPP_PROTOCOL(ibuf) >= 0x0021 && PPP_PROTOCOL(ibuf) <= 0x00fa))
|
||||
printk(KERN_DEBUG
|
||||
"mppe_incomp[%d]: incompressible (unencrypted) data! "
|
||||
"(proto %04x)\n", state->unit, PPP_PROTOCOL(ibuf));
|
||||
|
||||
state->stats.inc_bytes += icnt;
|
||||
state->stats.inc_packets++;
|
||||
state->stats.unc_bytes += icnt;
|
||||
state->stats.unc_packets++;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Module interface table
|
||||
*************************************************************/
|
||||
|
||||
/*
|
||||
* Procedures exported to if_ppp.c.
|
||||
*/
|
||||
static struct compressor ppp_mppe = {
|
||||
.compress_proto = CI_MPPE,
|
||||
.comp_alloc = mppe_alloc,
|
||||
.comp_free = mppe_free,
|
||||
.comp_init = mppe_comp_init,
|
||||
.comp_reset = mppe_comp_reset,
|
||||
.compress = mppe_compress,
|
||||
.comp_stat = mppe_comp_stats,
|
||||
.decomp_alloc = mppe_alloc,
|
||||
.decomp_free = mppe_free,
|
||||
.decomp_init = mppe_decomp_init,
|
||||
.decomp_reset = mppe_decomp_reset,
|
||||
.decompress = mppe_decompress,
|
||||
.incomp = mppe_incomp,
|
||||
.decomp_stat = mppe_comp_stats,
|
||||
.owner = THIS_MODULE,
|
||||
.comp_extra = MPPE_PAD,
|
||||
};
|
||||
|
||||
/*
|
||||
* ppp_mppe_init()
|
||||
*
|
||||
* Prior to allowing load, try to load the arc4 and sha1 crypto
|
||||
* libraries. The actual use will be allocated later, but
|
||||
* this way the module will fail to insmod if they aren't available.
|
||||
*/
|
||||
|
||||
static int __init ppp_mppe_init(void)
|
||||
{
|
||||
int answer;
|
||||
if (!(crypto_has_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC) &&
|
||||
crypto_has_hash("sha1", 0, CRYPTO_ALG_ASYNC)))
|
||||
return -ENODEV;
|
||||
|
||||
sha_pad = kmalloc(sizeof(struct sha_pad), GFP_KERNEL);
|
||||
if (!sha_pad)
|
||||
return -ENOMEM;
|
||||
sha_pad_init(sha_pad);
|
||||
|
||||
answer = ppp_register_compressor(&ppp_mppe);
|
||||
|
||||
if (answer == 0)
|
||||
printk(KERN_INFO "PPP MPPE Compression module registered\n");
|
||||
else
|
||||
kfree(sha_pad);
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
static void __exit ppp_mppe_cleanup(void)
|
||||
{
|
||||
ppp_unregister_compressor(&ppp_mppe);
|
||||
kfree(sha_pad);
|
||||
}
|
||||
|
||||
module_init(ppp_mppe_init);
|
||||
module_exit(ppp_mppe_cleanup);
|
86
drivers/net/ppp/ppp_mppe.h
Звичайний файл
86
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)
|
790
drivers/net/ppp/ppp_synctty.c
Звичайний файл
790
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 <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/if_ppp.h>
|
||||
#include <linux/ppp_channel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define PPP_VERSION "2.4.2"
|
||||
|
||||
/* Structure for storing local state. */
|
||||
struct syncppp {
|
||||
struct tty_struct *tty;
|
||||
unsigned int flags;
|
||||
unsigned int rbits;
|
||||
int mru;
|
||||
spinlock_t xmit_lock;
|
||||
spinlock_t recv_lock;
|
||||
unsigned long xmit_flags;
|
||||
u32 xaccm[8];
|
||||
u32 raccm;
|
||||
unsigned int bytes_sent;
|
||||
unsigned int bytes_rcvd;
|
||||
|
||||
struct sk_buff *tpkt;
|
||||
unsigned long last_xmit;
|
||||
|
||||
struct sk_buff_head rqueue;
|
||||
|
||||
struct tasklet_struct tsk;
|
||||
|
||||
atomic_t refcnt;
|
||||
struct completion dead_cmp;
|
||||
struct ppp_channel chan; /* interface to generic ppp layer */
|
||||
};
|
||||
|
||||
/* Bit numbers in xmit_flags */
|
||||
#define XMIT_WAKEUP 0
|
||||
#define XMIT_FULL 1
|
||||
|
||||
/* Bits in rbits */
|
||||
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
|
||||
|
||||
#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */
|
||||
|
||||
/*
|
||||
* Prototypes.
|
||||
*/
|
||||
static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
|
||||
static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
|
||||
static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
static void ppp_sync_process(unsigned long arg);
|
||||
static int ppp_sync_push(struct syncppp *ap);
|
||||
static void ppp_sync_flush_output(struct syncppp *ap);
|
||||
static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
|
||||
char *flags, int count);
|
||||
|
||||
static const struct ppp_channel_ops sync_ops = {
|
||||
.start_xmit = ppp_sync_send,
|
||||
.ioctl = ppp_sync_ioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility procedures to print a buffer in hex/ascii
|
||||
*/
|
||||
static void
|
||||
ppp_print_hex (register __u8 * out, const __u8 * in, int count)
|
||||
{
|
||||
register __u8 next_ch;
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
|
||||
while (count-- > 0) {
|
||||
next_ch = *in++;
|
||||
*out++ = hex[(next_ch >> 4) & 0x0F];
|
||||
*out++ = hex[next_ch & 0x0F];
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ppp_print_char (register __u8 * out, const __u8 * in, int count)
|
||||
{
|
||||
register __u8 next_ch;
|
||||
|
||||
while (count-- > 0) {
|
||||
next_ch = *in++;
|
||||
|
||||
if (next_ch < 0x20 || next_ch > 0x7e)
|
||||
*out++ = '.';
|
||||
else {
|
||||
*out++ = next_ch;
|
||||
if (next_ch == '%') /* printk/syslogd has a bug !! */
|
||||
*out++ = '%';
|
||||
}
|
||||
}
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
static void
|
||||
ppp_print_buffer (const char *name, const __u8 *buf, int count)
|
||||
{
|
||||
__u8 line[44];
|
||||
|
||||
if (name != NULL)
|
||||
printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count);
|
||||
|
||||
while (count > 8) {
|
||||
memset (line, 32, 44);
|
||||
ppp_print_hex (line, buf, 8);
|
||||
ppp_print_char (&line[8 * 3], buf, 8);
|
||||
printk(KERN_DEBUG "%s\n", line);
|
||||
count -= 8;
|
||||
buf += 8;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
memset (line, 32, 44);
|
||||
ppp_print_hex (line, buf, count);
|
||||
ppp_print_char (&line[8 * 3], buf, count);
|
||||
printk(KERN_DEBUG "%s\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Routines implementing the synchronous PPP line discipline.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have a potential race on dereferencing tty->disc_data,
|
||||
* because the tty layer provides no locking at all - thus one
|
||||
* cpu could be running ppp_synctty_receive while another
|
||||
* calls ppp_synctty_close, which zeroes tty->disc_data and
|
||||
* frees the memory that ppp_synctty_receive is using. The best
|
||||
* way to fix this is to use a rwlock in the tty struct, but for now
|
||||
* we use a single global rwlock for all ttys in ppp line discipline.
|
||||
*
|
||||
* FIXME: Fixed in tty_io nowadays.
|
||||
*/
|
||||
static DEFINE_RWLOCK(disc_data_lock);
|
||||
|
||||
static struct syncppp *sp_get(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap;
|
||||
|
||||
read_lock(&disc_data_lock);
|
||||
ap = tty->disc_data;
|
||||
if (ap != NULL)
|
||||
atomic_inc(&ap->refcnt);
|
||||
read_unlock(&disc_data_lock);
|
||||
return ap;
|
||||
}
|
||||
|
||||
static void sp_put(struct syncppp *ap)
|
||||
{
|
||||
if (atomic_dec_and_test(&ap->refcnt))
|
||||
complete(&ap->dead_cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a tty is put into sync-PPP line discipline.
|
||||
*/
|
||||
static int
|
||||
ppp_sync_open(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap;
|
||||
int err;
|
||||
int speed;
|
||||
|
||||
if (tty->ops->write == NULL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ap = kzalloc(sizeof(*ap), GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!ap)
|
||||
goto out;
|
||||
|
||||
/* initialize the syncppp structure */
|
||||
ap->tty = tty;
|
||||
ap->mru = PPP_MRU;
|
||||
spin_lock_init(&ap->xmit_lock);
|
||||
spin_lock_init(&ap->recv_lock);
|
||||
ap->xaccm[0] = ~0U;
|
||||
ap->xaccm[3] = 0x60000000U;
|
||||
ap->raccm = ~0U;
|
||||
|
||||
skb_queue_head_init(&ap->rqueue);
|
||||
tasklet_init(&ap->tsk, ppp_sync_process, (unsigned long) ap);
|
||||
|
||||
atomic_set(&ap->refcnt, 1);
|
||||
init_completion(&ap->dead_cmp);
|
||||
|
||||
ap->chan.private = ap;
|
||||
ap->chan.ops = &sync_ops;
|
||||
ap->chan.mtu = PPP_MRU;
|
||||
ap->chan.hdrlen = 2; /* for A/C bytes */
|
||||
speed = tty_get_baud_rate(tty);
|
||||
ap->chan.speed = speed;
|
||||
err = ppp_register_channel(&ap->chan);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
tty->disc_data = ap;
|
||||
tty->receive_room = 65536;
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
kfree(ap);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the tty is put into another line discipline
|
||||
* or it hangs up. We have to wait for any cpu currently
|
||||
* executing in any of the other ppp_synctty_* routines to
|
||||
* finish before we can call ppp_unregister_channel and free
|
||||
* the syncppp struct. This routine must be called from
|
||||
* process context, not interrupt or softirq context.
|
||||
*/
|
||||
static void
|
||||
ppp_sync_close(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap;
|
||||
|
||||
write_lock_irq(&disc_data_lock);
|
||||
ap = tty->disc_data;
|
||||
tty->disc_data = NULL;
|
||||
write_unlock_irq(&disc_data_lock);
|
||||
if (!ap)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We have now ensured that nobody can start using ap from now
|
||||
* on, but we have to wait for all existing users to finish.
|
||||
* Note that ppp_unregister_channel ensures that no calls to
|
||||
* our channel ops (i.e. ppp_sync_send/ioctl) are in progress
|
||||
* by the time it returns.
|
||||
*/
|
||||
if (!atomic_dec_and_test(&ap->refcnt))
|
||||
wait_for_completion(&ap->dead_cmp);
|
||||
tasklet_kill(&ap->tsk);
|
||||
|
||||
ppp_unregister_channel(&ap->chan);
|
||||
skb_queue_purge(&ap->rqueue);
|
||||
kfree_skb(ap->tpkt);
|
||||
kfree(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on tty hangup in process context.
|
||||
*
|
||||
* Wait for I/O to driver to complete and unregister PPP channel.
|
||||
* This is already done by the close routine, so just call that.
|
||||
*/
|
||||
static int ppp_sync_hangup(struct tty_struct *tty)
|
||||
{
|
||||
ppp_sync_close(tty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read does nothing - no data is ever available this way.
|
||||
* Pppd reads and writes packets via /dev/ppp instead.
|
||||
*/
|
||||
static ssize_t
|
||||
ppp_sync_read(struct tty_struct *tty, struct file *file,
|
||||
unsigned char __user *buf, size_t count)
|
||||
{
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write on the tty does nothing, the packets all come in
|
||||
* from the ppp generic stuff.
|
||||
*/
|
||||
static ssize_t
|
||||
ppp_sync_write(struct tty_struct *tty, struct file *file,
|
||||
const unsigned char *buf, size_t count)
|
||||
{
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int
|
||||
ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct syncppp *ap = sp_get(tty);
|
||||
int __user *p = (int __user *)arg;
|
||||
int err, val;
|
||||
|
||||
if (!ap)
|
||||
return -ENXIO;
|
||||
err = -EFAULT;
|
||||
switch (cmd) {
|
||||
case PPPIOCGCHAN:
|
||||
err = -EFAULT;
|
||||
if (put_user(ppp_channel_index(&ap->chan), p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGUNIT:
|
||||
err = -EFAULT;
|
||||
if (put_user(ppp_unit_number(&ap->chan), p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case TCFLSH:
|
||||
/* flush our buffers and the serial port's buffer */
|
||||
if (arg == TCIOFLUSH || arg == TCOFLUSH)
|
||||
ppp_sync_flush_output(ap);
|
||||
err = tty_perform_flush(tty, arg);
|
||||
break;
|
||||
|
||||
case FIONREAD:
|
||||
val = 0;
|
||||
if (put_user(val, p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = tty_mode_ioctl(tty, file, cmd, arg);
|
||||
break;
|
||||
}
|
||||
|
||||
sp_put(ap);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* No kernel lock - fine */
|
||||
static unsigned int
|
||||
ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* May sleep, don't call from interrupt level or with interrupts disabled */
|
||||
static void
|
||||
ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
|
||||
char *cflags, int count)
|
||||
{
|
||||
struct syncppp *ap = sp_get(tty);
|
||||
unsigned long flags;
|
||||
|
||||
if (!ap)
|
||||
return;
|
||||
spin_lock_irqsave(&ap->recv_lock, flags);
|
||||
ppp_sync_input(ap, buf, cflags, count);
|
||||
spin_unlock_irqrestore(&ap->recv_lock, flags);
|
||||
if (!skb_queue_empty(&ap->rqueue))
|
||||
tasklet_schedule(&ap->tsk);
|
||||
sp_put(ap);
|
||||
tty_unthrottle(tty);
|
||||
}
|
||||
|
||||
static void
|
||||
ppp_sync_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap = sp_get(tty);
|
||||
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (!ap)
|
||||
return;
|
||||
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
|
||||
tasklet_schedule(&ap->tsk);
|
||||
sp_put(ap);
|
||||
}
|
||||
|
||||
|
||||
static struct tty_ldisc_ops ppp_sync_ldisc = {
|
||||
.owner = THIS_MODULE,
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "pppsync",
|
||||
.open = ppp_sync_open,
|
||||
.close = ppp_sync_close,
|
||||
.hangup = ppp_sync_hangup,
|
||||
.read = ppp_sync_read,
|
||||
.write = ppp_sync_write,
|
||||
.ioctl = ppp_synctty_ioctl,
|
||||
.poll = ppp_sync_poll,
|
||||
.receive_buf = ppp_sync_receive,
|
||||
.write_wakeup = ppp_sync_wakeup,
|
||||
};
|
||||
|
||||
static int __init
|
||||
ppp_sync_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tty_register_ldisc(N_SYNC_PPP, &ppp_sync_ldisc);
|
||||
if (err != 0)
|
||||
printk(KERN_ERR "PPP_sync: error %d registering line disc.\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following routines provide the PPP channel interface.
|
||||
*/
|
||||
static int
|
||||
ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct syncppp *ap = chan->private;
|
||||
int err, val;
|
||||
u32 accm[8];
|
||||
void __user *argp = (void __user *)arg;
|
||||
u32 __user *p = argp;
|
||||
|
||||
err = -EFAULT;
|
||||
switch (cmd) {
|
||||
case PPPIOCGFLAGS:
|
||||
val = ap->flags | ap->rbits;
|
||||
if (put_user(val, (int __user *) argp))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
case PPPIOCSFLAGS:
|
||||
if (get_user(val, (int __user *) argp))
|
||||
break;
|
||||
ap->flags = val & ~SC_RCV_BITS;
|
||||
spin_lock_irq(&ap->recv_lock);
|
||||
ap->rbits = val & SC_RCV_BITS;
|
||||
spin_unlock_irq(&ap->recv_lock);
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGASYNCMAP:
|
||||
if (put_user(ap->xaccm[0], p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
case PPPIOCSASYNCMAP:
|
||||
if (get_user(ap->xaccm[0], p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGRASYNCMAP:
|
||||
if (put_user(ap->raccm, p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
case PPPIOCSRASYNCMAP:
|
||||
if (get_user(ap->raccm, p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGXASYNCMAP:
|
||||
if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
case PPPIOCSXASYNCMAP:
|
||||
if (copy_from_user(accm, argp, sizeof(accm)))
|
||||
break;
|
||||
accm[2] &= ~0x40000000U; /* can't escape 0x5e */
|
||||
accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
|
||||
memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGMRU:
|
||||
if (put_user(ap->mru, (int __user *) argp))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
case PPPIOCSMRU:
|
||||
if (get_user(val, (int __user *) argp))
|
||||
break;
|
||||
if (val < PPP_MRU)
|
||||
val = PPP_MRU;
|
||||
ap->mru = val;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOTTY;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called at softirq level to deliver received packets
|
||||
* to the ppp_generic code, and to tell the ppp_generic code
|
||||
* if we can accept more output now.
|
||||
*/
|
||||
static void ppp_sync_process(unsigned long arg)
|
||||
{
|
||||
struct syncppp *ap = (struct syncppp *) arg;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* process received packets */
|
||||
while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
|
||||
if (skb->len == 0) {
|
||||
/* zero length buffers indicate error */
|
||||
ppp_input_error(&ap->chan, 0);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
else
|
||||
ppp_input(&ap->chan, skb);
|
||||
}
|
||||
|
||||
/* try to push more stuff out */
|
||||
if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap))
|
||||
ppp_output_wakeup(&ap->chan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Procedures for encapsulation and framing.
|
||||
*/
|
||||
|
||||
static struct sk_buff*
|
||||
ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
|
||||
{
|
||||
int proto;
|
||||
unsigned char *data;
|
||||
int islcp;
|
||||
|
||||
data = skb->data;
|
||||
proto = get_unaligned_be16(data);
|
||||
|
||||
/* LCP packets with codes between 1 (configure-request)
|
||||
* and 7 (code-reject) must be sent as though no options
|
||||
* have been negotiated.
|
||||
*/
|
||||
islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
|
||||
|
||||
/* compress protocol field if option enabled */
|
||||
if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp)
|
||||
skb_pull(skb,1);
|
||||
|
||||
/* prepend address/control fields if necessary */
|
||||
if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
|
||||
if (skb_headroom(skb) < 2) {
|
||||
struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
|
||||
if (npkt == NULL) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
skb_reserve(npkt,2);
|
||||
skb_copy_from_linear_data(skb,
|
||||
skb_put(npkt, skb->len), skb->len);
|
||||
kfree_skb(skb);
|
||||
skb = npkt;
|
||||
}
|
||||
skb_push(skb,2);
|
||||
skb->data[0] = PPP_ALLSTATIONS;
|
||||
skb->data[1] = PPP_UI;
|
||||
}
|
||||
|
||||
ap->last_xmit = jiffies;
|
||||
|
||||
if (skb && ap->flags & SC_LOG_OUTPKT)
|
||||
ppp_print_buffer ("send buffer", skb->data, skb->len);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit-side routines.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Send a packet to the peer over an sync tty line.
|
||||
* Returns 1 iff the packet was accepted.
|
||||
* If the packet was not accepted, we will call ppp_output_wakeup
|
||||
* at some later time.
|
||||
*/
|
||||
static int
|
||||
ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb)
|
||||
{
|
||||
struct syncppp *ap = chan->private;
|
||||
|
||||
ppp_sync_push(ap);
|
||||
|
||||
if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
|
||||
return 0; /* already full */
|
||||
skb = ppp_sync_txmunge(ap, skb);
|
||||
if (skb != NULL)
|
||||
ap->tpkt = skb;
|
||||
else
|
||||
clear_bit(XMIT_FULL, &ap->xmit_flags);
|
||||
|
||||
ppp_sync_push(ap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push as much data as possible out to the tty.
|
||||
*/
|
||||
static int
|
||||
ppp_sync_push(struct syncppp *ap)
|
||||
{
|
||||
int sent, done = 0;
|
||||
struct tty_struct *tty = ap->tty;
|
||||
int tty_stuffed = 0;
|
||||
|
||||
if (!spin_trylock_bh(&ap->xmit_lock))
|
||||
return 0;
|
||||
for (;;) {
|
||||
if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
|
||||
tty_stuffed = 0;
|
||||
if (!tty_stuffed && ap->tpkt) {
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
sent = tty->ops->write(tty, ap->tpkt->data, ap->tpkt->len);
|
||||
if (sent < 0)
|
||||
goto flush; /* error, e.g. loss of CD */
|
||||
if (sent < ap->tpkt->len) {
|
||||
tty_stuffed = 1;
|
||||
} else {
|
||||
kfree_skb(ap->tpkt);
|
||||
ap->tpkt = NULL;
|
||||
clear_bit(XMIT_FULL, &ap->xmit_flags);
|
||||
done = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* haven't made any progress */
|
||||
spin_unlock_bh(&ap->xmit_lock);
|
||||
if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
|
||||
(!tty_stuffed && ap->tpkt)))
|
||||
break;
|
||||
if (!spin_trylock_bh(&ap->xmit_lock))
|
||||
break;
|
||||
}
|
||||
return done;
|
||||
|
||||
flush:
|
||||
if (ap->tpkt) {
|
||||
kfree_skb(ap->tpkt);
|
||||
ap->tpkt = NULL;
|
||||
clear_bit(XMIT_FULL, &ap->xmit_flags);
|
||||
done = 1;
|
||||
}
|
||||
spin_unlock_bh(&ap->xmit_lock);
|
||||
return done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush output from our internal buffers.
|
||||
* Called for the TCFLSH ioctl.
|
||||
*/
|
||||
static void
|
||||
ppp_sync_flush_output(struct syncppp *ap)
|
||||
{
|
||||
int done = 0;
|
||||
|
||||
spin_lock_bh(&ap->xmit_lock);
|
||||
if (ap->tpkt != NULL) {
|
||||
kfree_skb(ap->tpkt);
|
||||
ap->tpkt = NULL;
|
||||
clear_bit(XMIT_FULL, &ap->xmit_flags);
|
||||
done = 1;
|
||||
}
|
||||
spin_unlock_bh(&ap->xmit_lock);
|
||||
if (done)
|
||||
ppp_output_wakeup(&ap->chan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive-side routines.
|
||||
*/
|
||||
|
||||
/* called when the tty driver has data for us.
|
||||
*
|
||||
* Data is frame oriented: each call to ppp_sync_input is considered
|
||||
* a whole frame. If the 1st flag byte is non-zero then the whole
|
||||
* frame is considered to be in error and is tossed.
|
||||
*/
|
||||
static void
|
||||
ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
|
||||
char *flags, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char *p;
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (ap->flags & SC_LOG_INPKT)
|
||||
ppp_print_buffer ("receive buffer", buf, count);
|
||||
|
||||
/* stuff the chars in the skb */
|
||||
skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2);
|
||||
if (!skb) {
|
||||
printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
|
||||
goto err;
|
||||
}
|
||||
/* Try to get the payload 4-byte aligned */
|
||||
if (buf[0] != PPP_ALLSTATIONS)
|
||||
skb_reserve(skb, 2 + (buf[0] & 1));
|
||||
|
||||
if (flags && *flags) {
|
||||
/* error flag set, ignore frame */
|
||||
goto err;
|
||||
} else if (count > skb_tailroom(skb)) {
|
||||
/* packet overflowed MRU */
|
||||
goto err;
|
||||
}
|
||||
|
||||
p = skb_put(skb, count);
|
||||
memcpy(p, buf, count);
|
||||
|
||||
/* strip address/control field if present */
|
||||
p = skb->data;
|
||||
if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
|
||||
/* chop off address/control */
|
||||
if (skb->len < 3)
|
||||
goto err;
|
||||
p = skb_pull(skb, 2);
|
||||
}
|
||||
|
||||
/* decompress protocol field if compressed */
|
||||
if (p[0] & 1) {
|
||||
/* protocol is compressed */
|
||||
skb_push(skb, 1)[0] = 0;
|
||||
} else if (skb->len < 2)
|
||||
goto err;
|
||||
|
||||
/* queue the frame to be processed */
|
||||
skb_queue_tail(&ap->rqueue, skb);
|
||||
return;
|
||||
|
||||
err:
|
||||
/* queue zero length packet as error indication */
|
||||
if (skb || (skb = dev_alloc_skb(0))) {
|
||||
skb_trim(skb, 0);
|
||||
skb_queue_tail(&ap->rqueue, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void __exit
|
||||
ppp_sync_cleanup(void)
|
||||
{
|
||||
if (tty_unregister_ldisc(N_SYNC_PPP) != 0)
|
||||
printk(KERN_ERR "failed to unregister Sync PPP line discipline\n");
|
||||
}
|
||||
|
||||
module_init(ppp_sync_init);
|
||||
module_exit(ppp_sync_cleanup);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_LDISC(N_SYNC_PPP);
|
1208
drivers/net/ppp/pppoe.c
Звичайний файл
1208
drivers/net/ppp/pppoe.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
149
drivers/net/ppp/pppox.c
Звичайний файл
149
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 <mostrows@speakeasy.net>
|
||||
*
|
||||
* 051000 : Initialization cleanup
|
||||
*
|
||||
* License:
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/if_pppox.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/if_ppp.h>
|
||||
#include <linux/ppp_channel.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1];
|
||||
|
||||
int register_pppox_proto(int proto_num, const struct pppox_proto *pp)
|
||||
{
|
||||
if (proto_num < 0 || proto_num > PX_MAX_PROTO)
|
||||
return -EINVAL;
|
||||
if (pppox_protos[proto_num])
|
||||
return -EALREADY;
|
||||
pppox_protos[proto_num] = pp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unregister_pppox_proto(int proto_num)
|
||||
{
|
||||
if (proto_num >= 0 && proto_num <= PX_MAX_PROTO)
|
||||
pppox_protos[proto_num] = NULL;
|
||||
}
|
||||
|
||||
void pppox_unbind_sock(struct sock *sk)
|
||||
{
|
||||
/* Clear connection to ppp device, if attached. */
|
||||
|
||||
if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
|
||||
ppp_unregister_channel(&pppox_sk(sk)->chan);
|
||||
sk->sk_state = PPPOX_DEAD;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(register_pppox_proto);
|
||||
EXPORT_SYMBOL(unregister_pppox_proto);
|
||||
EXPORT_SYMBOL(pppox_unbind_sock);
|
||||
|
||||
int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct pppox_sock *po = pppox_sk(sk);
|
||||
int rc;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (cmd) {
|
||||
case PPPIOCGCHAN: {
|
||||
int index;
|
||||
rc = -ENOTCONN;
|
||||
if (!(sk->sk_state & PPPOX_CONNECTED))
|
||||
break;
|
||||
|
||||
rc = -EINVAL;
|
||||
index = ppp_channel_index(&po->chan);
|
||||
if (put_user(index , (int __user *) arg))
|
||||
break;
|
||||
|
||||
rc = 0;
|
||||
sk->sk_state |= PPPOX_BOUND;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = pppox_protos[sk->sk_protocol]->ioctl ?
|
||||
pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return rc;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pppox_ioctl);
|
||||
|
||||
static int pppox_create(struct net *net, struct socket *sock, int protocol,
|
||||
int kern)
|
||||
{
|
||||
int rc = -EPROTOTYPE;
|
||||
|
||||
if (protocol < 0 || protocol > PX_MAX_PROTO)
|
||||
goto out;
|
||||
|
||||
rc = -EPROTONOSUPPORT;
|
||||
if (!pppox_protos[protocol])
|
||||
request_module("pppox-proto-%d", protocol);
|
||||
if (!pppox_protos[protocol] ||
|
||||
!try_module_get(pppox_protos[protocol]->owner))
|
||||
goto out;
|
||||
|
||||
rc = pppox_protos[protocol]->create(net, sock);
|
||||
|
||||
module_put(pppox_protos[protocol]->owner);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct net_proto_family pppox_proto_family = {
|
||||
.family = PF_PPPOX,
|
||||
.create = pppox_create,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init pppox_init(void)
|
||||
{
|
||||
return sock_register(&pppox_proto_family);
|
||||
}
|
||||
|
||||
static void __exit pppox_exit(void)
|
||||
{
|
||||
sock_unregister(PF_PPPOX);
|
||||
}
|
||||
|
||||
module_init(pppox_init);
|
||||
module_exit(pppox_exit);
|
||||
|
||||
MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
|
||||
MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
|
||||
MODULE_LICENSE("GPL");
|
717
drivers/net/ppp/pptp.c
Звичайний файл
717
drivers/net/ppp/pptp.c
Звичайний файл
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
* Point-to-Point Tunneling Protocol for Linux
|
||||
*
|
||||
* Authors: Dmitry Kozlov <xeb@mail.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ppp_channel.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/if_pppox.h>
|
||||
#include <linux/if_ppp.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
#include <net/protocol.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/icmp.h>
|
||||
#include <net/route.h>
|
||||
#include <net/gre.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define PPTP_DRIVER_VERSION "0.8.5"
|
||||
|
||||
#define MAX_CALLID 65535
|
||||
|
||||
static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1);
|
||||
static struct pppox_sock **callid_sock;
|
||||
|
||||
static DEFINE_SPINLOCK(chan_lock);
|
||||
|
||||
static struct proto pptp_sk_proto __read_mostly;
|
||||
static const struct ppp_channel_ops pptp_chan_ops;
|
||||
static const struct proto_ops pptp_ops;
|
||||
|
||||
#define PPP_LCP_ECHOREQ 0x09
|
||||
#define PPP_LCP_ECHOREP 0x0A
|
||||
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
|
||||
|
||||
#define MISSING_WINDOW 20
|
||||
#define WRAPPED(curseq, lastseq)\
|
||||
((((curseq) & 0xffffff00) == 0) &&\
|
||||
(((lastseq) & 0xffffff00) == 0xffffff00))
|
||||
|
||||
#define PPTP_GRE_PROTO 0x880B
|
||||
#define PPTP_GRE_VER 0x1
|
||||
|
||||
#define PPTP_GRE_FLAG_C 0x80
|
||||
#define PPTP_GRE_FLAG_R 0x40
|
||||
#define PPTP_GRE_FLAG_K 0x20
|
||||
#define PPTP_GRE_FLAG_S 0x10
|
||||
#define PPTP_GRE_FLAG_A 0x80
|
||||
|
||||
#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
|
||||
#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
|
||||
#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
|
||||
#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
|
||||
#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)
|
||||
|
||||
#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header))
|
||||
struct pptp_gre_header {
|
||||
u8 flags;
|
||||
u8 ver;
|
||||
u16 protocol;
|
||||
u16 payload_len;
|
||||
u16 call_id;
|
||||
u32 seq;
|
||||
u32 ack;
|
||||
} __packed;
|
||||
|
||||
static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
|
||||
{
|
||||
struct pppox_sock *sock;
|
||||
struct pptp_opt *opt;
|
||||
|
||||
rcu_read_lock();
|
||||
sock = rcu_dereference(callid_sock[call_id]);
|
||||
if (sock) {
|
||||
opt = &sock->proto.pptp;
|
||||
if (opt->dst_addr.sin_addr.s_addr != s_addr)
|
||||
sock = NULL;
|
||||
else
|
||||
sock_hold(sk_pppox(sock));
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int lookup_chan_dst(u16 call_id, __be32 d_addr)
|
||||
{
|
||||
struct pppox_sock *sock;
|
||||
struct pptp_opt *opt;
|
||||
int i;
|
||||
|
||||
rcu_read_lock();
|
||||
for (i = find_next_bit(callid_bitmap, MAX_CALLID, 1); i < MAX_CALLID;
|
||||
i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) {
|
||||
sock = rcu_dereference(callid_sock[i]);
|
||||
if (!sock)
|
||||
continue;
|
||||
opt = &sock->proto.pptp;
|
||||
if (opt->dst_addr.call_id == call_id &&
|
||||
opt->dst_addr.sin_addr.s_addr == d_addr)
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return i < MAX_CALLID;
|
||||
}
|
||||
|
||||
static int add_chan(struct pppox_sock *sock)
|
||||
{
|
||||
static int call_id;
|
||||
|
||||
spin_lock(&chan_lock);
|
||||
if (!sock->proto.pptp.src_addr.call_id) {
|
||||
call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1);
|
||||
if (call_id == MAX_CALLID) {
|
||||
call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1);
|
||||
if (call_id == MAX_CALLID)
|
||||
goto out_err;
|
||||
}
|
||||
sock->proto.pptp.src_addr.call_id = call_id;
|
||||
} else if (test_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap))
|
||||
goto out_err;
|
||||
|
||||
set_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
|
||||
rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], sock);
|
||||
spin_unlock(&chan_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
spin_unlock(&chan_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void del_chan(struct pppox_sock *sock)
|
||||
{
|
||||
spin_lock(&chan_lock);
|
||||
clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
|
||||
rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id], NULL);
|
||||
spin_unlock(&chan_lock);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk = (struct sock *) chan->private;
|
||||
struct pppox_sock *po = pppox_sk(sk);
|
||||
struct pptp_opt *opt = &po->proto.pptp;
|
||||
struct pptp_gre_header *hdr;
|
||||
unsigned int header_len = sizeof(*hdr);
|
||||
struct flowi4 fl4;
|
||||
int islcp;
|
||||
int len;
|
||||
unsigned char *data;
|
||||
__u32 seq_recv;
|
||||
|
||||
|
||||
struct rtable *rt;
|
||||
struct net_device *tdev;
|
||||
struct iphdr *iph;
|
||||
int max_headroom;
|
||||
|
||||
if (sk_pppox(po)->sk_state & PPPOX_DEAD)
|
||||
goto tx_error;
|
||||
|
||||
rt = ip_route_output_ports(&init_net, &fl4, NULL,
|
||||
opt->dst_addr.sin_addr.s_addr,
|
||||
opt->src_addr.sin_addr.s_addr,
|
||||
0, 0, IPPROTO_GRE,
|
||||
RT_TOS(0), 0);
|
||||
if (IS_ERR(rt))
|
||||
goto tx_error;
|
||||
|
||||
tdev = rt->dst.dev;
|
||||
|
||||
max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph) + sizeof(*hdr) + 2;
|
||||
|
||||
if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
|
||||
struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
|
||||
if (!new_skb) {
|
||||
ip_rt_put(rt);
|
||||
goto tx_error;
|
||||
}
|
||||
if (skb->sk)
|
||||
skb_set_owner_w(new_skb, skb->sk);
|
||||
kfree_skb(skb);
|
||||
skb = new_skb;
|
||||
}
|
||||
|
||||
data = skb->data;
|
||||
islcp = ((data[0] << 8) + data[1]) == PPP_LCP && 1 <= data[2] && data[2] <= 7;
|
||||
|
||||
/* compress protocol field */
|
||||
if ((opt->ppp_flags & SC_COMP_PROT) && data[0] == 0 && !islcp)
|
||||
skb_pull(skb, 1);
|
||||
|
||||
/* Put in the address/control bytes if necessary */
|
||||
if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) {
|
||||
data = skb_push(skb, 2);
|
||||
data[0] = PPP_ALLSTATIONS;
|
||||
data[1] = PPP_UI;
|
||||
}
|
||||
|
||||
len = skb->len;
|
||||
|
||||
seq_recv = opt->seq_recv;
|
||||
|
||||
if (opt->ack_sent == seq_recv)
|
||||
header_len -= sizeof(hdr->ack);
|
||||
|
||||
/* Push down and install GRE header */
|
||||
skb_push(skb, header_len);
|
||||
hdr = (struct pptp_gre_header *)(skb->data);
|
||||
|
||||
hdr->flags = PPTP_GRE_FLAG_K;
|
||||
hdr->ver = PPTP_GRE_VER;
|
||||
hdr->protocol = htons(PPTP_GRE_PROTO);
|
||||
hdr->call_id = htons(opt->dst_addr.call_id);
|
||||
|
||||
hdr->flags |= PPTP_GRE_FLAG_S;
|
||||
hdr->seq = htonl(++opt->seq_sent);
|
||||
if (opt->ack_sent != seq_recv) {
|
||||
/* send ack with this message */
|
||||
hdr->ver |= PPTP_GRE_FLAG_A;
|
||||
hdr->ack = htonl(seq_recv);
|
||||
opt->ack_sent = seq_recv;
|
||||
}
|
||||
hdr->payload_len = htons(len);
|
||||
|
||||
/* Push down and install the IP header. */
|
||||
|
||||
skb_reset_transport_header(skb);
|
||||
skb_push(skb, sizeof(*iph));
|
||||
skb_reset_network_header(skb);
|
||||
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
|
||||
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED);
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
iph->version = 4;
|
||||
iph->ihl = sizeof(struct iphdr) >> 2;
|
||||
if (ip_dont_fragment(sk, &rt->dst))
|
||||
iph->frag_off = htons(IP_DF);
|
||||
else
|
||||
iph->frag_off = 0;
|
||||
iph->protocol = IPPROTO_GRE;
|
||||
iph->tos = 0;
|
||||
iph->daddr = fl4.daddr;
|
||||
iph->saddr = fl4.saddr;
|
||||
iph->ttl = ip4_dst_hoplimit(&rt->dst);
|
||||
iph->tot_len = htons(skb->len);
|
||||
|
||||
skb_dst_drop(skb);
|
||||
skb_dst_set(skb, &rt->dst);
|
||||
|
||||
nf_reset(skb);
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
ip_select_ident(iph, &rt->dst, NULL);
|
||||
ip_send_check(iph);
|
||||
|
||||
ip_local_out(skb);
|
||||
|
||||
tx_error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct pppox_sock *po = pppox_sk(sk);
|
||||
struct pptp_opt *opt = &po->proto.pptp;
|
||||
int headersize, payload_len, seq;
|
||||
__u8 *payload;
|
||||
struct pptp_gre_header *header;
|
||||
|
||||
if (!(sk->sk_state & PPPOX_CONNECTED)) {
|
||||
if (sock_queue_rcv_skb(sk, skb))
|
||||
goto drop;
|
||||
return NET_RX_SUCCESS;
|
||||
}
|
||||
|
||||
header = (struct pptp_gre_header *)(skb->data);
|
||||
|
||||
/* test if acknowledgement present */
|
||||
if (PPTP_GRE_IS_A(header->ver)) {
|
||||
__u32 ack = (PPTP_GRE_IS_S(header->flags)) ?
|
||||
header->ack : header->seq; /* ack in different place if S = 0 */
|
||||
|
||||
ack = ntohl(ack);
|
||||
|
||||
if (ack > opt->ack_recv)
|
||||
opt->ack_recv = ack;
|
||||
/* also handle sequence number wrap-around */
|
||||
if (WRAPPED(ack, opt->ack_recv))
|
||||
opt->ack_recv = ack;
|
||||
}
|
||||
|
||||
/* test if payload present */
|
||||
if (!PPTP_GRE_IS_S(header->flags))
|
||||
goto drop;
|
||||
|
||||
headersize = sizeof(*header);
|
||||
payload_len = ntohs(header->payload_len);
|
||||
seq = ntohl(header->seq);
|
||||
|
||||
/* no ack present? */
|
||||
if (!PPTP_GRE_IS_A(header->ver))
|
||||
headersize -= sizeof(header->ack);
|
||||
/* check for incomplete packet (length smaller than expected) */
|
||||
if (skb->len - headersize < payload_len)
|
||||
goto drop;
|
||||
|
||||
payload = skb->data + headersize;
|
||||
/* check for expected sequence number */
|
||||
if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) {
|
||||
if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) &&
|
||||
(PPP_PROTOCOL(payload) == PPP_LCP) &&
|
||||
((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)))
|
||||
goto allow_packet;
|
||||
} else {
|
||||
opt->seq_recv = seq;
|
||||
allow_packet:
|
||||
skb_pull(skb, headersize);
|
||||
|
||||
if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) {
|
||||
/* chop off address/control */
|
||||
if (skb->len < 3)
|
||||
goto drop;
|
||||
skb_pull(skb, 2);
|
||||
}
|
||||
|
||||
if ((*skb->data) & 1) {
|
||||
/* protocol is compressed */
|
||||
skb_push(skb, 1)[0] = 0;
|
||||
}
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
skb_set_network_header(skb, skb->head-skb->data);
|
||||
ppp_input(&po->chan, skb);
|
||||
|
||||
return NET_RX_SUCCESS;
|
||||
}
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
static int pptp_rcv(struct sk_buff *skb)
|
||||
{
|
||||
struct pppox_sock *po;
|
||||
struct pptp_gre_header *header;
|
||||
struct iphdr *iph;
|
||||
|
||||
if (skb->pkt_type != PACKET_HOST)
|
||||
goto drop;
|
||||
|
||||
if (!pskb_may_pull(skb, 12))
|
||||
goto drop;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
header = (struct pptp_gre_header *)skb->data;
|
||||
|
||||
if (ntohs(header->protocol) != PPTP_GRE_PROTO || /* PPTP-GRE protocol for PPTP */
|
||||
PPTP_GRE_IS_C(header->flags) || /* flag C should be clear */
|
||||
PPTP_GRE_IS_R(header->flags) || /* flag R should be clear */
|
||||
!PPTP_GRE_IS_K(header->flags) || /* flag K should be set */
|
||||
(header->flags&0xF) != 0) /* routing and recursion ctrl = 0 */
|
||||
/* if invalid, discard this packet */
|
||||
goto drop;
|
||||
|
||||
po = lookup_chan(htons(header->call_id), iph->saddr);
|
||||
if (po) {
|
||||
skb_dst_drop(skb);
|
||||
nf_reset(skb);
|
||||
return sk_receive_skb(sk_pppox(po), skb, 0);
|
||||
}
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
static int pptp_bind(struct socket *sock, struct sockaddr *uservaddr,
|
||||
int sockaddr_len)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
|
||||
struct pppox_sock *po = pppox_sk(sk);
|
||||
struct pptp_opt *opt = &po->proto.pptp;
|
||||
int error = 0;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
opt->src_addr = sp->sa_addr.pptp;
|
||||
if (add_chan(po)) {
|
||||
release_sock(sk);
|
||||
error = -EBUSY;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
|
||||
int sockaddr_len, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
|
||||
struct pppox_sock *po = pppox_sk(sk);
|
||||
struct pptp_opt *opt = &po->proto.pptp;
|
||||
struct rtable *rt;
|
||||
struct flowi4 fl4;
|
||||
int error = 0;
|
||||
|
||||
if (sp->sa_protocol != PX_PROTO_PPTP)
|
||||
return -EINVAL;
|
||||
|
||||
if (lookup_chan_dst(sp->sa_addr.pptp.call_id, sp->sa_addr.pptp.sin_addr.s_addr))
|
||||
return -EALREADY;
|
||||
|
||||
lock_sock(sk);
|
||||
/* Check for already bound sockets */
|
||||
if (sk->sk_state & PPPOX_CONNECTED) {
|
||||
error = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check for already disconnected sockets, on attempts to disconnect */
|
||||
if (sk->sk_state & PPPOX_DEAD) {
|
||||
error = -EALREADY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr) {
|
||||
error = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
po->chan.private = sk;
|
||||
po->chan.ops = &pptp_chan_ops;
|
||||
|
||||
rt = ip_route_output_ports(&init_net, &fl4, sk,
|
||||
opt->dst_addr.sin_addr.s_addr,
|
||||
opt->src_addr.sin_addr.s_addr,
|
||||
0, 0,
|
||||
IPPROTO_GRE, RT_CONN_FLAGS(sk), 0);
|
||||
if (IS_ERR(rt)) {
|
||||
error = -EHOSTUNREACH;
|
||||
goto end;
|
||||
}
|
||||
sk_setup_caps(sk, &rt->dst);
|
||||
|
||||
po->chan.mtu = dst_mtu(&rt->dst);
|
||||
if (!po->chan.mtu)
|
||||
po->chan.mtu = PPP_MTU;
|
||||
ip_rt_put(rt);
|
||||
po->chan.mtu -= PPTP_HEADER_OVERHEAD;
|
||||
|
||||
po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
|
||||
error = ppp_register_channel(&po->chan);
|
||||
if (error) {
|
||||
pr_err("PPTP: failed to register PPP channel (%d)\n", error);
|
||||
goto end;
|
||||
}
|
||||
|
||||
opt->dst_addr = sp->sa_addr.pptp;
|
||||
sk->sk_state = PPPOX_CONNECTED;
|
||||
|
||||
end:
|
||||
release_sock(sk);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
|
||||
int *usockaddr_len, int peer)
|
||||
{
|
||||
int len = sizeof(struct sockaddr_pppox);
|
||||
struct sockaddr_pppox sp;
|
||||
|
||||
sp.sa_family = AF_PPPOX;
|
||||
sp.sa_protocol = PX_PROTO_PPTP;
|
||||
sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
|
||||
|
||||
memcpy(uaddr, &sp, len);
|
||||
|
||||
*usockaddr_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pptp_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct pppox_sock *po;
|
||||
struct pptp_opt *opt;
|
||||
int error = 0;
|
||||
|
||||
if (!sk)
|
||||
return 0;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sock_flag(sk, SOCK_DEAD)) {
|
||||
release_sock(sk);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
po = pppox_sk(sk);
|
||||
opt = &po->proto.pptp;
|
||||
del_chan(po);
|
||||
|
||||
pppox_unbind_sock(sk);
|
||||
sk->sk_state = PPPOX_DEAD;
|
||||
|
||||
sock_orphan(sk);
|
||||
sock->sk = NULL;
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void pptp_sock_destruct(struct sock *sk)
|
||||
{
|
||||
if (!(sk->sk_state & PPPOX_DEAD)) {
|
||||
del_chan(pppox_sk(sk));
|
||||
pppox_unbind_sock(sk);
|
||||
}
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
}
|
||||
|
||||
static int pptp_create(struct net *net, struct socket *sock)
|
||||
{
|
||||
int error = -ENOMEM;
|
||||
struct sock *sk;
|
||||
struct pppox_sock *po;
|
||||
struct pptp_opt *opt;
|
||||
|
||||
sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pptp_sk_proto);
|
||||
if (!sk)
|
||||
goto out;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
sock->state = SS_UNCONNECTED;
|
||||
sock->ops = &pptp_ops;
|
||||
|
||||
sk->sk_backlog_rcv = pptp_rcv_core;
|
||||
sk->sk_state = PPPOX_NONE;
|
||||
sk->sk_type = SOCK_STREAM;
|
||||
sk->sk_family = PF_PPPOX;
|
||||
sk->sk_protocol = PX_PROTO_PPTP;
|
||||
sk->sk_destruct = pptp_sock_destruct;
|
||||
|
||||
po = pppox_sk(sk);
|
||||
opt = &po->proto.pptp;
|
||||
|
||||
opt->seq_sent = 0; opt->seq_recv = 0;
|
||||
opt->ack_recv = 0; opt->ack_sent = 0;
|
||||
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct sock *sk = (struct sock *) chan->private;
|
||||
struct pppox_sock *po = pppox_sk(sk);
|
||||
struct pptp_opt *opt = &po->proto.pptp;
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int err, val;
|
||||
|
||||
err = -EFAULT;
|
||||
switch (cmd) {
|
||||
case PPPIOCGFLAGS:
|
||||
val = opt->ppp_flags;
|
||||
if (put_user(val, p))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
case PPPIOCSFLAGS:
|
||||
if (get_user(val, p))
|
||||
break;
|
||||
opt->ppp_flags = val & ~SC_RCV_BITS;
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
err = -ENOTTY;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct ppp_channel_ops pptp_chan_ops = {
|
||||
.start_xmit = pptp_xmit,
|
||||
.ioctl = pptp_ppp_ioctl,
|
||||
};
|
||||
|
||||
static struct proto pptp_sk_proto __read_mostly = {
|
||||
.name = "PPTP",
|
||||
.owner = THIS_MODULE,
|
||||
.obj_size = sizeof(struct pppox_sock),
|
||||
};
|
||||
|
||||
static const struct proto_ops pptp_ops = {
|
||||
.family = AF_PPPOX,
|
||||
.owner = THIS_MODULE,
|
||||
.release = pptp_release,
|
||||
.bind = pptp_bind,
|
||||
.connect = pptp_connect,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.accept = sock_no_accept,
|
||||
.getname = pptp_getname,
|
||||
.poll = sock_no_poll,
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = sock_no_setsockopt,
|
||||
.getsockopt = sock_no_getsockopt,
|
||||
.sendmsg = sock_no_sendmsg,
|
||||
.recvmsg = sock_no_recvmsg,
|
||||
.mmap = sock_no_mmap,
|
||||
.ioctl = pppox_ioctl,
|
||||
};
|
||||
|
||||
static const struct pppox_proto pppox_pptp_proto = {
|
||||
.create = pptp_create,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct gre_protocol gre_pptp_protocol = {
|
||||
.handler = pptp_rcv,
|
||||
};
|
||||
|
||||
static int __init pptp_init_module(void)
|
||||
{
|
||||
int err = 0;
|
||||
pr_info("PPTP driver version " PPTP_DRIVER_VERSION "\n");
|
||||
|
||||
callid_sock = vzalloc((MAX_CALLID + 1) * sizeof(void *));
|
||||
if (!callid_sock) {
|
||||
pr_err("PPTP: cann't allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
|
||||
if (err) {
|
||||
pr_err("PPTP: can't add gre protocol\n");
|
||||
goto out_mem_free;
|
||||
}
|
||||
|
||||
err = proto_register(&pptp_sk_proto, 0);
|
||||
if (err) {
|
||||
pr_err("PPTP: can't register sk_proto\n");
|
||||
goto out_gre_del_protocol;
|
||||
}
|
||||
|
||||
err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
|
||||
if (err) {
|
||||
pr_err("PPTP: can't register pppox_proto\n");
|
||||
goto out_unregister_sk_proto;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister_sk_proto:
|
||||
proto_unregister(&pptp_sk_proto);
|
||||
out_gre_del_protocol:
|
||||
gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
|
||||
out_mem_free:
|
||||
vfree(callid_sock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit pptp_exit_module(void)
|
||||
{
|
||||
unregister_pppox_proto(PX_PROTO_PPTP);
|
||||
proto_unregister(&pptp_sk_proto);
|
||||
gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
|
||||
vfree(callid_sock);
|
||||
}
|
||||
|
||||
module_init(pptp_init_module);
|
||||
module_exit(pptp_exit_module);
|
||||
|
||||
MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol");
|
||||
MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
|
||||
MODULE_LICENSE("GPL");
|
Посилання в новій задачі
Заблокувати користувача