Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+207
View File
@@ -0,0 +1,207 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# 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, available at
<https://ppp.samba.org>. 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"
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"
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"
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)"
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_SHA1
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"
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
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 PPPOX
tristate
config PPPOE
tristate "PPP over Ethernet"
select PPPOX
help
Support for PPP over Ethernet.
To work in client mode, pppd version 2.4.2 or later is required.
To work in server mode, the pppoe-server command from the RP-PPPoE
package is also required, available at
<https://dianne.skoll.ca/projects/rp-pppoe/>.
choice
prompt "Number of PPPoE hash bits"
default PPPOE_HASH_BITS_4
depends on PPPOE
help
Select the number of bits used for hashing PPPoE interfaces.
Larger sizes reduces the risk of hash collisions at the cost
of slightly increased memory usage.
This hash table is on a per outer ethernet interface.
config PPPOE_HASH_BITS_1
bool "1 bit (2 buckets)"
config PPPOE_HASH_BITS_2
bool "2 bits (4 buckets)"
config PPPOE_HASH_BITS_4
bool "4 bits (16 buckets)"
config PPPOE_HASH_BITS_8
bool "8 bits (256 buckets)"
endchoice
config PPPOE_HASH_BITS
int
default 1 if PPPOE_HASH_BITS_1
default 2 if PPPOE_HASH_BITS_2
default 4 if PPPOE_HASH_BITS_4
default 8 if PPPOE_HASH_BITS_8
default 4
config PPTP
tristate "PPP over IPv4 (PPTP)"
depends on NET_IPGRE_DEMUX
select PPPOX
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"
depends on L2TP
select PPPOX
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.
if TTY
config PPP_ASYNC
tristate "PPP support for async serial ports"
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"
help
Say Y (or M) here if you want to be able to use PPP over synchronous
(HDLC) tty devices. These devices are often used for high-speed leased
lines like T1/E1.
To compile this driver as a module, choose M here.
endif # TTY
endif # PPP
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
#
# 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_PPPOX) += pppox.o
obj-$(CONFIG_PPPOE) += pppoe.o
obj-$(CONFIG_PPTP) += pptp.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+635
View File
@@ -0,0 +1,635 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ppp_deflate.c - interface the zlib procedures for Deflate compression
* and decompression (as used by gzip) to the PPP code.
*
* Copyright 1994-1998 Paul Mackerras.
*/
#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 <linux/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_obj(*state);
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 && olen <= osize) {
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) {
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_obj(*state);
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 rc;
rc = ppp_register_compressor(&ppp_deflate);
if (rc)
return rc;
rc = ppp_register_compressor(&ppp_deflate_draft);
if (rc) {
ppp_unregister_compressor(&ppp_deflate);
return rc;
}
pr_info("PPP Deflate Compression module registered\n");
return 0;
}
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_DESCRIPTION("PPP Deflate compression module");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE));
MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT));
File diff suppressed because it is too large Load Diff
+616
View File
@@ -0,0 +1,616 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*
*
* 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 <crypto/arc4.h>
#include <crypto/sha1.h>
#include <linux/err.h>
#include <linux/fips.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/mm.h>
#include <linux/ppp_defs.h>
#include <linux/ppp-comp.h>
#include <linux/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");
#define SHA1_PAD_SIZE 40
static const u8 sha_pad1[SHA1_PAD_SIZE] = { 0 };
static const u8 sha_pad2[SHA1_PAD_SIZE] = { [0 ... SHA1_PAD_SIZE - 1] = 0xF2 };
/*
* State for an MPPE (de)compressor.
*/
struct ppp_mppe_state {
struct arc4_ctx arc4;
unsigned char sha1_digest[SHA1_DIGEST_SIZE];
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 sha1_ctx ctx;
sha1_init(&ctx);
sha1_update(&ctx, state->master_key, state->keylen);
sha1_update(&ctx, sha_pad1, sizeof(sha_pad1));
sha1_update(&ctx, state->session_key, state->keylen);
sha1_update(&ctx, sha_pad2, sizeof(sha_pad2));
sha1_final(&ctx, 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)
{
get_new_key_from_sha(state);
if (!initial_key) {
arc4_setkey(&state->arc4, state->sha1_digest, state->keylen);
arc4_crypt(&state->arc4, state->session_key, state->sha1_digest,
state->keylen);
} 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;
}
arc4_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;
if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
options[0] != CI_MPPE || options[1] != CILEN_MPPE ||
fips_enabled)
return NULL;
state = kzalloc_obj(*state);
if (state == NULL)
return NULL;
/* 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;
}
/*
* Deallocate space for a (de)compressor.
*/
static void mppe_free(void *arg)
{
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
kfree_sensitive(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) {
printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n",
debugstr, unit, (state->keylen == 16) ? 128 : 40,
(state->stateful) ? "stateful" : "stateless");
printk(KERN_DEBUG
"%s[%d]: keys: master: %*phN initial session: %*phN\n",
debugstr, unit,
(int)sizeof(state->master_key), state->master_key,
(int)sizeof(state->session_key), state->session_key);
}
/*
* 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;
int proto;
/*
* 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;
arc4_crypt(&state->arc4, obuf, ibuf, isize);
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;
unsigned ccount;
int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
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;
goto sanity_error;
}
if (!state->stateful && !flushed) {
printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
"stateless mode!\n", state->unit);
state->sanity_errors += 100;
goto sanity_error;
}
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;
goto sanity_error;
}
/*
* Check the coherency count.
*/
if (!state->stateful) {
/* Discard late packet */
if ((ccount - state->ccount) % MPPE_CCOUNT_SPACE
> MPPE_CCOUNT_SPACE / 2) {
state->sanity_errors++;
goto sanity_error;
}
/* 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.
*/
arc4_crypt(&state->arc4, obuf, ibuf, 1);
/*
* 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. */
arc4_crypt(&state->arc4, obuf + 1, ibuf + 1, isize - 1);
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;
sanity_error:
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;
}
/*
* 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,
};
static int __init ppp_mppe_init(void)
{
int answer;
if (fips_enabled)
return -ENODEV;
answer = ppp_register_compressor(&ppp_mppe);
if (answer == 0)
printk(KERN_INFO "PPP MPPE Compression module registered\n");
return answer;
}
static void __exit ppp_mppe_cleanup(void)
{
ppp_unregister_compressor(&ppp_mppe);
}
module_init(ppp_mppe_init);
module_exit(ppp_mppe_cleanup);
+87
View File
@@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define MPPE_PAD 4 /* MPPE growth per frame */
#define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */
/* option bits for ccp_options.mppe */
#define MPPE_OPT_40 0x01 /* 40 bit */
#define MPPE_OPT_128 0x02 /* 128 bit */
#define MPPE_OPT_STATEFUL 0x04 /* stateful mode */
/* unsupported opts */
#define MPPE_OPT_56 0x08 /* 56 bit */
#define MPPE_OPT_MPPC 0x10 /* MPPC compression */
#define MPPE_OPT_D 0x20 /* Unknown */
#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
#define MPPE_OPT_UNKNOWN 0x40 /* Bits !defined in RFC 3078 were set */
/*
* This is not nice ... the alternative is a bitfield struct though.
* And unfortunately, we cannot share the same bits for the option
* names above since C and H are the same bit. We could do a u_int32
* but then we have to do a htonl() all the time and/or we still need
* to know which octet is which.
*/
#define MPPE_C_BIT 0x01 /* MPPC */
#define MPPE_D_BIT 0x10 /* Obsolete, usage unknown */
#define MPPE_L_BIT 0x20 /* 40-bit */
#define MPPE_S_BIT 0x40 /* 128-bit */
#define MPPE_M_BIT 0x80 /* 56-bit, not supported */
#define MPPE_H_BIT 0x01 /* Stateless (in a different byte) */
/* Does not include H bit; used for least significant octet only. */
#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
/* Build a CI from mppe opts (see RFC 3078) */
#define MPPE_OPTS_TO_CI(opts, ci) \
do { \
u_char *ptr = ci; /* u_char[4] */ \
\
/* H bit */ \
if (opts & MPPE_OPT_STATEFUL) \
*ptr++ = 0x0; \
else \
*ptr++ = MPPE_H_BIT; \
*ptr++ = 0; \
*ptr++ = 0; \
\
/* S,L bits */ \
*ptr = 0; \
if (opts & MPPE_OPT_128) \
*ptr |= MPPE_S_BIT; \
if (opts & MPPE_OPT_40) \
*ptr |= MPPE_L_BIT; \
/* M,D,C bits not supported */ \
} while (/* CONSTCOND */ 0)
/* The reverse of the above */
#define MPPE_CI_TO_OPTS(ci, opts) \
do { \
u_char *ptr = ci; /* u_char[4] */ \
\
opts = 0; \
\
/* H bit */ \
if (!(ptr[0] & MPPE_H_BIT)) \
opts |= MPPE_OPT_STATEFUL; \
\
/* S,L bits */ \
if (ptr[3] & MPPE_S_BIT) \
opts |= MPPE_OPT_128; \
if (ptr[3] & MPPE_L_BIT) \
opts |= MPPE_OPT_40; \
\
/* M,D,C bits */ \
if (ptr[3] & MPPE_M_BIT) \
opts |= MPPE_OPT_56; \
if (ptr[3] & MPPE_D_BIT) \
opts |= MPPE_OPT_D; \
if (ptr[3] & MPPE_C_BIT) \
opts |= MPPE_OPT_MPPC; \
\
/* Other bits */ \
if (ptr[0] & ~MPPE_H_BIT) \
opts |= MPPE_OPT_UNKNOWN; \
if (ptr[1] || ptr[2]) \
opts |= MPPE_OPT_UNKNOWN; \
if (ptr[3] & ~MPPE_ALL_BITS) \
opts |= MPPE_OPT_UNKNOWN; \
} while (/* CONSTCOND */ 0)
+734
View File
@@ -0,0 +1,734 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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 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/ppp-ioctl.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 <linux/refcount.h>
#include <linux/unaligned.h>
#include <linux/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;
refcount_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(struct tasklet_struct *t);
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 u8 *buf, const u8 *flags,
int count);
static const struct ppp_channel_ops sync_ops = {
.start_xmit = ppp_sync_send,
.ioctl = ppp_sync_ioctl,
};
/*
* Utility procedure to print a buffer in hex/ascii
*/
static void
ppp_print_buffer (const char *name, const __u8 *buf, int count)
{
if (name != NULL)
printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, count);
}
/*
* 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)
refcount_inc(&ap->refcnt);
read_unlock(&disc_data_lock);
return ap;
}
static void sp_put(struct syncppp *ap)
{
if (refcount_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_obj(*ap);
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_setup(&ap->tsk, ppp_sync_process);
refcount_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 (!refcount_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 void ppp_sync_hangup(struct tty_struct *tty)
{
ppp_sync_close(tty);
}
/*
* 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, u8 *buf, size_t count,
void **cookie, unsigned long offset)
{
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 u8 *buf,
size_t count)
{
return -EAGAIN;
}
static int
ppp_synctty_ioctl(struct tty_struct *tty, 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 = n_tty_ioctl_helper(tty, cmd, arg);
break;
case FIONREAD:
val = 0;
if (put_user(val, p))
break;
err = 0;
break;
default:
err = tty_mode_ioctl(tty, cmd, arg);
break;
}
sp_put(ap);
return err;
}
/* May sleep, don't call from interrupt level or with interrupts disabled */
static void
ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
size_t 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,
.num = N_SYNC_PPP,
.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,
.receive_buf = ppp_sync_receive,
.write_wakeup = ppp_sync_wakeup,
};
static int __init
ppp_sync_init(void)
{
int err;
err = tty_register_ldisc(&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 > U16_MAX) {
err = -EINVAL;
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(struct tasklet_struct *t)
{
struct syncppp *ap = from_tasklet(ap, t, tsk);
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);
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;
/* Ensure we can safely access protocol field and LCP code */
if (!pskb_may_pull(skb, 3)) {
kfree_skb(skb);
return NULL;
}
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);
consume_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 {
consume_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 u8 *buf, const u8 *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;
}
skb_put_data(skb, buf, count);
/* strip address/control field if present */
p = skb->data;
if (skb->len >= 2 && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
/* chop off address/control */
if (skb->len < 3)
goto err;
p = skb_pull(skb, 2);
}
/* PPP packet length should be >= 2 bytes when protocol field is not
* compressed.
*/
if (!(p[0] & 0x01) && 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)
{
tty_unregister_ldisc(&ppp_sync_ldisc);
}
module_init(ppp_sync_init);
module_exit(ppp_sync_cleanup);
MODULE_DESCRIPTION("PPP synchronous TTY channel module");
MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_SYNC_PPP);
File diff suppressed because it is too large Load Diff
+155
View File
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/** -*- 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:
*/
#include <linux/string.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/compat.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/ppp-ioctl.h>
#include <linux/ppp_channel.h>
#include <linux/kmod.h>
#include <net/sock.h>
#include <linux/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)) {
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);
#ifdef CONFIG_COMPAT
int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg));
}
EXPORT_SYMBOL(pppox_compat_ioctl);
#endif
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("net-pf-%d-proto-%d", PF_PPPOX, protocol);
if (!pppox_protos[protocol] ||
!try_module_get(pppox_protos[protocol]->owner))
goto out;
rc = pppox_protos[protocol]->create(net, sock, kern);
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");
MODULE_ALIAS_NETPROTO(PF_PPPOX);
+704
View File
@@ -0,0 +1,704 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Point-to-Point Tunneling Protocol for Linux
*
* Authors: Dmitry Kozlov <xeb@mail.ru>
*/
#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/ppp-ioctl.h>
#include <linux/notifier.h>
#include <linux/file.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/rcupdate.h>
#include <linux/security.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 <net/pptp.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 __rcu **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;
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(&sock->sk);
}
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();
i = 1;
for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
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,
struct pptp_addr *sa)
{
static int call_id;
spin_lock(&chan_lock);
if (!sa->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;
}
sa->call_id = call_id;
} else if (test_bit(sa->call_id, callid_bitmap)) {
goto out_err;
}
sock->proto.pptp.src_addr = *sa;
set_bit(sa->call_id, callid_bitmap);
rcu_assign_pointer(callid_sock[sa->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_INIT_POINTER(callid_sock[sock->proto.pptp.src_addr.call_id], NULL);
spin_unlock(&chan_lock);
}
static struct rtable *pptp_route_output(const struct pppox_sock *po,
struct flowi4 *fl4)
{
const struct sock *sk = &po->sk;
struct net *net;
net = sock_net(sk);
flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0,
RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0,
po->proto.pptp.dst_addr.sin_addr.s_addr,
po->proto.pptp.src_addr.sin_addr.s_addr,
0, 0, sock_net_uid(net, sk));
security_sk_classify_flow(sk, flowi4_to_flowi_common(fl4));
return ip_route_output_flow(net, fl4, sk);
}
static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
{
struct sock *sk = chan->private;
struct pppox_sock *po = pppox_sk(sk);
struct net *net = sock_net(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 (po->sk.sk_state & PPPOX_DEAD)
goto tx_drop;
rt = pptp_route_output(po, &fl4);
if (IS_ERR(rt))
goto tx_drop;
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)
goto tx_error;
if (skb->sk)
skb_set_owner_w(new_skb, skb->sk);
consume_skb(skb);
skb = new_skb;
}
/* Ensure we can safely access protocol field and LCP code */
if (!pskb_may_pull(skb, 3))
goto tx_error;
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->gre_hd.flags = GRE_KEY | GRE_VERSION_1 | GRE_SEQ;
hdr->gre_hd.protocol = GRE_PROTO_PPP;
hdr->call_id = htons(opt->dst_addr.call_id);
hdr->seq = htonl(++opt->seq_sent);
if (opt->ack_sent != seq_recv) {
/* send ack with this message */
hdr->gre_hd.flags |= GRE_ACK;
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_ct(skb);
skb->ip_summed = CHECKSUM_NONE;
ip_select_ident(net, skb, NULL);
ip_send_check(iph);
ip_local_out(net, skb->sk, skb);
return 1;
tx_error:
ip_rt_put(rt);
tx_drop:
kfree_skb(skb);
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);
headersize = sizeof(*header);
/* test if acknowledgement present */
if (GRE_IS_ACK(header->gre_hd.flags)) {
__u32 ack;
if (!pskb_may_pull(skb, headersize))
goto drop;
header = (struct pptp_gre_header *)(skb->data);
/* ack in different place if S = 0 */
ack = GRE_IS_SEQ(header->gre_hd.flags) ? ntohl(header->ack) :
ntohl(header->seq);
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;
} else {
headersize -= sizeof(header->ack);
}
/* test if payload present */
if (!GRE_IS_SEQ(header->gre_hd.flags))
goto drop;
payload_len = ntohs(header->payload_len);
seq = ntohl(header->seq);
/* check for incomplete packet (length smaller than expected) */
if (!pskb_may_pull(skb, 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);
}
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 (header->gre_hd.protocol != GRE_PROTO_PPP || /* PPTP-GRE protocol for PPTP */
GRE_IS_CSUM(header->gre_hd.flags) || /* flag CSUM should be clear */
GRE_IS_ROUTING(header->gre_hd.flags) || /* flag ROUTING should be clear */
!GRE_IS_KEY(header->gre_hd.flags) || /* flag KEY should be set */
(header->gre_hd.flags & GRE_FLAGS)) /* flag Recursion Ctrl should be clear */
/* if invalid, discard this packet */
goto drop;
po = lookup_chan(ntohs(header->call_id), iph->saddr);
if (po) {
skb_dst_drop(skb);
nf_reset_ct(skb);
return sk_receive_skb(&po->sk, skb, 0);
}
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
static int pptp_bind(struct socket *sock, struct sockaddr_unsized *uservaddr,
int sockaddr_len)
{
struct sock *sk = sock->sk;
struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
struct pppox_sock *po = pppox_sk(sk);
int error = 0;
if (sockaddr_len < sizeof(struct sockaddr_pppox))
return -EINVAL;
lock_sock(sk);
if (sk->sk_state & PPPOX_DEAD) {
error = -EALREADY;
goto out;
}
if (sk->sk_state & PPPOX_BOUND) {
error = -EBUSY;
goto out;
}
if (add_chan(po, &sp->sa_addr.pptp))
error = -EBUSY;
else
sk->sk_state |= PPPOX_BOUND;
out:
release_sock(sk);
return error;
}
static int pptp_connect(struct socket *sock, struct sockaddr_unsized *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 (sockaddr_len < sizeof(struct sockaddr_pppox))
return -EINVAL;
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 = pptp_route_output(po, &fl4);
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_MRU;
po->chan.mtu -= PPTP_HEADER_OVERHEAD;
po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
po->chan.direct_xmit = true;
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 peer)
{
int len = sizeof(struct sockaddr_pppox);
struct sockaddr_pppox sp;
memset(&sp.sa_addr, 0, sizeof(sp.sa_addr));
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);
return len;
}
static int pptp_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct pppox_sock *po;
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);
del_chan(po);
synchronize_rcu();
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);
dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1));
}
static int pptp_create(struct net *net, struct socket *sock, int kern)
{
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, kern);
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 = 0xffffffff;
opt->ack_recv = 0; opt->ack_sent = 0xffffffff;
error = 0;
out:
return error;
}
static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
unsigned long arg)
{
struct sock *sk = 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,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.mmap = sock_no_mmap,
.ioctl = pppox_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = pppox_compat_ioctl,
#endif
};
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(array_size(sizeof(void *), (MAX_CALLID + 1)));
if (!callid_sock)
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");
MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_PPTP);