Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -32,6 +32,9 @@ struct netns_can {
	struct timer_list can_stattimer;/* timer for statistics update */
	struct s_stats *can_stats;	/* packet statistics */
	struct s_pstats *can_pstats;	/* receive list statistics */

	/* CAN GW per-net gateway jobs */
	struct hlist_head cgw_list;
};

#endif /* __NETNS_CAN_H__ */
+44 −28
Original line number Diff line number Diff line
/*
 * 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 @@ 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 */
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 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
	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(&notifier);

	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);