|
@@ -1,7 +1,7 @@
|
|
|
/*
|
|
|
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
|
|
|
*
|
|
|
- * Copyright (c) 2011 Volkswagen Group Electronic Research
|
|
|
+ * Copyright (c) 2017 Volkswagen Group Electronic Research
|
|
|
* All rights reserved.
|
|
|
*
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
@@ -59,7 +59,7 @@
|
|
|
#include <net/net_namespace.h>
|
|
|
#include <net/sock.h>
|
|
|
|
|
|
-#define CAN_GW_VERSION "20130117"
|
|
|
+#define CAN_GW_VERSION "20170425"
|
|
|
#define CAN_GW_NAME "can-gw"
|
|
|
|
|
|
MODULE_DESCRIPTION("PF_CAN netlink gateway");
|
|
@@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops,
|
|
|
__stringify(CGW_MAX_HOPS) " hops, "
|
|
|
"default: " __stringify(CGW_DEFAULT_HOPS) ")");
|
|
|
|
|
|
-static HLIST_HEAD(cgw_list);
|
|
|
static struct notifier_block notifier;
|
|
|
-
|
|
|
static struct kmem_cache *cgw_cache __read_mostly;
|
|
|
|
|
|
/* structure that contains the (on-the-fly) CAN frame modifications */
|
|
@@ -438,16 +436,16 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
|
|
|
gwj->handled_frames++;
|
|
|
}
|
|
|
|
|
|
-static inline int cgw_register_filter(struct cgw_job *gwj)
|
|
|
+static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
|
|
|
{
|
|
|
- return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id,
|
|
|
+ return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
|
|
|
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
|
|
|
gwj, "gw", NULL);
|
|
|
}
|
|
|
|
|
|
-static inline void cgw_unregister_filter(struct cgw_job *gwj)
|
|
|
+static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
|
|
|
{
|
|
|
- can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id,
|
|
|
+ can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
|
|
|
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
|
|
|
}
|
|
|
|
|
@@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb,
|
|
|
unsigned long msg, void *ptr)
|
|
|
{
|
|
|
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
|
|
+ struct net *net = dev_net(dev);
|
|
|
|
|
|
- if (!net_eq(dev_net(dev), &init_net))
|
|
|
- return NOTIFY_DONE;
|
|
|
if (dev->type != ARPHRD_CAN)
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
@@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb,
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
- hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
|
|
|
+ hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
|
|
|
|
|
|
if (gwj->src.dev == dev || gwj->dst.dev == dev) {
|
|
|
hlist_del(&gwj->list);
|
|
|
- cgw_unregister_filter(gwj);
|
|
|
+ cgw_unregister_filter(net, gwj);
|
|
|
kmem_cache_free(cgw_cache, gwj);
|
|
|
}
|
|
|
}
|
|
@@ -592,12 +589,13 @@ cancel:
|
|
|
/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
|
|
|
static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
{
|
|
|
+ struct net *net = sock_net(skb->sk);
|
|
|
struct cgw_job *gwj = NULL;
|
|
|
int idx = 0;
|
|
|
int s_idx = cb->args[0];
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- hlist_for_each_entry_rcu(gwj, &cgw_list, list) {
|
|
|
+ hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
|
|
|
if (idx < s_idx)
|
|
|
goto cont;
|
|
|
|
|
@@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
|
|
|
static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
struct netlink_ext_ack *extack)
|
|
|
{
|
|
|
+ struct net *net = sock_net(skb->sk);
|
|
|
struct rtcanmsg *r;
|
|
|
struct cgw_job *gwj;
|
|
|
struct cf_mod mod;
|
|
@@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
/* check for updating an existing job with identical uid */
|
|
|
- hlist_for_each_entry(gwj, &cgw_list, list) {
|
|
|
+ hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
|
|
|
|
|
|
if (gwj->mod.uid != mod.uid)
|
|
|
continue;
|
|
@@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
|
|
|
err = -ENODEV;
|
|
|
|
|
|
- gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
|
|
|
+ gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
|
|
|
|
|
|
if (!gwj->src.dev)
|
|
|
goto out;
|
|
@@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
if (gwj->src.dev->type != ARPHRD_CAN)
|
|
|
goto out;
|
|
|
|
|
|
- gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
|
|
|
+ gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
|
|
|
|
|
|
if (!gwj->dst.dev)
|
|
|
goto out;
|
|
@@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
- err = cgw_register_filter(gwj);
|
|
|
+ err = cgw_register_filter(net, gwj);
|
|
|
if (!err)
|
|
|
- hlist_add_head_rcu(&gwj->list, &cgw_list);
|
|
|
+ hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
|
|
|
out:
|
|
|
if (err)
|
|
|
kmem_cache_free(cgw_cache, gwj);
|
|
@@ -908,16 +907,16 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static void cgw_remove_all_jobs(void)
|
|
|
+static void cgw_remove_all_jobs(struct net *net)
|
|
|
{
|
|
|
struct cgw_job *gwj = NULL;
|
|
|
struct hlist_node *nx;
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
- hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
|
|
|
+ hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
|
|
|
hlist_del(&gwj->list);
|
|
|
- cgw_unregister_filter(gwj);
|
|
|
+ cgw_unregister_filter(net, gwj);
|
|
|
kmem_cache_free(cgw_cache, gwj);
|
|
|
}
|
|
|
}
|
|
@@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void)
|
|
|
static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
struct netlink_ext_ack *extack)
|
|
|
{
|
|
|
+ struct net *net = sock_net(skb->sk);
|
|
|
struct cgw_job *gwj = NULL;
|
|
|
struct hlist_node *nx;
|
|
|
struct rtcanmsg *r;
|
|
@@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
|
|
|
/* two interface indices both set to 0 => remove all entries */
|
|
|
if (!ccgw.src_idx && !ccgw.dst_idx) {
|
|
|
- cgw_remove_all_jobs();
|
|
|
+ cgw_remove_all_jobs(net);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
/* remove only the first matching entry */
|
|
|
- hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
|
|
|
+ hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
|
|
|
|
|
|
if (gwj->flags != r->flags)
|
|
|
continue;
|
|
@@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
continue;
|
|
|
|
|
|
hlist_del(&gwj->list);
|
|
|
- cgw_unregister_filter(gwj);
|
|
|
+ cgw_unregister_filter(net, gwj);
|
|
|
kmem_cache_free(cgw_cache, gwj);
|
|
|
err = 0;
|
|
|
break;
|
|
@@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int __net_init cangw_pernet_init(struct net *net)
|
|
|
+{
|
|
|
+ INIT_HLIST_HEAD(&net->can.cgw_list);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void __net_exit cangw_pernet_exit(struct net *net)
|
|
|
+{
|
|
|
+ rtnl_lock();
|
|
|
+ cgw_remove_all_jobs(net);
|
|
|
+ rtnl_unlock();
|
|
|
+}
|
|
|
+
|
|
|
+static struct pernet_operations cangw_pernet_ops = {
|
|
|
+ .init = cangw_pernet_init,
|
|
|
+ .exit = cangw_pernet_exit,
|
|
|
+};
|
|
|
+
|
|
|
static __init int cgw_module_init(void)
|
|
|
{
|
|
|
/* sanitize given module parameter */
|
|
@@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void)
|
|
|
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
|
|
|
max_hops);
|
|
|
|
|
|
+ register_pernet_subsys(&cangw_pernet_ops);
|
|
|
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
|
|
|
0, 0, NULL);
|
|
|
|
|
@@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void)
|
|
|
|
|
|
unregister_netdevice_notifier(¬ifier);
|
|
|
|
|
|
- rtnl_lock();
|
|
|
- cgw_remove_all_jobs();
|
|
|
- rtnl_unlock();
|
|
|
-
|
|
|
+ unregister_pernet_subsys(&cangw_pernet_ops);
|
|
|
rcu_barrier(); /* Wait for completion of call_rcu()'s */
|
|
|
|
|
|
kmem_cache_destroy(cgw_cache);
|