forked from Minki/linux
497888cf69
All these are instances of #define NAME value; or #define NAME(params_opt) value; These of course fail to build when used in contexts like if(foo $OP NAME) while(bar $OP NAME) and may silently generate the wrong code in contexts such as foo = NAME + 1; /* foo = value; + 1; */ bar = NAME - 1; /* bar = value; - 1; */ baz = NAME & quux; /* baz = value; & quux; */ Reported on comp.lang.c, Message-ID: <ab0d55fe-25e5-482b-811e-c475aa6065c3@c29g2000yqd.googlegroups.com> Initial analysis of the dangers provided by Keith Thompson in that thread. There are many more instances of more complicated macros having unnecessary trailing semicolons, but this pile seems to be all of the cases of simple values suffering from the problem. (Thus things that are likely to be found in one of the contexts above, more complicated ones aren't.) Signed-off-by: Phil Carmody <ext-phil.2.carmody@nokia.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
930 lines
23 KiB
C
930 lines
23 KiB
C
/*
|
|
* BSD compression module
|
|
*
|
|
* Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
|
|
* The whole module is now SKB based.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Update: The Berkeley copyright was changed, and the change
|
|
* is retroactive to all "true" BSD software (ie everything
|
|
* from UCB as opposed to other peoples code that just carried
|
|
* the same license). The new copyright doesn't clash with the
|
|
* GPL, so the module-only restriction has been removed..
|
|
*/
|
|
|
|
/*
|
|
* Original copyright notice:
|
|
*
|
|
* Copyright (c) 1985, 1986 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* James A. Woods, derived from original work by Spencer Thomas
|
|
* and Joseph Orost.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/in.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h> /* used in new tty drivers */
|
|
#include <linux/signal.h> /* used in new tty drivers */
|
|
#include <linux/bitops.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/byteorder.h>
|
|
#include <asm/types.h>
|
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/inet.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/ppp_defs.h>
|
|
|
|
#include <linux/isdn.h>
|
|
#include <linux/isdn_ppp.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/ppp-comp.h>
|
|
|
|
#include "isdn_ppp.h"
|
|
|
|
MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
#define BSD_VERSION(x) ((x) >> 5)
|
|
#define BSD_NBITS(x) ((x) & 0x1F)
|
|
|
|
#define BSD_CURRENT_VERSION 1
|
|
|
|
#define DEBUG 1
|
|
|
|
/*
|
|
* A dictionary for doing BSD compress.
|
|
*/
|
|
|
|
struct bsd_dict {
|
|
u32 fcode;
|
|
u16 codem1; /* output of hash table -1 */
|
|
u16 cptr; /* map code to hash table entry */
|
|
};
|
|
|
|
struct bsd_db {
|
|
int totlen; /* length of this structure */
|
|
unsigned int hsize; /* size of the hash table */
|
|
unsigned char hshift; /* used in hash function */
|
|
unsigned char n_bits; /* current bits/code */
|
|
unsigned char maxbits; /* maximum bits/code */
|
|
unsigned char debug; /* non-zero if debug desired */
|
|
unsigned char unit; /* ppp unit number */
|
|
u16 seqno; /* sequence # of next packet */
|
|
unsigned int mru; /* size of receive (decompress) bufr */
|
|
unsigned int maxmaxcode; /* largest valid code */
|
|
unsigned int max_ent; /* largest code in use */
|
|
unsigned int in_count; /* uncompressed bytes, aged */
|
|
unsigned int bytes_out; /* compressed bytes, aged */
|
|
unsigned int ratio; /* recent compression ratio */
|
|
unsigned int checkpoint; /* when to next check the ratio */
|
|
unsigned int clear_count; /* times dictionary cleared */
|
|
unsigned int incomp_count; /* incompressible packets */
|
|
unsigned int incomp_bytes; /* incompressible bytes */
|
|
unsigned int uncomp_count; /* uncompressed packets */
|
|
unsigned int uncomp_bytes; /* uncompressed bytes */
|
|
unsigned int comp_count; /* compressed packets */
|
|
unsigned int comp_bytes; /* compressed bytes */
|
|
unsigned short *lens; /* array of lengths of codes */
|
|
struct bsd_dict *dict; /* dictionary */
|
|
int xmit;
|
|
};
|
|
|
|
#define BSD_OVHD 2 /* BSD compress overhead/packet */
|
|
#define MIN_BSD_BITS 9
|
|
#define BSD_INIT_BITS MIN_BSD_BITS
|
|
#define MAX_BSD_BITS 15
|
|
|
|
/*
|
|
* the next two codes should not be changed lightly, as they must not
|
|
* lie within the contiguous general code space.
|
|
*/
|
|
#define CLEAR 256 /* table clear output code */
|
|
#define FIRST 257 /* first free entry */
|
|
#define LAST 255
|
|
|
|
#define MAXCODE(b) ((1 << (b)) - 1)
|
|
#define BADCODEM1 MAXCODE(MAX_BSD_BITS)
|
|
|
|
#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
|
|
^ (unsigned long)(prefix))
|
|
#define BSD_KEY(prefix,suffix) ((((unsigned long)(suffix)) << 16) \
|
|
+ (unsigned long)(prefix))
|
|
|
|
#define CHECK_GAP 10000 /* Ratio check interval */
|
|
|
|
#define RATIO_SCALE_LOG 8
|
|
#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
|
|
#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
|
|
|
|
/*
|
|
* clear the dictionary
|
|
*/
|
|
|
|
static void bsd_clear(struct bsd_db *db)
|
|
{
|
|
db->clear_count++;
|
|
db->max_ent = FIRST-1;
|
|
db->n_bits = BSD_INIT_BITS;
|
|
db->bytes_out = 0;
|
|
db->in_count = 0;
|
|
db->incomp_count = 0;
|
|
db->ratio = 0;
|
|
db->checkpoint = CHECK_GAP;
|
|
}
|
|
|
|
/*
|
|
* If the dictionary is full, then see if it is time to reset it.
|
|
*
|
|
* Compute the compression ratio using fixed-point arithmetic
|
|
* with 8 fractional bits.
|
|
*
|
|
* Since we have an infinite stream instead of a single file,
|
|
* watch only the local compression ratio.
|
|
*
|
|
* Since both peers must reset the dictionary at the same time even in
|
|
* the absence of CLEAR codes (while packets are incompressible), they
|
|
* must compute the same ratio.
|
|
*/
|
|
static int bsd_check (struct bsd_db *db) /* 1=output CLEAR */
|
|
{
|
|
unsigned int new_ratio;
|
|
|
|
if (db->in_count >= db->checkpoint)
|
|
{
|
|
/* age the ratio by limiting the size of the counts */
|
|
if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
|
|
{
|
|
db->in_count -= (db->in_count >> 2);
|
|
db->bytes_out -= (db->bytes_out >> 2);
|
|
}
|
|
|
|
db->checkpoint = db->in_count + CHECK_GAP;
|
|
|
|
if (db->max_ent >= db->maxmaxcode)
|
|
{
|
|
/* Reset the dictionary only if the ratio is worse,
|
|
* or if it looks as if it has been poisoned
|
|
* by incompressible data.
|
|
*
|
|
* This does not overflow, because
|
|
* db->in_count <= RATIO_MAX.
|
|
*/
|
|
|
|
new_ratio = db->in_count << RATIO_SCALE_LOG;
|
|
if (db->bytes_out != 0)
|
|
{
|
|
new_ratio /= db->bytes_out;
|
|
}
|
|
|
|
if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
|
|
{
|
|
bsd_clear (db);
|
|
return 1;
|
|
}
|
|
db->ratio = new_ratio;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return statistics.
|
|
*/
|
|
|
|
static void bsd_stats (void *state, struct compstat *stats)
|
|
{
|
|
struct bsd_db *db = (struct bsd_db *) state;
|
|
|
|
stats->unc_bytes = db->uncomp_bytes;
|
|
stats->unc_packets = db->uncomp_count;
|
|
stats->comp_bytes = db->comp_bytes;
|
|
stats->comp_packets = db->comp_count;
|
|
stats->inc_bytes = db->incomp_bytes;
|
|
stats->inc_packets = db->incomp_count;
|
|
stats->in_count = db->in_count;
|
|
stats->bytes_out = db->bytes_out;
|
|
}
|
|
|
|
/*
|
|
* Reset state, as on a CCP ResetReq.
|
|
*/
|
|
static void bsd_reset (void *state,unsigned char code, unsigned char id,
|
|
unsigned char *data, unsigned len,
|
|
struct isdn_ppp_resetparams *rsparm)
|
|
{
|
|
struct bsd_db *db = (struct bsd_db *) state;
|
|
|
|
bsd_clear(db);
|
|
db->seqno = 0;
|
|
db->clear_count = 0;
|
|
}
|
|
|
|
/*
|
|
* Release the compression structure
|
|
*/
|
|
static void bsd_free (void *state)
|
|
{
|
|
struct bsd_db *db = (struct bsd_db *) state;
|
|
|
|
if (db) {
|
|
/*
|
|
* Release the dictionary
|
|
*/
|
|
vfree(db->dict);
|
|
db->dict = NULL;
|
|
|
|
/*
|
|
* Release the string buffer
|
|
*/
|
|
vfree(db->lens);
|
|
db->lens = NULL;
|
|
|
|
/*
|
|
* Finally release the structure itself.
|
|
*/
|
|
kfree(db);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate space for a (de) compressor.
|
|
*/
|
|
static void *bsd_alloc (struct isdn_ppp_comp_data *data)
|
|
{
|
|
int bits;
|
|
unsigned int hsize, hshift, maxmaxcode;
|
|
struct bsd_db *db;
|
|
int decomp;
|
|
|
|
static unsigned int htab[][2] = {
|
|
{ 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } ,
|
|
{ 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 }
|
|
};
|
|
|
|
if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
|
|
|| BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
|
|
return NULL;
|
|
|
|
bits = BSD_NBITS(data->options[0]);
|
|
|
|
if(bits < 9 || bits > 15)
|
|
return NULL;
|
|
|
|
hsize = htab[bits-9][0];
|
|
hshift = htab[bits-9][1];
|
|
|
|
/*
|
|
* Allocate the main control structure for this instance.
|
|
*/
|
|
maxmaxcode = MAXCODE(bits);
|
|
db = kzalloc (sizeof (struct bsd_db),GFP_KERNEL);
|
|
if (!db)
|
|
return NULL;
|
|
|
|
db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
|
|
decomp = db->xmit ? 0 : 1;
|
|
|
|
/*
|
|
* Allocate space for the dictionary. This may be more than one page in
|
|
* length.
|
|
*/
|
|
db->dict = vmalloc(hsize * sizeof(struct bsd_dict));
|
|
if (!db->dict) {
|
|
bsd_free (db);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is the compression buffer then there is no length data.
|
|
* For decompression, the length information is needed as well.
|
|
*/
|
|
if (!decomp)
|
|
db->lens = NULL;
|
|
else {
|
|
db->lens = vmalloc((maxmaxcode + 1) * sizeof(db->lens[0]));
|
|
if (!db->lens) {
|
|
bsd_free (db);
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the data information for the compression code
|
|
*/
|
|
db->totlen = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
|
|
db->hsize = hsize;
|
|
db->hshift = hshift;
|
|
db->maxmaxcode = maxmaxcode;
|
|
db->maxbits = bits;
|
|
|
|
return (void *) db;
|
|
}
|
|
|
|
/*
|
|
* Initialize the database.
|
|
*/
|
|
static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
|
|
{
|
|
struct bsd_db *db = state;
|
|
int indx;
|
|
int decomp;
|
|
|
|
if(!state || !data) {
|
|
printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
|
|
return 0;
|
|
}
|
|
|
|
decomp = db->xmit ? 0 : 1;
|
|
|
|
if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
|
|
|| (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
|
|
|| (BSD_NBITS(data->options[0]) != db->maxbits)
|
|
|| (decomp && db->lens == NULL)) {
|
|
printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
|
|
return 0;
|
|
}
|
|
|
|
if (decomp)
|
|
for(indx=LAST;indx>=0;indx--)
|
|
db->lens[indx] = 1;
|
|
|
|
indx = db->hsize;
|
|
while (indx-- != 0) {
|
|
db->dict[indx].codem1 = BADCODEM1;
|
|
db->dict[indx].cptr = 0;
|
|
}
|
|
|
|
db->unit = unit;
|
|
db->mru = 0;
|
|
|
|
db->debug = 1;
|
|
|
|
bsd_reset(db,0,0,NULL,0,NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Obtain pointers to the various structures in the compression tables
|
|
*/
|
|
|
|
#define dict_ptrx(p,idx) &(p->dict[idx])
|
|
#define lens_ptrx(p,idx) &(p->lens[idx])
|
|
|
|
#ifdef DEBUG
|
|
static unsigned short *lens_ptr(struct bsd_db *db, int idx)
|
|
{
|
|
if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
|
|
printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
|
|
idx = 0;
|
|
}
|
|
return lens_ptrx (db, idx);
|
|
}
|
|
|
|
static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
|
|
{
|
|
if ((unsigned int) idx >= (unsigned int) db->hsize) {
|
|
printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
|
|
idx = 0;
|
|
}
|
|
return dict_ptrx (db, idx);
|
|
}
|
|
|
|
#else
|
|
#define lens_ptr(db,idx) lens_ptrx(db,idx)
|
|
#define dict_ptr(db,idx) dict_ptrx(db,idx)
|
|
#endif
|
|
|
|
/*
|
|
* compress a packet
|
|
*/
|
|
static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
|
|
{
|
|
struct bsd_db *db;
|
|
int hshift;
|
|
unsigned int max_ent;
|
|
unsigned int n_bits;
|
|
unsigned int bitno;
|
|
unsigned long accm;
|
|
int ent;
|
|
unsigned long fcode;
|
|
struct bsd_dict *dictp;
|
|
unsigned char c;
|
|
int hval,disp,ilen,mxcode;
|
|
unsigned char *rptr = skb_in->data;
|
|
int isize = skb_in->len;
|
|
|
|
#define OUTPUT(ent) \
|
|
{ \
|
|
bitno -= n_bits; \
|
|
accm |= ((ent) << bitno); \
|
|
do { \
|
|
if(skb_out && skb_tailroom(skb_out) > 0) \
|
|
*(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
|
|
accm <<= 8; \
|
|
bitno += 8; \
|
|
} while (bitno <= 24); \
|
|
}
|
|
|
|
/*
|
|
* If the protocol is not in the range we're interested in,
|
|
* just return without compressing the packet. If it is,
|
|
* the protocol becomes the first byte to compress.
|
|
*/
|
|
printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
|
|
|
|
ent = proto;
|
|
if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
|
|
return 0;
|
|
|
|
db = (struct bsd_db *) state;
|
|
hshift = db->hshift;
|
|
max_ent = db->max_ent;
|
|
n_bits = db->n_bits;
|
|
bitno = 32;
|
|
accm = 0;
|
|
mxcode = MAXCODE (n_bits);
|
|
|
|
/* This is the PPP header information */
|
|
if(skb_out && skb_tailroom(skb_out) >= 2) {
|
|
char *v = skb_put(skb_out,2);
|
|
/* we only push our own data on the header,
|
|
AC,PC and protos is pushed by caller */
|
|
v[0] = db->seqno >> 8;
|
|
v[1] = db->seqno;
|
|
}
|
|
|
|
ilen = ++isize; /* This is off by one, but that is what is in draft! */
|
|
|
|
while (--ilen > 0) {
|
|
c = *rptr++;
|
|
fcode = BSD_KEY (ent, c);
|
|
hval = BSD_HASH (ent, c, hshift);
|
|
dictp = dict_ptr (db, hval);
|
|
|
|
/* Validate and then check the entry. */
|
|
if (dictp->codem1 >= max_ent)
|
|
goto nomatch;
|
|
|
|
if (dictp->fcode == fcode) {
|
|
ent = dictp->codem1 + 1;
|
|
continue; /* found (prefix,suffix) */
|
|
}
|
|
|
|
/* continue probing until a match or invalid entry */
|
|
disp = (hval == 0) ? 1 : hval;
|
|
|
|
do {
|
|
hval += disp;
|
|
if (hval >= db->hsize)
|
|
hval -= db->hsize;
|
|
dictp = dict_ptr (db, hval);
|
|
if (dictp->codem1 >= max_ent)
|
|
goto nomatch;
|
|
} while (dictp->fcode != fcode);
|
|
|
|
ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */
|
|
continue;
|
|
|
|
nomatch:
|
|
OUTPUT(ent); /* output the prefix */
|
|
|
|
/* code -> hashtable */
|
|
if (max_ent < db->maxmaxcode) {
|
|
struct bsd_dict *dictp2;
|
|
struct bsd_dict *dictp3;
|
|
int indx;
|
|
|
|
/* expand code size if needed */
|
|
if (max_ent >= mxcode) {
|
|
db->n_bits = ++n_bits;
|
|
mxcode = MAXCODE (n_bits);
|
|
}
|
|
|
|
/*
|
|
* Invalidate old hash table entry using
|
|
* this code, and then take it over.
|
|
*/
|
|
dictp2 = dict_ptr (db, max_ent + 1);
|
|
indx = dictp2->cptr;
|
|
dictp3 = dict_ptr (db, indx);
|
|
|
|
if (dictp3->codem1 == max_ent)
|
|
dictp3->codem1 = BADCODEM1;
|
|
|
|
dictp2->cptr = hval;
|
|
dictp->codem1 = max_ent;
|
|
dictp->fcode = fcode;
|
|
db->max_ent = ++max_ent;
|
|
|
|
if (db->lens) {
|
|
unsigned short *len1 = lens_ptr (db, max_ent);
|
|
unsigned short *len2 = lens_ptr (db, ent);
|
|
*len1 = *len2 + 1;
|
|
}
|
|
}
|
|
ent = c;
|
|
}
|
|
|
|
OUTPUT(ent); /* output the last code */
|
|
|
|
if(skb_out)
|
|
db->bytes_out += skb_out->len; /* Do not count bytes from here */
|
|
db->uncomp_bytes += isize;
|
|
db->in_count += isize;
|
|
++db->uncomp_count;
|
|
++db->seqno;
|
|
|
|
if (bitno < 32)
|
|
++db->bytes_out; /* must be set before calling bsd_check */
|
|
|
|
/*
|
|
* Generate the clear command if needed
|
|
*/
|
|
|
|
if (bsd_check(db))
|
|
OUTPUT (CLEAR);
|
|
|
|
/*
|
|
* Pad dribble bits of last code with ones.
|
|
* Do not emit a completely useless byte of ones.
|
|
*/
|
|
if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0)
|
|
*(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
|
|
|
|
/*
|
|
* Increase code size if we would have without the packet
|
|
* boundary because the decompressor will do so.
|
|
*/
|
|
if (max_ent >= mxcode && max_ent < db->maxmaxcode)
|
|
db->n_bits++;
|
|
|
|
/* If output length is too large then this is an incompressible frame. */
|
|
if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
|
|
++db->incomp_count;
|
|
db->incomp_bytes += isize;
|
|
return 0;
|
|
}
|
|
|
|
/* Count the number of compressed frames */
|
|
++db->comp_count;
|
|
db->comp_bytes += skb_out->len;
|
|
return skb_out->len;
|
|
|
|
#undef OUTPUT
|
|
}
|
|
|
|
/*
|
|
* Update the "BSD Compress" dictionary on the receiver for
|
|
* incompressible data by pretending to compress the incoming data.
|
|
*/
|
|
static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
|
|
{
|
|
bsd_compress (state, skb_in, NULL, proto);
|
|
}
|
|
|
|
/*
|
|
* Decompress "BSD Compress".
|
|
*/
|
|
static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
|
|
struct isdn_ppp_resetparams *rsparm)
|
|
{
|
|
struct bsd_db *db;
|
|
unsigned int max_ent;
|
|
unsigned long accm;
|
|
unsigned int bitno; /* 1st valid bit in accm */
|
|
unsigned int n_bits;
|
|
unsigned int tgtbitno; /* bitno when we have a code */
|
|
struct bsd_dict *dictp;
|
|
int seq;
|
|
unsigned int incode;
|
|
unsigned int oldcode;
|
|
unsigned int finchar;
|
|
unsigned char *p,*ibuf;
|
|
int ilen;
|
|
int codelen;
|
|
int extra;
|
|
|
|
db = (struct bsd_db *) state;
|
|
max_ent = db->max_ent;
|
|
accm = 0;
|
|
bitno = 32; /* 1st valid bit in accm */
|
|
n_bits = db->n_bits;
|
|
tgtbitno = 32 - n_bits; /* bitno when we have a code */
|
|
|
|
printk(KERN_DEBUG "bsd_decompress called\n");
|
|
|
|
if(!skb_in || !skb_out) {
|
|
printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
|
|
return DECOMP_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Get the sequence number.
|
|
*/
|
|
if( (p = skb_pull(skb_in,2)) == NULL) {
|
|
return DECOMP_ERROR;
|
|
}
|
|
p-=2;
|
|
seq = (p[0] << 8) + p[1];
|
|
ilen = skb_in->len;
|
|
ibuf = skb_in->data;
|
|
|
|
/*
|
|
* Check the sequence number and give up if it differs from
|
|
* the value we're expecting.
|
|
*/
|
|
if (seq != db->seqno) {
|
|
if (db->debug) {
|
|
printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
|
|
db->unit, seq, db->seqno - 1);
|
|
}
|
|
return DECOMP_ERROR;
|
|
}
|
|
|
|
++db->seqno;
|
|
db->bytes_out += ilen;
|
|
|
|
if(skb_tailroom(skb_out) > 0)
|
|
*(skb_put(skb_out,1)) = 0;
|
|
else
|
|
return DECOMP_ERR_NOMEM;
|
|
|
|
oldcode = CLEAR;
|
|
|
|
/*
|
|
* Keep the checkpoint correctly so that incompressible packets
|
|
* clear the dictionary at the proper times.
|
|
*/
|
|
|
|
for (;;) {
|
|
if (ilen-- <= 0) {
|
|
db->in_count += (skb_out->len - 1); /* don't count the header */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Accumulate bytes until we have a complete code.
|
|
* Then get the next code, relying on the 32-bit,
|
|
* unsigned accm to mask the result.
|
|
*/
|
|
|
|
bitno -= 8;
|
|
accm |= *ibuf++ << bitno;
|
|
if (tgtbitno < bitno)
|
|
continue;
|
|
|
|
incode = accm >> tgtbitno;
|
|
accm <<= n_bits;
|
|
bitno += n_bits;
|
|
|
|
/*
|
|
* The dictionary must only be cleared at the end of a packet.
|
|
*/
|
|
|
|
if (incode == CLEAR) {
|
|
if (ilen > 0) {
|
|
if (db->debug)
|
|
printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
|
|
return DECOMP_FATALERROR; /* probably a bug */
|
|
}
|
|
bsd_clear(db);
|
|
break;
|
|
}
|
|
|
|
if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
|
|
|| (incode > max_ent && oldcode == CLEAR)) {
|
|
if (db->debug) {
|
|
printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
|
|
db->unit, incode, oldcode);
|
|
printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
|
|
max_ent, skb_out->len, db->seqno);
|
|
}
|
|
return DECOMP_FATALERROR; /* probably a bug */
|
|
}
|
|
|
|
/* Special case for KwKwK string. */
|
|
if (incode > max_ent) {
|
|
finchar = oldcode;
|
|
extra = 1;
|
|
} else {
|
|
finchar = incode;
|
|
extra = 0;
|
|
}
|
|
|
|
codelen = *(lens_ptr (db, finchar));
|
|
if( skb_tailroom(skb_out) < codelen + extra) {
|
|
if (db->debug) {
|
|
printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
|
|
#ifdef DEBUG
|
|
printk(KERN_DEBUG " len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
|
|
ilen, finchar, codelen, skb_out->len);
|
|
#endif
|
|
}
|
|
return DECOMP_FATALERROR;
|
|
}
|
|
|
|
/*
|
|
* Decode this code and install it in the decompressed buffer.
|
|
*/
|
|
|
|
p = skb_put(skb_out,codelen);
|
|
p += codelen;
|
|
while (finchar > LAST) {
|
|
struct bsd_dict *dictp2 = dict_ptr (db, finchar);
|
|
|
|
dictp = dict_ptr (db, dictp2->cptr);
|
|
|
|
#ifdef DEBUG
|
|
if (--codelen <= 0 || dictp->codem1 != finchar-1) {
|
|
if (codelen <= 0) {
|
|
printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
|
|
printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
|
|
} else {
|
|
if (dictp->codem1 != finchar-1) {
|
|
printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
|
|
printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
|
|
}
|
|
}
|
|
return DECOMP_FATALERROR;
|
|
}
|
|
#endif
|
|
|
|
{
|
|
u32 fcode = dictp->fcode;
|
|
*--p = (fcode >> 16) & 0xff;
|
|
finchar = fcode & 0xffff;
|
|
}
|
|
}
|
|
*--p = finchar;
|
|
|
|
#ifdef DEBUG
|
|
if (--codelen != 0)
|
|
printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
|
|
#endif
|
|
|
|
if (extra) /* the KwKwK case again */
|
|
*(skb_put(skb_out,1)) = finchar;
|
|
|
|
/*
|
|
* If not first code in a packet, and
|
|
* if not out of code space, then allocate a new code.
|
|
*
|
|
* Keep the hash table correct so it can be used
|
|
* with uncompressed packets.
|
|
*/
|
|
if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
|
|
struct bsd_dict *dictp2, *dictp3;
|
|
u16 *lens1, *lens2;
|
|
unsigned long fcode;
|
|
int hval, disp, indx;
|
|
|
|
fcode = BSD_KEY(oldcode,finchar);
|
|
hval = BSD_HASH(oldcode,finchar,db->hshift);
|
|
dictp = dict_ptr (db, hval);
|
|
|
|
/* look for a free hash table entry */
|
|
if (dictp->codem1 < max_ent) {
|
|
disp = (hval == 0) ? 1 : hval;
|
|
do {
|
|
hval += disp;
|
|
if (hval >= db->hsize)
|
|
hval -= db->hsize;
|
|
dictp = dict_ptr (db, hval);
|
|
} while (dictp->codem1 < max_ent);
|
|
}
|
|
|
|
/*
|
|
* Invalidate previous hash table entry
|
|
* assigned this code, and then take it over
|
|
*/
|
|
|
|
dictp2 = dict_ptr (db, max_ent + 1);
|
|
indx = dictp2->cptr;
|
|
dictp3 = dict_ptr (db, indx);
|
|
|
|
if (dictp3->codem1 == max_ent)
|
|
dictp3->codem1 = BADCODEM1;
|
|
|
|
dictp2->cptr = hval;
|
|
dictp->codem1 = max_ent;
|
|
dictp->fcode = fcode;
|
|
db->max_ent = ++max_ent;
|
|
|
|
/* Update the length of this string. */
|
|
lens1 = lens_ptr (db, max_ent);
|
|
lens2 = lens_ptr (db, oldcode);
|
|
*lens1 = *lens2 + 1;
|
|
|
|
/* Expand code size if needed. */
|
|
if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
|
|
db->n_bits = ++n_bits;
|
|
tgtbitno = 32-n_bits;
|
|
}
|
|
}
|
|
oldcode = incode;
|
|
}
|
|
|
|
++db->comp_count;
|
|
++db->uncomp_count;
|
|
db->comp_bytes += skb_in->len - BSD_OVHD;
|
|
db->uncomp_bytes += skb_out->len;
|
|
|
|
if (bsd_check(db)) {
|
|
if (db->debug)
|
|
printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
|
|
db->unit, db->seqno - 1);
|
|
}
|
|
return skb_out->len;
|
|
}
|
|
|
|
/*************************************************************
|
|
* Table of addresses for the BSD compression module
|
|
*************************************************************/
|
|
|
|
static struct isdn_ppp_compressor ippp_bsd_compress = {
|
|
.owner = THIS_MODULE,
|
|
.num = CI_BSD_COMPRESS,
|
|
.alloc = bsd_alloc,
|
|
.free = bsd_free,
|
|
.init = bsd_init,
|
|
.reset = bsd_reset,
|
|
.compress = bsd_compress,
|
|
.decompress = bsd_decompress,
|
|
.incomp = bsd_incomp,
|
|
.stat = bsd_stats,
|
|
};
|
|
|
|
/*************************************************************
|
|
* Module support routines
|
|
*************************************************************/
|
|
|
|
static int __init isdn_bsdcomp_init(void)
|
|
{
|
|
int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
|
|
if (answer == 0)
|
|
printk (KERN_INFO "PPP BSD Compression module registered\n");
|
|
return answer;
|
|
}
|
|
|
|
static void __exit isdn_bsdcomp_exit(void)
|
|
{
|
|
isdn_ppp_unregister_compressor (&ippp_bsd_compress);
|
|
}
|
|
|
|
module_init(isdn_bsdcomp_init);
|
|
module_exit(isdn_bsdcomp_exit);
|