📄 ppp_generic.c
字号:
}/* No kernel lock - fine */unsigned intppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait){ unsigned int mask; struct channel *pch = chan->ppp; mask = POLLOUT | POLLWRNORM; if (pch != 0) { poll_wait(file, &pch->file.rwait, wait); if (skb_peek(&pch->file.rq) != 0) mask |= POLLIN | POLLRDNORM; } return mask;}int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg){ struct channel *pch = chan->ppp; int err = -ENOTTY; int unit; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (pch == 0) return -EINVAL; switch (cmd) { case PPPIOCATTACH: if (get_user(unit, (int *) arg)) break; err = ppp_connect_channel(pch, unit); break; case PPPIOCDETACH: err = ppp_disconnect_channel(pch); break; } return err;}/* * Compression control. *//* Process the PPPIOCSCOMPRESS ioctl. */static intppp_set_compress(struct ppp *ppp, unsigned long arg){ int err; struct compressor *cp; struct ppp_option_data data; void *state; unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];#ifdef CONFIG_KMOD char modname[32];#endif err = -EFAULT; if (copy_from_user(&data, (void *) arg, sizeof(data)) || (data.length <= CCP_MAX_OPTION_LENGTH && copy_from_user(ccp_option, data.ptr, data.length))) goto out; err = -EINVAL; if (data.length > CCP_MAX_OPTION_LENGTH || ccp_option[1] < 2 || ccp_option[1] > data.length) goto out; cp = find_compressor(ccp_option[0]);#ifdef CONFIG_KMOD if (cp == 0) { sprintf(modname, "ppp-compress-%d", ccp_option[0]); request_module(modname); cp = find_compressor(ccp_option[0]); }#endif /* CONFIG_KMOD */ if (cp == 0) goto out; err = -ENOBUFS; if (data.transmit) { ppp_xmit_lock(ppp); ppp->xstate &= ~SC_COMP_RUN; if (ppp->xc_state != 0) { ppp->xcomp->comp_free(ppp->xc_state); ppp->xc_state = 0; } ppp_xmit_unlock(ppp); state = cp->comp_alloc(ccp_option, data.length); if (state != 0) { ppp_xmit_lock(ppp); ppp->xcomp = cp; ppp->xc_state = state; ppp_xmit_unlock(ppp); err = 0; } } else { ppp_recv_lock(ppp); ppp->rstate &= ~SC_DECOMP_RUN; if (ppp->rc_state != 0) { ppp->rcomp->decomp_free(ppp->rc_state); ppp->rc_state = 0; } ppp_recv_unlock(ppp); state = cp->decomp_alloc(ccp_option, data.length); if (state != 0) { ppp_recv_lock(ppp); ppp->rcomp = cp; ppp->rc_state = state; ppp_recv_unlock(ppp); err = 0; } } out: return err;}/* * Look at a CCP packet and update our state accordingly. * We assume the caller has the xmit or recv path locked. */static voidppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound){ unsigned char *dp = skb->data + 2; int len; if (skb->len < CCP_HDRLEN + 2 || skb->len < (len = CCP_LENGTH(dp)) + 2) return; /* too short */ switch (CCP_CODE(dp)) { case CCP_CONFREQ: case CCP_TERMREQ: case CCP_TERMACK: /* * CCP is going down - disable compression. */ if (inbound) ppp->rstate &= ~SC_DECOMP_RUN; else ppp->xstate &= ~SC_COMP_RUN; break; case CCP_CONFACK: if ((ppp->flags & (SC_CCP_OPEN | SC_CCP_UP)) != SC_CCP_OPEN) break; dp += CCP_HDRLEN; len -= CCP_HDRLEN; if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp)) break; if (inbound) { /* we will start receiving compressed packets */ if (ppp->rc_state == 0) break; if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len, ppp->file.index, 0, ppp->mru, ppp->debug)) { ppp->rstate |= SC_DECOMP_RUN; ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR); } } else { /* we will soon start sending compressed packets */ if (ppp->xc_state == 0) break; if (ppp->xcomp->comp_init(ppp->xc_state, dp, len, ppp->file.index, 0, ppp->debug)) ppp->xstate |= SC_COMP_RUN; } break; case CCP_RESETACK: /* reset the [de]compressor */ if ((ppp->flags & SC_CCP_UP) == 0) break; if (inbound) { if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)) { ppp->rcomp->decomp_reset(ppp->rc_state); ppp->rstate &= ~SC_DC_ERROR; } } else { if (ppp->xc_state && (ppp->xstate & SC_COMP_RUN)) ppp->xcomp->comp_reset(ppp->xc_state); } break; }}/* Free up compression resources. */static voidppp_ccp_closed(struct ppp *ppp){ ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP); ppp->xstate &= ~SC_COMP_RUN; if (ppp->xc_state) { ppp->xcomp->comp_free(ppp->xc_state); ppp->xc_state = 0; } ppp->xstate &= ~SC_DECOMP_RUN; if (ppp->rc_state) { ppp->rcomp->decomp_free(ppp->rc_state); ppp->rc_state = 0; }}/* List of compressors. */static LIST_HEAD(compressor_list);static spinlock_t compressor_list_lock = SPIN_LOCK_UNLOCKED;struct compressor_entry { struct list_head list; struct compressor *comp;};static struct compressor_entry *find_comp_entry(int proto){ struct compressor_entry *ce; struct list_head *list = &compressor_list; while ((list = list->next) != &compressor_list) { ce = list_entry(list, struct compressor_entry, list); if (ce->comp->compress_proto == proto) return ce; } return 0;}/* Register a compressor */intppp_register_compressor(struct compressor *cp){ struct compressor_entry *ce; int ret; spin_lock(&compressor_list_lock); ret = -EEXIST; if (find_comp_entry(cp->compress_proto) != 0) goto out; ret = -ENOMEM; ce = kmalloc(sizeof(struct compressor_entry), GFP_KERNEL); if (ce == 0) goto out; ret = 0; ce->comp = cp; list_add(&ce->list, &compressor_list); out: spin_unlock(&compressor_list_lock); return ret;}/* Unregister a compressor */voidppp_unregister_compressor(struct compressor *cp){ struct compressor_entry *ce; spin_lock(&compressor_list_lock); ce = find_comp_entry(cp->compress_proto); if (ce != 0 && ce->comp == cp) { list_del(&ce->list); kfree(ce); } spin_unlock(&compressor_list_lock);}/* Find a compressor. */static struct compressor *find_compressor(int type){ struct compressor_entry *ce; struct compressor *cp = 0; spin_lock(&compressor_list_lock); ce = find_comp_entry(type); if (ce != 0) cp = ce->comp; spin_unlock(&compressor_list_lock); return cp;}/* * Miscelleneous stuff. */static voidppp_get_stats(struct ppp *ppp, struct ppp_stats *st){ struct slcompress *vj = ppp->vj; memset(st, 0, sizeof(*st)); st->p.ppp_ipackets = ppp->stats.rx_packets; st->p.ppp_ierrors = ppp->stats.rx_errors; st->p.ppp_ibytes = ppp->stats.rx_bytes; st->p.ppp_opackets = ppp->stats.tx_packets; st->p.ppp_oerrors = ppp->stats.tx_errors; st->p.ppp_obytes = ppp->stats.tx_bytes; if (vj == 0) return; st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; st->vj.vjs_compressed = vj->sls_o_compressed; st->vj.vjs_searches = vj->sls_o_searches; st->vj.vjs_misses = vj->sls_o_misses; st->vj.vjs_errorin = vj->sls_i_error; st->vj.vjs_tossed = vj->sls_i_tossed; st->vj.vjs_uncompressedin = vj->sls_i_uncompressed; st->vj.vjs_compressedin = vj->sls_i_compressed;}/* * Stuff for handling the lists of ppp units and channels * and for initialization. *//* * Create a new ppp interface unit. Fails if it can't allocate memory * or if there is already a unit with the requested number. * unit == -1 means allocate a new number. */static struct ppp *ppp_create_interface(int unit, int *retp){ struct ppp *ppp; struct net_device *dev; struct list_head *list; int last_unit = -1; int ret = -EEXIST; int i; spin_lock(&all_ppp_lock); list = &all_ppp_units; while ((list = list->next) != &all_ppp_units) { ppp = list_entry(list, struct ppp, file.list); if ((unit < 0 && ppp->file.index > last_unit + 1) || (unit >= 0 && unit < ppp->file.index)) break; if (unit == ppp->file.index) goto out; /* unit already exists */ last_unit = ppp->file.index; } if (unit < 0) unit = last_unit + 1; /* Create a new ppp structure and link it before `list'. */ ret = -ENOMEM; ppp = kmalloc(sizeof(struct ppp), GFP_KERNEL); if (ppp == 0) goto out; memset(ppp, 0, sizeof(struct ppp)); dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); if (dev == 0) { kfree(ppp); goto out; } memset(dev, 0, sizeof(struct net_device)); ppp->file.index = unit; ppp->mru = PPP_MRU; init_ppp_file(&ppp->file, INTERFACE); for (i = 0; i < NUM_NP; ++i) ppp->npmode[i] = NPMODE_PASS; INIT_LIST_HEAD(&ppp->channels); spin_lock_init(&ppp->rlock); spin_lock_init(&ppp->wlock);#ifdef CONFIG_PPP_MULTILINK ppp->minseq = -1; skb_queue_head_init(&ppp->mrq);#endif /* CONFIG_PPP_MULTILINK */ ppp->dev = dev; dev->init = ppp_net_init; sprintf(dev->name, "ppp%d", unit); dev->priv = ppp; dev->features |= NETIF_F_DYNALLOC; rtnl_lock(); ret = register_netdevice(dev); rtnl_unlock(); if (ret != 0) { printk(KERN_ERR "PPP: couldn't register device (%d)\n", ret); kfree(dev); kfree(ppp); goto out; } list_add(&ppp->file.list, list->prev); out: spin_unlock(&all_ppp_lock); *retp = ret; if (ret != 0) ppp = 0; return ppp;}/* * Initialize a ppp_file structure. */static voidinit_ppp_file(struct ppp_file *pf, int kind){ pf->kind = kind; skb_queue_head_init(&pf->xq); skb_queue_head_init(&pf->rq); atomic_set(&pf->refcnt, 1); init_waitqueue_head(&pf->rwait);}/* * Free up all the resources used by a ppp interface unit. */static void ppp_destroy_interface(struct ppp *ppp){ struct net_device *dev; spin_lock(&all_ppp_lock); list_del(&ppp->file.list); /* Last fd open to this ppp unit is being closed or detached: mark the interface down, free the ppp unit */ ppp_lock(ppp); ppp_ccp_closed(ppp); if (ppp->vj) { slhc_free(ppp->vj); ppp->vj = 0; } skb_queue_purge(&ppp->file.xq); skb_queue_purge(&ppp->file.rq);#ifdef CONFIG_PPP_MULTILINK skb_queue_purge(&ppp->mrq);#endif /* CONFIG_PPP_MULTILINK */ dev = ppp->dev; ppp->dev = 0; ppp_unlock(ppp); if (dev) { rtnl_lock(); dev_close(dev); unregister_netdevice(dev); rtnl_unlock(); } /* * We can't acquire any new channels (since we have the * all_ppp_lock) so if n_channels is 0, we can free the * ppp structure. Otherwise we leave it around until the * last channel disconnects from it. */ if (ppp->n_channels == 0) kfree(ppp); spin_unlock(&all_ppp_lock);}/* * Locate an existing ppp unit. * The caller should have locked the all_ppp_lock. */static struct ppp *ppp_find_unit(int unit){ struct ppp *ppp; struct list_head *list; list = &all_ppp_units; while ((list = list->next) != &all_ppp_units) { ppp = list_entry(list, struct ppp, file.list); if (ppp->file.index == unit) return ppp; } return 0;}/* * Locate an existing ppp channel. * The caller should have locked the all_channels_lock. */static struct channel *ppp_find_channel(int unit){ struct channel *pch; struct list_head *list; list = &all_channels; while ((list = list->next) != &all_channels) { pch = list_entry(list, struct channel, file.list); if (pch->file.index == unit) return pch; } return 0;}/* * Connect a PPP channel to a PPP interface unit. */static intppp_connect_channel(struct channel *pch, int unit){ struct ppp *ppp; int ret = -ENXIO; int hdrlen; spin_lock(&all_ppp_lock); ppp = ppp_find_unit(unit); if (ppp == 0) goto out; write_lock_bh(&pch->upl); ret = -EINVAL; if (pch->ppp != 0) goto outw; ppp_lock(ppp); spin_lock_bh(&pch->downl); if (pch->chan == 0) /* need to check this?? */ goto outr; if (pch->file.hdrlen > ppp->file.hdrlen) ppp->file.hdrlen = pch->file.hdrlen; hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */ if (ppp->dev && hdrlen > ppp->dev->hard_header_len) ppp->dev->hard_header_len = hdrlen; list_add_tail(&pch->clist, &ppp->channels); ++ppp->n_channels; pch->ppp = ppp; ret = 0; outr: spin_unlock_bh(&pch->downl); ppp_unlock(ppp); outw: write_unlock_bh(&pch->upl); out: spin_unlock(&all_ppp_lock); return ret;}/* * Disconnect a channel from its ppp unit. */static intppp_disconnect_channel(struct channel *pch){ struct ppp *ppp; int err = -EINVAL; int dead; write_lock_bh(&pch->upl); ppp = pch->ppp; if (ppp != 0) { /* remove it from the ppp unit's list */ pch->ppp = NULL; ppp_lock(ppp); list_del(&pch->clist); --ppp->n_channels; dead = ppp->dev == 0 && ppp->n_channels == 0; ppp_unlock(ppp); if (dead) /* Last disconnect from a ppp unit that is already dead: free it. */ kfree(ppp); err = 0; } write_unlock_bh(&pch->upl); return err;}/* * Free up the resources used by a ppp channel. */static void ppp_destroy_channel(struct channel *pch){ skb_queue_purge(&pch->file.xq); skb_queue_purge(&pch->file.rq); kfree(pch);}void __exit ppp_cleanup(void){ /* should never happen */ if (!list_empty(&all_ppp_units) || !list_empty(&all_channels)) printk(KERN_ERR "PPP: removing module but units remain!\n"); if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0) printk(KERN_ERR "PPP: failed to unregister PPP device\n"); devfs_unregister(devfs_handle);}module_init(ppp_init);module_exit(ppp_cleanup);EXPORT_SYMBOL(ppp_register_channel);EXPORT_SYMBOL(ppp_unregister_channel);EXPORT_SYMBOL(ppp_channel_index);EXPORT_SYMBOL(ppp_unit_number);EXPORT_SYMBOL(ppp_input);EXPORT_SYMBOL(ppp_input_error);EXPORT_SYMBOL(ppp_output_wakeup);EXPORT_SYMBOL(ppp_register_compressor);EXPORT_SYMBOL(ppp_unregister_compressor);EXPORT_SYMBOL(ppp_channel_read);EXPORT_SYMBOL(ppp_channel_write);EXPORT_SYMBOL(ppp_channel_poll);EXPORT_SYMBOL(ppp_channel_ioctl);EXPORT_SYMBOL(all_ppp_units); /* for debugging */EXPORT_SYMBOL(all_channels); /* for debugging */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -