📄 isdn_ppp.c
字号:
This looks like a major redesign of the interface would be nice, but I don't have an idea how to do it better. *//* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is getting that lengthy because there is no simple "send-this-frame-out" function above but every wrapper does a bit different. Hope I guess correct in this hack... */static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, unsigned char code, unsigned char id, unsigned char *data, int len){ struct sk_buff *skb; unsigned char *p; int hl; int cnt = 0; isdn_net_local *lp = is->lp; /* Alloc large enough skb */ hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; skb = alloc_skb(len + hl + 16,GFP_ATOMIC); if(!skb) { printk(KERN_WARNING "ippp: CCP cannot send reset - out of memory\n"); return; } skb_reserve(skb, hl); /* We may need to stuff an address and control field first */ if(!(is->pppcfg & SC_COMP_AC)) { p = skb_put(skb, 2); *p++ = 0xff; *p++ = 0x03; } /* Stuff proto, code, id and length */ p = skb_put(skb, 6); *p++ = (proto >> 8); *p++ = (proto & 0xff); *p++ = code; *p++ = id; cnt = 4 + len; *p++ = (cnt >> 8); *p++ = (cnt & 0xff); /* Now stuff remaining bytes */ if(len) { p = skb_put(skb, len); memcpy(p, data, len); } /* skb is now ready for xmit */ printk(KERN_DEBUG "Sending CCP Frame:\n"); isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot); isdn_net_write_super(lp, skb);}/* Allocate the reset state vector */static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is){ struct ippp_ccp_reset *r; r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL); if(!r) { printk(KERN_ERR "ippp_ccp: failed to allocate reset data" " structure - no mem\n"); return NULL; } memset(r, 0, sizeof(struct ippp_ccp_reset)); printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r); is->reset = r; return r;}/* Destroy the reset state vector. Kill all pending timers first. */static void isdn_ppp_ccp_reset_free(struct ippp_struct *is){ unsigned int id; printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n", is->reset); for(id = 0; id < 256; id++) { if(is->reset->rs[id]) { isdn_ppp_ccp_reset_free_state(is, (unsigned char)id); } } kfree(is->reset); is->reset = NULL;}/* Free a given state and clear everything up for later reallocation */static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, unsigned char id){ struct ippp_ccp_reset_state *rs; if(is->reset->rs[id]) { printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id); rs = is->reset->rs[id]; /* Make sure the kernel will not call back later */ if(rs->ta) del_timer(&rs->timer); is->reset->rs[id] = NULL; kfree(rs); } else { printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id); }}/* The timer callback function which is called when a ResetReq has timed out, aka has never been answered by a ResetAck */static void isdn_ppp_ccp_timer_callback(unsigned long closure){ struct ippp_ccp_reset_state *rs = (struct ippp_ccp_reset_state *)closure; if(!rs) { printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n"); return; } if(rs->ta && rs->state == CCPResetSentReq) { /* We are correct here */ printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n", rs->id); if(!rs->expra) { /* Hmm, there is no Ack really expected. We can clean up the state now, it will be reallocated if the decompressor insists on another reset */ rs->ta = 0; isdn_ppp_ccp_reset_free_state(rs->is, rs->id); return; } /* Push it again */ isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id, rs->data, rs->dlen); /* Restart timer */ rs->timer.expires = jiffies + HZ*5; add_timer(&rs->timer); } else { printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n", rs->state); }}/* Allocate a new reset transaction state */static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is, unsigned char id){ struct ippp_ccp_reset_state *rs; if(is->reset->rs[id]) { printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n", id); return NULL; } else { rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL); if(!rs) return NULL; memset(rs, 0, sizeof(struct ippp_ccp_reset_state)); rs->state = CCPResetIdle; rs->is = is; rs->id = id; rs->timer.data = (unsigned long)rs; rs->timer.function = isdn_ppp_ccp_timer_callback; is->reset->rs[id] = rs; } return rs;}/* A decompressor wants a reset with a set of parameters - do what is necessary to fulfill it */static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is, struct isdn_ppp_resetparams *rp){ struct ippp_ccp_reset_state *rs; if(rp->valid) { /* The decompressor defines parameters by itself */ if(rp->rsend) { /* And he wants us to send a request */ if(!(rp->idval)) { printk(KERN_ERR "ippp_ccp: decompressor must" " specify reset id\n"); return; } if(is->reset->rs[rp->id]) { /* There is already a transaction in existence for this id. May be still waiting for a Ack or may be wrong. */ rs = is->reset->rs[rp->id]; if(rs->state == CCPResetSentReq && rs->ta) { printk(KERN_DEBUG "ippp_ccp: reset" " trans still in progress" " for id %d\n", rp->id); } else { printk(KERN_WARNING "ippp_ccp: reset" " trans in wrong state %d for" " id %d\n", rs->state, rp->id); } } else { /* Ok, this is a new transaction */ printk(KERN_DEBUG "ippp_ccp: new trans for id" " %d to be started\n", rp->id); rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id); if(!rs) { printk(KERN_ERR "ippp_ccp: out of mem" " allocing ccp trans\n"); return; } rs->state = CCPResetSentReq; rs->expra = rp->expra; if(rp->dtval) { rs->dlen = rp->dlen; memcpy(rs->data, rp->data, rp->dlen); } /* HACK TODO - add link comp here */ isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ, rs->id, rs->data, rs->dlen); /* Start the timer */ rs->timer.expires = jiffies + 5*HZ; add_timer(&rs->timer); rs->ta = 1; } } else { printk(KERN_DEBUG "ippp_ccp: no reset sent\n"); } } else { /* The reset params are invalid. The decompressor does not care about them, so we just send the minimal requests and increase ids only when an Ack is received for a given id */ if(is->reset->rs[is->reset->lastid]) { /* There is already a transaction in existence for this id. May be still waiting for a Ack or may be wrong. */ rs = is->reset->rs[is->reset->lastid]; if(rs->state == CCPResetSentReq && rs->ta) { printk(KERN_DEBUG "ippp_ccp: reset" " trans still in progress" " for id %d\n", rp->id); } else { printk(KERN_WARNING "ippp_ccp: reset" " trans in wrong state %d for" " id %d\n", rs->state, rp->id); } } else { printk(KERN_DEBUG "ippp_ccp: new trans for id" " %d to be started\n", is->reset->lastid); rs = isdn_ppp_ccp_reset_alloc_state(is, is->reset->lastid); if(!rs) { printk(KERN_ERR "ippp_ccp: out of mem" " allocing ccp trans\n"); return; } rs->state = CCPResetSentReq; /* We always expect an Ack if the decompressor doesnt know better */ rs->expra = 1; rs->dlen = 0; /* HACK TODO - add link comp here */ isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ, rs->id, NULL, 0); /* Start the timer */ rs->timer.expires = jiffies + 5*HZ; add_timer(&rs->timer); rs->ta = 1; } }}/* An Ack was received for this id. This means we stop the timer and clean up the state prior to calling the decompressors reset routine. */static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is, unsigned char id){ struct ippp_ccp_reset_state *rs = is->reset->rs[id]; if(rs) { if(rs->ta && rs->state == CCPResetSentReq) { /* Great, we are correct */ if(!rs->expra) printk(KERN_DEBUG "ippp_ccp: ResetAck received" " for id %d but not expected\n", id); } else { printk(KERN_INFO "ippp_ccp: ResetAck received out of" "sync for id %d\n", id); } if(rs->ta) { rs->ta = 0; del_timer(&rs->timer); } isdn_ppp_ccp_reset_free_state(is, id); } else { printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id" " %d\n", id); } /* Make sure the simple reset stuff uses a new id next time */ is->reset->lastid++;}/* * decompress packet * * if master = 0, we're trying to uncompress an per-link compressed packet, * as opposed to an compressed reconstructed-from-MPPP packet. * proto is updated to protocol field of uncompressed packet. * * retval: decompressed packet, * same packet if uncompressed, * NULL if decompression error */static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master, int *proto){ void *stat = NULL; struct isdn_ppp_compressor *ipc = NULL; struct sk_buff *skb_out; int len; struct ippp_struct *ri; struct isdn_ppp_resetparams rsparm; unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; if(!master) { // per-link decompression stat = is->link_decomp_stat; ipc = is->link_decompressor; ri = is; } else { stat = master->decomp_stat; ipc = master->decompressor; ri = master; } if (!ipc) { // no decompressor -> we can't decompress. printk(KERN_DEBUG "ippp: no decompressor defined!\n"); return skb; } if (!stat) // if we have a compressor, stat has been set as well BUG(); if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) { // compressed packets are compressed by their protocol type // Set up reset params for the decompressor memset(&rsparm, 0, sizeof(rsparm)); rsparm.data = rsdata; rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; /* !!!HACK,HACK,HACK!!! 2048 is only assumed */ skb_out = dev_alloc_skb(2048); len = ipc->decompress(stat, skb, skb_out, &rsparm); kfree_skb(skb); if (len <= 0) { switch(len) { case DECOMP_ERROR: ri->pppcfg |= SC_DC_ERROR; printk(KERN_INFO "ippp: decomp wants reset %s params\n", rsparm.valid ? "with" : "without"); isdn_ppp_ccp_reset_trans(ri, &rsparm); break; case DECOMP_FATALERROR: ri->pppcfg |= SC_DC_FERROR; /* Kick ipppd to recognize the error */ isdn_ppp_ccp_kickup(ri); break; } kfree_skb(skb_out); return NULL; } if (isdn_ppp_skip_ac(ri, skb) < 0) { kfree_skb(skb); return NULL; } *proto = isdn_ppp_strip_proto(skb); if (*proto < 0) { kfree_skb(skb); return NULL; } return skb_out; } else { // uncompressed packets are fed through the decompressor to // update the decompressor state ipc->incomp(stat, skb, *proto); return skb; }}/* * compress a frame * type=0: normal/bundle compression * =1: link compression * returns original skb if we haven't compressed the frame * and a new skb pointer if we've done it */static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, struct ippp_struct *is,struct ippp_struct *master,int type){ int ret; int new_proto; struct isdn_ppp_compressor *compressor; void *stat; struct sk_buff *skb_out; /* we do not compress control protocols */ if(*proto < 0 || *proto > 0x3fff) { return skb_in; } if(type) { /* type=1 => Link compression */ return skb_in; } else { if(!master) { compressor = is->compressor; stat = is->comp_stat; } else { compressor = master->compressor; stat = master->comp_stat; } new_proto = PPP_COMP; } if(!compressor) { printk(KERN_ERR "isdn_ppp: No compressor set!\n"); return skb_in; } if(!stat) { printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n"); return skb_in; } /* Allow for at least 150 % expansion (for now) */ skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 + skb_headroom(skb_in), GFP_ATOMIC); if(!skb_out) return skb_in; skb_reserve(skb_out, skb_headroom(skb_in)); ret = (compressor->compress)(stat,skb_in,skb_out,*proto); if(!ret) { dev_kfree_skb(skb_out); return skb_in; } dev_kfree_skb(skb_in); *proto = new_proto; return skb_out;}/* * we received a CCP frame .. * not a clean solution, but we MUST handle a few cases in the kernel */static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto){ struct ippp_struct *is = ippp_table[lp->ppp_slot]; struct ippp_struct *mis; int len; struct isdn_ppp_resetparams rsparm; unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; printk(KERN_DEBUG "Received CCP frame from peer\n"); isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot); if(lp->master) mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]; else mis = is; switch(skb->data[0]) { case CCP_CONFREQ: case CCP_TERMREQ: case CCP_TERMACK: if(is->debug & 0x10) printk(KERN_DEBUG "Disable (de)compression here!\n"); if(proto == PPP_CCP) mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON); else is->compflags &= ~(SC_LINK_DECOMP_ON|SC
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -