Commit 1ef83310 authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

can: network namespace support for CAN gateway

The CAN gateway was not implemented as per-net in the initial network
namespace support by Mario Kicherer (8e8cda6d).
This patch enables the CAN gateway to be used in different namespaces.
Signed-off-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 384317ef
...@@ -32,6 +32,9 @@ struct netns_can { ...@@ -32,6 +32,9 @@ struct netns_can {
struct timer_list can_stattimer;/* timer for statistics update */ struct timer_list can_stattimer;/* timer for statistics update */
struct s_stats *can_stats; /* packet statistics */ struct s_stats *can_stats; /* packet statistics */
struct s_pstats *can_pstats; /* receive list statistics */ struct s_pstats *can_pstats; /* receive list statistics */
/* CAN GW per-net gateway jobs */
struct hlist_head cgw_list;
}; };
#endif /* __NETNS_CAN_H__ */ #endif /* __NETNS_CAN_H__ */
/* /*
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
#define CAN_GW_VERSION "20130117" #define CAN_GW_VERSION "20170425"
#define CAN_GW_NAME "can-gw" #define CAN_GW_NAME "can-gw"
MODULE_DESCRIPTION("PF_CAN netlink gateway"); MODULE_DESCRIPTION("PF_CAN netlink gateway");
...@@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops, ...@@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops,
__stringify(CGW_MAX_HOPS) " hops, " __stringify(CGW_MAX_HOPS) " hops, "
"default: " __stringify(CGW_DEFAULT_HOPS) ")"); "default: " __stringify(CGW_DEFAULT_HOPS) ")");
static HLIST_HEAD(cgw_list);
static struct notifier_block notifier; static struct notifier_block notifier;
static struct kmem_cache *cgw_cache __read_mostly; static struct kmem_cache *cgw_cache __read_mostly;
/* structure that contains the (on-the-fly) CAN frame modifications */ /* 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) ...@@ -438,16 +436,16 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
gwj->handled_frames++; 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->ccgw.filter.can_mask, can_can_gw_rcv,
gwj, "gw", NULL); 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); gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
} }
...@@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb, ...@@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb,
unsigned long msg, void *ptr) unsigned long msg, void *ptr)
{ {
struct net_device *dev = netdev_notifier_info_to_dev(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) if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb, ...@@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb,
ASSERT_RTNL(); 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) { if (gwj->src.dev == dev || gwj->dst.dev == dev) {
hlist_del(&gwj->list); hlist_del(&gwj->list);
cgw_unregister_filter(gwj); cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
} }
} }
...@@ -592,12 +589,13 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type, ...@@ -592,12 +589,13 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */ /* 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) 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; struct cgw_job *gwj = NULL;
int idx = 0; int idx = 0;
int s_idx = cb->args[0]; int s_idx = cb->args[0];
rcu_read_lock(); 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) if (idx < s_idx)
goto cont; goto cont;
...@@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, ...@@ -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, static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk);
struct rtcanmsg *r; struct rtcanmsg *r;
struct cgw_job *gwj; struct cgw_job *gwj;
struct cf_mod mod; struct cf_mod mod;
...@@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL(); ASSERT_RTNL();
/* check for updating an existing job with identical uid */ /* 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) if (gwj->mod.uid != mod.uid)
continue; continue;
...@@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
err = -ENODEV; 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) if (!gwj->src.dev)
goto out; goto out;
...@@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (gwj->src.dev->type != ARPHRD_CAN) if (gwj->src.dev->type != ARPHRD_CAN)
goto out; 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) if (!gwj->dst.dev)
goto out; goto out;
...@@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL(); ASSERT_RTNL();
err = cgw_register_filter(gwj); err = cgw_register_filter(net, gwj);
if (!err) if (!err)
hlist_add_head_rcu(&gwj->list, &cgw_list); hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
out: out:
if (err) if (err)
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
...@@ -908,16 +907,16 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -908,16 +907,16 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
return err; return err;
} }
static void cgw_remove_all_jobs(void) static void cgw_remove_all_jobs(struct net *net)
{ {
struct cgw_job *gwj = NULL; struct cgw_job *gwj = NULL;
struct hlist_node *nx; struct hlist_node *nx;
ASSERT_RTNL(); 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); hlist_del(&gwj->list);
cgw_unregister_filter(gwj); cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
} }
} }
...@@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void) ...@@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void)
static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk);
struct cgw_job *gwj = NULL; struct cgw_job *gwj = NULL;
struct hlist_node *nx; struct hlist_node *nx;
struct rtcanmsg *r; struct rtcanmsg *r;
...@@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -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 */ /* two interface indices both set to 0 => remove all entries */
if (!ccgw.src_idx && !ccgw.dst_idx) { if (!ccgw.src_idx && !ccgw.dst_idx) {
cgw_remove_all_jobs(); cgw_remove_all_jobs(net);
return 0; return 0;
} }
...@@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL(); ASSERT_RTNL();
/* remove only the first matching entry */ /* 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) if (gwj->flags != r->flags)
continue; continue;
...@@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
continue; continue;
hlist_del(&gwj->list); hlist_del(&gwj->list);
cgw_unregister_filter(gwj); cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
err = 0; err = 0;
break; break;
...@@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
return err; 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) static __init int cgw_module_init(void)
{ {
/* sanitize given module parameter */ /* sanitize given module parameter */
...@@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void) ...@@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void)
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
max_hops); max_hops);
register_pernet_subsys(&cangw_pernet_ops);
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
0, 0, NULL); 0, 0, NULL);
...@@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void) ...@@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void)
unregister_netdevice_notifier(&notifier); unregister_netdevice_notifier(&notifier);
rtnl_lock(); unregister_pernet_subsys(&cangw_pernet_ops);
cgw_remove_all_jobs();
rtnl_unlock();
rcu_barrier(); /* Wait for completion of call_rcu()'s */ rcu_barrier(); /* Wait for completion of call_rcu()'s */
kmem_cache_destroy(cgw_cache); kmem_cache_destroy(cgw_cache);
......
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