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

📄 isdn_ppp.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
		break;	default:		printk(KERN_ERR "isdn_ppp_autodial_filter: unsupported protocol 0x%x.\n",		       skb->protocol);		return 1;	}	/* the filter instructions are constructed assuming	 * a four-byte PPP header on each packet. we have to	 * temporarily remove part of the fake header stuck on	 * earlier.	 */	*skb_pull(skb, IPPP_MAX_HEADER - 4) = 1; /* indicate outbound */	{		u_int16_t *p = (u_int16_t *) skb->data;		p++;		*p   = htons(proto);	}		drop |= is->pass_filter	        && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0;	drop |= is->active_filter	        && sk_run_filter(skb, is->active_filter, is->active_len) == 0;		skb_push(skb, IPPP_MAX_HEADER - 4);	return drop;}#endif#ifdef CONFIG_ISDN_MPP/* this is _not_ rfc1990 header, but something we convert both short and long * headers to for convinience's sake: * 	byte 0 is flags as in rfc1990 *	bytes 1...4 is 24-bit seqence number converted to host byte order  */#define MP_HEADER_LEN	5#define MP_LONGSEQ_MASK		0x00ffffff#define MP_SHORTSEQ_MASK	0x00000fff#define MP_LONGSEQ_MAX		MP_LONGSEQ_MASK#define MP_SHORTSEQ_MAX		MP_SHORTSEQ_MASK#define MP_LONGSEQ_MAXBIT	((MP_LONGSEQ_MASK+1)>>1)#define MP_SHORTSEQ_MAXBIT	((MP_SHORTSEQ_MASK+1)>>1)/* sequence-wrap safe comparisions (for long sequence)*/ #define MP_LT(a,b)	((a-b)&MP_LONGSEQ_MAXBIT)#define MP_LE(a,b) 	!((b-a)&MP_LONGSEQ_MAXBIT)#define MP_GT(a,b) 	((b-a)&MP_LONGSEQ_MAXBIT)#define MP_GE(a,b)	!((a-b)&MP_LONGSEQ_MAXBIT)#define MP_SEQ(f)	((*(u32*)(f->data+1)))#define MP_FLAGS(f)	(f->data[0])static int isdn_ppp_mp_bundle_array_init(void){	int i;	int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);	if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, 							GFP_KERNEL)) == NULL )		return -ENOMEM;	memset(isdn_ppp_bundle_arr, 0, sz);	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )		spin_lock_init(&isdn_ppp_bundle_arr[i].lock);	return 0;}static ippp_bundle * isdn_ppp_mp_bundle_alloc(void){	int i;	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )		if (isdn_ppp_bundle_arr[i].ref_ct <= 0)			return (isdn_ppp_bundle_arr + i);	return NULL;}static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ){	struct ippp_struct * is;	if (lp->ppp_slot < 0) {		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",			__FUNCTION__, lp->ppp_slot);		return(-EINVAL);	}	is = ippp_table[lp->ppp_slot];	if (add_to) {		if( lp->netdev->pb )			lp->netdev->pb->ref_ct--;		lp->netdev->pb = add_to;	} else {		/* first link in a bundle */		is->mp_seqno = 0;		if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)			return -ENOMEM;		lp->next = lp->last = lp;	/* nobody else in a queue */		lp->netdev->pb->frags = NULL;		lp->netdev->pb->frames = 0;		lp->netdev->pb->seq = UINT_MAX;	}	lp->netdev->pb->ref_ct++;		is->last_link_seqno = 0;	return 0;}static u32 isdn_ppp_mp_get_seq( int short_seq, 					struct sk_buff * skb, u32 last_seq );static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,			struct sk_buff * from, struct sk_buff * to );static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,				struct sk_buff * from, struct sk_buff * to );static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 							struct sk_buff *skb){	struct ippp_struct *is;	isdn_net_local * lpq;	ippp_bundle * mp;	isdn_mppp_stats * stats;	struct sk_buff * newfrag, * frag, * start, *nextf;	u32 newseq, minseq, thisseq;	unsigned long flags;	int slot;	spin_lock_irqsave(&net_dev->pb->lock, flags);    	mp = net_dev->pb;        stats = &mp->stats;	slot = lp->ppp_slot;	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {		printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",			__FUNCTION__, lp->ppp_slot);		stats->frame_drops++;		dev_kfree_skb(skb);		spin_unlock_irqrestore(&mp->lock, flags);		return;	}	is = ippp_table[slot];    	if( ++mp->frames > stats->max_queue_len )		stats->max_queue_len = mp->frames;		if (is->debug & 0x8)		isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);	newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, 						skb, is->last_link_seqno);	/* if this packet seq # is less than last already processed one,	 * toss it right away, but check for sequence start case first 	 */	if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) {		mp->seq = newseq;	/* the first packet: required for					 * rfc1990 non-compliant clients --					 * prevents constant packet toss */	} else if( MP_LT(newseq, mp->seq) ) {		stats->frame_drops++;		isdn_ppp_mp_free_skb(mp, skb);		spin_unlock_irqrestore(&mp->lock, flags);		return;	}		/* find the minimum received sequence number over all links */	is->last_link_seqno = minseq = newseq;	for (lpq = net_dev->queue;;) {		slot = lpq->ppp_slot;		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {			printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",				__FUNCTION__, lpq->ppp_slot);		} else {			u32 lls = ippp_table[slot]->last_link_seqno;			if (MP_LT(lls, minseq))				minseq = lls;		}		if ((lpq = lpq->next) == net_dev->queue)			break;	}	if (MP_LT(minseq, mp->seq))		minseq = mp->seq;	/* can't go beyond already processed					 * packets */	newfrag = skb;  	/* if this new fragment is before the first one, then enqueue it now. */  	if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {		newfrag->next = frag;    		mp->frags = frag = newfrag;    		newfrag = NULL;  	}  	start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&				MP_SEQ(frag) == mp->seq ? frag : NULL;	/* 	 * main fragment traversing loop	 *	 * try to accomplish several tasks:	 * - insert new fragment into the proper sequence slot (once that's done	 *   newfrag will be set to NULL)	 * - reassemble any complete fragment sequence (non-null 'start'	 *   indicates there is a continguous sequence present)	 * - discard any incomplete sequences that are below minseq -- due	 *   to the fact that sender always increment sequence number, if there	 *   is an incomplete sequence below minseq, no new fragments would	 *   come to complete such sequence and it should be discarded	 *	 * loop completes when we accomplished the following tasks:	 * - new fragment is inserted in the proper sequence ('newfrag' is 	 *   set to NULL)	 * - we hit a gap in the sequence, so no reassembly/processing is 	 *   possible ('start' would be set to NULL)	 *	 * algorightm for this code is derived from code in the book	 * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)	 */  	while (start != NULL || newfrag != NULL) {    		thisseq = MP_SEQ(frag);    		nextf = frag->next;    		/* drop any duplicate fragments */    		if (newfrag != NULL && thisseq == newseq) {      			isdn_ppp_mp_free_skb(mp, newfrag);      			newfrag = NULL;    		}    		/* insert new fragment before next element if possible. */    		if (newfrag != NULL && (nextf == NULL || 						MP_LT(newseq, MP_SEQ(nextf)))) {      			newfrag->next = nextf;      			frag->next = nextf = newfrag;      			newfrag = NULL;    		}    		if (start != NULL) {	    		/* check for misplaced start */      			if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {				printk(KERN_WARNING"isdn_mppp(seq %d): new "				      "BEGIN flag with no prior END", thisseq);				stats->seqerrs++;				stats->frame_drops++;				start = isdn_ppp_mp_discard(mp, start,frag);				nextf = frag->next;      			}    		} else if (MP_LE(thisseq, minseq)) {		      			if (MP_FLAGS(frag) & MP_BEGIN_FRAG)				start = frag;      			else {				if (MP_FLAGS(frag) & MP_END_FRAG)	  				stats->frame_drops++;				if( mp->frags == frag )					mp->frags = nextf;					isdn_ppp_mp_free_skb(mp, frag);				frag = nextf;				continue;      			}		}				/* if start is non-null and we have end fragment, then		 * we have full reassembly sequence -- reassemble 		 * and process packet now		 */    		if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {      			minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;      			/* Reassemble the packet then dispatch it */			isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);            			start = NULL;      			frag = NULL;      			mp->frags = nextf;    		}		/* check if need to update start pointer: if we just		 * reassembled the packet and sequence is contiguous		 * then next fragment should be the start of new reassembly		 * if sequence is contiguous, but we haven't reassembled yet,		 * keep going.		 * if sequence is not contiguous, either clear everyting		 * below low watermark and set start to the next frag or		 * clear start ptr.		 */     		if (nextf != NULL && 		    ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {      			/* if we just reassembled and the next one is here, 			 * then start another reassembly. */      			if (frag == NULL) {				if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)	  				start = nextf;				else				{	  				printk(KERN_WARNING"isdn_mppp(seq %d):"						" END flag with no following "						"BEGIN", thisseq);					stats->seqerrs++;				}			}    		} else {			if ( nextf != NULL && frag != NULL &&						MP_LT(thisseq, minseq)) {				/* we've got a break in the sequence				 * and we not at the end yet				 * and we did not just reassembled				 *(if we did, there wouldn't be anything before)				 * and we below the low watermark 			 	 * discard all the frames below low watermark 				 * and start over */				stats->frame_drops++;				mp->frags = isdn_ppp_mp_discard(mp,start,nextf);			}			/* break in the sequence, no reassembly */      			start = NULL;    		}	  			    		frag = nextf;  	}	/* while -- main loop */	  	if (mp->frags == NULL)    		mp->frags = frag;			/* rather straighforward way to deal with (not very) possible 	 * queue overflow */	if (mp->frames > MP_MAX_QUEUE_LEN) {		stats->overflows++;		while (mp->frames > MP_MAX_QUEUE_LEN) {			frag = mp->frags->next;			isdn_ppp_mp_free_skb(mp, mp->frags);			mp->frags = frag;		}	}	spin_unlock_irqrestore(&mp->lock, flags);}static void isdn_ppp_mp_cleanup( isdn_net_local * lp ){	struct sk_buff * frag = lp->netdev->pb->frags;	struct sk_buff * nextfrag;    	while( frag ) {		nextfrag = frag->next;		isdn_ppp_mp_free_skb(lp->netdev->pb, frag);		frag = nextfrag;	}	lp->netdev->pb->frags = NULL;}static u32 isdn_ppp_mp_get_seq( int short_seq, 					struct sk_buff * skb, u32 last_seq ){	u32 seq;	int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);      	if( !short_seq )	{		seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;		skb_push(skb,1);	}	else	{		/* convert 12-bit short seq number to 24-bit long one 	 	*/		seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;			/* check for seqence wrap */		if( !(seq &  MP_SHORTSEQ_MAXBIT) && 		     (last_seq &  MP_SHORTSEQ_MAXBIT) && 		     (unsigned long)last_seq <= MP_LONGSEQ_MAX )			seq |= (last_seq + MP_SHORTSEQ_MAX+1) & 					(~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);		else			seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);				skb_push(skb, 3);	/* put converted seqence back in skb */	}	*(u32*)(skb->data+1) = seq; 	/* put seqence back in _host_ byte					 * order */	skb->data[0] = flags;	        /* restore flags */	return seq;}struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,			struct sk_buff * from, struct sk_buff * to ){	if( from )		while (from != to) {	  		struct sk_buff * next = from->next;			isdn_ppp_mp_free_skb(mp, from);	  		from = next;		}	return from;}void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,				struct sk_buff * from, struct sk_buff * to ){	ippp_bundle * mp = net_dev->pb;	int proto;	struct sk_buff * skb;	unsigned int tot_len;	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",			__FUNCTION__, lp->ppp_slot);		return;	}	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {		if( ippp_table[lp->ppp_slot]->debug & 0x40 )			printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "					"len %d\n", MP_SEQ(from), from->len );		skb = from;		skb_pull(skb, MP_HEADER_LEN);		mp->frames--;		} else {		struct sk_buff * frag;		int n;		for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)			tot_len += frag->len - MP_HEADER_LEN;		if( ippp_table[lp->ppp_slot]->debug & 0x40 )			printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "				"to %d, len %d\n", MP_SEQ(from), 				(MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );		if( (skb = dev_alloc_skb(tot_len)) == NULL ) {			printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "					"of size %d\n", tot_len);			isdn_ppp_mp_discard(mp, from, to);			return;		}		while( from != to ) {			unsigned int len = from->len - MP_HEADER_LEN;			memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);			frag = from->next;			isdn_ppp_mp_free_skb(mp, from);			from = frag; 		}	}   	proto = isdn_ppp_strip_proto(skb);	isdn_ppp_push_higher(net_dev, lp, skb, proto);}static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb){	dev_kfree_skb(skb);	mp->frames--;}static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb ){	printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n", 		slot, (int) skb->len, 		(int) skb->data[0], (int) skb->data[1], (int) skb->data[2],		(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);}static intisdn_ppp_bundle(struct ippp_struct *is, int unit){	char ifn[IFNAMSIZ + 1];	isdn_net_dev *p;	isdn_net_local *lp, *nlp;	int rc;	unsigned long flags;	sprintf(ifn, "ippp%d", unit);	p = isdn_net_findif(ifn);	if (!p) {		printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);		return -EINVAL;	}    	spin_lock_irqsave(&p->pb->lock, flags);	nlp = is->lp;	lp = p->queue;	if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||		lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) {		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",			nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? 			nlp->ppp_slot : lp->ppp_slot );		rc = -EINVAL;		goto out; 	}	isdn_net_add_to_bundle(p, nlp);

⌨️ 快捷键说明

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