[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20120710032710.GQ19462@kvack.org>
Date: Mon, 9 Jul 2012 23:27:10 -0400
From: Benjamin LaHaise <bcrl@...ck.org>
To: James Chapman <jchapman@...alix.com>
Cc: netdev@...r.kernel.org, linux-ppp@...r.kernel.org
Subject: [RFC PATCH v2 net-next] Re: [RFC PATCH] ppp: add support for L2 multihop / tunnel switching
Hello all,
Here is v2 of the PPP multihop patch. This version adds a notifier hook to
make sure that the multihop reference is dropped when the multihop target
gets unregistered, ensuring that the references are properly dropped witout
leaking the devices.
-ben
Not-yet-signed-off-by: Benjamin LaHaise <bcrl@...ck.org>
drivers/net/ppp/ppp_generic.c | 119 ++++++++++++++++++++++++++++++++++++++++--
include/linux/if_ether.h | 1
include/linux/ppp-ioctl.h | 1
3 files changed, 118 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 5c05572..6dc7eff 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -121,6 +121,7 @@ struct ppp {
unsigned long last_xmit; /* jiffies when last pkt sent 9c */
unsigned long last_recv; /* jiffies when last pkt rcvd a0 */
struct net_device *dev; /* network interface device a4 */
+ struct net_device *multihop_if; /* if to forward incoming frames to */
int closing; /* is device closing down? a8 */
#ifdef CONFIG_PPP_MULTILINK
int nxchan; /* next channel to send something on */
@@ -272,6 +273,7 @@ static void unit_put(struct idr *p, int n);
static void *unit_find(struct idr *p, int n);
static struct class *ppp_class;
+static const struct net_device_ops ppp_netdev_ops;
/* per net-namespace data */
static inline struct ppp_net *ppp_pernet(struct net *net)
@@ -380,8 +382,9 @@ static int ppp_release(struct inode *unused, struct file *file)
file->private_data = NULL;
if (pf->kind == INTERFACE) {
ppp = PF_TO_PPP(pf);
- if (file == ppp->owner)
+ if (file == ppp->owner) {
ppp_shutdown_interface(ppp);
+ }
}
if (atomic_dec_and_test(&pf->refcnt)) {
switch (pf->kind) {
@@ -553,6 +556,41 @@ static int get_filter(void __user *arg, struct sock_filter **p)
}
#endif /* CONFIG_PPP_FILTER */
+static int ppp_multihop_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct net_device *event_dev = (struct net_device *)ptr;
+ struct net_device *master = event_dev->master;
+ struct ppp *ppp;
+
+ if (event_dev->netdev_ops != &ppp_netdev_ops)
+ return NOTIFY_DONE;
+ if (!master || (master->netdev_ops != &ppp_netdev_ops))
+ return NOTIFY_DONE;
+
+ ppp = netdev_priv(master);
+
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ ppp_lock(ppp);
+ BUG_ON(ppp->multihop_if != event_dev);
+ ppp->multihop_if = NULL;
+ netdev_set_master(event_dev, NULL);
+ ppp_unlock(ppp);
+ dev_put(event_dev);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ppp_multihop_notifier = {
+ .notifier_call = ppp_multihop_event,
+};
+
static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct ppp_file *pf = file->private_data;
@@ -738,6 +776,46 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
err = 0;
break;
+ case PPPIOCSMULTIHOP_IF:
+ {
+ struct net_device *multihop_if;
+ if (get_user(val, p))
+ break;
+ rtnl_lock();
+ ppp_lock(ppp);
+ err = 0;
+ multihop_if = ppp->multihop_if;
+ if (multihop_if && (val == -1)) {
+ ppp->multihop_if = NULL;
+ BUG_ON(multihop_if->master != ppp->dev);
+ netdev_set_master(multihop_if, NULL);
+ goto out_multihop;
+ }
+ err = -EBUSY;
+ multihop_if = NULL;
+ if (ppp->multihop_if)
+ goto out_multihop;
+ multihop_if = dev_get_by_index(&init_net, val);
+ err = -ENOENT;
+ if (!multihop_if)
+ goto out_multihop;
+ err = -EINVAL;
+ if (multihop_if->netdev_ops != &ppp_netdev_ops)
+ goto out_multihop;
+ err = netdev_set_master(multihop_if, ppp->dev);
+ if (err)
+ goto out_multihop;
+ ppp->multihop_if = multihop_if;
+ multihop_if = NULL;
+ err = 0;
+out_multihop:
+ ppp_unlock(ppp);
+ rtnl_unlock();
+ if (multihop_if)
+ dev_put(multihop_if);
+ break;
+ }
+
#ifdef CONFIG_PPP_FILTER
case PPPIOCSPASS:
{
@@ -901,6 +979,7 @@ static int __init ppp_init(void)
pr_info("PPP generic driver version " PPP_VERSION "\n");
+ register_netdevice_notifier(&ppp_multihop_notifier);
err = register_pernet_device(&ppp_net_ops);
if (err) {
pr_err("failed to register PPP pernet device (%d)\n", err);
@@ -942,6 +1021,9 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
int npi, proto;
unsigned char *pp;
+ if (skb->protocol == htons(ETH_P_PPP))
+ goto queue;
+
npi = ethertype_to_npindex(ntohs(skb->protocol));
if (npi < 0)
goto outf;
@@ -968,6 +1050,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
proto = npindex_to_proto[npi];
put_unaligned_be16(proto, pp);
+queue:
skb_queue_tail(&ppp->file.xq, skb);
ppp_xmit_process(ppp);
return NETDEV_TX_OK;
@@ -1131,6 +1214,9 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
int len;
unsigned char *cp;
+ if (skb->protocol == htons(ETH_P_PPP))
+ goto xmit;
+
if (proto < 0x8000) {
#ifdef CONFIG_PPP_FILTER
/* check if we should pass this packet */
@@ -1228,6 +1314,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
return;
}
+xmit:
ppp->xmit_pending = skb;
ppp_push(ppp);
return;
@@ -1259,7 +1346,8 @@ ppp_push(struct ppp *ppp)
return;
}
- if ((ppp->flags & SC_MULTILINK) == 0) {
+ if (((ppp->flags & SC_MULTILINK) == 0) ||
+ (skb->protocol == htons(ETH_P_PPP))) {
/* not doing multilink: send it down the first channel */
list = list->next;
pch = list_entry(list, struct channel, clist);
@@ -1599,6 +1687,14 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
goto done;
}
+ if (pch->ppp && pch->ppp->multihop_if) {
+ skb->protocol = htons(ETH_P_PPP);
+ skb->dev = pch->ppp->multihop_if;
+ skb->ip_summed = CHECKSUM_NONE;
+ dev_queue_xmit(skb);
+ goto done;
+ }
+
proto = PPP_PROTO(skb);
if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
/* put it on the channel queue */
@@ -2709,18 +2805,28 @@ static void ppp_shutdown_interface(struct ppp *ppp)
{
struct ppp_net *pn;
+ rtnl_lock();
pn = ppp_pernet(ppp->ppp_net);
mutex_lock(&pn->all_ppp_mutex);
/* This will call dev_close() for us. */
ppp_lock(ppp);
if (!ppp->closing) {
+ struct net_device *multihop_if = ppp->multihop_if;
ppp->closing = 1;
+ ppp->multihop_if = NULL;
ppp_unlock(ppp);
+ if (multihop_if)
+ netdev_set_master(multihop_if, NULL);
+ rtnl_unlock();
unregister_netdev(ppp->dev);
unit_put(&pn->units_idr, ppp->file.index);
- } else
+ if (multihop_if)
+ dev_put(multihop_if);
+ } else {
ppp_unlock(ppp);
+ rtnl_unlock();
+ }
ppp->file.dead = 1;
ppp->owner = NULL;
@@ -2764,6 +2870,12 @@ static void ppp_destroy_interface(struct ppp *ppp)
#endif /* CONFIG_PPP_FILTER */
kfree_skb(ppp->xmit_pending);
+ if (ppp->multihop_if) {
+ struct net_device *multihop_if = ppp->multihop_if;
+ ppp->multihop_if = NULL;
+ netdev_set_master(multihop_if, NULL);
+ dev_put(multihop_if);
+ }
free_netdev(ppp->dev);
}
@@ -2901,6 +3013,7 @@ static void __exit ppp_cleanup(void)
device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
class_destroy(ppp_class);
unregister_pernet_device(&ppp_net_ops);
+ unregister_netdevice_notifier(&ppp_multihop_notifier);
}
/*
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 167ce5b..fe47a70 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -120,6 +120,7 @@
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
+#define ETH_P_PPP 0x00F8 /* Dummy type for PPP multihop */
/*
* This is an Ethernet frame header.
diff --git a/include/linux/ppp-ioctl.h b/include/linux/ppp-ioctl.h
index 2d9a885..5571375 100644
--- a/include/linux/ppp-ioctl.h
+++ b/include/linux/ppp-ioctl.h
@@ -81,6 +81,7 @@ struct pppol2tp_ioc_stats {
* Ioctl definitions.
*/
+#define PPPIOCSMULTIHOP_IF _IOWR('t', 91, int) /* set multihop if */
#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */
#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */
#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists