18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * em_canid.c Ematch rule to match CAN frames according to their CAN IDs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Idea: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> 68c2ecf20Sopenharmony_ci * Copyright: (c) 2011 Czech Technical University in Prague 78c2ecf20Sopenharmony_ci * (c) 2011 Volkswagen Group Research 88c2ecf20Sopenharmony_ci * Authors: Michal Sojka <sojkam1@fel.cvut.cz> 98c2ecf20Sopenharmony_ci * Pavel Pisa <pisa@cmp.felk.cvut.cz> 108c2ecf20Sopenharmony_ci * Rostislav Lisovy <lisovy@gmail.cz> 118c2ecf20Sopenharmony_ci * Funded by: Volkswagen Group Research 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 208c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 218c2ecf20Sopenharmony_ci#include <linux/can.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define EM_CAN_RULES_MAX 500 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct canid_match { 268c2ecf20Sopenharmony_ci /* For each SFF CAN ID (11 bit) there is one record in this bitfield */ 278c2ecf20Sopenharmony_ci DECLARE_BITMAP(match_sff, (1 << CAN_SFF_ID_BITS)); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci int rules_count; 308c2ecf20Sopenharmony_ci int sff_rules_count; 318c2ecf20Sopenharmony_ci int eff_rules_count; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* 348c2ecf20Sopenharmony_ci * Raw rules copied from netlink message; Used for sending 358c2ecf20Sopenharmony_ci * information to userspace (when 'tc filter show' is invoked) 368c2ecf20Sopenharmony_ci * AND when matching EFF frames 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci struct can_filter rules_raw[]; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/** 428c2ecf20Sopenharmony_ci * em_canid_get_id() - Extracts Can ID out of the sk_buff structure. 438c2ecf20Sopenharmony_ci * @skb: buffer to extract Can ID from 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic canid_t em_canid_get_id(struct sk_buff *skb) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci /* CAN ID is stored within the data field */ 488c2ecf20Sopenharmony_ci struct can_frame *cf = (struct can_frame *)skb->data; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return cf->can_id; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void em_canid_sff_match_add(struct canid_match *cm, u32 can_id, 548c2ecf20Sopenharmony_ci u32 can_mask) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int i; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * Limit can_mask and can_id to SFF range to 608c2ecf20Sopenharmony_ci * protect against write after end of array 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci can_mask &= CAN_SFF_MASK; 638c2ecf20Sopenharmony_ci can_id &= can_mask; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Single frame */ 668c2ecf20Sopenharmony_ci if (can_mask == CAN_SFF_MASK) { 678c2ecf20Sopenharmony_ci set_bit(can_id, cm->match_sff); 688c2ecf20Sopenharmony_ci return; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* All frames */ 728c2ecf20Sopenharmony_ci if (can_mask == 0) { 738c2ecf20Sopenharmony_ci bitmap_fill(cm->match_sff, (1 << CAN_SFF_ID_BITS)); 748c2ecf20Sopenharmony_ci return; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Individual frame filter. 798c2ecf20Sopenharmony_ci * Add record (set bit to 1) for each ID that 808c2ecf20Sopenharmony_ci * conforms particular rule 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci for (i = 0; i < (1 << CAN_SFF_ID_BITS); i++) { 838c2ecf20Sopenharmony_ci if ((i & can_mask) == can_id) 848c2ecf20Sopenharmony_ci set_bit(i, cm->match_sff); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline struct canid_match *em_canid_priv(struct tcf_ematch *m) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return (struct canid_match *)m->data; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int em_canid_match(struct sk_buff *skb, struct tcf_ematch *m, 948c2ecf20Sopenharmony_ci struct tcf_pkt_info *info) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct canid_match *cm = em_canid_priv(m); 978c2ecf20Sopenharmony_ci canid_t can_id; 988c2ecf20Sopenharmony_ci int match = 0; 998c2ecf20Sopenharmony_ci int i; 1008c2ecf20Sopenharmony_ci const struct can_filter *lp; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci can_id = em_canid_get_id(skb); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (can_id & CAN_EFF_FLAG) { 1058c2ecf20Sopenharmony_ci for (i = 0, lp = cm->rules_raw; 1068c2ecf20Sopenharmony_ci i < cm->eff_rules_count; i++, lp++) { 1078c2ecf20Sopenharmony_ci if (!(((lp->can_id ^ can_id) & lp->can_mask))) { 1088c2ecf20Sopenharmony_ci match = 1; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } else { /* SFF */ 1138c2ecf20Sopenharmony_ci can_id &= CAN_SFF_MASK; 1148c2ecf20Sopenharmony_ci match = (test_bit(can_id, cm->match_sff) ? 1 : 0); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return match; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int em_canid_change(struct net *net, void *data, int len, 1218c2ecf20Sopenharmony_ci struct tcf_ematch *m) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct can_filter *conf = data; /* Array with rules */ 1248c2ecf20Sopenharmony_ci struct canid_match *cm; 1258c2ecf20Sopenharmony_ci int i; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!len) 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (len % sizeof(struct can_filter)) 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (len > sizeof(struct can_filter) * EM_CAN_RULES_MAX) 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci cm = kzalloc(sizeof(struct canid_match) + len, GFP_KERNEL); 1378c2ecf20Sopenharmony_ci if (!cm) 1388c2ecf20Sopenharmony_ci return -ENOMEM; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci cm->rules_count = len / sizeof(struct can_filter); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * We need two for() loops for copying rules into two contiguous 1448c2ecf20Sopenharmony_ci * areas in rules_raw to process all eff rules with a simple loop. 1458c2ecf20Sopenharmony_ci * NB: The configuration interface supports sff and eff rules. 1468c2ecf20Sopenharmony_ci * We do not support filters here that match for the same can_id 1478c2ecf20Sopenharmony_ci * provided in a SFF and EFF frame (e.g. 0x123 / 0x80000123). 1488c2ecf20Sopenharmony_ci * For this (unusual case) two filters have to be specified. The 1498c2ecf20Sopenharmony_ci * SFF/EFF separation is done with the CAN_EFF_FLAG in the can_id. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Fill rules_raw with EFF rules first */ 1538c2ecf20Sopenharmony_ci for (i = 0; i < cm->rules_count; i++) { 1548c2ecf20Sopenharmony_ci if (conf[i].can_id & CAN_EFF_FLAG) { 1558c2ecf20Sopenharmony_ci memcpy(cm->rules_raw + cm->eff_rules_count, 1568c2ecf20Sopenharmony_ci &conf[i], 1578c2ecf20Sopenharmony_ci sizeof(struct can_filter)); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci cm->eff_rules_count++; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* append SFF frame rules */ 1648c2ecf20Sopenharmony_ci for (i = 0; i < cm->rules_count; i++) { 1658c2ecf20Sopenharmony_ci if (!(conf[i].can_id & CAN_EFF_FLAG)) { 1668c2ecf20Sopenharmony_ci memcpy(cm->rules_raw 1678c2ecf20Sopenharmony_ci + cm->eff_rules_count 1688c2ecf20Sopenharmony_ci + cm->sff_rules_count, 1698c2ecf20Sopenharmony_ci &conf[i], sizeof(struct can_filter)); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci cm->sff_rules_count++; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci em_canid_sff_match_add(cm, 1748c2ecf20Sopenharmony_ci conf[i].can_id, conf[i].can_mask); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci m->datalen = sizeof(struct canid_match) + len; 1798c2ecf20Sopenharmony_ci m->data = (unsigned long)cm; 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void em_canid_destroy(struct tcf_ematch *m) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct canid_match *cm = em_canid_priv(m); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci kfree(cm); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int em_canid_dump(struct sk_buff *skb, struct tcf_ematch *m) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct canid_match *cm = em_canid_priv(m); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * When configuring this ematch 'rules_count' is set not to exceed 1968c2ecf20Sopenharmony_ci * 'rules_raw' array size 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (nla_put_nohdr(skb, sizeof(struct can_filter) * cm->rules_count, 1998c2ecf20Sopenharmony_ci &cm->rules_raw) < 0) 2008c2ecf20Sopenharmony_ci return -EMSGSIZE; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic struct tcf_ematch_ops em_canid_ops = { 2068c2ecf20Sopenharmony_ci .kind = TCF_EM_CANID, 2078c2ecf20Sopenharmony_ci .change = em_canid_change, 2088c2ecf20Sopenharmony_ci .match = em_canid_match, 2098c2ecf20Sopenharmony_ci .destroy = em_canid_destroy, 2108c2ecf20Sopenharmony_ci .dump = em_canid_dump, 2118c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2128c2ecf20Sopenharmony_ci .link = LIST_HEAD_INIT(em_canid_ops.link) 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int __init init_em_canid(void) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci return tcf_em_register(&em_canid_ops); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void __exit exit_em_canid(void) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci tcf_em_unregister(&em_canid_ops); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cimodule_init(init_em_canid); 2288c2ecf20Sopenharmony_cimodule_exit(exit_em_canid); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciMODULE_ALIAS_TCF_EMATCH(TCF_EM_CANID); 231