diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2019-08-10 15:18:10 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2019-08-13 11:32:21 -0400 |
commit | 456a8a646b2563438c16a9b27decf9aa717f1ebb (patch) | |
tree | 4af45d1dc678b1479a13915bfc9c306961b0d9e9 /net/can | |
parent | e9dc7c60507c822992e793bd3845f0556ae0ff98 (diff) |
can: gw: add support for CAN FD frames
Introduce CAN FD support which needs an extension of the netlink API to
pass CAN FD type content to the kernel which has a different size to
Classic CAN. Additionally the struct canfd_frame has a new 'flags' element
that can now be modified with can-gw.
The new CGW_FLAGS_CAN_FD option flag defines whether the routing job
handles Classic CAN or CAN FD frames. This setting is very strict at
reception time and enables the new possibilities, e.g. CGW_FDMOD_* and
modifying the flags element of struct canfd_frame, only when
CGW_FLAGS_CAN_FD is set.
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 | 214 |
1 files changed, 187 insertions, 27 deletions
diff --git a/net/can/gw.c b/net/can/gw.c index 3a1ad206fbef..65d60c93af29 100644 --- a/net/can/gw.c +++ b/net/can/gw.c | |||
@@ -1,7 +1,7 @@ | |||
1 | // SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) | 1 | // SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) |
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) 2017 Volkswagen Group Electronic Research | 4 | * Copyright (c) 2019 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 "20170425" | 62 | #define CAN_GW_VERSION "20190810" |
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"); |
@@ -156,17 +156,50 @@ struct cgw_job { | |||
156 | 156 | ||
157 | MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id) | 157 | MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id) |
158 | MODFUNC(mod_and_len, cf->len &= mod->modframe.and.len) | 158 | MODFUNC(mod_and_len, cf->len &= mod->modframe.and.len) |
159 | MODFUNC(mod_and_flags, cf->flags &= mod->modframe.and.flags) | ||
159 | MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data) | 160 | MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data) |
160 | MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id) | 161 | MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id) |
161 | MODFUNC(mod_or_len, cf->len |= mod->modframe.or.len) | 162 | MODFUNC(mod_or_len, cf->len |= mod->modframe.or.len) |
163 | MODFUNC(mod_or_flags, cf->flags |= mod->modframe.or.flags) | ||
162 | MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data) | 164 | MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data) |
163 | MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id) | 165 | MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id) |
164 | MODFUNC(mod_xor_len, cf->len ^= mod->modframe.xor.len) | 166 | MODFUNC(mod_xor_len, cf->len ^= mod->modframe.xor.len) |
167 | MODFUNC(mod_xor_flags, cf->flags ^= mod->modframe.xor.flags) | ||
165 | MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data) | 168 | MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data) |
166 | MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id) | 169 | MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id) |
167 | MODFUNC(mod_set_len, cf->len = mod->modframe.set.len) | 170 | MODFUNC(mod_set_len, cf->len = mod->modframe.set.len) |
171 | MODFUNC(mod_set_flags, cf->flags = mod->modframe.set.flags) | ||
168 | MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data) | 172 | MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data) |
169 | 173 | ||
174 | static void mod_and_fddata(struct canfd_frame *cf, struct cf_mod *mod) | ||
175 | { | ||
176 | int i; | ||
177 | |||
178 | for (i = 0; i < CANFD_MAX_DLEN; i += 8) | ||
179 | *(u64 *)(cf->data + i) &= *(u64 *)(mod->modframe.and.data + i); | ||
180 | } | ||
181 | |||
182 | static void mod_or_fddata(struct canfd_frame *cf, struct cf_mod *mod) | ||
183 | { | ||
184 | int i; | ||
185 | |||
186 | for (i = 0; i < CANFD_MAX_DLEN; i += 8) | ||
187 | *(u64 *)(cf->data + i) |= *(u64 *)(mod->modframe.or.data + i); | ||
188 | } | ||
189 | |||
190 | static void mod_xor_fddata(struct canfd_frame *cf, struct cf_mod *mod) | ||
191 | { | ||
192 | int i; | ||
193 | |||
194 | for (i = 0; i < CANFD_MAX_DLEN; i += 8) | ||
195 | *(u64 *)(cf->data + i) ^= *(u64 *)(mod->modframe.xor.data + i); | ||
196 | } | ||
197 | |||
198 | static void mod_set_fddata(struct canfd_frame *cf, struct cf_mod *mod) | ||
199 | { | ||
200 | memcpy(cf->data, mod->modframe.set.data, CANFD_MAX_DLEN); | ||
201 | } | ||
202 | |||
170 | static void canframecpy(struct canfd_frame *dst, struct can_frame *src) | 203 | static void canframecpy(struct canfd_frame *dst, struct can_frame *src) |
171 | { | 204 | { |
172 | /* Copy the struct members separately to ensure that no uninitialized | 205 | /* Copy the struct members separately to ensure that no uninitialized |
@@ -179,10 +212,26 @@ static void canframecpy(struct canfd_frame *dst, struct can_frame *src) | |||
179 | *(u64 *)dst->data = *(u64 *)src->data; | 212 | *(u64 *)dst->data = *(u64 *)src->data; |
180 | } | 213 | } |
181 | 214 | ||
182 | static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re) | 215 | static void canfdframecpy(struct canfd_frame *dst, struct canfd_frame *src) |
216 | { | ||
217 | /* Copy the struct members separately to ensure that no uninitialized | ||
218 | * data are copied in the 2 bytes hole of the struct. This is needed | ||
219 | * to make easy compares of the data in the struct cf_mod. | ||
220 | */ | ||
221 | |||
222 | dst->can_id = src->can_id; | ||
223 | dst->flags = src->flags; | ||
224 | dst->len = src->len; | ||
225 | memcpy(dst->data, src->data, CANFD_MAX_DLEN); | ||
226 | } | ||
227 | |||
228 | static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re, struct rtcanmsg *r) | ||
183 | { | 229 | { |
184 | s8 dlen = CAN_MAX_DLEN; | 230 | s8 dlen = CAN_MAX_DLEN; |
185 | 231 | ||
232 | if (r->flags & CGW_FLAGS_CAN_FD) | ||
233 | dlen = CANFD_MAX_DLEN; | ||
234 | |||
186 | /* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0] | 235 | /* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0] |
187 | * relative to received dlc -1 .. -8 : | 236 | * relative to received dlc -1 .. -8 : |
188 | * e.g. for received dlc = 8 | 237 | * e.g. for received dlc = 8 |
@@ -351,6 +400,15 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) | |||
351 | struct sk_buff *nskb; | 400 | struct sk_buff *nskb; |
352 | int modidx = 0; | 401 | int modidx = 0; |
353 | 402 | ||
403 | /* process strictly Classic CAN or CAN FD frames */ | ||
404 | if (gwj->flags & CGW_FLAGS_CAN_FD) { | ||
405 | if (skb->len != CANFD_MTU) | ||
406 | return; | ||
407 | } else { | ||
408 | if (skb->len != CAN_MTU) | ||
409 | return; | ||
410 | } | ||
411 | |||
354 | /* Do not handle CAN frames routed more than 'max_hops' times. | 412 | /* Do not handle CAN frames routed more than 'max_hops' times. |
355 | * In general we should never catch this delimiter which is intended | 413 | * In general we should never catch this delimiter which is intended |
356 | * to cover a misconfiguration protection (e.g. circular CAN routes). | 414 | * to cover a misconfiguration protection (e.g. circular CAN routes). |
@@ -419,23 +477,19 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) | |||
419 | int max_len = nskb->len - offsetof(struct canfd_frame, data); | 477 | int max_len = nskb->len - offsetof(struct canfd_frame, data); |
420 | 478 | ||
421 | /* dlc may have changed, make sure it fits to the CAN frame */ | 479 | /* dlc may have changed, make sure it fits to the CAN frame */ |
422 | if (cf->len > max_len) | 480 | if (cf->len > max_len) { |
423 | goto out_delete; | 481 | /* delete frame due to misconfiguration */ |
424 | 482 | gwj->deleted_frames++; | |
425 | /* check for checksum updates in classic CAN length only */ | 483 | kfree_skb(nskb); |
426 | if (gwj->mod.csumfunc.crc8) { | 484 | return; |
427 | if (cf->len > 8) | ||
428 | goto out_delete; | ||
429 | |||
430 | (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8); | ||
431 | } | 485 | } |
432 | 486 | ||
433 | if (gwj->mod.csumfunc.xor) { | 487 | /* check for checksum updates */ |
434 | if (cf->len > 8) | 488 | if (gwj->mod.csumfunc.crc8) |
435 | goto out_delete; | 489 | (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8); |
436 | 490 | ||
491 | if (gwj->mod.csumfunc.xor) | ||
437 | (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor); | 492 | (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor); |
438 | } | ||
439 | } | 493 | } |
440 | 494 | ||
441 | /* clear the skb timestamp if not configured the other way */ | 495 | /* clear the skb timestamp if not configured the other way */ |
@@ -447,13 +501,6 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) | |||
447 | gwj->dropped_frames++; | 501 | gwj->dropped_frames++; |
448 | else | 502 | else |
449 | gwj->handled_frames++; | 503 | gwj->handled_frames++; |
450 | |||
451 | return; | ||
452 | |||
453 | out_delete: | ||
454 | /* delete frame due to misconfiguration */ | ||
455 | gwj->deleted_frames++; | ||
456 | kfree_skb(nskb); | ||
457 | } | 504 | } |
458 | 505 | ||
459 | static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj) | 506 | static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj) |
@@ -535,7 +582,37 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type, | |||
535 | goto cancel; | 582 | goto cancel; |
536 | } | 583 | } |
537 | 584 | ||
538 | if (1) { | 585 | if (gwj->flags & CGW_FLAGS_CAN_FD) { |
586 | struct cgw_fdframe_mod mb; | ||
587 | |||
588 | if (gwj->mod.modtype.and) { | ||
589 | memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf)); | ||
590 | mb.modtype = gwj->mod.modtype.and; | ||
591 | if (nla_put(skb, CGW_FDMOD_AND, sizeof(mb), &mb) < 0) | ||
592 | goto cancel; | ||
593 | } | ||
594 | |||
595 | if (gwj->mod.modtype.or) { | ||
596 | memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf)); | ||
597 | mb.modtype = gwj->mod.modtype.or; | ||
598 | if (nla_put(skb, CGW_FDMOD_OR, sizeof(mb), &mb) < 0) | ||
599 | goto cancel; | ||
600 | } | ||
601 | |||
602 | if (gwj->mod.modtype.xor) { | ||
603 | memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf)); | ||
604 | mb.modtype = gwj->mod.modtype.xor; | ||
605 | if (nla_put(skb, CGW_FDMOD_XOR, sizeof(mb), &mb) < 0) | ||
606 | goto cancel; | ||
607 | } | ||
608 | |||
609 | if (gwj->mod.modtype.set) { | ||
610 | memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf)); | ||
611 | mb.modtype = gwj->mod.modtype.set; | ||
612 | if (nla_put(skb, CGW_FDMOD_SET, sizeof(mb), &mb) < 0) | ||
613 | goto cancel; | ||
614 | } | ||
615 | } else { | ||
539 | struct cgw_frame_mod mb; | 616 | struct cgw_frame_mod mb; |
540 | 617 | ||
541 | if (gwj->mod.modtype.and) { | 618 | if (gwj->mod.modtype.and) { |
@@ -645,6 +722,10 @@ static const struct nla_policy cgw_policy[CGW_MAX + 1] = { | |||
645 | [CGW_FILTER] = { .len = sizeof(struct can_filter) }, | 722 | [CGW_FILTER] = { .len = sizeof(struct can_filter) }, |
646 | [CGW_LIM_HOPS] = { .type = NLA_U8 }, | 723 | [CGW_LIM_HOPS] = { .type = NLA_U8 }, |
647 | [CGW_MOD_UID] = { .type = NLA_U32 }, | 724 | [CGW_MOD_UID] = { .type = NLA_U32 }, |
725 | [CGW_FDMOD_AND] = { .len = sizeof(struct cgw_fdframe_mod) }, | ||
726 | [CGW_FDMOD_OR] = { .len = sizeof(struct cgw_fdframe_mod) }, | ||
727 | [CGW_FDMOD_XOR] = { .len = sizeof(struct cgw_fdframe_mod) }, | ||
728 | [CGW_FDMOD_SET] = { .len = sizeof(struct cgw_fdframe_mod) }, | ||
648 | }; | 729 | }; |
649 | 730 | ||
650 | /* check for common and gwtype specific attributes */ | 731 | /* check for common and gwtype specific attributes */ |
@@ -652,6 +733,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | |||
652 | u8 gwtype, void *gwtypeattr, u8 *limhops) | 733 | u8 gwtype, void *gwtypeattr, u8 *limhops) |
653 | { | 734 | { |
654 | struct nlattr *tb[CGW_MAX + 1]; | 735 | struct nlattr *tb[CGW_MAX + 1]; |
736 | struct rtcanmsg *r = nlmsg_data(nlh); | ||
655 | int modidx = 0; | 737 | int modidx = 0; |
656 | int err = 0; | 738 | int err = 0; |
657 | 739 | ||
@@ -671,7 +753,85 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | |||
671 | } | 753 | } |
672 | 754 | ||
673 | /* check for AND/OR/XOR/SET modifications */ | 755 | /* check for AND/OR/XOR/SET modifications */ |
674 | if (1) { | 756 | if (r->flags & CGW_FLAGS_CAN_FD) { |
757 | struct cgw_fdframe_mod mb; | ||
758 | |||
759 | if (tb[CGW_FDMOD_AND]) { | ||
760 | nla_memcpy(&mb, tb[CGW_FDMOD_AND], CGW_FDMODATTR_LEN); | ||
761 | |||
762 | canfdframecpy(&mod->modframe.and, &mb.cf); | ||
763 | mod->modtype.and = mb.modtype; | ||
764 | |||
765 | if (mb.modtype & CGW_MOD_ID) | ||
766 | mod->modfunc[modidx++] = mod_and_id; | ||
767 | |||
768 | if (mb.modtype & CGW_MOD_LEN) | ||
769 | mod->modfunc[modidx++] = mod_and_len; | ||
770 | |||
771 | if (mb.modtype & CGW_MOD_FLAGS) | ||
772 | mod->modfunc[modidx++] = mod_and_flags; | ||
773 | |||
774 | if (mb.modtype & CGW_MOD_DATA) | ||
775 | mod->modfunc[modidx++] = mod_and_fddata; | ||
776 | } | ||
777 | |||
778 | if (tb[CGW_FDMOD_OR]) { | ||
779 | nla_memcpy(&mb, tb[CGW_FDMOD_OR], CGW_FDMODATTR_LEN); | ||
780 | |||
781 | canfdframecpy(&mod->modframe.or, &mb.cf); | ||
782 | mod->modtype.or = mb.modtype; | ||
783 | |||
784 | if (mb.modtype & CGW_MOD_ID) | ||
785 | mod->modfunc[modidx++] = mod_or_id; | ||
786 | |||
787 | if (mb.modtype & CGW_MOD_LEN) | ||
788 | mod->modfunc[modidx++] = mod_or_len; | ||
789 | |||
790 | if (mb.modtype & CGW_MOD_FLAGS) | ||
791 | mod->modfunc[modidx++] = mod_or_flags; | ||
792 | |||
793 | if (mb.modtype & CGW_MOD_DATA) | ||
794 | mod->modfunc[modidx++] = mod_or_fddata; | ||
795 | } | ||
796 | |||
797 | if (tb[CGW_FDMOD_XOR]) { | ||
798 | nla_memcpy(&mb, tb[CGW_FDMOD_XOR], CGW_FDMODATTR_LEN); | ||
799 | |||
800 | canfdframecpy(&mod->modframe.xor, &mb.cf); | ||
801 | mod->modtype.xor = mb.modtype; | ||
802 | |||
803 | if (mb.modtype & CGW_MOD_ID) | ||
804 | mod->modfunc[modidx++] = mod_xor_id; | ||
805 | |||
806 | if (mb.modtype & CGW_MOD_LEN) | ||
807 | mod->modfunc[modidx++] = mod_xor_len; | ||
808 | |||
809 | if (mb.modtype & CGW_MOD_FLAGS) | ||
810 | mod->modfunc[modidx++] = mod_xor_flags; | ||
811 | |||
812 | if (mb.modtype & CGW_MOD_DATA) | ||
813 | mod->modfunc[modidx++] = mod_xor_fddata; | ||
814 | } | ||
815 | |||
816 | if (tb[CGW_FDMOD_SET]) { | ||
817 | nla_memcpy(&mb, tb[CGW_FDMOD_SET], CGW_FDMODATTR_LEN); | ||
818 | |||
819 | canfdframecpy(&mod->modframe.set, &mb.cf); | ||
820 | mod->modtype.set = mb.modtype; | ||
821 | |||
822 | if (mb.modtype & CGW_MOD_ID) | ||
823 | mod->modfunc[modidx++] = mod_set_id; | ||
824 | |||
825 | if (mb.modtype & CGW_MOD_LEN) | ||
826 | mod->modfunc[modidx++] = mod_set_len; | ||
827 | |||
828 | if (mb.modtype & CGW_MOD_FLAGS) | ||
829 | mod->modfunc[modidx++] = mod_set_flags; | ||
830 | |||
831 | if (mb.modtype & CGW_MOD_DATA) | ||
832 | mod->modfunc[modidx++] = mod_set_fddata; | ||
833 | } | ||
834 | } else { | ||
675 | struct cgw_frame_mod mb; | 835 | struct cgw_frame_mod mb; |
676 | 836 | ||
677 | if (tb[CGW_MOD_AND]) { | 837 | if (tb[CGW_MOD_AND]) { |
@@ -745,7 +905,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | |||
745 | struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]); | 905 | struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]); |
746 | 906 | ||
747 | err = cgw_chk_csum_parms(c->from_idx, c->to_idx, | 907 | err = cgw_chk_csum_parms(c->from_idx, c->to_idx, |
748 | c->result_idx); | 908 | c->result_idx, r); |
749 | if (err) | 909 | if (err) |
750 | return err; | 910 | return err; |
751 | 911 | ||
@@ -768,7 +928,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, | |||
768 | struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]); | 928 | struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]); |
769 | 929 | ||
770 | err = cgw_chk_csum_parms(c->from_idx, c->to_idx, | 930 | err = cgw_chk_csum_parms(c->from_idx, c->to_idx, |
771 | c->result_idx); | 931 | c->result_idx, r); |
772 | if (err) | 932 | if (err) |
773 | return err; | 933 | return err; |
774 | 934 | ||