patch-2.3.99-pre4 linux/drivers/net/ppp_generic.c
Next file: linux/drivers/net/ppp_synctty.c
Previous file: linux/drivers/net/ppp_async.c
Back to the patch index
Back to the overall index
- Lines: 534
- Date:
Mon Apr 10 23:05:35 2000
- Orig file:
v2.3.99-pre3/linux/drivers/net/ppp_generic.c
- Orig date:
Sun Mar 19 18:35:30 2000
diff -u --recursive --new-file v2.3.99-pre3/linux/drivers/net/ppp_generic.c linux/drivers/net/ppp_generic.c
@@ -19,7 +19,7 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 20000313==
+ * ==FILEVERSION 20000406==
*/
#include <linux/config.h>
@@ -55,8 +55,8 @@
#define NP_AT 3 /* Appletalk protocol */
#define NUM_NP 4 /* Number of NPs. */
-#define MPHDRLEN 4 /* multilink protocol header length */
-#define MPHDRLEN_SSN 2 /* ditto with short sequence numbers */
+#define MPHDRLEN 6 /* multilink protocol header length */
+#define MPHDRLEN_SSN 4 /* ditto with short sequence numbers */
#define MIN_FRAG_SIZE 64
/*
@@ -125,12 +125,13 @@
/*
* Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
- * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ.
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP.
* Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
* Bits in xstate: SC_COMP_RUN
*/
#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
- |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ)
+ |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \
+ |SC_COMP_TCP|SC_REJ_COMP_TCP)
/*
* Private data structure for each channel.
@@ -182,6 +183,14 @@
/* We limit the length of ppp->file.rq to this (arbitrary) value */
#define PPP_MAX_RQLEN 32
+/*
+ * Maximum number of multilink fragments queued up.
+ * This has to be large enough to cope with the maximum latency of
+ * the slowest channel relative to the others. Strictly it should
+ * depend on the number of channels and their characteristics.
+ */
+#define PPP_MP_MAX_QLEN 128
+
/* Multilink header bits. */
#define B 0x80 /* this fragment begins a packet */
#define E 0x40 /* this fragment ends a packet */
@@ -351,10 +360,13 @@
add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
- ret = -EAGAIN;
skb = skb_dequeue(&pf->rq);
if (skb)
break;
+ ret = 0;
+ if (pf->kind == CHANNEL && PF_TO_CHANNEL(pf)->chan == 0)
+ break;
+ ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK)
break;
ret = -ERESTARTSYS;
@@ -487,7 +499,7 @@
spin_lock_bh(&pch->downl);
chan = pch->chan;
err = -ENOTTY;
- if (chan->ops->ioctl)
+ if (chan && chan->ops->ioctl)
err = chan->ops->ioctl(chan, cmd, arg);
spin_unlock_bh(&pch->downl);
}
@@ -767,6 +779,7 @@
outf:
kfree_skb(skb);
+ ++ppp->stats.tx_dropped;
return 0;
}
@@ -1003,8 +1016,15 @@
pch = list_entry(list, struct channel, clist);
spin_lock_bh(&pch->downl);
- if (skb_queue_len(&pch->file.xq) == 0
- && pch->chan->ops->start_xmit(pch->chan, skb))
+ if (pch->chan) {
+ if (pch->chan->ops->start_xmit(pch->chan, skb))
+ skb = 0;
+ } else {
+ /* channel got unregistered */
+ kfree_skb(skb);
+ skb = 0;
+ }
+ if (skb_queue_len(&pch->file.xq) == 0 && skb == 0)
ppp->xmit_pending = 0;
spin_unlock_bh(&pch->downl);
return;
@@ -1029,7 +1049,8 @@
static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
{
int nch, len, fragsize;
- int i, bits, hdrlen;
+ int i, bits, hdrlen, mtu;
+ int flen, fnb;
unsigned char *p, *q;
struct list_head *list;
struct channel *pch;
@@ -1037,6 +1058,7 @@
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);
@@ -1087,8 +1109,6 @@
/* create a fragment for each channel */
bits = B;
- hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
- /* XXX gotta do A/C and prot compression here */
do {
list = list->next;
if (list == &ppp->channels) {
@@ -1099,47 +1119,73 @@
++i;
if (!pch->avail)
continue;
- if (fragsize >= len) {
- fragsize = len;
- bits |= E;
+
+ /* 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;
}
- frag = alloc_skb(fragsize + hdrlen, GFP_ATOMIC);
- if (frag != 0) {
- q = skb_put(frag, fragsize + hdrlen);
+
+ /*
+ * 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[0] = bits + ((ppp->nxseq >> 8) & 0xf);
- q[1] = ppp->nxseq;
- } else {
- q[0] = bits;
- q[1] = ppp->nxseq >> 16;
- q[2] = ppp->nxseq >> 8;
+ 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, fragsize);
+ memcpy(q + hdrlen, p, fnb);
/* try to send it down the channel */
- spin_lock_bh(&pch->downl);
chan = pch->chan;
- if (chan != 0) {
- if (!chan->ops->start_xmit(chan, frag))
- skb_queue_tail(&pch->file.xq, frag);
- } else {
- /* channel got unregistered, too bad */
- kfree_skb(skb);
- }
- spin_unlock_bh(&pch->downl);
+ 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;
}
- p += fragsize;
- len -= fragsize;
- ++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 */
@@ -1201,7 +1247,7 @@
proto = PPP_PROTO(skb);
read_lock_bh(&pch->upl);
- if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) {
+ 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 */
@@ -1306,12 +1352,7 @@
}
len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
if (len <= 0) {
- int i;
printk(KERN_DEBUG "PPP: VJ decompression error\n");
- printk(KERN_DEBUG "PPP: len = %d data =", skb->len);
- for (i = 0; i < 16 && i < skb->len; ++i)
- printk(" %.2x", skb->data[i]);
- printk("\n");
goto err;
}
len += 2;
@@ -1426,11 +1467,11 @@
static void
ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- u32 mask, seq, minseq;
+ u32 mask, seq;
struct list_head *l;
- int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4;
+ int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
- if (skb->len < mphdrlen + 3)
+ if (skb->len < mphdrlen + 1 || ppp->mrru == 0)
goto err; /* no good, throw it away */
/* Decode sequence number and begin/end bits */
@@ -1442,22 +1483,38 @@
mask = 0xffffff;
}
skb->BEbits = skb->data[2];
- skb_pull(skb, mphdrlen + 2); /* pull off PPP and MP headers*/
+ skb_pull(skb, mphdrlen); /* pull off PPP and MP headers */
- /* Expand sequence number to 32 bits */
- seq |= pch->lastseq & ~mask;
- if (seq_before(seq, pch->lastseq)) {
- if (seq_after(seq, pch->lastseq - 100)) {
- printk(KERN_DEBUG "PPP: MP fragments out of order"
- " (%u, %u)\n", pch->lastseq, seq);
- goto err;
- }
+ /*
+ * 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
@@ -1465,17 +1522,23 @@
* The list of channels can't change because we have the receive
* side of the ppp unit locked.
*/
- minseq = seq;
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;
}
- ppp->minseq = minseq;
+ 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);
@@ -1523,16 +1586,17 @@
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 toss the skb */
- printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n",
+ /* this can't happen, anyway ignore the skb */
+ printk(KERN_ERR "ppp_mp_reconstruct bad seq %u < %u\n",
p->sequence, seq);
- __skb_unlink(p, list);
- kfree_skb(p);
+ head = next;
continue;
}
if (p->sequence != seq) {
@@ -1542,8 +1606,8 @@
break;
/* Fragment `seq' is lost, keep going. */
lost = 1;
- seq = seq_before(p->sequence, minseq)?
- p->sequence: minseq;
+ seq = seq_before(minseq, p->sequence)?
+ minseq + 1: p->sequence;
next = p;
continue;
}
@@ -1560,22 +1624,31 @@
if (p->BEbits & B) {
head = p;
lost = 0;
- /* reset len, allow for protocol ID compression */
- len = p->data[0] & 1;
+ len = 0;
}
len += p->len;
/* Got a complete packet yet? */
if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
- if (len > ppp->mrru) {
+ 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;
}
/*
@@ -1593,19 +1666,18 @@
if (tail != NULL) {
/* If we have discarded any fragments,
signal a receive error. */
- if (head->sequence != ppp->nextseq)
+ 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);
-
- /* uncompress protocol ID */
- if (head->data[0] & 1)
- *skb_put(skb, 1) = 0;
- p = head;
- for (;;) {
- memcpy(skb_put(skb, p->len), p->data, p->len);
- if (p == tail)
- break;
- p = p->next;
}
+
+ 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;
}
@@ -1642,6 +1714,9 @@
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);
@@ -1653,13 +1728,30 @@
}
/*
- * Return the unit number associated with a channel.
+ * 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;
- return pch->ppp->file.index;
+ if (pch != 0) {
+ read_lock_bh(&pch->upl);
+ if (pch->ppp != 0)
+ unit = pch->ppp->file.index;
+ read_unlock_bh(&pch->upl);
+ }
+ return unit;
}
/*
@@ -1760,6 +1852,8 @@
int err = -ENOTTY;
int unit;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
if (pch == 0)
return -EINVAL;
switch (cmd) {
@@ -2104,6 +2198,10 @@
INIT_LIST_HEAD(&ppp->channels);
spin_lock_init(&ppp->rlock);
spin_lock_init(&ppp->wlock);
+#ifdef CONFIG_PPP_MULTILINK
+ ppp->minseq = -1;
+ skb_queue_head_init(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
ppp->dev = dev;
dev->init = ppp_net_init;
@@ -2163,6 +2261,9 @@
}
skb_queue_purge(&ppp->file.xq);
skb_queue_purge(&ppp->file.rq);
+#ifdef CONFIG_PPP_MULTILINK
+ skb_queue_purge(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
dev = ppp->dev;
ppp->dev = 0;
ppp_unlock(ppp);
@@ -2247,10 +2348,12 @@
if (pch->chan == 0) /* need to check this?? */
goto outr;
- hdrlen = pch->chan->hdrlen + PPP_HDRLEN;
+ if (pch->file.hdrlen > ppp->file.hdrlen)
+ ppp->file.hdrlen = pch->file.hdrlen;
+ hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */
if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
ppp->dev->hard_header_len = hdrlen;
- list_add(&pch->clist, &ppp->channels);
+ list_add_tail(&pch->clist, &ppp->channels);
++ppp->n_channels;
pch->ppp = ppp;
ret = 0;
@@ -2319,6 +2422,7 @@
EXPORT_SYMBOL(ppp_register_channel);
EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
EXPORT_SYMBOL(ppp_unit_number);
EXPORT_SYMBOL(ppp_input);
EXPORT_SYMBOL(ppp_input_error);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)