diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2017-04-25 02:19:43 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2017-04-25 03:04:30 -0400 |
commit | 1ef83310b81551079af992c4cbb5e089dd1397be (patch) | |
tree | d2d39af10ced10c0afc7a4057c30ccc603468626 /net/can | |
parent | 384317ef4187f59a1cb7a6163444d757340b3bb4 (diff) |
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 (8e8cda6d737d).
This patch enables the CAN gateway to be used in different namespaces.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'net/can')
-rw-r--r-- | net/can/gw.c | 72 |
1 files changed, 44 insertions, 28 deletions
diff --git a/net/can/gw.c b/net/can/gw.c index ad5bf5d508d3..29748d844c3f 100644 --- a/net/can/gw.c +++ b/net/can/gw.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * gw.c - CAN frame Gateway/Router/Bridge with netlink interface | 2 | * gw.c - CAN frame Gateway/Router/Bridge with netlink interface |
3 | * | 3 | * |
4 | * Copyright (c) 2011 Volkswagen Group Electronic Research | 4 | * Copyright (c) 2017 Volkswagen Group Electronic Research |
5 | * All rights reserved. | 5 | * All rights reserved. |
6 | * | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | 7 | * Redistribution and use in source and binary forms, with or without |
@@ -59,7 +59,7 @@ | |||
59 | #include <net/net_namespace.h> | 59 | #include <net/net_namespace.h> |
60 | #include <net/sock.h> | 60 | #include <net/sock.h> |
61 | 61 | ||
62 | #define CAN_GW_VERSION "20130117" | 62 | #define CAN_GW_VERSION "20170425" |
63 | #define CAN_GW_NAME "can-gw" | 63 | #define CAN_GW_NAME "can-gw" |
64 | 64 | ||
65 | MODULE_DESCRIPTION("PF_CAN netlink gateway"); | 65 | MODULE_DESCRIPTION("PF_CAN netlink gateway"); |
@@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops, | |||
79 | __stringify(CGW_MAX_HOPS) " hops, " | 79 | __stringify(CGW_MAX_HOPS) " hops, " |
80 | "default: " __stringify(CGW_DEFAULT_HOPS) ")"); | 80 | "default: " __stringify(CGW_DEFAULT_HOPS) ")"); |
81 | 81 | ||
82 | static HLIST_HEAD(cgw_list); | ||
83 | static struct notifier_block notifier; | 82 | static struct notifier_block notifier; |
84 | |||
85 | static struct kmem_cache *cgw_cache __read_mostly; | 83 | static struct kmem_cache *cgw_cache __read_mostly; |
86 | 84 | ||
87 | /* structure that contains the (on-the-fly) CAN frame modifications */ | 85 | /* 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 | gwj->handled_frames++; | 436 | gwj->handled_frames++; |
439 | } | 437 | } |
440 | 438 | ||
441 | static inline int cgw_register_filter(struct cgw_job *gwj) | 439 | static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj) |
442 | { | 440 | { |
443 | return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, | 441 | return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id, |
444 | gwj->ccgw.filter.can_mask, can_can_gw_rcv, | 442 | gwj->ccgw.filter.can_mask, can_can_gw_rcv, |
445 | gwj, "gw", NULL); | 443 | gwj, "gw", NULL); |
446 | } | 444 | } |
447 | 445 | ||
448 | static inline void cgw_unregister_filter(struct cgw_job *gwj) | 446 | static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj) |
449 | { | 447 | { |
450 | can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, | 448 | can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id, |
451 | gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj); | 449 | gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj); |
452 | } | 450 | } |
453 | 451 | ||
@@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb, | |||
455 | unsigned long msg, void *ptr) | 453 | unsigned long msg, void *ptr) |
456 | { | 454 | { |
457 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 455 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
456 | struct net *net = dev_net(dev); | ||
458 | 457 | ||
459 | if (!net_eq(dev_net(dev), &init_net)) | ||
460 | return NOTIFY_DONE; | ||
461 | if (dev->type != ARPHRD_CAN) | 458 | if (dev->type != ARPHRD_CAN) |
462 | return NOTIFY_DONE; | 459 | return NOTIFY_DONE; |
463 | 460 | ||
@@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb, | |||
468 | 465 | ||
469 | ASSERT_RTNL(); | 466 | ASSERT_RTNL(); |
470 | 467 | ||
471 | hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { | 468 | hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { |
472 | 469 | ||
473 | if (gwj->src.dev == dev || gwj->dst.dev == dev) { | 470 | if (gwj->src.dev == dev || gwj->dst.dev == dev) { |
474 | hlist_del(&gwj->list); | 471 | hlist_del(&gwj->list); |
475 | cgw_unregister_filter(gwj); | 472 | cgw_unregister_filter(net, gwj); |
476 | kmem_cache_free(cgw_cache, gwj); | 473 | kmem_cache_free(cgw_cache, gwj); |
477 | } | 474 | } |
478 | } | 475 | } |
@@ -592,12 +589,13 @@ cancel: | |||
592 | /* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */ | 589 | /* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */ |
593 | static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb) | 590 | static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb) |
594 | { | 591 | { |
592 | struct net *net = sock_net(skb->sk); | ||
595 | struct cgw_job *gwj = NULL; | 593 | struct cgw_job *gwj = NULL; |
596 | int idx = 0; | 594 | int idx = 0; |
597 | int s_idx = cb->args[0]; | 595 | int s_idx = cb->args[0]; |
598 | 596 | ||
599 | rcu_read_lock(); | 597 | rcu_read_lock(); |
600 | hlist_for_each_entry_rcu(gwj, &cgw_list, list) { | 598 | hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) { |
601 | if (idx < s_idx) | 599 | if (idx < s_idx) |
602 | goto cont; | 600 | goto cont; |
603 | 601 | ||
@@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | |||
812 | static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, | 810 | static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, |
813 | struct netlink_ext_ack *extack) | 811 | struct netlink_ext_ack *extack) |
814 | { | 812 | { |
813 | struct net *net = sock_net(skb->sk); | ||
815 | struct rtcanmsg *r; | 814 | struct rtcanmsg *r; |
816 | struct cgw_job *gwj; | 815 | struct cgw_job *gwj; |
817 | struct cf_mod mod; | 816 | struct cf_mod mod; |
@@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
842 | ASSERT_RTNL(); | 841 | ASSERT_RTNL(); |
843 | 842 | ||
844 | /* check for updating an existing job with identical uid */ | 843 | /* check for updating an existing job with identical uid */ |
845 | hlist_for_each_entry(gwj, &cgw_list, list) { | 844 | hlist_for_each_entry(gwj, &net->can.cgw_list, list) { |
846 | 845 | ||
847 | if (gwj->mod.uid != mod.uid) | 846 | if (gwj->mod.uid != mod.uid) |
848 | continue; | 847 | continue; |
@@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
880 | 879 | ||
881 | err = -ENODEV; | 880 | err = -ENODEV; |
882 | 881 | ||
883 | gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx); | 882 | gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx); |
884 | 883 | ||
885 | if (!gwj->src.dev) | 884 | if (!gwj->src.dev) |
886 | goto out; | 885 | goto out; |
@@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
888 | if (gwj->src.dev->type != ARPHRD_CAN) | 887 | if (gwj->src.dev->type != ARPHRD_CAN) |
889 | goto out; | 888 | goto out; |
890 | 889 | ||
891 | gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx); | 890 | gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx); |
892 | 891 | ||
893 | if (!gwj->dst.dev) | 892 | if (!gwj->dst.dev) |
894 | goto out; | 893 | goto out; |
@@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
898 | 897 | ||
899 | ASSERT_RTNL(); | 898 | ASSERT_RTNL(); |
900 | 899 | ||
901 | err = cgw_register_filter(gwj); | 900 | err = cgw_register_filter(net, gwj); |
902 | if (!err) | 901 | if (!err) |
903 | hlist_add_head_rcu(&gwj->list, &cgw_list); | 902 | hlist_add_head_rcu(&gwj->list, &net->can.cgw_list); |
904 | out: | 903 | out: |
905 | if (err) | 904 | if (err) |
906 | kmem_cache_free(cgw_cache, gwj); | 905 | kmem_cache_free(cgw_cache, gwj); |
@@ -908,16 +907,16 @@ out: | |||
908 | return err; | 907 | return err; |
909 | } | 908 | } |
910 | 909 | ||
911 | static void cgw_remove_all_jobs(void) | 910 | static void cgw_remove_all_jobs(struct net *net) |
912 | { | 911 | { |
913 | struct cgw_job *gwj = NULL; | 912 | struct cgw_job *gwj = NULL; |
914 | struct hlist_node *nx; | 913 | struct hlist_node *nx; |
915 | 914 | ||
916 | ASSERT_RTNL(); | 915 | ASSERT_RTNL(); |
917 | 916 | ||
918 | hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { | 917 | hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { |
919 | hlist_del(&gwj->list); | 918 | hlist_del(&gwj->list); |
920 | cgw_unregister_filter(gwj); | 919 | cgw_unregister_filter(net, gwj); |
921 | kmem_cache_free(cgw_cache, gwj); | 920 | kmem_cache_free(cgw_cache, gwj); |
922 | } | 921 | } |
923 | } | 922 | } |
@@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void) | |||
925 | static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, | 924 | static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, |
926 | struct netlink_ext_ack *extack) | 925 | struct netlink_ext_ack *extack) |
927 | { | 926 | { |
927 | struct net *net = sock_net(skb->sk); | ||
928 | struct cgw_job *gwj = NULL; | 928 | struct cgw_job *gwj = NULL; |
929 | struct hlist_node *nx; | 929 | struct hlist_node *nx; |
930 | struct rtcanmsg *r; | 930 | struct rtcanmsg *r; |
@@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
953 | 953 | ||
954 | /* two interface indices both set to 0 => remove all entries */ | 954 | /* two interface indices both set to 0 => remove all entries */ |
955 | if (!ccgw.src_idx && !ccgw.dst_idx) { | 955 | if (!ccgw.src_idx && !ccgw.dst_idx) { |
956 | cgw_remove_all_jobs(); | 956 | cgw_remove_all_jobs(net); |
957 | return 0; | 957 | return 0; |
958 | } | 958 | } |
959 | 959 | ||
@@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
962 | ASSERT_RTNL(); | 962 | ASSERT_RTNL(); |
963 | 963 | ||
964 | /* remove only the first matching entry */ | 964 | /* remove only the first matching entry */ |
965 | hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { | 965 | hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { |
966 | 966 | ||
967 | if (gwj->flags != r->flags) | 967 | if (gwj->flags != r->flags) |
968 | continue; | 968 | continue; |
@@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
985 | continue; | 985 | continue; |
986 | 986 | ||
987 | hlist_del(&gwj->list); | 987 | hlist_del(&gwj->list); |
988 | cgw_unregister_filter(gwj); | 988 | cgw_unregister_filter(net, gwj); |
989 | kmem_cache_free(cgw_cache, gwj); | 989 | kmem_cache_free(cgw_cache, gwj); |
990 | err = 0; | 990 | err = 0; |
991 | break; | 991 | break; |
@@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
994 | return err; | 994 | return err; |
995 | } | 995 | } |
996 | 996 | ||
997 | static int __net_init cangw_pernet_init(struct net *net) | ||
998 | { | ||
999 | INIT_HLIST_HEAD(&net->can.cgw_list); | ||
1000 | return 0; | ||
1001 | } | ||
1002 | |||
1003 | static void __net_exit cangw_pernet_exit(struct net *net) | ||
1004 | { | ||
1005 | rtnl_lock(); | ||
1006 | cgw_remove_all_jobs(net); | ||
1007 | rtnl_unlock(); | ||
1008 | } | ||
1009 | |||
1010 | static struct pernet_operations cangw_pernet_ops = { | ||
1011 | .init = cangw_pernet_init, | ||
1012 | .exit = cangw_pernet_exit, | ||
1013 | }; | ||
1014 | |||
997 | static __init int cgw_module_init(void) | 1015 | static __init int cgw_module_init(void) |
998 | { | 1016 | { |
999 | /* sanitize given module parameter */ | 1017 | /* sanitize given module parameter */ |
@@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void) | |||
1002 | pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", | 1020 | pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", |
1003 | max_hops); | 1021 | max_hops); |
1004 | 1022 | ||
1023 | register_pernet_subsys(&cangw_pernet_ops); | ||
1005 | cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), | 1024 | cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), |
1006 | 0, 0, NULL); | 1025 | 0, 0, NULL); |
1007 | 1026 | ||
@@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void) | |||
1031 | 1050 | ||
1032 | unregister_netdevice_notifier(¬ifier); | 1051 | unregister_netdevice_notifier(¬ifier); |
1033 | 1052 | ||
1034 | rtnl_lock(); | 1053 | unregister_pernet_subsys(&cangw_pernet_ops); |
1035 | cgw_remove_all_jobs(); | ||
1036 | rtnl_unlock(); | ||
1037 | |||
1038 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ | 1054 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ |
1039 | 1055 | ||
1040 | kmem_cache_destroy(cgw_cache); | 1056 | kmem_cache_destroy(cgw_cache); |