Commit bb457b47 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN/PPP: Separate out and rewrite MPPP code

The MPPP code was badly broken by the previous interface changes for ISDN
network interfaces and sync-PPP, and in need of a serious cleanup. Now
it's basically mostly rewritten, in a separate file but only lightly
tested.
parent 38bfd3a1
......@@ -21,6 +21,7 @@ isdn-objs := isdn_net.o isdn_net_lib.o \
isdn-objs-$(CONFIG_ISDN_PPP) += isdn_ppp.o isdn_ppp_ccp.o
isdn-objs-$(CONFIG_ISDN_PPP_VJ) += isdn_ppp_vj.o
isdn-objs-$(CONFIG_ISDN_MPP) += isdn_ppp_mp.o
isdn-objs-$(CONFIG_ISDN_X25) += isdn_concap.o isdn_x25iface.o
isdn-objs-$(CONFIG_ISDN_AUDIO) += isdn_audio.o
isdn-objs-$(CONFIG_ISDN_TTY_FAX) += isdn_ttyfax.o
......
......@@ -459,11 +459,12 @@ isdn_net_addslave(char *parm)
rtnl_lock();
if (netif_running(&mlp->dev))
return -EBUSY;
if (netif_running(&mlp->dev)) {
retval = -EBUSY;
goto out;
}
retval = isdn_net_addif(p, mlp);
out:
rtnl_unlock();
return retval;
}
......@@ -814,6 +815,29 @@ isdn_net_dial_out(char *name)
return isdn_net_dial(idev);
}
static int
__isdn_net_dial_slave(isdn_net_local *mlp)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &mlp->slaves, slaves) {
if (isdn_net_dial(idev) == 0)
return 0;
}
return -EBUSY;
}
static int
isdn_net_dial_slave(char *name)
{
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
return __isdn_net_dial_slave(idev->mlp);
}
/*
* Force a hangup of a network-interface.
*/
......@@ -972,22 +996,20 @@ isdn_net_ioctl(struct inode *ino, struct file *file, uint cmd, ulong arg)
}
retval = isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
break;
#ifdef CONFIG_ISDN_PPP
case IIOCNETALN: /* Add link */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_ppp_dial_slave(name);
retval = isdn_net_dial_slave(name);
break;
case IIOCNETDLN: /* Delete link */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_ppp_hangup_slave(name);
retval = isdn_net_force_hangup(name);
break;
#endif
default:
retval = -ENOTTY;
}
......@@ -1348,22 +1370,6 @@ isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
return 0;
}
static void
isdn_net_dial_slave(isdn_net_local *mlp)
{
isdn_net_dev *idev;
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
return;
list_for_each_entry(idev, &mlp->slaves, slaves) {
if (fsm_event(&idev->fi, EV_DO_DIAL, NULL) != -ESRCH) {
break;
}
}
}
static int
accept_icall(struct fsm_inst *fi, int pr, void *arg)
{
......@@ -2224,7 +2230,8 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
} else {
/* subsequent overload: if slavedelay exceeded, start dialing */
if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
isdn_net_dial_slave(mlp);
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_AUTO)
__isdn_net_dial_slave(mlp);
}
}
} else {
......
This diff is collapsed.
......@@ -27,6 +27,12 @@ isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,
int
isdn_ppp_strip_proto(struct sk_buff *skb, u16 *proto);
void
ippp_xmit(isdn_net_dev *idev, struct sk_buff *skb, u16 proto);
void
ippp_receive(isdn_net_dev *idev, struct sk_buff *skb, u16 proto);
struct sk_buff *
isdn_ppp_dev_alloc_skb(void *priv, int len, int gfp_mask);
......
#include "isdn_ppp_mp.h"
#include "isdn_ppp_ccp.h"
#include "isdn_common.h"
#include "isdn_net.h"
#include "isdn_ppp.h"
int
ippp_mp_bind(isdn_net_dev *idev)
{
isdn_net_local *lp = idev->mlp;
/* seq no last seen, maybe set to bundle min, when joining? */
idev->mp_rxseq = 0;
if (!list_empty(&lp->online))
return 0;
/* first channel for this link, do some setup */
lp->mp_cfg = 0; /* MPPP configuration */
lp->mp_txseq = 0; /* MPPP tx sequence number */
lp->mp_rxseq = (u32) -1;
skb_queue_head_init(&lp->mp_frags);
return 0;
}
int
ippp_mp_bundle(isdn_net_dev *idev, int unit)
{
isdn_net_local *lp = idev->mlp;
char ifn[IFNAMSIZ + 1];
isdn_net_dev *n_idev;
printk(KERN_DEBUG "%s: %s: slave unit: %d\n",
__FUNCTION__, idev->name, unit);
sprintf(ifn, "ippp%d", unit);
list_for_each_entry(n_idev, &lp->slaves, slaves) {
if (strcmp(n_idev->name, ifn) == 0)
goto found;
}
printk(KERN_INFO "%s: cannot find %s\n", __FUNCTION__, ifn);
return -ENODEV;
found:
if (!n_idev->ipppd) {
printk(KERN_INFO "%s: no ipppd?\n", __FUNCTION__);
return -ENXIO;
}
n_idev->pppcfg |= SC_ENABLE_IP;
isdn_net_online(n_idev);
return 0;
}
void
ippp_mp_disconnected(isdn_net_dev *idev)
{
isdn_net_local *lp = idev->mlp;
if (!list_empty(&lp->online))
return;
/* we're the last link going down */
skb_queue_purge(&lp->mp_frags);
}
void
ippp_mp_xmit(isdn_net_dev *idev, struct sk_buff *skb, u16 proto)
{
isdn_net_local *lp = idev->mlp;
unsigned char *p;
long txseq;
if (!(lp->mp_cfg & SC_MP_PROT)) {
return ippp_xmit(idev, skb, proto);
}
/* we could do something smarter than just sending
* the complete packet as fragment... */
txseq = lp->mp_txseq++;
if (lp->mp_cfg & SC_OUT_SHORT_SEQ) {
/* sequence number: 12bit */
p = skb_push(skb, 3);
p[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((txseq >> 8) & 0xf);
p[1] = txseq & 0xff;
p[2] = proto;
} else {
/* sequence number: 24bit */
p = skb_push(skb, 5);
p[0] = MP_BEGIN_FRAG | MP_END_FRAG;
p[1] = (txseq >> 16) & 0xff;
p[2] = (txseq >> 8) & 0xff;
p[3] = (txseq >> 0) & 0xff;
p[4] = proto;
}
proto = PPP_MP;
skb = ippp_ccp_compress(idev->ccp, skb, &proto);
ippp_xmit(idev, skb, proto);
}
static void mp_receive(isdn_net_dev *idev, struct sk_buff *skb);
void
ippp_mp_receive(isdn_net_dev *idev, struct sk_buff *skb, u16 proto)
{
isdn_net_local *lp = idev->mlp;
{static int cnt; if (cnt++ % 7 == 0) return;}
if (lp->mp_cfg & SC_REJ_MP_PROT)
goto out;
skb = ippp_ccp_decompress(idev->ccp, skb, &proto);
if (!skb)
goto drop;
if (proto == PPP_MP)
return mp_receive(idev, skb);
out:
return ippp_receive(idev, skb, proto);
drop:
lp->stats.rx_errors++;
kfree_skb(skb);
}
#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)
static void
print_recv_pkt(int slot, struct sk_buff *skb)
{
printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n",
slot, skb->len,
skb->data[0], skb->data[1], skb->data[2],
skb->data[3], skb->data[4], skb->data[5]);
}
#define MP_SEQUENCE(skb) (skb)->priority
#define MP_FLAGS(skb) (skb)->cb[0]
static u32
get_seq(struct sk_buff *skb, u32 last_seq, int short_seq)
{
u32 seq;
u16 shseq;
u8 flags;
int delta;
get_u8(skb->data, &flags);
if (short_seq) {
/* convert 12-bit short seq number to 24-bit long one */
get_u16(skb->data, &shseq);
delta = (shseq & MP_SHORTSEQ_MASK) -
(last_seq & MP_SHORTSEQ_MASK);
/* check for seqence wrap */
if (delta < 0)
delta += MP_SHORTSEQ_MAX + 1;
seq = last_seq + delta;
skb_pull(skb, 2);
} else {
get_u32(skb->data, &seq);
skb_pull(skb, 4);
}
seq &= MP_LONGSEQ_MASK;
MP_SEQUENCE(skb) = seq;
MP_FLAGS(skb) = flags;
return seq;
}
static int
mp_insert_frag(struct sk_buff_head *frags, struct sk_buff *skb)
{
struct sk_buff *p;
/* If our queue of not yet reassembled fragments grows too
large, throw away the oldest fragment */
if (skb_queue_len(frags) > MP_MAX_QUEUE_LEN)
kfree_skb(skb_dequeue(frags));
for (p = frags->next; p != (struct sk_buff *) frags; p = p->next) {
if (MP_LE(MP_SEQUENCE(skb), MP_SEQUENCE(p)))
break;
}
/* duplicate ? */
if (MP_SEQUENCE(skb) == MP_SEQUENCE(p))
return -EBUSY;
__skb_insert(skb, p->prev, p, frags);
return 0;
}
struct sk_buff *
mp_complete_seq(isdn_net_local *lp, struct sk_buff *b, struct sk_buff *e)
{
struct sk_buff *p, *n, *skb;
int len = 0;
if (b->next == e) {
/* sequence with only one frag */
HERE;
skb_unlink(b);
return b;
}
for (p = b, n = p->next; p != e; p = n, n = p->next ) {
len += p->len;
}
// FIXME check against mrru?
skb = dev_alloc_skb(len);
if (!skb)
lp->stats.rx_errors++;
for (p = b, n = p->next; p != e; p = n, n = p->next ) {
if (skb)
memcpy(skb_put(skb, p->len), p->data, p->len);
skb_unlink(p);
kfree_skb(p);
}
return skb;
}
struct sk_buff *
mp_reassemble(isdn_net_local *lp)
{
struct sk_buff_head *frags = &lp->mp_frags;
struct sk_buff *p, *n, *pp, *start;
u32 min_seq = lp->mp_rxseq;
u32 next_seq = 0;
again:
start = NULL;
for (p = frags->next, n = p->next; p != (struct sk_buff *) frags; p = n, n = p->next ) {
if (!start) {
if (MP_FLAGS(p) & MP_BEGIN_FRAG) {
start = p;
next_seq = MP_SEQUENCE(p);
} else {
/* start frag is missing */
goto frag_missing;
}
}
/* we've seen the first fragment of this series */
if (MP_SEQUENCE(p) != next_seq) {
/* previous frag is missing */
goto frag_missing;
}
if (MP_FLAGS(p) & MP_END_FRAG) {
/* we got a full sequence */
return mp_complete_seq(lp, start, p->next);
}
next_seq = MP_SEQUENCE(p) + 1;
}
return NULL;
frag_missing:
if (MP_SEQUENCE(p) - 1 > min_seq)
/* may come later */
return NULL;
/* for all fragments up to p */
p = p->next;
for (pp = frags->next, n = pp->next; pp != p; pp = n, n = pp->next ) {
skb_unlink(pp);
kfree_skb(pp);
lp->stats.rx_errors++;
}
goto again;
}
static void
mp_receive(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_local *lp = idev->mlp;
isdn_net_dev *qdev;
struct sk_buff_head *frags = &lp->mp_frags;
u32 seq;
u16 proto;
print_recv_pkt(-1, skb);
if (skb->len < (lp->mp_cfg & SC_IN_SHORT_SEQ ? 2 : 4))
goto drop;
seq = get_seq(skb, idev->mp_rxseq, lp->mp_cfg & SC_IN_SHORT_SEQ);
idev->mp_rxseq = seq;
if (lp->mp_rxseq == (u32) -1) {
/* first packet */
lp->mp_rxseq = seq;
}
if (MP_LT(seq, lp->mp_rxseq)) {
goto drop;
}
/* Find the minimum sequence number received over all channels.
* No fragments with numbers lower than this will arrive later. */
lp->mp_rxseq = seq;
list_for_each_entry(qdev, &lp->online, online) {
if (MP_LT(qdev->mp_rxseq, lp->mp_rxseq))
lp->mp_rxseq = qdev->mp_rxseq;
}
/* Insert the skb into the list of received fragments, ordered by
* sequence number */
if (mp_insert_frag(frags, skb))
goto drop;
while ((skb = mp_reassemble(lp))) {
if (isdn_ppp_strip_proto(skb, &proto)) {
kfree_skb(skb);
continue;
}
ippp_receive(idev, skb, proto);
}
return;
drop:
lp->stats.rx_errors++;
kfree_skb(skb);
}
#ifndef __ISDN_PPP_MP_H__
#define __ISDN_PPP_MP_H__
#include <linux/kernel.h>
#include <linux/isdn.h>
#ifdef CONFIG_ISDN_MPP
int ippp_mp_bind(isdn_net_dev *idev);
void ippp_mp_disconnected(isdn_net_dev *idev);
int ippp_mp_bundle(isdn_net_dev *idev, int val);
void ippp_mp_xmit(isdn_net_dev *idev, struct sk_buff *skb, u16 proto);
void ippp_mp_receive(isdn_net_dev *idev, struct sk_buff *skb, u16 proto);
#else
static inline int
ippp_mp_bind(isdn_net_dev *idev)
{
return 0;
}
static void
ippp_mp_disconnected(isdn_net_dev *idev)
{
}
static inline int
ippp_mp_bundle(isdn_net_dev *idev, int val)
{
return -EINVAL;
}
static inline void
ippp_mp_xmit(isdn_net_dev *idev, struct sk_buff *skb, u16 proto)
{
ippp_xmit(idev, skb, proto);
}
static inline void
ippp_mp_receive(isdn_net_dev *idev, struct sk_buff *skb, u16 proto)
{
ippp_receive(idev, skb, proto);
}
#endif
#endif
......@@ -312,6 +312,13 @@ struct isdn_netif_ops {
void (*close)(struct isdn_net_local_s *);
};
typedef struct {
unsigned long seqerrs;
unsigned long frame_drops;
unsigned long overflows;
unsigned long max_queue_len;
} isdn_mppp_stats;
/* Local interface-data */
typedef struct isdn_net_local_s {
ulong magic;
......@@ -357,8 +364,12 @@ typedef struct isdn_net_local_s {
struct concap_device_ops *dops; /* callbacks used by encapsulator */
#endif
#ifdef CONFIG_ISDN_PPP
unsigned int mpppcfg;
long mp_seqno;
unsigned int mp_cfg;
u32 mp_txseq;
struct sk_buff_head mp_frags; /* fragments sl list */
u32 mp_rxseq; /* last processed packet seq #: any
packets with smaller seq # will
be dropped unconditionally */
struct ippp_ccp *ccp;
unsigned long debug;
#ifdef CONFIG_ISDN_PPP_VJ
......@@ -427,7 +438,7 @@ typedef struct isdn_net_dev_s {
struct list_head global_list; /* global list of all isdn_net_devs */
#ifdef CONFIG_ISDN_PPP
unsigned int pppcfg;
unsigned int pppseq; /* last seq no seen */
u32 mp_rxseq; /* last seq no seen on this channel */
struct ippp_ccp *ccp;
unsigned long debug;
......
......@@ -130,24 +130,8 @@ struct isdn_ppp_compressor {
extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
typedef struct {
unsigned long seqerrs;
unsigned long frame_drops;
unsigned long overflows;
unsigned long max_queue_len;
} isdn_mppp_stats;
typedef struct {
int mp_mrru; /* unused */
struct sk_buff * frags; /* fragments sl list -- use skb->next */
long frames; /* number of frames in the frame list */
unsigned int seq; /* last processed packet seq #: any packets
* with smaller seq # will be dropped
* unconditionally */
spinlock_t lock;
int ref_ct;
/* statistics */
isdn_mppp_stats stats;
} ippp_bundle;
#define IPPP_MAX_RQ_LEN 8
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment