📄 ppp_generic.c
字号:
if (cmd == PPPIOCGNPMODE) { err = -EFAULT; npi.mode = ppp->npmode[i]; if (copy_to_user((void *) arg, &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_MULTILINK case PPPIOCSMRRU: if (get_user(val, (int *) arg)) 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; switch (cmd) { case PPPIOCNEWUNIT: /* Create a new ppp unit */ if (get_user(unit, (int *) arg)) break; ppp = ppp_create_interface(unit, &err); if (ppp == 0) break; file->private_data = &ppp->file; err = -EFAULT; if (put_user(ppp->file.index, (int *) arg)) break; err = 0; break; case PPPIOCATTACH: /* Attach to an existing ppp unit */ if (get_user(unit, (int *) arg)) break; spin_lock(&all_ppp_lock); ppp = ppp_find_unit(unit); if (ppp != 0) atomic_inc(&ppp->file.refcnt); spin_unlock(&all_ppp_lock); err = -ENXIO; if (ppp == 0) break; file->private_data = &ppp->file; err = 0; break; case PPPIOCATTCHAN: if (get_user(unit, (int *) arg)) break; spin_lock_bh(&all_channels_lock); chan = ppp_find_channel(unit); if (chan != 0) atomic_inc(&chan->file.refcnt); spin_unlock_bh(&all_channels_lock); err = -ENXIO; if (chan == 0) break; file->private_data = &chan->file; err = 0; break; default: err = -ENOTTY; } return err;}static 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 108static devfs_handle_t devfs_handle;/* Called at boot time if ppp is compiled into the kernel, or at module load time (from init_module) if compiled as a module. */int __init ppp_init(void){ int err; printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); devfs_handle = devfs_register(NULL, "ppp", DEVFS_FL_DEFAULT, PPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &ppp_device_fops, NULL); return 0;}/* * 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_headroom(skb) < PPP_HDRLEN) { struct sk_buff *ns; ns = alloc_skb(skb->len + dev->hard_header_len, GFP_ATOMIC); if (ns == 0) goto outf; skb_reserve(ns, dev->hard_header_len); memcpy(skb_put(ns, skb->len), skb->data, skb->len); kfree_skb(skb); skb = ns; } 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 *addr = (void *) 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 != 0) ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c); if (ppp->rc_state != 0) 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;}intppp_net_init(struct net_device *dev){ dev->hard_header_len = PPP_HDRLEN; dev->mtu = PPP_MTU; dev->hard_start_xmit = ppp_start_xmit; dev->get_stats = ppp_net_stats; dev->do_ioctl = ppp_net_ioctl; dev->addr_len = 0; dev->tx_queue_len = 3; dev->type = ARPHRD_PPP; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev_init_buffers(dev); return 0;}/* * 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); ppp_push(ppp); while (ppp->xmit_pending == 0 && (skb = skb_dequeue(&ppp->file.xq)) != 0) 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 == 0 && skb_peek(&ppp->file.xq) == 0 && ppp->dev != 0) netif_wake_queue(ppp->dev); ppp_xmit_unlock(ppp);}/* * 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; ++ppp->stats.tx_packets; ppp->stats.tx_bytes += skb->len - 2; switch (proto) { case PPP_IP: if (ppp->vj == 0 || (ppp->flags & SC_COMP_TCP) == 0) break; /* try to do VJ TCP header compression */ new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2, GFP_ATOMIC); if (new_skb == 0) { printk(KERN_ERR "PPP: no memory (VJ comp pkt)\n"); goto drop; } skb_reserve(new_skb, ppp->dev->hard_header_len - 2); cp = skb->data + 2; len = slhc_compress(ppp->vj, cp, skb->len - 2, new_skb->data + 2, &cp, !(ppp->flags & SC_NO_TCP_CCID)); if (cp == skb->data + 2) { /* didn't compress */ kfree_skb(new_skb); } else { if (cp[0] & SL_TYPE_COMPRESSED_TCP) { proto = PPP_VJC_COMP; cp[0] &= ~SL_TYPE_COMPRESSED_TCP; } else { proto = PPP_VJC_UNCOMP; cp[0] = skb->data[2]; } kfree_skb(skb); skb = new_skb; cp = skb_put(skb, len + 2); cp[0] = 0; cp[1] = proto; } break; case PPP_CCP: /* peek at outbound CCP frames */ ppp_ccp_peek(ppp, skb, 0); break; } /* try to do packet compression */ if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state != 0 && proto != PPP_LCP && proto != PPP_CCP) { new_skb = alloc_skb(ppp->dev->mtu + ppp->dev->hard_header_len, GFP_ATOMIC); if (new_skb == 0) { printk(KERN_ERR "PPP: no memory (comp pkt)\n"); goto drop; } 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, ppp->dev->mtu + PPP_HDRLEN); 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 { /* didn't compress, or CCP not up yet */ kfree_skb(new_skb); } } /* for data packets, record the time */ if (proto < 0x8000) ppp->last_xmit = jiffies; /* * If we are waiting for traffic (demand dialling), * queue it up for pppd to receive. */ if (ppp->flags & SC_LOOP_TRAFFIC) { if (ppp->file.rq.qlen > PPP_MAX_RQLEN) goto drop; skb_queue_tail(&ppp->file.rq, skb); wake_up_interruptible(&ppp->file.rwait); return; } ppp->xmit_pending = skb; ppp_push(ppp); return; drop: kfree_skb(skb); ++ppp->stats.tx_errors;}/* * Try to send the frame in xmit_pending. * The caller should have the xmit path locked. */static voidppp_push(struct ppp *ppp){ struct list_head *list; struct channel *pch; struct sk_buff *skb = ppp->xmit_pending; if (skb == 0) return; list = &ppp->channels; if (list_empty(list)) { /* nowhere to send the packet, just drop it */ ppp->xmit_pending = 0; kfree_skb(skb); return; } if ((ppp->flags & SC_MULTILINK) == 0) { /* not doing multilink: send it down the first channel */ list = list->next; pch = list_entry(list, struct channel, clist); spin_lock_bh(&pch->downl); if (pch->chan) { if (pch->chan->ops->start_xmit(pch->chan, skb)) ppp->xmit_pending = 0; } else { /* channel got unregistered */ kfree_skb(skb); ppp->xmit_pending = 0; } spin_unlock_bh(&pch->downl); return; }#ifdef CONFIG_PPP_MULTILINK /* Multilink: fragment the packet over as many links as can take the packet at the moment. */ if (!ppp_mp_explode(ppp, skb)) return;#endif /* CONFIG_PPP_MULTILINK */ ppp->xmit_pending = 0; kfree_skb(skb);}#ifdef CONFIG_PPP_MULTILINK/* * Divide a packet to be transmitted into fragments and * send them out the individual links. */static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb){ int nch, len, fragsize; int i, bits, hdrlen, mtu; int flen, fnb; unsigned char *p, *q; struct list_head *list; struct channel *pch; struct sk_buff *frag; struct ppp_channel *chan; nch = 0; hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; list = &ppp->channels; while ((list = list->next) != &ppp->channels) { pch = list_entry(list, struct channel, clist); nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0); /* * If a channel hasn't had a fragment yet, it has to get * one before we send any fragments on later channels. * If it can't take a fragment now, don't give any * to subsequent channels. */ if (!pch->had_frag && !pch->avail) { while ((list = list->next) != &ppp->channels) { pch = list_entry(list, struct channel, clist); pch->avail = 0; } break; } } if (nch == 0) return 0; /* can't take now, leave it in xmit_pending */ /* Do protocol field compression (XXX this should be optional) */ p = skb->data; len = skb->len; if (*p == 0) { ++p; --len; } /* decide on fragment size */ fragsize = len; if (nch > 1) { int maxch = ROUNDUP(len, MIN_FRAG_SIZE); if (nch > maxch) nch = maxch; fragsize = ROUNDUP(fragsize, nch); } /* skip to the channel after the one we last used and start at that one */ for (i = 0; i < ppp->nxchan; ++i) { list = list->next; if (list == &ppp->channels) { i = 0; break; } } /* create a fragment for each channel */ bits = B; do { list = list->next; if (list == &ppp->channels) { i = 0; continue; } pch = list_entry(list, struct channel, clist); ++i; if (!pch->avail) continue; /* check the channel's mtu and whether it is still attached. */ spin_lock_bh(&pch->downl); if (pch->chan == 0 || (mtu = pch->chan->mtu) < hdrlen) { /* can't use this channel */ spin_unlock_bh(&pch->downl); pch->avail = 0; if (--nch == 0) break; continue; } /* * We have to create multiple fragments for this channel * if fragsize is greater than the channel's mtu. */ if (fragsize > len) fragsize = len; for (flen = fragsize; flen > 0; flen -= fnb) { fnb = flen; if (fnb > mtu + 2 - hdrlen) fnb = mtu + 2 - hdrlen; if (fnb >= len) bits |= E; frag = alloc_skb(fnb + hdrlen, GFP_ATOMIC); if (frag == 0) goto noskb; q = skb_put(frag, fnb + hdrlen); /* make the MP header */ q[0] = PPP_MP >> 8; q[1] = PPP_MP; if (ppp->flags & SC_MP_XSHORTSEQ) { q[2] = bits + ((ppp->nxseq >> 8) & 0xf); q[3] = ppp->nxseq; } else { q[2] = bits; q[3] = ppp->nxseq >> 16; q[4] = ppp->nxseq >> 8; q[5] = ppp->nxseq; } /* copy the data in */ memcpy(q + hdrlen, p, fnb); /* try to send it down the channel */ chan = pch->chan; if (!chan->ops->start_xmit(chan, frag)) skb_queue_tail(&pch->file.xq, frag); pch->had_frag = 1; p += fnb; len -= fnb; ++ppp->nxseq; bits = 0; } spin_unlock_bh(&pch->downl); } while (len > 0); ppp->nxchan = i; return 1; noskb: spin_unlock_bh(&pch->downl); if (ppp->debug & 1) printk(KERN_ERR "PPP: no memory (fragment)\n"); ++ppp->stats.tx_errors; ++ppp->nxseq; return 1; /* abandon the frame */}#endif /* CONFIG_PPP_MULTILINK *//* * Try to send data out on a channel. */static voidppp_channel_push(struct channel *pch){ struct sk_buff *skb; struct ppp *ppp; spin_lock_bh(&pch->downl); if (pch->chan != 0) { while (skb_queue_len(&pch->file.xq) > 0) { skb = skb_dequeue(&pch->file.xq); if (!pch->chan->ops->start_xmit(pch->chan, skb)) { /* put the packet back and try again later */ skb_queue_head(&pch->file.xq, skb); break; } } } else { /* channel got deregistered */ skb_queue_purge(&pch->file.xq); } spin_unlock_bh(&pch->downl); /* see if there is anything from the attached unit to be sent */ if (skb_queue_len(&pch->file.xq) == 0) { read_lock_bh(&pch->upl); ppp = pch->ppp; if (ppp != 0) ppp_xmit_process(ppp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -