diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2013-08-26 09:05:36 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2013-08-29 16:58:24 -0400 |
commit | 391ac1282dd7ff1cb8245cccc5262e8e4173edc4 (patch) | |
tree | 891c2d57e8b2424b72e07815b7f203317257c93e | |
parent | 1149108e2fbf98899447d4567901bf07825ee576 (diff) |
can: gw: add a per rule limitation of frame hops
Usually the received CAN frames can be processed/routed as much as 'max_hops'
times (which is given at module load time of the can-gw module).
Introduce a new configuration option to reduce the number of possible hops
for a specific gateway rule to a value smaller then max_hops.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r-- | include/uapi/linux/can/gw.h | 9 | ||||
-rw-r--r-- | net/can/gw.c | 35 |
2 files changed, 39 insertions, 5 deletions
diff --git a/include/uapi/linux/can/gw.h b/include/uapi/linux/can/gw.h index ae07bec74f4b..4e27c82b564a 100644 --- a/include/uapi/linux/can/gw.h +++ b/include/uapi/linux/can/gw.h | |||
@@ -45,6 +45,7 @@ enum { | |||
45 | CGW_DST_IF, /* ifindex of destination network interface */ | 45 | CGW_DST_IF, /* ifindex of destination network interface */ |
46 | CGW_FILTER, /* specify struct can_filter on source CAN device */ | 46 | CGW_FILTER, /* specify struct can_filter on source CAN device */ |
47 | CGW_DELETED, /* number of deleted CAN frames (see max_hops param) */ | 47 | CGW_DELETED, /* number of deleted CAN frames (see max_hops param) */ |
48 | CGW_LIM_HOPS, /* limit the number of hops of this specific rule */ | ||
48 | __CGW_MAX | 49 | __CGW_MAX |
49 | }; | 50 | }; |
50 | 51 | ||
@@ -116,13 +117,19 @@ enum { | |||
116 | * Sets a CAN receive filter for the gateway job specified by the | 117 | * Sets a CAN receive filter for the gateway job specified by the |
117 | * struct can_filter described in include/linux/can.h | 118 | * struct can_filter described in include/linux/can.h |
118 | * | 119 | * |
119 | * CGW_MOD_XXX (length 17 bytes): | 120 | * CGW_MOD_(AND|OR|XOR|SET) (length 17 bytes): |
120 | * Specifies a modification that's done to a received CAN frame before it is | 121 | * Specifies a modification that's done to a received CAN frame before it is |
121 | * send out to the destination interface. | 122 | * send out to the destination interface. |
122 | * | 123 | * |
123 | * <struct can_frame> data used as operator | 124 | * <struct can_frame> data used as operator |
124 | * <u8> affected CAN frame elements | 125 | * <u8> affected CAN frame elements |
125 | * | 126 | * |
127 | * CGW_LIM_HOPS (length 1 byte): | ||
128 | * Limit the number of hops of this specific rule. Usually the received CAN | ||
129 | * frame can be processed as much as 'max_hops' times (which is given at module | ||
130 | * load time of the can-gw module). This value is used to reduce the number of | ||
131 | * possible hops for this gateway rule to a value smaller then max_hops. | ||
132 | * | ||
126 | * CGW_CS_XOR (length 4 bytes): | 133 | * CGW_CS_XOR (length 4 bytes): |
127 | * Set a simple XOR checksum starting with an initial value into | 134 | * Set a simple XOR checksum starting with an initial value into |
128 | * data[result-idx] using data[start-idx] .. data[end-idx] | 135 | * data[result-idx] using data[start-idx] .. data[end-idx] |
diff --git a/net/can/gw.c b/net/can/gw.c index 2f291f961a17..3f9b0f3a2818 100644 --- a/net/can/gw.c +++ b/net/can/gw.c | |||
@@ -146,6 +146,7 @@ struct cgw_job { | |||
146 | /* tbc */ | 146 | /* tbc */ |
147 | }; | 147 | }; |
148 | u8 gwtype; | 148 | u8 gwtype; |
149 | u8 limit_hops; | ||
149 | u16 flags; | 150 | u16 flags; |
150 | }; | 151 | }; |
151 | 152 | ||
@@ -402,6 +403,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) | |||
402 | 403 | ||
403 | /* put the incremented hop counter in the cloned skb */ | 404 | /* put the incremented hop counter in the cloned skb */ |
404 | cgw_hops(nskb) = cgw_hops(skb) + 1; | 405 | cgw_hops(nskb) = cgw_hops(skb) + 1; |
406 | |||
407 | /* first processing of this CAN frame -> adjust to private hop limit */ | ||
408 | if (gwj->limit_hops && cgw_hops(nskb) == 1) | ||
409 | cgw_hops(nskb) = max_hops - gwj->limit_hops + 1; | ||
410 | |||
405 | nskb->dev = gwj->dst.dev; | 411 | nskb->dev = gwj->dst.dev; |
406 | 412 | ||
407 | /* pointer to modifiable CAN frame */ | 413 | /* pointer to modifiable CAN frame */ |
@@ -509,6 +515,11 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type, | |||
509 | 515 | ||
510 | /* check non default settings of attributes */ | 516 | /* check non default settings of attributes */ |
511 | 517 | ||
518 | if (gwj->limit_hops) { | ||
519 | if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0) | ||
520 | goto cancel; | ||
521 | } | ||
522 | |||
512 | if (gwj->mod.modtype.and) { | 523 | if (gwj->mod.modtype.and) { |
513 | memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf)); | 524 | memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf)); |
514 | mb.modtype = gwj->mod.modtype.and; | 525 | mb.modtype = gwj->mod.modtype.and; |
@@ -606,11 +617,12 @@ static const struct nla_policy cgw_policy[CGW_MAX+1] = { | |||
606 | [CGW_SRC_IF] = { .type = NLA_U32 }, | 617 | [CGW_SRC_IF] = { .type = NLA_U32 }, |
607 | [CGW_DST_IF] = { .type = NLA_U32 }, | 618 | [CGW_DST_IF] = { .type = NLA_U32 }, |
608 | [CGW_FILTER] = { .len = sizeof(struct can_filter) }, | 619 | [CGW_FILTER] = { .len = sizeof(struct can_filter) }, |
620 | [CGW_LIM_HOPS] = { .type = NLA_U8 }, | ||
609 | }; | 621 | }; |
610 | 622 | ||
611 | /* check for common and gwtype specific attributes */ | 623 | /* check for common and gwtype specific attributes */ |
612 | static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | 624 | static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, |
613 | u8 gwtype, void *gwtypeattr) | 625 | u8 gwtype, void *gwtypeattr, u8 *limhops) |
614 | { | 626 | { |
615 | struct nlattr *tb[CGW_MAX+1]; | 627 | struct nlattr *tb[CGW_MAX+1]; |
616 | struct cgw_frame_mod mb; | 628 | struct cgw_frame_mod mb; |
@@ -625,6 +637,13 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | |||
625 | if (err < 0) | 637 | if (err < 0) |
626 | return err; | 638 | return err; |
627 | 639 | ||
640 | if (tb[CGW_LIM_HOPS]) { | ||
641 | *limhops = nla_get_u8(tb[CGW_LIM_HOPS]); | ||
642 | |||
643 | if (*limhops < 1 || *limhops > max_hops) | ||
644 | return -EINVAL; | ||
645 | } | ||
646 | |||
628 | /* check for AND/OR/XOR/SET modifications */ | 647 | /* check for AND/OR/XOR/SET modifications */ |
629 | 648 | ||
630 | if (tb[CGW_MOD_AND]) { | 649 | if (tb[CGW_MOD_AND]) { |
@@ -782,6 +801,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
782 | { | 801 | { |
783 | struct rtcanmsg *r; | 802 | struct rtcanmsg *r; |
784 | struct cgw_job *gwj; | 803 | struct cgw_job *gwj; |
804 | u8 limhops = 0; | ||
785 | int err = 0; | 805 | int err = 0; |
786 | 806 | ||
787 | if (!capable(CAP_NET_ADMIN)) | 807 | if (!capable(CAP_NET_ADMIN)) |
@@ -808,7 +828,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
808 | gwj->flags = r->flags; | 828 | gwj->flags = r->flags; |
809 | gwj->gwtype = r->gwtype; | 829 | gwj->gwtype = r->gwtype; |
810 | 830 | ||
811 | err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw); | 831 | err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw, |
832 | &limhops); | ||
812 | if (err < 0) | 833 | if (err < 0) |
813 | goto out; | 834 | goto out; |
814 | 835 | ||
@@ -836,6 +857,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
836 | if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops) | 857 | if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops) |
837 | goto put_src_dst_out; | 858 | goto put_src_dst_out; |
838 | 859 | ||
860 | gwj->limit_hops = limhops; | ||
861 | |||
839 | ASSERT_RTNL(); | 862 | ASSERT_RTNL(); |
840 | 863 | ||
841 | err = cgw_register_filter(gwj); | 864 | err = cgw_register_filter(gwj); |
@@ -867,13 +890,14 @@ static void cgw_remove_all_jobs(void) | |||
867 | } | 890 | } |
868 | } | 891 | } |
869 | 892 | ||
870 | static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) | 893 | static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) |
871 | { | 894 | { |
872 | struct cgw_job *gwj = NULL; | 895 | struct cgw_job *gwj = NULL; |
873 | struct hlist_node *nx; | 896 | struct hlist_node *nx; |
874 | struct rtcanmsg *r; | 897 | struct rtcanmsg *r; |
875 | struct cf_mod mod; | 898 | struct cf_mod mod; |
876 | struct can_can_gw ccgw; | 899 | struct can_can_gw ccgw; |
900 | u8 limhops = 0; | ||
877 | int err = 0; | 901 | int err = 0; |
878 | 902 | ||
879 | if (!capable(CAP_NET_ADMIN)) | 903 | if (!capable(CAP_NET_ADMIN)) |
@@ -890,7 +914,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
890 | if (r->gwtype != CGW_TYPE_CAN_CAN) | 914 | if (r->gwtype != CGW_TYPE_CAN_CAN) |
891 | return -EINVAL; | 915 | return -EINVAL; |
892 | 916 | ||
893 | err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw); | 917 | err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops); |
894 | if (err < 0) | 918 | if (err < 0) |
895 | return err; | 919 | return err; |
896 | 920 | ||
@@ -910,6 +934,9 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
910 | if (gwj->flags != r->flags) | 934 | if (gwj->flags != r->flags) |
911 | continue; | 935 | continue; |
912 | 936 | ||
937 | if (gwj->limit_hops != limhops) | ||
938 | continue; | ||
939 | |||
913 | if (memcmp(&gwj->mod, &mod, sizeof(mod))) | 940 | if (memcmp(&gwj->mod, &mod, sizeof(mod))) |
914 | continue; | 941 | continue; |
915 | 942 | ||