isdn: isdn_ppp: Use SKB list facilities instead of home-grown implementation.
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
8fc5387cb8
commit
38783e6713
@ -1533,8 +1533,10 @@ static int isdn_ppp_mp_bundle_array_init(void)
|
|||||||
int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
|
int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
|
||||||
if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL )
|
if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL )
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
|
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
|
||||||
spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
|
spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
|
||||||
|
skb_queue_head_init(&isdn_ppp_bundle_arr[i].frags);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1567,7 +1569,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
|
|||||||
if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
|
if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
lp->next = lp->last = lp; /* nobody else in a queue */
|
lp->next = lp->last = lp; /* nobody else in a queue */
|
||||||
lp->netdev->pb->frags = NULL;
|
skb_queue_head_init(&lp->netdev->pb->frags);
|
||||||
lp->netdev->pb->frames = 0;
|
lp->netdev->pb->frames = 0;
|
||||||
lp->netdev->pb->seq = UINT_MAX;
|
lp->netdev->pb->seq = UINT_MAX;
|
||||||
}
|
}
|
||||||
@ -1579,28 +1581,29 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
|
|||||||
|
|
||||||
static u32 isdn_ppp_mp_get_seq( int short_seq,
|
static u32 isdn_ppp_mp_get_seq( int short_seq,
|
||||||
struct sk_buff * skb, u32 last_seq );
|
struct sk_buff * skb, u32 last_seq );
|
||||||
static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
|
static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from,
|
||||||
struct sk_buff * from, struct sk_buff * to );
|
struct sk_buff *to);
|
||||||
static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
|
static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp,
|
||||||
struct sk_buff * from, struct sk_buff * to );
|
struct sk_buff *from, struct sk_buff *to,
|
||||||
static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
|
u32 lastseq);
|
||||||
|
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_print_recv_pkt( int slot, struct sk_buff * skb );
|
||||||
|
|
||||||
static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct ippp_struct *is;
|
struct sk_buff *newfrag, *frag, *start, *nextf;
|
||||||
isdn_net_local * lpq;
|
|
||||||
ippp_bundle * mp;
|
|
||||||
isdn_mppp_stats * stats;
|
|
||||||
struct sk_buff * newfrag, * frag, * start, *nextf;
|
|
||||||
u32 newseq, minseq, thisseq;
|
u32 newseq, minseq, thisseq;
|
||||||
|
isdn_mppp_stats *stats;
|
||||||
|
struct ippp_struct *is;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
isdn_net_local *lpq;
|
||||||
|
ippp_bundle *mp;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
spin_lock_irqsave(&net_dev->pb->lock, flags);
|
spin_lock_irqsave(&net_dev->pb->lock, flags);
|
||||||
mp = net_dev->pb;
|
mp = net_dev->pb;
|
||||||
stats = &mp->stats;
|
stats = &mp->stats;
|
||||||
slot = lp->ppp_slot;
|
slot = lp->ppp_slot;
|
||||||
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
|
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
|
||||||
printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",
|
printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",
|
||||||
@ -1611,20 +1614,19 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
is = ippp_table[slot];
|
is = ippp_table[slot];
|
||||||
if( ++mp->frames > stats->max_queue_len )
|
if (++mp->frames > stats->max_queue_len)
|
||||||
stats->max_queue_len = mp->frames;
|
stats->max_queue_len = mp->frames;
|
||||||
|
|
||||||
if (is->debug & 0x8)
|
if (is->debug & 0x8)
|
||||||
isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
|
isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
|
||||||
|
|
||||||
newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
|
newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
|
||||||
skb, is->last_link_seqno);
|
skb, is->last_link_seqno);
|
||||||
|
|
||||||
|
|
||||||
/* if this packet seq # is less than last already processed one,
|
/* if this packet seq # is less than last already processed one,
|
||||||
* toss it right away, but check for sequence start case first
|
* toss it right away, but check for sequence start case first
|
||||||
*/
|
*/
|
||||||
if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) {
|
if (mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT)) {
|
||||||
mp->seq = newseq; /* the first packet: required for
|
mp->seq = newseq; /* the first packet: required for
|
||||||
* rfc1990 non-compliant clients --
|
* rfc1990 non-compliant clients --
|
||||||
* prevents constant packet toss */
|
* prevents constant packet toss */
|
||||||
@ -1634,7 +1636,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
|||||||
spin_unlock_irqrestore(&mp->lock, flags);
|
spin_unlock_irqrestore(&mp->lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find the minimum received sequence number over all links */
|
/* find the minimum received sequence number over all links */
|
||||||
is->last_link_seqno = minseq = newseq;
|
is->last_link_seqno = minseq = newseq;
|
||||||
for (lpq = net_dev->queue;;) {
|
for (lpq = net_dev->queue;;) {
|
||||||
@ -1655,22 +1657,31 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
|||||||
* packets */
|
* packets */
|
||||||
newfrag = skb;
|
newfrag = skb;
|
||||||
|
|
||||||
/* if this new fragment is before the first one, then enqueue it now. */
|
/* Insert new fragment into the proper sequence slot. */
|
||||||
if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {
|
skb_queue_walk(&mp->frags, frag) {
|
||||||
newfrag->next = frag;
|
if (MP_SEQ(frag) == newseq) {
|
||||||
mp->frags = frag = newfrag;
|
isdn_ppp_mp_free_skb(mp, newfrag);
|
||||||
newfrag = NULL;
|
newfrag = NULL;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
if (MP_LT(newseq, MP_SEQ(frag))) {
|
||||||
|
__skb_queue_before(&mp->frags, frag, newfrag);
|
||||||
|
newfrag = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newfrag)
|
||||||
|
__skb_queue_tail(&mp->frags, newfrag);
|
||||||
|
|
||||||
start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&
|
frag = skb_peek(&mp->frags);
|
||||||
MP_SEQ(frag) == mp->seq ? frag : NULL;
|
start = ((MP_FLAGS(frag) & MP_BEGIN_FRAG) &&
|
||||||
|
(MP_SEQ(frag) == mp->seq)) ? frag : NULL;
|
||||||
|
if (!start)
|
||||||
|
goto check_overflow;
|
||||||
|
|
||||||
/*
|
/* main fragment traversing loop
|
||||||
* main fragment traversing loop
|
|
||||||
*
|
*
|
||||||
* try to accomplish several tasks:
|
* 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'
|
* - reassemble any complete fragment sequence (non-null 'start'
|
||||||
* indicates there is a continguous sequence present)
|
* indicates there is a continguous sequence present)
|
||||||
* - discard any incomplete sequences that are below minseq -- due
|
* - discard any incomplete sequences that are below minseq -- due
|
||||||
@ -1679,71 +1690,46 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
|||||||
* come to complete such sequence and it should be discarded
|
* come to complete such sequence and it should be discarded
|
||||||
*
|
*
|
||||||
* loop completes when we accomplished the following tasks:
|
* 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
|
* - we hit a gap in the sequence, so no reassembly/processing is
|
||||||
* possible ('start' would be set to NULL)
|
* possible ('start' would be set to NULL)
|
||||||
*
|
*
|
||||||
* algorithm for this code is derived from code in the book
|
* algorithm for this code is derived from code in the book
|
||||||
* 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
|
* 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
|
||||||
*/
|
*/
|
||||||
while (start != NULL || newfrag != NULL) {
|
skb_queue_walk_safe(&mp->frags, frag, nextf) {
|
||||||
|
thisseq = MP_SEQ(frag);
|
||||||
|
|
||||||
thisseq = MP_SEQ(frag);
|
/* check for misplaced start */
|
||||||
nextf = frag->next;
|
if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
|
||||||
|
printk(KERN_WARNING"isdn_mppp(seq %d): new "
|
||||||
/* drop any duplicate fragments */
|
"BEGIN flag with no prior END", thisseq);
|
||||||
if (newfrag != NULL && thisseq == newseq) {
|
stats->seqerrs++;
|
||||||
isdn_ppp_mp_free_skb(mp, newfrag);
|
stats->frame_drops++;
|
||||||
newfrag = NULL;
|
isdn_ppp_mp_discard(mp, start, frag);
|
||||||
}
|
start = frag;
|
||||||
|
} else if (MP_LE(thisseq, minseq)) {
|
||||||
/* insert new fragment before next element if possible. */
|
if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
|
||||||
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;
|
start = frag;
|
||||||
else {
|
else {
|
||||||
if (MP_FLAGS(frag) & MP_END_FRAG)
|
if (MP_FLAGS(frag) & MP_END_FRAG)
|
||||||
stats->frame_drops++;
|
stats->frame_drops++;
|
||||||
if( mp->frags == frag )
|
__skb_unlink(skb, &mp->frags);
|
||||||
mp->frags = nextf;
|
|
||||||
isdn_ppp_mp_free_skb(mp, frag);
|
isdn_ppp_mp_free_skb(mp, frag);
|
||||||
frag = nextf;
|
|
||||||
continue;
|
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;
|
/* if we have end fragment, then we have full reassembly
|
||||||
}
|
* sequence -- reassemble and process packet now
|
||||||
|
*/
|
||||||
|
if (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, frag, thisseq);
|
||||||
|
|
||||||
|
start = NULL;
|
||||||
|
frag = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* check if need to update start pointer: if we just
|
/* check if need to update start pointer: if we just
|
||||||
* reassembled the packet and sequence is contiguous
|
* reassembled the packet and sequence is contiguous
|
||||||
@ -1754,26 +1740,25 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
|||||||
* below low watermark and set start to the next frag or
|
* below low watermark and set start to the next frag or
|
||||||
* clear start ptr.
|
* clear start ptr.
|
||||||
*/
|
*/
|
||||||
if (nextf != NULL &&
|
if (nextf != (struct sk_buff *)&mp->frags &&
|
||||||
((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
|
((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
|
||||||
/* if we just reassembled and the next one is here,
|
/* if we just reassembled and the next one is here,
|
||||||
* then start another reassembly. */
|
* then start another reassembly.
|
||||||
|
*/
|
||||||
if (frag == NULL) {
|
if (frag == NULL) {
|
||||||
if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
|
if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
|
||||||
start = nextf;
|
start = nextf;
|
||||||
else
|
else {
|
||||||
{
|
printk(KERN_WARNING"isdn_mppp(seq %d):"
|
||||||
printk(KERN_WARNING"isdn_mppp(seq %d):"
|
" END flag with no following "
|
||||||
" END flag with no following "
|
"BEGIN", thisseq);
|
||||||
"BEGIN", thisseq);
|
|
||||||
stats->seqerrs++;
|
stats->seqerrs++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
} else {
|
if (nextf != (struct sk_buff *)&mp->frags &&
|
||||||
if ( nextf != NULL && frag != NULL &&
|
frag != NULL &&
|
||||||
MP_LT(thisseq, minseq)) {
|
MP_LT(thisseq, minseq)) {
|
||||||
/* we've got a break in the sequence
|
/* we've got a break in the sequence
|
||||||
* and we not at the end yet
|
* and we not at the end yet
|
||||||
* and we did not just reassembled
|
* and we did not just reassembled
|
||||||
@ -1782,41 +1767,39 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
|
|||||||
* discard all the frames below low watermark
|
* discard all the frames below low watermark
|
||||||
* and start over */
|
* and start over */
|
||||||
stats->frame_drops++;
|
stats->frame_drops++;
|
||||||
mp->frags = isdn_ppp_mp_discard(mp,start,nextf);
|
isdn_ppp_mp_discard(mp, start, nextf);
|
||||||
}
|
}
|
||||||
/* break in the sequence, no reassembly */
|
/* break in the sequence, no reassembly */
|
||||||
start = NULL;
|
start = NULL;
|
||||||
}
|
}
|
||||||
|
if (!start)
|
||||||
frag = nextf;
|
break;
|
||||||
} /* while -- main loop */
|
}
|
||||||
|
|
||||||
if (mp->frags == NULL)
|
check_overflow:
|
||||||
mp->frags = frag;
|
|
||||||
|
|
||||||
/* rather straighforward way to deal with (not very) possible
|
/* rather straighforward way to deal with (not very) possible
|
||||||
* queue overflow */
|
* queue overflow
|
||||||
|
*/
|
||||||
if (mp->frames > MP_MAX_QUEUE_LEN) {
|
if (mp->frames > MP_MAX_QUEUE_LEN) {
|
||||||
stats->overflows++;
|
stats->overflows++;
|
||||||
while (mp->frames > MP_MAX_QUEUE_LEN) {
|
skb_queue_walk_safe(&mp->frags, frag, nextf) {
|
||||||
frag = mp->frags->next;
|
if (mp->frames <= MP_MAX_QUEUE_LEN)
|
||||||
isdn_ppp_mp_free_skb(mp, mp->frags);
|
break;
|
||||||
mp->frags = frag;
|
__skb_unlink(frag, &mp->frags);
|
||||||
|
isdn_ppp_mp_free_skb(mp, frag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&mp->lock, flags);
|
spin_unlock_irqrestore(&mp->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
|
static void isdn_ppp_mp_cleanup(isdn_net_local *lp)
|
||||||
{
|
{
|
||||||
struct sk_buff * frag = lp->netdev->pb->frags;
|
struct sk_buff *skb, *tmp;
|
||||||
struct sk_buff * nextfrag;
|
|
||||||
while( frag ) {
|
skb_queue_walk_safe(&lp->netdev->pb->frags, skb, tmp) {
|
||||||
nextfrag = frag->next;
|
__skb_unlink(skb, &lp->netdev->pb->frags);
|
||||||
isdn_ppp_mp_free_skb(lp->netdev->pb, frag);
|
isdn_ppp_mp_free_skb(lp->netdev->pb, skb);
|
||||||
frag = nextfrag;
|
|
||||||
}
|
}
|
||||||
lp->netdev->pb->frags = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 isdn_ppp_mp_get_seq( int short_seq,
|
static u32 isdn_ppp_mp_get_seq( int short_seq,
|
||||||
@ -1853,72 +1836,115 @@ static u32 isdn_ppp_mp_get_seq( int short_seq,
|
|||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
|
static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from,
|
||||||
struct sk_buff * from, struct sk_buff * to )
|
struct sk_buff *to)
|
||||||
{
|
{
|
||||||
if( from )
|
if (from) {
|
||||||
while (from != to) {
|
struct sk_buff *skb, *tmp;
|
||||||
struct sk_buff * next = from->next;
|
int freeing = 0;
|
||||||
isdn_ppp_mp_free_skb(mp, from);
|
|
||||||
from = next;
|
skb_queue_walk_safe(&mp->frags, skb, tmp) {
|
||||||
|
if (skb == to)
|
||||||
|
break;
|
||||||
|
if (skb == from)
|
||||||
|
freeing = 1;
|
||||||
|
if (!freeing)
|
||||||
|
continue;
|
||||||
|
__skb_unlink(skb, &mp->frags);
|
||||||
|
isdn_ppp_mp_free_skb(mp, skb);
|
||||||
}
|
}
|
||||||
return from;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
|
static unsigned int calc_tot_len(struct sk_buff_head *queue,
|
||||||
struct sk_buff * from, struct sk_buff * to )
|
struct sk_buff *from, struct sk_buff *to)
|
||||||
{
|
{
|
||||||
ippp_bundle * mp = net_dev->pb;
|
unsigned int tot_len = 0;
|
||||||
int proto;
|
struct sk_buff *skb;
|
||||||
struct sk_buff * skb;
|
int found_start = 0;
|
||||||
|
|
||||||
|
skb_queue_walk(queue, skb) {
|
||||||
|
if (skb == from)
|
||||||
|
found_start = 1;
|
||||||
|
if (!found_start)
|
||||||
|
continue;
|
||||||
|
tot_len += skb->len - MP_HEADER_LEN;
|
||||||
|
if (skb == to)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return tot_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reassemble packet using fragments in the reassembly queue from
|
||||||
|
* 'from' until 'to', inclusive.
|
||||||
|
*/
|
||||||
|
static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp,
|
||||||
|
struct sk_buff *from, struct sk_buff *to,
|
||||||
|
u32 lastseq)
|
||||||
|
{
|
||||||
|
ippp_bundle *mp = net_dev->pb;
|
||||||
unsigned int tot_len;
|
unsigned int tot_len;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int proto;
|
||||||
|
|
||||||
if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
|
if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
|
||||||
printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
|
printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
|
||||||
__func__, lp->ppp_slot);
|
__func__, lp->ppp_slot);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
|
|
||||||
if( ippp_table[lp->ppp_slot]->debug & 0x40 )
|
tot_len = calc_tot_len(&mp->frags, from, to);
|
||||||
|
|
||||||
|
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, "
|
printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "
|
||||||
"len %d\n", MP_SEQ(from), from->len );
|
"len %d\n", MP_SEQ(from), from->len);
|
||||||
skb = from;
|
skb = from;
|
||||||
skb_pull(skb, MP_HEADER_LEN);
|
skb_pull(skb, MP_HEADER_LEN);
|
||||||
|
__skb_unlink(skb, &mp->frags);
|
||||||
mp->frames--;
|
mp->frames--;
|
||||||
} else {
|
} else {
|
||||||
struct sk_buff * frag;
|
struct sk_buff *walk, *tmp;
|
||||||
int n;
|
int found_start = 0;
|
||||||
|
|
||||||
for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
|
if (ippp_table[lp->ppp_slot]->debug & 0x40)
|
||||||
tot_len += frag->len - MP_HEADER_LEN;
|
|
||||||
|
|
||||||
if( ippp_table[lp->ppp_slot]->debug & 0x40 )
|
|
||||||
printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
|
printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
|
||||||
"to %d, len %d\n", MP_SEQ(from),
|
"to %d, len %d\n", MP_SEQ(from), lastseq,
|
||||||
(MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
|
tot_len);
|
||||||
if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
|
|
||||||
|
skb = dev_alloc_skb(tot_len);
|
||||||
|
if (!skb)
|
||||||
printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
|
printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
|
||||||
"of size %d\n", tot_len);
|
"of size %d\n", tot_len);
|
||||||
isdn_ppp_mp_discard(mp, from, to);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while( from != to ) {
|
found_start = 0;
|
||||||
unsigned int len = from->len - MP_HEADER_LEN;
|
skb_queue_walk_safe(&mp->frags, walk, tmp) {
|
||||||
|
if (walk == from)
|
||||||
|
found_start = 1;
|
||||||
|
if (!found_start)
|
||||||
|
continue;
|
||||||
|
|
||||||
skb_copy_from_linear_data_offset(from, MP_HEADER_LEN,
|
if (skb) {
|
||||||
skb_put(skb,len),
|
unsigned int len = walk->len - MP_HEADER_LEN;
|
||||||
len);
|
skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN,
|
||||||
frag = from->next;
|
skb_put(skb, len),
|
||||||
isdn_ppp_mp_free_skb(mp, from);
|
len);
|
||||||
from = frag;
|
}
|
||||||
|
__skb_unlink(walk, &mp->frags);
|
||||||
|
isdn_ppp_mp_free_skb(mp, walk);
|
||||||
|
|
||||||
|
if (walk == to)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
proto = isdn_ppp_strip_proto(skb);
|
proto = isdn_ppp_strip_proto(skb);
|
||||||
isdn_ppp_push_higher(net_dev, lp, skb, proto);
|
isdn_ppp_push_higher(net_dev, lp, skb, proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
|
static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
mp->frames--;
|
mp->frames--;
|
||||||
|
@ -157,7 +157,7 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int mp_mrru; /* unused */
|
int mp_mrru; /* unused */
|
||||||
struct sk_buff * frags; /* fragments sl list -- use skb->next */
|
struct sk_buff_head frags; /* fragments sl list */
|
||||||
long frames; /* number of frames in the frame list */
|
long frames; /* number of frames in the frame list */
|
||||||
unsigned int seq; /* last processed packet seq #: any packets
|
unsigned int seq; /* last processed packet seq #: any packets
|
||||||
* with smaller seq # will be dropped
|
* with smaller seq # will be dropped
|
||||||
|
Loading…
Reference in New Issue
Block a user