ppp_generic.c
来自「linux 内核源代码」· C语言 代码 · 共 2,709 行 · 第 1/5 页
C
2,709 行
*p = code; return uprog.len;}#endif /* CONFIG_PPP_FILTER */static int ppp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct ppp_file *pf = file->private_data; struct ppp *ppp; int err = -EFAULT, val, val2, i; struct ppp_idle idle; struct npioctl npi; int unit, cflags; struct slcompress *vj; void __user *argp = (void __user *)arg; int __user *p = argp; if (!pf) return ppp_unattached_ioctl(pf, file, cmd, arg); if (cmd == PPPIOCDETACH) { /* * We have to be careful here... if the file descriptor * has been dup'd, we could have another process in the * middle of a poll using the same file *, so we had * better not free the interface data structures - * instead we fail the ioctl. Even in this case, we * shut down the interface if we are the owner of it. * Actually, we should get rid of PPPIOCDETACH, userland * (i.e. pppd) could achieve the same effect by closing * this fd and reopening /dev/ppp. */ err = -EINVAL; if (pf->kind == INTERFACE) { ppp = PF_TO_PPP(pf); if (file == ppp->owner) ppp_shutdown_interface(ppp); } if (atomic_read(&file->f_count) <= 2) { ppp_release(inode, file); err = 0; } else printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n", atomic_read(&file->f_count)); return err; } if (pf->kind == CHANNEL) { struct channel *pch = PF_TO_CHANNEL(pf); struct ppp_channel *chan; switch (cmd) { case PPPIOCCONNECT: if (get_user(unit, p)) break; err = ppp_connect_channel(pch, unit); break; case PPPIOCDISCONN: err = ppp_disconnect_channel(pch); break; default: down_read(&pch->chan_sem); chan = pch->chan; err = -ENOTTY; if (chan && chan->ops->ioctl) err = chan->ops->ioctl(chan, cmd, arg); up_read(&pch->chan_sem); } return err; } if (pf->kind != INTERFACE) { /* can't happen */ printk(KERN_ERR "PPP: not interface or channel??\n"); return -EINVAL; } ppp = PF_TO_PPP(pf); switch (cmd) { case PPPIOCSMRU: if (get_user(val, p)) break; ppp->mru = val; err = 0; break; case PPPIOCSFLAGS: if (get_user(val, p)) break; ppp_lock(ppp); cflags = ppp->flags & ~val; ppp->flags = val & SC_FLAG_BITS; ppp_unlock(ppp); if (cflags & SC_CCP_OPEN) ppp_ccp_closed(ppp); err = 0; break; case PPPIOCGFLAGS: val = ppp->flags | ppp->xstate | ppp->rstate; if (put_user(val, p)) break; err = 0; break; case PPPIOCSCOMPRESS: err = ppp_set_compress(ppp, arg); break; case PPPIOCGUNIT: if (put_user(ppp->file.index, p)) break; err = 0; break; case PPPIOCSDEBUG: if (get_user(val, p)) break; ppp->debug = val; err = 0; break; case PPPIOCGDEBUG: if (put_user(ppp->debug, p)) break; err = 0; break; case PPPIOCGIDLE: idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ; idle.recv_idle = (jiffies - ppp->last_recv) / HZ; if (copy_to_user(argp, &idle, sizeof(idle))) break; err = 0; break; case PPPIOCSMAXCID: if (get_user(val, p)) break; val2 = 15; if ((val >> 16) != 0) { val2 = val >> 16; val &= 0xffff; } vj = slhc_init(val2+1, val+1); if (!vj) { printk(KERN_ERR "PPP: no memory (VJ compressor)\n"); err = -ENOMEM; break; } ppp_lock(ppp); if (ppp->vj) slhc_free(ppp->vj); ppp->vj = vj; ppp_unlock(ppp); err = 0; break; case PPPIOCGNPMODE: case PPPIOCSNPMODE: if (copy_from_user(&npi, argp, sizeof(npi))) break; err = proto_to_npindex(npi.protocol); if (err < 0) break; i = err; if (cmd == PPPIOCGNPMODE) { err = -EFAULT; npi.mode = ppp->npmode[i]; if (copy_to_user(argp, &npi, sizeof(npi))) break; } else { ppp->npmode[i] = npi.mode; /* we may be able to transmit more packets now (??) */ netif_wake_queue(ppp->dev); } err = 0; break;#ifdef CONFIG_PPP_FILTER case PPPIOCSPASS: { struct sock_filter *code; err = get_filter(argp, &code); if (err >= 0) { ppp_lock(ppp); kfree(ppp->pass_filter); ppp->pass_filter = code; ppp->pass_len = err; ppp_unlock(ppp); err = 0; } break; } case PPPIOCSACTIVE: { struct sock_filter *code; err = get_filter(argp, &code); if (err >= 0) { ppp_lock(ppp); kfree(ppp->active_filter); ppp->active_filter = code; ppp->active_len = err; ppp_unlock(ppp); err = 0; } break; }#endif /* CONFIG_PPP_FILTER */#ifdef CONFIG_PPP_MULTILINK case PPPIOCSMRRU: if (get_user(val, p)) break; ppp_recv_lock(ppp); ppp->mrru = val; ppp_recv_unlock(ppp); err = 0; break;#endif /* CONFIG_PPP_MULTILINK */ default: err = -ENOTTY; } return err;}static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, unsigned int cmd, unsigned long arg){ int unit, err = -EFAULT; struct ppp *ppp; struct channel *chan; int __user *p = (int __user *)arg; switch (cmd) { case PPPIOCNEWUNIT: /* Create a new ppp unit */ if (get_user(unit, p)) break; ppp = ppp_create_interface(unit, &err); if (!ppp) break; file->private_data = &ppp->file; ppp->owner = file; err = -EFAULT; if (put_user(ppp->file.index, p)) break; err = 0; break; case PPPIOCATTACH: /* Attach to an existing ppp unit */ if (get_user(unit, p)) break; mutex_lock(&all_ppp_mutex); err = -ENXIO; ppp = ppp_find_unit(unit); if (ppp) { atomic_inc(&ppp->file.refcnt); file->private_data = &ppp->file; err = 0; } mutex_unlock(&all_ppp_mutex); break; case PPPIOCATTCHAN: if (get_user(unit, p)) break; spin_lock_bh(&all_channels_lock); err = -ENXIO; chan = ppp_find_channel(unit); if (chan) { atomic_inc(&chan->file.refcnt); file->private_data = &chan->file; err = 0; } spin_unlock_bh(&all_channels_lock); break; default: err = -ENOTTY; } return err;}static const struct file_operations ppp_device_fops = { .owner = THIS_MODULE, .read = ppp_read, .write = ppp_write, .poll = ppp_poll, .ioctl = ppp_ioctl, .open = ppp_open, .release = ppp_release};#define PPP_MAJOR 108/* Called at boot time if ppp is compiled into the kernel, or at module load time (from init_module) if compiled as a module. */static int __init ppp_init(void){ int err; printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (!err) { ppp_class = class_create(THIS_MODULE, "ppp"); if (IS_ERR(ppp_class)) { err = PTR_ERR(ppp_class); goto out_chrdev; } device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), "ppp"); }out: if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); return err;out_chrdev: unregister_chrdev(PPP_MAJOR, "ppp"); goto out;}/* * Network interface unit routines. */static intppp_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct ppp *ppp = (struct ppp *) dev->priv; int npi, proto; unsigned char *pp; npi = ethertype_to_npindex(ntohs(skb->protocol)); if (npi < 0) goto outf; /* Drop, accept or reject the packet */ switch (ppp->npmode[npi]) { case NPMODE_PASS: break; case NPMODE_QUEUE: /* it would be nice to have a way to tell the network system to queue this one up for later. */ goto outf; case NPMODE_DROP: case NPMODE_ERROR: goto outf; } /* Put the 2-byte PPP protocol number on the front, making sure there is room for the address and control fields. */ if (skb_cow_head(skb, PPP_HDRLEN)) goto outf; pp = skb_push(skb, 2); proto = npindex_to_proto[npi]; pp[0] = proto >> 8; pp[1] = proto; netif_stop_queue(dev); skb_queue_tail(&ppp->file.xq, skb); ppp_xmit_process(ppp); return 0; outf: kfree_skb(skb); ++ppp->stats.tx_dropped; return 0;}static struct net_device_stats *ppp_net_stats(struct net_device *dev){ struct ppp *ppp = (struct ppp *) dev->priv; return &ppp->stats;}static intppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct ppp *ppp = dev->priv; int err = -EFAULT; void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data; struct ppp_stats stats; struct ppp_comp_stats cstats; char *vers; switch (cmd) { case SIOCGPPPSTATS: ppp_get_stats(ppp, &stats); if (copy_to_user(addr, &stats, sizeof(stats))) break; err = 0; break; case SIOCGPPPCSTATS: memset(&cstats, 0, sizeof(cstats)); if (ppp->xc_state) ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c); if (ppp->rc_state) ppp->rcomp->decomp_stat(ppp->rc_state, &cstats.d); if (copy_to_user(addr, &cstats, sizeof(cstats))) break; err = 0; break; case SIOCGPPPVER: vers = PPP_VERSION; if (copy_to_user(addr, vers, strlen(vers) + 1)) break; err = 0; break; default: err = -EINVAL; } return err;}static void ppp_setup(struct net_device *dev){ dev->hard_header_len = PPP_HDRLEN; dev->mtu = PPP_MTU; dev->addr_len = 0; dev->tx_queue_len = 3; dev->type = ARPHRD_PPP; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;}/* * Transmit-side routines. *//* * Called to do any work queued up on the transmit side * that can now be done. */static voidppp_xmit_process(struct ppp *ppp){ struct sk_buff *skb; ppp_xmit_lock(ppp); if (ppp->dev) { ppp_push(ppp); while (!ppp->xmit_pending && (skb = skb_dequeue(&ppp->file.xq))) ppp_send_frame(ppp, skb); /* If there's no work left to do, tell the core net code that we can accept some more. */ if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq)) netif_wake_queue(ppp->dev); } ppp_xmit_unlock(ppp);}static inline struct sk_buff *pad_compress_skb(struct ppp *ppp, struct sk_buff *skb){ struct sk_buff *new_skb; int len; int new_skb_size = ppp->dev->mtu + ppp->xcomp->comp_extra + ppp->dev->hard_header_len; int compressor_skb_size = ppp->dev->mtu + ppp->xcomp->comp_extra + PPP_HDRLEN; new_skb = alloc_skb(new_skb_size, GFP_ATOMIC); if (!new_skb) { if (net_ratelimit()) printk(KERN_ERR "PPP: no memory (comp pkt)\n"); return NULL; } if (ppp->dev->hard_header_len > PPP_HDRLEN) skb_reserve(new_skb, ppp->dev->hard_header_len - PPP_HDRLEN); /* compressor still expects A/C bytes in hdr */ len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2, new_skb->data, skb->len + 2, compressor_skb_size); if (len > 0 && (ppp->flags & SC_CCP_UP)) { kfree_skb(skb); skb = new_skb; skb_put(skb, len); skb_pull(skb, 2); /* pull off A/C bytes */ } else if (len == 0) { /* didn't compress, or CCP not up yet */ kfree_skb(new_skb); new_skb = skb; } else { /* * (len < 0) * MPPE requires that we do not send unencrypted * frames. The compressor will return -1 if we * should drop the frame. We cannot simply test * the compress_proto because MPPE and MPPC share * the same number. */ if (net_ratelimit()) printk(KERN_ERR "ppp: compressor dropped pkt\n"); kfree_skb(skb); kfree_skb(new_skb); new_skb = NULL; } return new_skb;}/* * Compress and send a frame. * The caller should have locked the xmit path, * and xmit_pending should be 0. */static voidppp_send_frame(struct ppp *ppp, struct sk_buff *skb){ int proto = PPP_PROTO(skb); struct sk_buff *new_skb; int len; unsigned char *cp; if (proto < 0x8000) {#ifdef CONFIG_PPP_FILTER /* check if we should pass this packet */ /* the filter instructions are constructed assuming a four-byte PPP header on each packet */ *skb_push(skb, 2) = 1; if (ppp->pass_filter && sk_run_filter(skb, ppp->pass_filter, ppp->pass_len) == 0) { if (ppp->debug & 1) printk(KERN_DEBUG "PPP: outbound frame not passed\n"); kfree_skb(skb); return;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?