⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ppp_generic.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
		read_unlock_bh(&pch->upl);	}}/* * Receive-side routines. *//* misuse a few fields of the skb for MP reconstruction */#define sequence	priority#define BEbits		cb[0]static inline voidppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch){	ppp_recv_lock(ppp);	/* ppp->dev == 0 means interface is closing down */	if (ppp->dev != 0)		ppp_receive_frame(ppp, skb, pch);	else		kfree_skb(skb);	ppp_recv_unlock(ppp);}voidppp_input(struct ppp_channel *chan, struct sk_buff *skb){	struct channel *pch = chan->ppp;	int proto;	if (pch == 0 || skb->len == 0) {		kfree_skb(skb);		return;	}	proto = PPP_PROTO(skb);	read_lock_bh(&pch->upl);	if (pch->ppp == 0 || proto >= 0xc000 || proto == PPP_CCPFRAG) {		/* put it on the channel queue */		skb_queue_tail(&pch->file.rq, skb);		/* drop old frames if queue too long */		while (pch->file.rq.qlen > PPP_MAX_RQLEN		       && (skb = skb_dequeue(&pch->file.rq)) != 0)			kfree_skb(skb);		wake_up_interruptible(&pch->file.rwait);	} else {		ppp_do_recv(pch->ppp, skb, pch);	}	read_unlock_bh(&pch->upl);}/* Put a 0-length skb in the receive queue as an error indication */voidppp_input_error(struct ppp_channel *chan, int code){	struct channel *pch = chan->ppp;	struct sk_buff *skb;	if (pch == 0)		return;	read_lock_bh(&pch->upl);	if (pch->ppp != 0) {		skb = alloc_skb(0, GFP_ATOMIC);		if (skb != 0) {			skb->len = 0;		/* probably unnecessary */			skb->cb[0] = code;			ppp_do_recv(pch->ppp, skb, pch);		}	}	read_unlock_bh(&pch->upl);}/* * We come in here to process a received frame. * The receive side of the ppp unit is locked. */static voidppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch){	if (skb->len >= 2) {#ifdef CONFIG_PPP_MULTILINK		/* XXX do channel-level decompression here */		if (PPP_PROTO(skb) == PPP_MP)			ppp_receive_mp_frame(ppp, skb, pch);		else#endif /* CONFIG_PPP_MULTILINK */			ppp_receive_nonmp_frame(ppp, skb);		return;	}	if (skb->len > 0)		/* note: a 0-length skb is used as an error indication */		++ppp->stats.rx_length_errors;	kfree_skb(skb);	ppp_receive_error(ppp);}static voidppp_receive_error(struct ppp *ppp){	++ppp->stats.rx_errors;	if (ppp->vj != 0)		slhc_toss(ppp->vj);}static voidppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb){	struct sk_buff *ns;	int proto, len, npi;	/*	 * Decompress the frame, if compressed.	 * Note that some decompressors need to see uncompressed frames	 * that come in as well as compressed frames.	 */	if (ppp->rc_state != 0 && (ppp->rstate & SC_DECOMP_RUN)	    && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)		skb = ppp_decompress_frame(ppp, skb);	proto = PPP_PROTO(skb);	switch (proto) {	case PPP_VJC_COMP:		/* decompress VJ compressed packets */		if (ppp->vj == 0 || (ppp->flags & SC_REJ_COMP_TCP))			goto err;		if (skb_tailroom(skb) < 124) {			/* copy to a new sk_buff with more tailroom */			ns = dev_alloc_skb(skb->len + 128);			if (ns == 0) {				printk(KERN_ERR"PPP: no memory (VJ decomp)\n");				goto err;			}			skb_reserve(ns, 2);			memcpy(skb_put(ns, skb->len), skb->data, skb->len);			kfree_skb(skb);			skb = ns;		}		len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);		if (len <= 0) {			printk(KERN_DEBUG "PPP: VJ decompression error\n");			goto err;		}		len += 2;		if (len > skb->len)			skb_put(skb, len - skb->len);		else if (len < skb->len)			skb_trim(skb, len);		proto = PPP_IP;		break;	case PPP_VJC_UNCOMP:		if (ppp->vj == 0 || (ppp->flags & SC_REJ_COMP_TCP))			goto err;		if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) {			printk(KERN_ERR "PPP: VJ uncompressed error\n");			goto err;		}		proto = PPP_IP;		break;	case PPP_CCP:		ppp_ccp_peek(ppp, skb, 1);		break;	}	++ppp->stats.rx_packets;	ppp->stats.rx_bytes += skb->len - 2;	npi = proto_to_npindex(proto);	if (npi < 0) {		/* control or unknown frame - pass it to pppd */		skb_queue_tail(&ppp->file.rq, skb);		/* limit queue length by dropping old frames */		while (ppp->file.rq.qlen > PPP_MAX_RQLEN		       && (skb = skb_dequeue(&ppp->file.rq)) != 0)			kfree_skb(skb);		/* wake up any process polling or blocking on read */		wake_up_interruptible(&ppp->file.rwait);	} else {		/* network protocol frame - give it to the kernel */		ppp->last_recv = jiffies;		if ((ppp->dev->flags & IFF_UP) == 0		    || ppp->npmode[npi] != NPMODE_PASS) {			kfree_skb(skb);		} else {			skb_pull(skb, 2);	/* chop off protocol */			skb->dev = ppp->dev;			skb->protocol = htons(npindex_to_ethertype[npi]);			skb->mac.raw = skb->data;			netif_rx(skb);		}	}	return; err:	kfree_skb(skb);	ppp_receive_error(ppp);}static struct sk_buff *ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb){	int proto = PPP_PROTO(skb);	struct sk_buff *ns;	int len;	if (proto == PPP_COMP) {		ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN);		if (ns == 0) {			printk(KERN_ERR "ppp_decompress_frame: no memory\n");			goto err;		}		/* the decompressor still expects the A/C bytes in the hdr */		len = ppp->rcomp->decompress(ppp->rc_state, skb->data - 2,				skb->len + 2, ns->data, ppp->mru + PPP_HDRLEN);		if (len < 0) {			/* Pass the compressed frame to pppd as an			   error indication. */			if (len == DECOMP_FATALERROR)				ppp->rstate |= SC_DC_FERROR;			goto err;		}		kfree_skb(skb);		skb = ns;		skb_put(skb, len);		skb_pull(skb, 2);	/* pull off the A/C bytes */	} else {		/* Uncompressed frame - pass to decompressor so it		   can update its dictionary if necessary. */		if (ppp->rcomp->incomp)			ppp->rcomp->incomp(ppp->rc_state, skb->data - 2,					   skb->len + 2);	}	return skb; err:	ppp->rstate |= SC_DC_ERROR;	ppp_receive_error(ppp);	return skb;}#ifdef CONFIG_PPP_MULTILINK/* * Receive a multilink frame. * We put it on the reconstruction queue and then pull off * as many completed frames as we can. */static voidppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch){	u32 mask, seq;	struct list_head *l;	int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;	if (skb->len < mphdrlen + 1 || ppp->mrru == 0)		goto err;		/* no good, throw it away */	/* Decode sequence number and begin/end bits */	if (ppp->flags & SC_MP_SHORTSEQ) {		seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];		mask = 0xfff;	} else {		seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];		mask = 0xffffff;	}	skb->BEbits = skb->data[2];	skb_pull(skb, mphdrlen);	/* pull off PPP and MP headers */	/*	 * Do protocol ID decompression on the first fragment of each packet.	 */	if ((skb->BEbits & B) && (skb->data[0] & 1))		*skb_push(skb, 1) = 0;	/*	 * Expand sequence number to 32 bits, making it as close	 * as possible to ppp->minseq.	 */	seq |= ppp->minseq & ~mask;	if ((int)(ppp->minseq - seq) > (int)(mask >> 1))		seq += mask + 1;	else if ((int)(seq - ppp->minseq) > (int)(mask >> 1))		seq -= mask + 1;	/* should never happen */	skb->sequence = seq;	pch->lastseq = seq;	/*	 * If this packet comes before the next one we were expecting,	 * drop it.	 */	if (seq_before(seq, ppp->nextseq)) {		kfree_skb(skb);		++ppp->stats.rx_dropped;		ppp_receive_error(ppp);		return;	}	/*	 * Reevaluate minseq, the minimum over all channels of the	 * last sequence number received on each channel.  Because of	 * the increasing sequence number rule, we know that any fragment	 * before `minseq' which hasn't arrived is never going to arrive.	 * The list of channels can't change because we have the receive	 * side of the ppp unit locked.	 */	for (l = ppp->channels.next; l != &ppp->channels; l = l->next) {		struct channel *ch = list_entry(l, struct channel, clist);		if (seq_before(ch->lastseq, seq))			seq = ch->lastseq;	}	if (seq_before(ppp->minseq, seq))		ppp->minseq = seq;	/* Put the fragment on the reconstruction queue */	ppp_mp_insert(ppp, skb);	/* If the queue is getting long, don't wait any longer for packets	   before the start of the queue. */	if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN	    && seq_before(ppp->minseq, ppp->mrq.next->sequence))		ppp->minseq = ppp->mrq.next->sequence;	/* Pull completed packets off the queue and receive them. */	while ((skb = ppp_mp_reconstruct(ppp)) != 0)		ppp_receive_nonmp_frame(ppp, skb);	return; err:	kfree_skb(skb);	ppp_receive_error(ppp);}/* * Insert a fragment on the MP reconstruction queue. * The queue is ordered by increasing sequence number. */static voidppp_mp_insert(struct ppp *ppp, struct sk_buff *skb){	struct sk_buff *p;	struct sk_buff_head *list = &ppp->mrq;	u32 seq = skb->sequence;	/* N.B. we don't need to lock the list lock because we have the	   ppp unit receive-side lock. */	for (p = list->next; p != (struct sk_buff *)list; p = p->next)		if (seq_before(seq, p->sequence))			break;	__skb_insert(skb, p->prev, p, list);}/* * Reconstruct a packet from the MP fragment queue. * We go through increasing sequence numbers until we find a * complete packet, or we get to the sequence number for a fragment * which hasn't arrived but might still do so. */struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp){	u32 seq = ppp->nextseq;	u32 minseq = ppp->minseq;	struct sk_buff_head *list = &ppp->mrq;	struct sk_buff *p, *next;	struct sk_buff *head, *tail;	struct sk_buff *skb = NULL;	int lost = 0, len = 0;	if (ppp->mrru == 0)	/* do nothing until mrru is set */		return NULL;	head = list->next;	tail = NULL;	for (p = head; p != (struct sk_buff *) list; p = next) {		next = p->next;		if (seq_before(p->sequence, seq)) {			/* this can't happen, anyway ignore the skb */			printk(KERN_ERR "ppp_mp_reconstruct bad seq %u < %u\n",			       p->sequence, seq);			head = next;			continue;		}		if (p->sequence != seq) {			/* Fragment `seq' is missing.  If it is after			   minseq, it might arrive later, so stop here. */			if (seq_after(seq, minseq))				break;			/* Fragment `seq' is lost, keep going. */			lost = 1;			seq = seq_before(minseq, p->sequence)?				minseq + 1: p->sequence;			next = p;			continue;		}		/*		 * At this point we know that all the fragments from		 * ppp->nextseq to seq are either present or lost.		 * Also, there are no complete packets in the queue		 * that have no missing fragments and end before this		 * fragment.		 */		/* B bit set indicates this fragment starts a packet */		if (p->BEbits & B) {			head = p;			lost = 0;			len = 0;		}		len += p->len;		/* Got a complete packet yet? */		if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {			if (len > ppp->mrru + 2) {				++ppp->stats.rx_length_errors;				printk(KERN_DEBUG "PPP: reconstructed packet"				       " is too long (%d)\n", len);			} else if (p == head) {				/* fragment is complete packet - reuse skb */				tail = p;				skb = skb_get(p);				break;			} else if ((skb = dev_alloc_skb(len)) == NULL) {				++ppp->stats.rx_missed_errors;				printk(KERN_DEBUG "PPP: no memory for "				       "reconstructed packet");			} else {				tail = p;				break;			}			ppp->nextseq = seq + 1;		}		/*		 * If this is the ending fragment of a packet,		 * and we haven't found a complete valid packet yet,		 * we can discard up to and including this fragment.		 */		if (p->BEbits & E)			head = next;		++seq;	}	/* If we have a complete packet, copy it all into one skb. */	if (tail != NULL) {		/* If we have discarded any fragments,		   signal a receive error. */		if (head->sequence != ppp->nextseq) {			if (ppp->debug & 1)				printk(KERN_DEBUG "  missed pkts %u..%u\n",				       ppp->nextseq, head->sequence-1);			++ppp->stats.rx_dropped;			ppp_receive_error(ppp);		}		if (head != tail)			/* copy to a single skb */			for (p = head; p != tail->next; p = p->next)				memcpy(skb_put(skb, p->len), p->data, p->len);		ppp->nextseq = tail->sequence + 1;		head = tail->next;	}	/* Discard all the skbuffs that we have copied the data out of	   or that we can't use. */	while ((p = list->next) != head) {		__skb_unlink(p, list);		kfree_skb(p);	}	return skb;}#endif /* CONFIG_PPP_MULTILINK *//* * Channel interface. *//* * Create a new, unattached ppp channel. */intppp_register_channel(struct ppp_channel *chan){	struct channel *pch;	pch = kmalloc(sizeof(struct channel), GFP_ATOMIC);	if (pch == 0)		return -ENOMEM;	memset(pch, 0, sizeof(struct channel));	pch->ppp = NULL;	pch->chan = chan;	chan->ppp = pch;	init_ppp_file(&pch->file, CHANNEL);	pch->file.hdrlen = chan->hdrlen;#ifdef CONFIG_PPP_MULTILINK	pch->lastseq = -1;#endif /* CONFIG_PPP_MULTILINK */	spin_lock_init(&pch->downl);	pch->upl = RW_LOCK_UNLOCKED;	spin_lock_bh(&all_channels_lock);	pch->file.index = ++last_channel_index;	list_add(&pch->file.list, &all_channels);	spin_unlock_bh(&all_channels_lock);	MOD_INC_USE_COUNT;	return 0;}/* * Return the index of a channel. */int ppp_channel_index(struct ppp_channel *chan){	struct channel *pch = chan->ppp;	return pch->file.index;}/* * Return the PPP unit number to which a channel is connected. */int ppp_unit_number(struct ppp_channel *chan){	struct channel *pch = chan->ppp;	int unit = -1;	if (pch != 0) {		read_lock_bh(&pch->upl);		if (pch->ppp != 0)			unit = pch->ppp->file.index;		read_unlock_bh(&pch->upl);	}	return unit;}/* * Disconnect a channel from the generic layer. * This can be called from mainline or BH/softirq level. */voidppp_unregister_channel(struct ppp_channel *chan){	struct channel *pch = chan->ppp;	if (pch == 0)		return;		/* should never happen */	chan->ppp = 0;	/*	 * This ensures that we have returned from any calls into the	 * the channel's start_xmit or ioctl routine before we proceed.	 */	spin_lock_bh(&pch->downl);	pch->chan = 0;	spin_unlock_bh(&pch->downl);	ppp_disconnect_channel(pch);	wake_up_interruptible(&pch->file.rwait);	spin_lock_bh(&all_channels_lock);	list_del(&pch->file.list);	spin_unlock_bh(&all_channels_lock);	if (atomic_dec_and_test(&pch->file.refcnt))		ppp_destroy_channel(pch);	MOD_DEC_USE_COUNT;}/* * Callback from a channel when it can accept more to transmit. * This should be called at BH/softirq level, not interrupt level. */voidppp_output_wakeup(struct ppp_channel *chan){	struct channel *pch = chan->ppp;	if (pch == 0)		return;	ppp_channel_push(pch);}/* * This is basically temporary compatibility stuff. */ssize_tppp_channel_read(struct ppp_channel *chan, struct file *file,		 char *buf, size_t count){	struct channel *pch = chan->ppp;	if (pch == 0)		return -ENXIO;	return ppp_file_read(&pch->file, file, buf, count);}ssize_tppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count){	struct channel *pch = chan->ppp;	if (pch == 0)		return -ENXIO;	return ppp_file_write(&pch->file, buf, count);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -