📄 isdn_ppp.c
字号:
ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit; /* maybe also SC_CCP stuff */ ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg & (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP); ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg & (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ); rc = isdn_ppp_mp_init(nlp, p->pb);out: spin_unlock_irqrestore(&p->pb->lock, flags); return rc;} #endif /* CONFIG_ISDN_MPP */ /* * network device ioctl handlers */static intisdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev){ struct ppp_stats __user *res = ifr->ifr_data; struct ppp_stats t; isdn_net_local *lp = (isdn_net_local *) dev->priv; if (!access_ok(VERIFY_WRITE, res, sizeof(struct ppp_stats))) return -EFAULT; /* build a temporary stat struct and copy it to user space */ memset(&t, 0, sizeof(struct ppp_stats)); if (dev->flags & IFF_UP) { t.p.ppp_ipackets = lp->stats.rx_packets; t.p.ppp_ibytes = lp->stats.rx_bytes; t.p.ppp_ierrors = lp->stats.rx_errors; t.p.ppp_opackets = lp->stats.tx_packets; t.p.ppp_obytes = lp->stats.tx_bytes; t.p.ppp_oerrors = lp->stats.tx_errors;#ifdef CONFIG_ISDN_PPP_VJ if (slot >= 0 && ippp_table[slot]->slcomp) { struct slcompress *slcomp = ippp_table[slot]->slcomp; t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed; t.vj.vjs_compressed = slcomp->sls_o_compressed; t.vj.vjs_searches = slcomp->sls_o_searches; t.vj.vjs_misses = slcomp->sls_o_misses; t.vj.vjs_errorin = slcomp->sls_i_error; t.vj.vjs_tossed = slcomp->sls_i_tossed; t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed; t.vj.vjs_compressedin = slcomp->sls_i_compressed; }#endif } if (copy_to_user(res, &t, sizeof(struct ppp_stats))) return -EFAULT; return 0;}intisdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ int error=0; int len; isdn_net_local *lp = (isdn_net_local *) dev->priv; if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) return -EINVAL; switch (cmd) {#define PPP_VERSION "2.3.7" case SIOCGPPPVER: len = strlen(PPP_VERSION) + 1; if (copy_to_user(ifr->ifr_data, PPP_VERSION, len)) error = -EFAULT; break; case SIOCGPPPSTATS: error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev); break; default: error = -EINVAL; break; } return error;}static intisdn_ppp_if_get_unit(char *name){ int len, i, unit = 0, deci; len = strlen(name); if (strncmp("ippp", name, 4) || len > 8) return -1; for (i = 0, deci = 1; i < len; i++, deci *= 10) { char a = name[len - i - 1]; if (a >= '0' && a <= '9') unit += (a - '0') * deci; else break; } if (!i || len - i != 4) unit = -1; return unit;}intisdn_ppp_dial_slave(char *name){#ifdef CONFIG_ISDN_MPP isdn_net_dev *ndev; isdn_net_local *lp; struct net_device *sdev; if (!(ndev = isdn_net_findif(name))) return 1; lp = ndev->local; if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; sdev = lp->slave; while (sdev) { isdn_net_local *mlp = (isdn_net_local *) sdev->priv; if (!(mlp->flags & ISDN_NET_CONNECTED)) break; sdev = mlp->slave; } if (!sdev) return 2; isdn_net_dial_req((isdn_net_local *) sdev->priv); return 0;#else return -1;#endif}intisdn_ppp_hangup_slave(char *name){#ifdef CONFIG_ISDN_MPP isdn_net_dev *ndev; isdn_net_local *lp; struct net_device *sdev; if (!(ndev = isdn_net_findif(name))) return 1; lp = ndev->local; if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; sdev = lp->slave; while (sdev) { isdn_net_local *mlp = (isdn_net_local *) sdev->priv; if (mlp->slave) { /* find last connected link in chain */ isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv; if (!(nlp->flags & ISDN_NET_CONNECTED)) break; } else if (mlp->flags & ISDN_NET_CONNECTED) break; sdev = mlp->slave; } if (!sdev) return 2; isdn_net_hangup(sdev); return 0;#else return -1;#endif}/* * PPP compression stuff *//* Push an empty CCP Data Frame up to the daemon to wake it up and let it generate a CCP Reset-Request or tear down CCP altogether */static void isdn_ppp_ccp_kickup(struct ippp_struct *is){ isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);}/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary, but absolutely nontrivial. The most abstruse problem we are facing is that the generation, reception and all the handling of timeouts and resends including proper request id management should be entirely left to the (de)compressor, but indeed is not covered by the current API to the (de)compressor. The API is a prototype version from PPP where only some (de)compressors have yet been implemented and all of them are rather simple in their reset handling. Especially, their is only one outstanding ResetAck at a time with all of them and ResetReq/-Acks do not have parameters. For this very special case it was sufficient to just return an error code from the decompressor and have a single reset() entry to communicate all the necessary information between the framework and the (de)compressor. Bad enough, LZS is different (and any other compressor may be different, too). It has multiple histories (eventually) and needs to Reset each of them independently and thus uses multiple outstanding Acks and history numbers as an additional parameter to Reqs/Acks. All that makes it harder to port the reset state engine into the kernel because it is not just the same simple one as in (i)pppd but it must be able to pass additional parameters and have multiple out- standing Acks. We are trying to achieve the impossible by handling reset transactions independent by their id. The id MUST change when the data portion changes, thus any (de)compressor who uses more than one resettable state must provide and recognize individual ids for each individual reset transaction. The framework itself does _only_ differentiate them by id, because it has no other semantics like the (de)compressor might. 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 */ 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; } printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n", rs->id); /* 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 doesn't 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 = j
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -