#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/vm.h>
#if 0
#include <net/netisr.h>
#endif
#include <sys/syslog.h>
#include <netinet/in.h>
#include "ppp_defs.h" // public ppp values
#include "ppp_ip.h"
#include "if_ppplink.h" // public link API
#include "ppp_domain.h"
#include "ppp_if.h"
#include "ppp_domain.h"
#include "ppp_comp.h"
struct ppp_comp {
TAILQ_ENTRY(ppp_comp) next;
u_int32_t protocol;
void *userdata;
void *(*comp_alloc)
(u_char *options, int opt_len);
void (*comp_free)
(void *state);
int (*comp_init)
(void *state, u_char *options, int opt_len,
int unit, int hdrlen, int mtu, int debug);
void (*comp_reset)
(void *state);
int (*compress)
(void *state, mbuf_t *m);
void (*comp_stat)
(void *state, struct compstat *stats);
void *(*decomp_alloc)
(u_char *options, int opt_len);
void (*decomp_free)
(void *state);
int (*decomp_init)
(void *state, u_char *options, int opt_len,
int unit, int hdrlen, int mru, int debug);
void (*decomp_reset)
(void *state);
int (*decompress)
(void *state, mbuf_t *m);
void (*incomp)
(void *state, mbuf_t m);
void (*decomp_stat)
(void *state, struct compstat *stats);
};
struct ppp_comp *ppp_comp_find(u_int32_t proto);
static TAILQ_HEAD(, ppp_comp) ppp_comp_head;
int ppp_comp_init()
{
TAILQ_INIT(&ppp_comp_head);
return 0;
}
int ppp_comp_dispose()
{
struct ppp_comp *comp;
while (comp = TAILQ_FIRST(&ppp_comp_head)) {
TAILQ_REMOVE(&ppp_comp_head, comp, next);
FREE(comp, M_TEMP);
}
return 0;
}
struct ppp_comp *ppp_comp_find(u_int32_t proto)
{
struct ppp_comp *comp;
TAILQ_FOREACH(comp, &ppp_comp_head, next)
if (comp->protocol == proto)
return comp;
return 0;
}
int ppp_comp_register(struct ppp_comp_reg *compreg, ppp_comp_ref *compref)
{
struct ppp_comp *comp;
if (compreg == NULL
|| compreg->comp_alloc == NULL
|| compreg->comp_free == NULL
|| compreg->comp_init == NULL
|| compreg->comp_reset == NULL
|| compreg->compress == NULL
|| compreg->comp_stat == NULL
|| compreg->decomp_alloc == NULL
|| compreg->decomp_free == NULL
|| compreg->decomp_init == NULL
|| compreg->decomp_reset == NULL
|| compreg->decompress == NULL
|| compreg->incomp == NULL
|| compreg->decomp_stat == NULL)
return(EINVAL);
comp = ppp_comp_find(compreg->compress_proto);
if (comp != NULL)
return(EEXIST);
MALLOC(comp, struct ppp_comp *, sizeof(*comp), M_TEMP, M_WAITOK);
if (comp == NULL)
return(ENOMEM);
bzero((char *)comp, sizeof(*comp));
comp->protocol = compreg->compress_proto;
comp->comp_alloc = compreg->comp_alloc;
comp->comp_free = compreg->comp_free;
comp->comp_init = compreg->comp_init;
comp->comp_reset = compreg->comp_reset;
comp->compress = compreg->compress;
comp->comp_stat = compreg->comp_stat;
comp->decomp_alloc = compreg->decomp_alloc;
comp->decomp_free = compreg->decomp_free;
comp->decomp_init = compreg->decomp_init;
comp->decomp_reset = compreg->decomp_reset;
comp->decompress = compreg->decompress;
comp->incomp = compreg->incomp;
comp->decomp_stat = compreg->decomp_stat;
TAILQ_INSERT_TAIL(&ppp_comp_head, comp, next);
*compref = comp;
return 0;
}
int ppp_comp_deregister(ppp_comp_ref *compref)
{
struct ppp_comp *comp = (struct ppp_comp *)compref;
if (comp == NULL)
return(EINVAL);
TAILQ_REMOVE(&ppp_comp_head, comp, next);
FREE(comp, M_TEMP);
return(0);
}
int ppp_comp_setcompressor(struct ppp_if *wan, struct ppp_option_data *odp)
{
int error = 0, nb;
struct ppp_comp *cp;
struct ppp_option_data64 *odp64 = (struct ppp_option_data64 *)odp;
u_char ccp_option[CCP_MAX_OPTION_LENGTH];
user_addr_t ptr;
int transmit;
if (proc_is64bit(current_proc())) {
nb = odp64->length;
ptr = odp64->ptr;
transmit = odp64->transmit;
}
else {
nb = odp->length;
ptr = CAST_USER_ADDR_T(odp->ptr);
transmit = odp->transmit;
}
if (nb > sizeof(ccp_option))
nb = sizeof(ccp_option);
if (error = copyin(ptr, ccp_option, nb))
return (error);
if (ccp_option[1] < 2)
return (EINVAL);
cp = ppp_comp_find(ccp_option[0]);
if (cp == 0) {
LOGDBG(wan->net, (LOGVAL, "ppp%d: no compressor for [%x %x %x], %x\n",
ifnet_unit(wan->net), ccp_option[0], ccp_option[1],
ccp_option[2], nb));
return EINVAL;
}
if (transmit) {
if (wan->xc_state)
(*wan->xcomp->comp_free)(wan->xc_state);
wan->xcomp = cp;
wan->xc_state = cp->comp_alloc(ccp_option, nb);
if (!wan->xc_state) {
error = ENOMEM;
LOGDBG(wan->net, (LOGVAL, "ppp%d: comp_alloc failed\n", ifnet_unit(wan->net)));
}
wan->sc_flags &= ~SC_COMP_RUN;
}
else {
if (wan->rc_state)
(*wan->rcomp->decomp_free)(wan->rc_state);
wan->rcomp = cp;
wan->rc_state = cp->decomp_alloc(ccp_option, nb);
if (!wan->rc_state) {
error = ENOMEM;
LOGDBG(wan->net, (LOGVAL, "ppp%d: decomp_alloc failed\n", ifnet_unit(wan->net)));
}
wan->sc_flags &= ~SC_DECOMP_RUN;
}
return error;
}
void ppp_comp_getstats(struct ppp_if *wan, struct ppp_comp_stats *stats)
{
bzero(stats, sizeof(struct ppp_comp_stats));
if (wan->xc_state)
(*wan->xcomp->comp_stat)(wan->xc_state, &stats->c);
if (wan->rc_state)
(*wan->rcomp->decomp_stat)(wan->rc_state, &stats->d);
}
void ppp_comp_ccp(struct ppp_if *wan, mbuf_t m, int rcvd)
{
u_char *p = mbuf_data(m);
int slen;
slen = CCP_LENGTH(p);
if (slen > mbuf_pkthdr_len(m)) {
LOGDBG(wan->net, (LOGVAL, "ppp_comp_ccp: not enough data in mbuf (expected = %d, got = %d)\n",
slen, mbuf_pkthdr_len(m)));
return;
}
switch (CCP_CODE(p)) {
case CCP_CONFREQ:
case CCP_TERMREQ:
case CCP_TERMACK:
wan->sc_flags &= ~(rcvd ? SC_COMP_RUN : SC_DECOMP_RUN);
break;
case CCP_CONFACK:
if (wan->sc_flags & SC_CCP_OPEN && !(wan->sc_flags & SC_CCP_UP)
&& slen >= CCP_HDRLEN + CCP_OPT_MINLEN
&& slen >= CCP_OPT_LENGTH(p + CCP_HDRLEN) + CCP_HDRLEN) {
if (rcvd) {
if (wan->rc_state
&& (*wan->rcomp->decomp_init)
(wan->rc_state, p + CCP_HDRLEN, slen - CCP_HDRLEN,
ifnet_unit(wan->net), 0, wan->mru, ifnet_flags(wan->net) & IFF_DEBUG)) {
wan->sc_flags |= SC_DECOMP_RUN;
wan->sc_flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
}
} else {
if (wan->xc_state
&& (*wan->xcomp->comp_init)
(wan->xc_state, p + CCP_HDRLEN, slen - CCP_HDRLEN,
ifnet_unit(wan->net), 0, ifnet_mtu(wan->net), ifnet_flags(wan->net) & IFF_DEBUG)) {
wan->sc_flags |= SC_COMP_RUN;
}
}
}
break;
case CCP_RESETACK:
if (wan->sc_flags & SC_CCP_UP) {
if (rcvd) {
if (wan->rc_state && (wan->sc_flags & SC_DECOMP_RUN)) {
(*wan->rcomp->decomp_reset)(wan->rc_state);
wan->sc_flags &= ~SC_DC_ERROR;
}
} else {
if (wan->xc_state && (wan->sc_flags & SC_COMP_RUN))
(*wan->xcomp->comp_reset)(wan->xc_state);
}
}
break;
}
}
void ppp_comp_close(struct ppp_if *wan)
{
wan->sc_flags &= ~(SC_CCP_OPEN || SC_CCP_UP || SC_COMP_RUN || SC_DECOMP_RUN);
if (wan->xc_state) {
(*wan->xcomp->comp_free)(wan->xc_state);
wan->xc_state = NULL;
}
if (wan->rc_state) {
(*wan->rcomp->decomp_free)(wan->rc_state);
wan->rc_state = NULL;
}
}
void ppp_comp_logmbuf(char *msg, mbuf_t m)
{
int i, lcount, copycount, count;
char lbuf[16], *data;
if (m == NULL)
return;
log(LOGVAL, "%s: \n", msg);
for (count = mbuf_len(m), data = mbuf_data(m); m != NULL; ) {
for(lcount = 0; lcount < sizeof(lbuf); lcount += copycount) {
if (!count) {
m = mbuf_next(m);
if (m == NULL)
break;
count = mbuf_len(m);
data = mbuf_data(m);
}
copycount = (count > sizeof(lbuf) - lcount) ? sizeof(lbuf) - lcount : count;
bcopy(data, &lbuf[lcount], copycount);
data += copycount;
count -= copycount;
}
log(LOGVAL, "%s: 0x ", msg);
for(i = 0; i < lcount; i++) {
if (i == 8) log(LOGVAL, " ");
log(LOGVAL, "%02x ", (u_char)lbuf[i]);
}
for( ; i < sizeof(lbuf); i++) {
if (i == 8) log(LOGVAL, " ");
log(LOGVAL, " ");
}
log(LOGVAL, " '");
for(i = 0; i < lcount; i++)
log(LOGVAL, "%c",(lbuf[i]>=040 && lbuf[i]<=0176)?lbuf[i]:'.');
log(LOGVAL, "'\n");
}
}
int ppp_comp_compress(struct ppp_if *wan, mbuf_t *m)
{
if (wan->xc_state == 0 || (wan->sc_flags & SC_CCP_UP) == 0)
return COMP_NOTDONE;
return wan->xcomp->compress(wan->xc_state, m);
}
int ppp_comp_incompress(struct ppp_if *wan, mbuf_t m)
{
if ((wan->rc_state == 0) || (wan->sc_flags & (SC_DC_ERROR | SC_DC_FERROR)))
return 0;
wan->rcomp->incomp(wan->rc_state, m);
return 0;
}
int ppp_comp_decompress(struct ppp_if *wan, mbuf_t *m)
{
int err;
if ((wan->rc_state == 0) || (wan->sc_flags & (SC_DC_ERROR | SC_DC_FERROR)))
return DECOMP_ERROR;
err = wan->rcomp->decompress(wan->rc_state, m);
if (err != DECOMP_OK) {
if (err == DECOMP_FATALERROR)
wan->sc_flags |= SC_DC_FERROR;
wan->sc_flags |= SC_DC_ERROR;
ppp_if_error(wan->net);
}
return err;
}