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>
Цей коміт міститься в:
Jeff Kirsher
2011-07-31 02:38:19 -07:00
джерело aab3ac2610
коміт 224cf5ad14
15 змінених файлів з 203 додано та 181 видалено

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 Звичайний файл
Переглянути файл

@@ -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 Звичайний файл

Різницю між файлами не показано, бо вона завелика Завантажити різницю

1028
drivers/net/ppp/ppp_async.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 Звичайний файл

Різницю між файлами не показано, бо вона завелика Завантажити різницю

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 Звичайний файл
Переглянути файл

@@ -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 Звичайний файл
Переглянути файл

@@ -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 Звичайний файл

Різницю між файлами не показано, бо вона завелика Завантажити різницю

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 Звичайний файл
Переглянути файл

@@ -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");