(Note: vger hasn't processed my subscription request for linux-ppp yet,
so please cc: me on any resposnes. Thanks)
Hi! This is a first crack at implementing PPP-over-ATM using the new
clean PPP code introduced in kernel 2.3.13. For those on the ppp list
who aren't familiar with linux's ATM support, see:
http://icawww1.epfl.ch/linux-atm/
For those of you not familiar with PPP-over-ATM, see RFC 2364. The
protocol is becoming popular in DSL deployments (both in the provider
side and in some cases the CPE side) For quite some time when people
asked the linux-atm list about PPPoATM the standard response was "yuk -
the PPP code would need to be cleaned up first, it's too gross to hack on".
Unfortunately for us Mr. Mackerras seems to have called our bluff, and
now I guess we have to code something :-) The new PPP code is *so*
much nicer to work with.
A few important notes:
* It does not work as-is. This is only the kernel-side stuff, there
still need to be non-trivial modifications made to pppd in order to
get a working connection. Guess what my next project is? :-)
This release is mainly so both the PPP and the ATM camps can look
at it and yell at me before I screw up more.
* It is untested. I don't currently have access to any machine capable
of running 2.3.13, or even fully compiling it (I have non-i386 hardware
and those ports aren't all synced up with the MM changes yet) so I can't
even be sure this links. All I know is that I can get object files out
of it. There's probably tons of horrible bugs.
* The patch is versus 2.3.13 + the atm-0.61 patch from Werner. That
patching combination has 3 trivial rejects that need to be massaged by
hand.
I would really like if the PPP people (Paul?) could look at this and tell
me if it's about in the right direction. Since the ppp_generic stuff is
brand new I may have some things wrong.
The one place that is really gross is the ATTACH ioctl. Since the AAL5 PDU
is essentially a datagram socket, there isn't any reason for there to
be a ->connected flag - the ATM interface doesn't touch the PPP stuff until
it is bound to it. pppd can just deal with the raw interface while it is
negotiating the link, the only behavioral difference is that SC_COMP_PROT
doesn't get applied which is not a big deal. Doing it this way simplifies
things alot.
The problem is that there is a flag that needs to get passed in that
describes the encapsulation of the link (whether we're sending raw
PPP frames or if they're LLC encapsulated). My hacked-up solution is
to make a new ioctl PPPIOCATTACHENCAPS that takes an additional
argument of the encapsulation type. That way the PPP channel can
be set up in one step and placed directly in service.
I think the ideal solution would be a media-defendant section to
ATTACH, i.e.
struct ppp_attach_arg {
int unit; /* As before; backward compatible */
int opt_len; /* Length of options */
char options[1]; }; /* Media dependent options */
because I think this problem will come up for other backends. But again,
I'm new to the PPP list so maybe the folks here have a better idea.
Also, while writing this I found some buglets both in the PPP and the ATM
code. I'll send the PPP bugs to that mailing list under separate cover;
I'm still investigating the ATM bugs and will report back to that list
in a few days.
-Mitch
diff -u --recursive --new-file linux-2.3.13+0.61/Documentation/Configure.help
linux-2.3.13+0.61mnb1/Documentation/Configure.help
--- linux-2.3.13+0.61/Documentation/Configure.help Sun Aug 11 12:07:37 2019
+++ linux-2.3.13+0.61mnb1/Documentation/Configure.help Mon Aug 12 02:29:33 2019
@@ -5436,6 +5436,18 @@
into and removed from the running kernel). If you want to compile
it as a module, say M here and read Documentation/modules.txt.
+PPP support for ATM (RFC 2364)
+CONFIG_PPP_ATM
+ Say Y (or M) here if you want to be able to use PPP over ATM/AAL5.
+ This is sometimes needed to connect with DSL gear that hooks up to
+ the computer via an ATM link or uses an ATM-compatible adapter. If
+ your DSL modem hooks up to your computer with a different connection
+ (such as ethernet, USB, or the parallel port) you do NOT need this.
+
+ This code is also available as a module (code which can be inserted
+ into and removed from the running kernel). If you want to compile
+ it as a module, say M here and read Documentation/modules.txt.
+
PPP Deflate compression
CONFIG_PPP_DEFLATE
Support for the Deflate compression method for PPP, which uses the
diff -u --recursive --new-file linux-2.3.13+0.61/drivers/net/Config.in
linux-2.3.13+0.61mnb1/drivers/net/Config.in
--- linux-2.3.13+0.61/drivers/net/Config.in Sun Aug 11 10:32:54 2019
+++ linux-2.3.13+0.61mnb1/drivers/net/Config.in Mon Aug 12 01:36:42 2019
@@ -197,6 +197,9 @@
tristate 'PPP (point-to-point protocol) support' CONFIG_PPP
if [ ! "$CONFIG_PPP" = "n" ]; then
dep_tristate 'PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP
+ if [ ! "$CONFIG_ATM" = "n" ]; then
+ dep_tristate 'PPP support for ATM (RFC 2364)' CONFIG_PPP_ATM $CONFIG_PPP
+ fi
dep_tristate 'PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
dep_tristate 'PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP m
fi
diff -u --recursive --new-file linux-2.3.13+0.61/include/linux/if_ppp.h
linux-2.3.13+0.61mnb1/include/linux/if_ppp.h
--- linux-2.3.13+0.61/include/linux/if_ppp.h Sun Aug 11 10:32:56 2019
+++ linux-2.3.13+0.61mnb1/include/linux/if_ppp.h Mon Aug 12 02:50:27 2019
@@ -105,6 +105,12 @@
struct ppp_comp_stats stats;
};
+/* FOr PPPIOCATTACHENCALS */
+struct ifppp_unit_encaps {
+ int unit;
+ int encaps;
+};
+
#define ifr__name b.ifr_ifrn.ifrn_name
#define stats_ptr b.ifr_ifru.ifru_data
@@ -133,6 +139,7 @@
#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */
#define PPPIOCNEWUNIT _IOWR('t', 62, int) /* create new ppp unit */
#define PPPIOCATTACH _IOW('t', 61, int) /* attach to ppp unit */
+#define PPPIOCATTACHENCAPS _IOW('t', 91, struct ifppp_unit_encaps)
#define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit */
#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0)
diff -u --recursive --new-file linux-2.3.13+0.61/net/atm/Makefile
linux-2.3.13+0.61mnb1/net/atm/Makefile
--- linux-2.3.13+0.61/net/atm/Makefile Sun Aug 11 12:07:37 2019
+++ linux-2.3.13+0.61mnb1/net/atm/Makefile Mon Aug 12 01:41:56 2019
@@ -59,6 +59,11 @@
endif
endif
+ifeq ($(CONFIG_PPP_ATM),y)
+O_OBJS += atmppp.o
+NEED_IPCOM = ipcommon.o
+endif
+
endif
diff -u --recursive --new-file linux-2.3.13+0.61/net/atm/atmppp.c
linux-2.3.13+0.61mnb1/net/atm/atmppp.c
--- linux-2.3.13+0.61/net/atm/atmppp.c Wed Dec 31 18:00:00 1969
+++ linux-2.3.13+0.61mnb1/net/atm/atmppp.c Mon Aug 12 03:14:31 2019
@@ -0,0 +1,194 @@
+/* net/atm/atmppp.c - RFC2364 PPP over ATM/AAL5 */
+
+/* Written 1999 by Mitchell Blank Jr */
+/* Based on clip.c; 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+/* And on ppp_async.c; Copyright 1999 Paul Mackerras */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This driver provides the encapsulation and framing for sending
+ * and receiving PPP frames in ATM AAL5 PDUs.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/if.h>
+#include <asm/system.h> /* save/restore_flags */
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+
+#include "common.h"
+#include "atmppp.h"
+#include "ipcommon.h"
+
+/* TODO - disable before integration */
+#if 1
+#define DPRINTK(format,args...) printk(format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+struct atmppp_vcc {
+ struct atm_vcc *vcc; /* VCC descriptor */
+ void (*old_push)(struct atm_vcc *, struct sk_buff *);
+ /* keep old push fn for detaching */
+ int encaps; /* 0=VC-based, 1=LLC */
+ int flags; /* SC_COMP_PROT - compress protocol */
+ struct ppp_channel chan; /* interface to generic ppp layer */
+};
+#define ATMPPP_VCC(vcc) ((struct atmppp_vcc *) ((vcc)->user_back))
+
+/* Header used for LLC Encapsulated PPP */
+static const unsigned char pppllc[4] = { 0xFE, 0xFE, 0x03, 0xCF };
+
+/* Called when an AAL5 PDU comes in */
+void atmppp_push(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct atmppp_vcc *atmppp_vcc = ATMPPP_VCC(vcc);
+
+ DPRINTK("atmppp push\n");
+ if (!skb) {
+ DPRINTK("removing ATMPPP VCC %p\n",atmppp_vcc);
+ atmppp_vcc->old_push(vcc,NULL); /* pass on the bad news */
+ kfree(atmppp_vcc);
+ return;
+ }
+ atm_return(vcc, skb->truesize);
+ if(atmppp_vcc->encaps) {
+ if(skb->len < sizeof(pppllc) ||
+ memcmp(skb->data, pppllc, sizeof(pppllc))) goto err;
+ skb_pull(skb, sizeof(pppllc));
+ }
+ ppp_input(&atmppp_vcc->chan, skb);
+ return;
+ err:
+ ppp_input_error(&atmppp_vcc->chan, 0);
+}
+
+/* Called by the generic PPP layer when they want to send a packet through us */
+static int ppp_atm_send(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct atmppp_vcc *atmppp_vcc = (struct atmppp_vcc *) chan->private;
+
+ DPRINTK("ppp_atm_send (skb %p)\n", skb);
+ ATM_SKB(skb)->vcc = atmppp_vcc->vcc;
+ DPRINTK("using vcc %p\n", ATM_SKB(skb)->vcc);
+ if (skb->data[0]==0 && atmppp_vcc->flags&SC_COMP_PROT)
+ skb_pull(skb, 1);
+ if (atmppp_vcc->encaps) { /* LLC encapsulation needed */
+ if (skb_headroom(skb) < sizeof(pppllc)) {
+ struct sk_buff *n;
+ n = skb_realloc_headroom(skb, sizeof(pppllc));
+ kfree_skb(skb);
+ if(!(skb = n))
+ return 1;
+ }
+ memcpy(skb_push(skb, sizeof(pppllc)), pppllc, sizeof(pppllc));
+ }
+ atomic_add(skb->truesize, &ATM_SKB(skb)->vcc->tx_inuse);
+ ATM_SKB(skb)->iovcnt = 0;
+ ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options;
+ DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, ATM_SKB(skb)->vcc,
+ ATM_SKB(skb)->vcc->dev);
+ (void) ATM_SKB(skb)->vcc->dev->ops->send(ATM_SKB(skb)->vcc, skb);
+ return 1;
+}
+
+struct ppp_channel_ops atmppp_ops = {
+ ppp_atm_send
+};
+
+/* Called from atm_ioctl to bind a VCC to a PPP interface */
+int atmppp_assign_vcc(struct atm_vcc *vcc, int unit, int encaps)
+{
+ struct atmppp_vcc *atmppp_vcc;
+ struct sk_buff_head copy;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int err;
+
+ if (!vcc->push)
+ return -EBADFD;
+ atmppp_vcc = kmalloc(sizeof(*atmppp_vcc), GFP_KERNEL);
+ if(atmppp_vcc == 0)
+ return -ENOMEM;
+ memset(atmppp_vcc, 0, sizeof(*atmppp_vcc));
+ atmppp_vcc->vcc = vcc;
+ vcc->user_back = atmppp_vcc;
+ atmppp_vcc->old_push = vcc->push;
+ atmppp_vcc->encaps = encaps;
+ atmppp_vcc->chan.private = atmppp_vcc;
+ atmppp_vcc->chan.ops = &atmppp_ops;
+ err = ppp_register_channel(&atmppp_vcc->chan, unit);
+ if (err) {
+ kfree(atmppp_vcc);
+ return err;
+ }
+ MOD_INC_USE_COUNT;
+ save_flags(flags);
+ cli();
+ vcc->push = atmppp_push;
+ skb_migrate(&vcc->recvq, ©);
+ restore_flags(flags);
+ /* re-process waiting packets as PPP PDUs */
+ while ((skb = skb_dequeue(©)))
+ atmppp_push(vcc, skb);
+ return 0;
+}
+
+/* Called from atm_ioctl to unbind a VCC from a PPP interface */
+int atmppp_unassign_vcc(struct atm_vcc *vcc)
+{
+ struct atmppp_vcc *atmppp_vcc;
+ struct sk_buff_head copy;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ if (vcc->push != atmppp_push)
+ return -EBADFD;
+ atmppp_vcc = ATMPPP_VCC(vcc);
+ save_flags(flags);
+ cli();
+ vcc->push = atmppp_vcc->old_push;
+ skb_migrate(&vcc->recvq, ©);
+ restore_flags(flags);
+ ppp_unregister_channel(&atmppp_vcc->chan);
+ MOD_DEC_USE_COUNT;
+ vcc->user_back = 0;
+ kfree(atmppp_vcc);
+ while ((skb = skb_dequeue(©)))
+ kfree_skb(skb);
+ return 0;
+}
+
+/* Called from atm_ioctl to set flags. Note that SC_COMP_PROT is the only
+ * flag currently valid for ATM
+ */
+int atmppp_set_flags(struct atm_vcc *vcc, int *flags)
+{
+ if (vcc->push != atmppp_push)
+ return -EBADFD;
+ ATMPPP_VCC(vcc)->flags = *flags;
+ return 0;
+}
+
+/* Called from atm_ioctl to get flags */
+int atmppp_get_flags(struct atm_vcc *vcc, int *flags)
+{
+ if (vcc->push != atmppp_push)
+ return -EBADFD;
+ *flags = ATMPPP_VCC(vcc)->flags;
+ return 0;
+}
diff -u --recursive --new-file linux-2.3.13+0.61/net/atm/atmppp.h
linux-2.3.13+0.61mnb1/net/atm/atmppp.h
--- linux-2.3.13+0.61/net/atm/atmppp.h Wed Dec 31 18:00:00 1969
+++ linux-2.3.13+0.61mnb1/net/atm/atmppp.h Mon Aug 12 02:20:14 2019
@@ -0,0 +1,23 @@
+/* net/atm/atmppp.h - RFC2364 PPP over ATM/AAL5 */
+
+/* Written 1999 by Mitchell Blank Jr */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * These are the functions exported from atmppp.c for the purpose
+ * of linking up with common.c:atm_ioctl()
+ */
+
+#ifndef _NET_ATM_PPPATM_H
+#define _NET_ATM_PPPATM_H
+
+int atmppp_assign_vcc(struct atm_vcc *, int, int);
+int atmppp_unassign_vcc(struct atm_vcc *);
+int atmppp_set_flags(struct atm_vcc *, int *);
+int atmppp_get_flags(struct atm_vcc *, int *);
+
+#endif
diff -u --recursive --new-file linux-2.3.13+0.61/net/atm/common.c
linux-2.3.13+0.61mnb1/net/atm/common.c
--- linux-2.3.13+0.61/net/atm/common.c Sun Aug 11 12:07:37 2019
+++ linux-2.3.13+0.61mnb1/net/atm/common.c Mon Aug 12 03:00:24 2019
@@ -63,6 +63,12 @@
#endif
#endif
+#if defined(CONFIG_PPP_ATM) || defined(CONFIG_PPP_ATM_MODULE)
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include "atmppp.h"
+#endif
+
#include "resources.h" /* atm_find_dev */
#include "common.h" /* prototypes */
#include "protocols.h" /* atm_init_<transport> */
@@ -689,6 +695,29 @@
if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!atm_tcp_ops.remove_persistent) return -ENOPKG;
return atm_tcp_ops.remove_persistent((int) arg);
+#endif
+#if defined(CONFIG_PPP_ATM) || defined(CONFIG_PPP_ATM_MODULE)
+ case PPPIOCGFLAGS:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if ((error = atmppp_get_flags(vcc, &number)))
+ return error;
+ return put_user(number, (int *) arg) ? -EFAULT : 0;
+ case PPPIOCSFLAGS:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (get_user(number, (int *) arg))
+ return -EFAULT;
+ return atmppp_set_flags(vcc, &number);
+ case PPPIOCATTACHENCAPS:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (get_user(number,
+ &((struct ifppp_unit_encaps *) arg)->unit) ||
+ get_user(len,
+ &((struct ifppp_unit_encaps *) arg)->encaps))
+ return -EFAULT;
+ return atmppp_assign_vcc(vcc, number, len);
+ case PPPIOCDETACH:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ return atmppp_unassign_vcc(vcc);
#endif
default:
break;