📄 isdn_ppp.c
字号:
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 + -