summaryrefslogtreecommitdiffstats
path: root/drivers/isdn/isdn_ppp.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-08-25 05:30:00 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-08-25 05:30:00 +0000
commitee355114ec6062d00c1376b184b886a39e74fd4e (patch)
treeda9330872894208b121ac18e2abbc801b67f1509 /drivers/isdn/isdn_ppp.c
parentd1003cbd87479d4962fe017c0f913dde9c4d026a (diff)
Merge with Linux 2.4.0-test6-pre9.
Diffstat (limited to 'drivers/isdn/isdn_ppp.c')
-rw-r--r--drivers/isdn/isdn_ppp.c969
1 files changed, 489 insertions, 480 deletions
diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c
index 5fb2bab39..204995061 100644
--- a/drivers/isdn/isdn_ppp.c
+++ b/drivers/isdn/isdn_ppp.c
@@ -74,17 +74,18 @@ static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
#ifdef CONFIG_ISDN_MPP
-static int isdn_ppp_bundle(struct ippp_struct *, int unit);
-static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
-static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min);
-static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev, isdn_net_local *, long min);
-static void isdn_ppp_free_sqqueue(isdn_net_dev *);
-static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
- int BEbyte, long *sqno, int min_sqno);
-static void isdn_ppp_free_mpqueue(isdn_net_dev *);
-#endif
+static ippp_bundle * isdn_ppp_bundle_arr = NULL;
+
+static int isdn_ppp_mp_bundle_array_init(void);
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
+ struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
-char *isdn_ppp_revision = "$Revision: 1.69 $";
+static int isdn_ppp_bundle(struct ippp_struct *, int unit);
+#endif /* CONFIG_ISDN_MPP */
+
+char *isdn_ppp_revision = "$Revision: 1.77 $";
static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
@@ -119,7 +120,6 @@ isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot
int
isdn_ppp_free(isdn_net_local * lp)
{
- isdn_net_local *master_lp = lp;
unsigned long flags;
struct ippp_struct *is;
@@ -130,17 +130,18 @@ isdn_ppp_free(isdn_net_local * lp)
cli();
#ifdef CONFIG_ISDN_MPP
- if (lp->next == lp) { /* last link in queue? */
- master_lp->netdev->ib.bundled = 0;
- isdn_ppp_free_mpqueue(master_lp->netdev);
- isdn_ppp_free_sqqueue(master_lp->netdev);
- }
+ spin_lock(&lp->netdev->pb->lock);
#endif
-
isdn_net_rm_from_bundle(lp);
+#ifdef CONFIG_ISDN_MPP
+ if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */
+ isdn_ppp_mp_cleanup(lp);
+
+ lp->netdev->pb->ref_ct--;
+ spin_unlock(&lp->netdev->pb->lock);
+#endif /* CONFIG_ISDN_MPP */
is = ippp_table[lp->ppp_slot];
-
if ((is->state & IPPP_CONNECT))
isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
else if (is->state & IPPP_ASSIGNED)
@@ -153,7 +154,6 @@ isdn_ppp_free(isdn_net_local * lp)
lp->ppp_slot = -1; /* is this OK ?? */
restore_flags(flags);
-
return 0;
}
@@ -170,7 +170,6 @@ isdn_ppp_bind(isdn_net_local * lp)
save_flags(flags);
cli();
-
if (lp->pppbind < 0) { /* device bounded to ippp device ? */
isdn_net_dev *net_dev = dev->netdev;
char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */
@@ -207,20 +206,16 @@ isdn_ppp_bind(isdn_net_local * lp)
printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name);
return -1;
}
+
lp->ppp_slot = i;
-
- /* reset some values */
- lp->netdev->ib.bundled = 0;
- lp->netdev->ib.next_num = 0;
- lp->netdev->ib.modify = 0;
- lp->netdev->ib.last = NULL;
- lp->netdev->ib.min = 0;
- lp->netdev->ib.sq = NULL;
-
is = ippp_table[i];
is->lp = lp;
is->unit = unit;
is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */
+#ifdef CONFIG_ISDN_MPP
+ if (isdn_ppp_mp_init(lp, NULL) < 0)
+ return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */
restore_flags(flags);
@@ -313,7 +308,6 @@ isdn_ppp_open(int min, struct file *file)
is->mp_seqno = 0; /* MP sequence number */
is->pppcfg = 0; /* ppp configuration */
is->mpppcfg = 0; /* mppp configuration */
- is->range = 0x1000000; /* MP: 24 bit range */
is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
is->unit = -1; /* set, when we have our interface */
is->mru = 1524; /* MRU, default 1524 */
@@ -810,6 +804,11 @@ isdn_ppp_init(void)
{
int i,
j;
+
+#ifdef CONFIG_ISDN_MPP
+ if( isdn_ppp_mp_bundle_array_init() < 0 )
+ return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
if (!(ippp_table[i] = (struct ippp_struct *)
@@ -841,6 +840,12 @@ isdn_ppp_cleanup(void)
for (i = 0; i < ISDN_MAX_CHANNELS; i++)
kfree(ippp_table[i]);
+
+#ifdef CONFIG_ISDN_MPP
+ if (isdn_ppp_bundle_arr)
+ kfree(isdn_ppp_bundle_arr);
+#endif /* CONFIG_ISDN_MPP */
+
}
/*
@@ -866,9 +871,16 @@ static int isdn_ppp_strip_proto(struct sk_buff *skb)
void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
{
struct ippp_struct *is;
+ int slot;
int proto;
- is = ippp_table[lp->ppp_slot];
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
+ kfree_skb(skb);
+ return;
+ }
+ is = ippp_table[slot];
if (is->debug & 0x4) {
printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
@@ -890,7 +902,6 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf
#ifdef CONFIG_ISDN_MPP
if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
- int sqno_end;
if(is->compflags & SC_LINK_DECOMP_ON) {
if(proto == PPP_COMPFRAG) {
@@ -906,150 +917,12 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf
}
if (proto == PPP_MP) {
- isdn_net_local *lpq;
- long sqno, min_sqno, tseq;
-
- u_char BEbyte = skb->data[0];
- if (is->debug & 0x8)
- printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto,
- (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]);
- if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
- sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
- skb_pull(skb, 4);
- } else {
- sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
- skb_pull(skb, 2);
- }
-
- /*
- * new sequence number lower than last number? (this is only allowed
- * for overflow case)
- */
- if ((tseq = is->last_link_seqno) >= sqno) {
- int range = is->range;
- if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
- printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %ld, last: %ld !!!\n", sqno, tseq);
- else {
- sqno += range;
- is->last_link_seqno = sqno;
- }
- } else {
- /* here, we should also add an redundancy check */
- is->last_link_seqno = sqno;
- }
-
- /*
- * step over all links to find lowest link number
- */
- for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) {
- long lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
- if (lls >= 0 && lls < min_sqno)
- min_sqno = lls;
- lpq = lpq->next;
- if (lpq == net_dev->queue)
- break;
- }
-
- /*
- * for the case, that the last frame numbers of all
- * links are overflowed: mask/reduce the sequenece number to
- * 'normal' numbering.
- */
- if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {
- int mask = ippp_table[lpq->ppp_slot]->range-1; /* range is power of two, so a mask will do the job */
- isdn_ppp_mask_queue(net_dev, mask);
- net_dev->ib.next_num &= mask;
- {
- struct sqqueue *q = net_dev->ib.sq;
- while (q) {
- q->sqno_start &= mask;
- q->sqno_end &= mask;
- }
- }
- min_sqno &= mask;
- for (lpq = net_dev->queue;;) {
- if(ippp_table[lpq->ppp_slot]->last_link_seqno >= 0)
- ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
- lpq = lpq->next;
- if (lpq == net_dev->queue)
- break;
- }
- }
- if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
- static int dmes = 0;
- if( !dmes ) {
- printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
- dmes = 1;
- }
- if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb, BEbyte, &sqno, min_sqno)) < 0) {
- net_dev->ib.modify = 1; /* block timeout-timer */
- isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
- net_dev->ib.modify = 0;
- return; /* no packet complete */
- }
- } else
- sqno_end = sqno;
-
- if (is->debug & 0x40)
- printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n", min_sqno, sqno_end, net_dev->ib.next_num);
-
- /*
- * MP buffer management .. reorders incoming packets ..
- * lotsa mem-copies and not heavily tested.
- *
- * first check whether there is more than one link in the bundle
- * then check whether the number is in order
- */
- net_dev->ib.modify = 1; /* block timeout-timer */
- if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
- /*
- * packet is not 'in order'
- */
- struct sqqueue *q;
-
- q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
- if (!q) {
- net_dev->ib.modify = 0;
- printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
- dev_kfree_skb(skb);
- return; /* discard */
- }
- q->skb = skb;
- q->sqno_end = sqno_end;
- q->sqno_start = sqno;
- q->timer = jiffies + (ISDN_TIMER_1SEC) * 5; /* timeout after 5 seconds */
-
- if (!net_dev->ib.sq) {
- net_dev->ib.sq = q;
- q->next = NULL;
- } else {
- struct sqqueue *ql = net_dev->ib.sq;
- if (ql->sqno_start > q->sqno_start) {
- q->next = ql;
- net_dev->ib.sq = q;
- } else {
- while (ql->next && ql->next->sqno_start < q->sqno_start)
- ql = ql->next;
- q->next = ql->next;
- ql->next = q;
- }
- }
- } else {
- /*
- * packet was 'in order' .. push it higher
- */
- net_dev->ib.next_num = sqno_end + 1;
- proto = isdn_ppp_strip_proto(skb);
- isdn_ppp_push_higher(net_dev, lp, skb, proto);
- }
- isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
- net_dev->ib.modify = 0;
-
- } else
+ isdn_ppp_mp_receive(net_dev, lp, skb);
+ }
+ else
isdn_ppp_push_higher(net_dev, lp, skb, proto);
} else
-#endif
+#endif /* CONFIG_ISDN_MPP */
isdn_ppp_push_higher(net_dev, lp, skb, proto);
}
@@ -1061,8 +934,16 @@ static void
isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
{
struct net_device *dev = &net_dev->dev;
- struct ippp_struct *is = ippp_table[lp->ppp_slot];
+ struct ippp_struct *is;
+ int slot;
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot %d\n", lp->ppp_slot);
+ kfree_skb(skb);
+ return;
+ }
+ is = ippp_table[slot];
if (is->debug & 0x10) {
printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
@@ -1219,11 +1100,18 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
isdn_net_dev *nd;
unsigned int proto = PPP_IP; /* 0x21 */
struct ippp_struct *ipt,*ipts;
+ int slot;
mlp = (isdn_net_local *) (netdev->priv);
-
nd = mlp->netdev; /* get master lp */
- ipts = ippp_table[mlp->ppp_slot];
+
+ slot = mlp->ppp_slot;
+ if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", mlp->ppp_slot);
+ kfree_skb(skb);
+ return 0;
+ }
+ ipts = ippp_table[slot];
if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
if (ipts->debug & 0x1)
@@ -1247,12 +1135,18 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
lp = isdn_net_get_locked_lp(nd);
if (!lp) {
- printk(KERN_WARNING "%s: all channels busy - requeuing!\n", lp->name);
+ printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
return 1;
}
/* we have our lp locked from now on */
- ipt = ippp_table[lp->ppp_slot];
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", lp->ppp_slot);
+ kfree_skb(skb);
+ return 0;
+ }
+ ipt = ippp_table[slot];
lp->huptimer = 0;
/*
@@ -1399,351 +1293,466 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
#ifdef CONFIG_ISDN_MPP
-/*
- * free SQ queue
- * -------------
- * Note: We need two queues for MPPP. The SQ queue holds fully (re)assembled frames,
- * that can't be delivered, because there is an outstanding earlier frame
+/* 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
*/
-static void
-isdn_ppp_free_sqqueue(isdn_net_dev * p)
-{
- struct sqqueue *q = p->ib.sq;
+#define MP_HEADER_LEN 5
- p->ib.sq = NULL;
- while (q) {
- struct sqqueue *qn = q->next;
- if (q->skb)
- dev_kfree_skb(q->skb);
- kfree(q);
- q = qn;
- }
+#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)
-/*
- * free MP queue
- * -------------
- * Note: The MP queue holds all frame fragments of frames, that can't be
- * reassembled, because there is at least one missing fragment.
- */
-static void
-isdn_ppp_free_mpqueue(isdn_net_dev * p)
-{
- struct mpqueue *q = p->mp_last;
- p->mp_last = NULL;
+#define MP_SEQ(f) ((*(u32*)(f->data+1)))
+#define MP_FLAGS(f) (f->data[0])
- while (q) {
- struct mpqueue *ql = q->next;
- dev_kfree_skb(q->skb);
- kfree(q);
- q = ql;
- }
+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 int
-isdn_ppp_bundle(struct ippp_struct *is, int unit)
+static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
{
- char ifn[IFNAMSIZ + 1];
- long flags;
- isdn_net_dev *p;
- isdn_net_local *lp, *nlp;
-
- sprintf(ifn, "ippp%d", unit);
- p = isdn_net_findif(ifn);
- if (!p)
- return -1;
-
- save_flags(flags);
- cli();
- isdn_timer_ctrl(ISDN_TIMER_IPPP, 1); /* enable timer for ippp/MP */
-
- nlp = is->lp;
- lp = p->queue;
- isdn_net_add_to_bundle(p, nlp);
- p->ib.bundled = 1;
-
- ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
-/* maybe also SC_CCP stuff */
- ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
- (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
-
- ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
- (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
- restore_flags(flags);
- return 0;
+ 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;
}
-/*
- * Mask sequence numbers in MP queue
- */
-static void
-isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
{
- struct mpqueue *q = dev->mp_last;
- while (q) {
- q->sqno &= mask;
- q = q->next;
- }
+ struct ippp_struct * 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 = LONG_MAX;
+ }
+ lp->netdev->pb->ref_ct++;
+
+ is->last_link_seqno = 0;
+ return 0;
}
-/*
- * put a fragment at the right place into the MP queue
- * Also checks, whether this fragment completes a frame. In this case
- * the fragments are copied together into one SKB
- */
-static int
-isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno)
+static u32 isdn_ppp_mp_get_seq( int short_seq,
+ struct sk_buff * skb, u32 last_seq );
+struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+ struct sk_buff * from, struct sk_buff * to );
+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 mpqueue *qe,
- *q1,
- *q;
- long cnt,
- flags;
- int pktlen,
- sqno_end;
- int sqno = *sqnop;
-
- q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_ATOMIC);
- if (!q1) {
- printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
- save_flags(flags);
- cli();
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
- return -1;
+ 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 "isdn_ppp_mp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
+ stats->frame_drops++;
+ dev_kfree_skb(skb);
+ spin_unlock_irqrestore(&mp->lock, flags);
+ return;
}
- q1->skb = *skb;
- q1->sqno = sqno;
- q1->BEbyte = BEbyte;
- q1->time = jiffies;
+ 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);
- save_flags(flags);
- cli();
+ newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
+ skb, is->last_link_seqno);
- if (!(q = dev->mp_last)) {
- dev->mp_last = q1;
- q1->next = NULL;
- q1->last = NULL;
- isdn_ppp_cleanup_mpqueue(dev, min_sqno); /* not necessary */
- restore_flags(flags);
- return -1; /* -1 is not an error. Just says, that this fragment hasn't complete a full frame */
+
+ /* 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;
}
- for (;;) { /* the faster way would be to step from the queue-end to the start */
- if (sqno > q->sqno) {
- if (q->next) {
- q = q->next;
- continue;
- }
- q->next = q1;
- q1->next = NULL;
- q1->last = q;
- break;
+
+ /* 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 "isdn_ppp_mp_receive: lpq->ppp_slot %d\n", lpq->ppp_slot);
+ } else {
+ u32 lls = ippp_table[slot]->last_link_seqno;
+ if (MP_LT(lls, minseq))
+ minseq = lls;
}
- if (sqno == q->sqno)
- printk(KERN_WARNING "isdn_fill_mpqueue: illegal sqno received!!\n");
- q1->last = q->last;
- q1->next = q;
- if (q->last) {
- q->last->next = q1;
- } else
- dev->mp_last = q1;
- q->last = q1;
- break;
+ if ((lpq = lpq->next) == net_dev->queue)
+ break;
}
-
-/* now we check whether we completed a packet with this fragment */
- pktlen = -q1->skb->len;
- q = q1;
- cnt = q1->sqno;
- while (!(q->BEbyte & MP_END_FRAG)) {
- cnt++;
- if (!(q->next) || q->next->sqno != cnt) {
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
- return -1;
+ 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;
+ }
}
- pktlen += q->skb->len;
- q = q->next;
- }
- pktlen += q->skb->len;
- qe = q;
-
- q = q1;
- cnt = q1->sqno;
- while (!(q->BEbyte & MP_BEGIN_FRAG)) {
- cnt--;
- if (!(q->last) || q->last->sqno != cnt) {
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
- return -1;
+
+ /* 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;
}
- pktlen += q->skb->len;
- q = q->last;
}
- pktlen += q->skb->len;
+ spin_unlock_irqrestore(&mp->lock, flags);
+}
- if (q->last)
- q->last->next = qe->next;
+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
- dev->mp_last = qe->next;
+ {
+ /* 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;
+}
- if (qe->next)
- qe->next->last = q->last;
- qe->next = NULL;
- sqno_end = qe->sqno;
- *sqnop = q->sqno;
+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;
+}
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
+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( 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;
+ }
- *skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
+ while( from != to ) {
+ unsigned int len = from->len - MP_HEADER_LEN;
- if (!(*skb)) {
- while (q) {
- struct mpqueue *ql = q->next;
- dev_kfree_skb(q->skb);
- kfree(q);
- q = ql;
+ memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+ frag = from->next;
+ isdn_ppp_mp_free_skb(mp, from);
+ from = frag;
}
- return -2;
}
- cnt = 0;
- skb_put(*skb, pktlen);
- while (q) {
- struct mpqueue *ql = q->next;
- memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
- cnt += q->skb->len;
- dev_kfree_skb(q->skb);
- kfree(q);
- q = ql;
- }
-
- return sqno_end;
+ proto = isdn_ppp_strip_proto(skb);
+ isdn_ppp_push_higher(net_dev, lp, skb, proto);
}
-/*
- * check sq-queue, whether we have still buffered the next packet(s)
- * or packets with a sqno less or equal to min_sqno
- * net_dev: master netdevice , lp: 'real' local connection
- */
-static void
-isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp, long min_sqno)
+static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
{
- struct sqqueue *q;
+ dev_kfree_skb(skb);
+ mp->frames--;
+}
- while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno)) {
- int proto;
- if (q->sqno_start != net_dev->ib.next_num) {
- printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n", net_dev->ib.next_num);
-#ifdef CONFIG_ISDN_PPP_VJ
- slhc_toss(ippp_table[net_dev->local->ppp_slot]->slcomp);
-#endif
- }
- proto = isdn_ppp_strip_proto(q->skb);
- isdn_ppp_push_higher(net_dev, lp, q->skb, proto);
- net_dev->ib.sq = q->next;
- net_dev->ib.next_num = q->sqno_end + 1;
- kfree(q);
- }
+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]);
}
-/*
- * remove stale packets from list
- */
-static void
-isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno)
+static int
+isdn_ppp_bundle(struct ippp_struct *is, int unit)
{
-#ifdef CONFIG_ISDN_PPP_VJ
- int toss = 0;
-#endif
- /* currently we just discard ancient packets.
- To do:
- Maybe, as long as there's no B-packet in front and sqno <= min_sqno: discard.
- If sqno < min_sqno and there are gaps: discard (the gaps won't be filled anyway).
- Packets with sqno > min_sqno: Larger than mp_mrru: If sum of all pktlen of pending
- packets large than mrru: discard - packets need to be consecutive, though, if not
- there could be an B and an E-packet in between.
- */
+ char ifn[IFNAMSIZ + 1];
+ isdn_net_dev *p;
+ isdn_net_local *lp, *nlp;
+ int rc;
+ unsigned long flags;
- struct mpqueue *ql,
- *q = dev->mp_last;
- while(q && (q->sqno < min_sqno) ) {
- if ( (q->BEbyte & MP_END_FRAG) ||
- (q->next && (q->next->sqno <= min_sqno) && (q->next->BEbyte & MP_BEGIN_FRAG)) ) {
- printk(KERN_DEBUG "ippp: freeing stale packet(s), min_sq: %ld!\n",min_sqno);
- if ((dev->mp_last = q->next))
- q->next->last = NULL;
- while (q) {
- ql = q->last;
- printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno);
- dev_kfree_skb(q->skb);
- kfree(q);
-#ifdef CONFIG_ISDN_PPP_VJ
- toss = 1;
-#endif
- q = ql;
- }
- q = dev->mp_last;
- } else
- q = q->next;
+ sprintf(ifn, "ippp%d", unit);
+ p = isdn_net_findif(ifn);
+ if (!p) {
+ printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
+ return -EINVAL;
}
-#ifdef CONFIG_ISDN_PPP_VJ
- /* did we free a stale frame ? */
- if (toss)
- slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp);
-#endif
-}
-#endif
-/*
- * a buffered packet timed-out?
- */
-void
-isdn_ppp_timer_timeout(void)
-{
-#ifdef CONFIG_ISDN_MPP
- isdn_net_dev *net_dev = dev->netdev;
- struct sqqueue *q,
- *ql = NULL,
- *qn;
-
- while (net_dev) {
- isdn_net_local *lp = net_dev->local;
- if (net_dev->ib.modify || lp->master) { /* interface locked or slave? */
- net_dev = net_dev->next;
- continue;
- }
- q = net_dev->ib.sq;
- while (q) {
- if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
+ spin_lock_irqsave(&p->pb->lock, flags);
-#ifdef CONFIG_ISDN_PPP_VJ
- /* did we step over a missing frame ? */
- if (q->sqno_start != net_dev->ib.next_num)
- slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
-#endif
+ 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;
+ }
- ql = net_dev->ib.sq;
- net_dev->ib.sq = q->next;
- net_dev->ib.next_num = q->sqno_end + 1;
- q->next = NULL;
- for (; ql;) {
- int proto = isdn_ppp_strip_proto(ql->skb);
- isdn_ppp_push_higher(net_dev, lp, ql->skb, proto);
- qn = ql->next;
- kfree(ql);
- ql = qn;
- }
- q = net_dev->ib.sq;
- } else
- q = q->next;
- }
- net_dev = net_dev->next;
- }
-#endif
-}
+ isdn_net_add_to_bundle(p, nlp);
+
+ ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
+ /* maybe also SC_CCP stuff */
+ ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
+ (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+ ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
+ (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+ rc = isdn_ppp_mp_init(nlp, p->pb);
+out:
+ spin_unlock_irqrestore(&p->pb->lock, flags);
+ return rc;
+}
+
+#endif /* CONFIG_ISDN_MPP */
+
/*
* network device ioctl handlers
*/