1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 3 4 #include "ksz9477.h" 5 #include "ksz9477_reg.h" 6 #include "ksz_common.h" 7 8 #define ETHER_TYPE_FULL_MASK cpu_to_be16(~0) 9 #define KSZ9477_MAX_TC 7 10 11 /** 12 * ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure 13 * ACL entries accordingly. 14 * @dev: Pointer to the ksz_device. 15 * @port: Port number. 16 * @extack: Pointer to the netlink_ext_ack. 17 * @rule: Pointer to the flow_rule. 18 * @cookie: The cookie to associate with the entry. 19 * @prio: The priority of the entry. 20 * 21 * This function parses the Layer 2 key from the flow rule and configures 22 * the corresponding ACL entries. It checks for unsupported offloads and 23 * available entries before proceeding with the configuration. 24 * 25 * Returns: 0 on success or a negative error code on failure. 26 */ 27 static int ksz9477_flower_parse_key_l2(struct ksz_device *dev, int port, 28 struct netlink_ext_ack *extack, 29 struct flow_rule *rule, 30 unsigned long cookie, u32 prio) 31 { 32 struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv; 33 struct flow_match_eth_addrs ematch; 34 struct ksz9477_acl_entries *acles; 35 int required_entries; 36 u8 *src_mac = NULL; 37 u8 *dst_mac = NULL; 38 u16 ethtype = 0; 39 40 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 41 struct flow_match_basic match; 42 43 flow_rule_match_basic(rule, &match); 44 45 if (match.key->n_proto) { 46 if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) { 47 NL_SET_ERR_MSG_MOD(extack, 48 "ethernet type mask must be a full mask"); 49 return -EINVAL; 50 } 51 52 ethtype = be16_to_cpu(match.key->n_proto); 53 } 54 } 55 56 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 57 flow_rule_match_eth_addrs(rule, &ematch); 58 59 if (!is_zero_ether_addr(ematch.key->src)) { 60 if (!is_broadcast_ether_addr(ematch.mask->src)) 61 goto not_full_mask_err; 62 63 src_mac = ematch.key->src; 64 } 65 66 if (!is_zero_ether_addr(ematch.key->dst)) { 67 if (!is_broadcast_ether_addr(ematch.mask->dst)) 68 goto not_full_mask_err; 69 70 dst_mac = ematch.key->dst; 71 } 72 } 73 74 acles = &acl->acles; 75 /* ACL supports only one MAC per entry */ 76 required_entries = src_mac && dst_mac ? 2 : 1; 77 78 /* Check if there are enough available entries */ 79 if (acles->entries_count + required_entries > KSZ9477_ACL_MAX_ENTRIES) { 80 NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached"); 81 return -EOPNOTSUPP; 82 } 83 84 ksz9477_acl_match_process_l2(dev, port, ethtype, src_mac, dst_mac, 85 cookie, prio); 86 87 return 0; 88 89 not_full_mask_err: 90 NL_SET_ERR_MSG_MOD(extack, "MAC address mask must be a full mask"); 91 return -EOPNOTSUPP; 92 } 93 94 /** 95 * ksz9477_flower_parse_key - Parse flow rule keys for a specified port on a 96 * ksz_device. 97 * @dev: The ksz_device instance. 98 * @port: The port number to parse the flow rule keys for. 99 * @extack: The netlink extended ACK for reporting errors. 100 * @rule: The flow_rule to parse. 101 * @cookie: The cookie to associate with the entry. 102 * @prio: The priority of the entry. 103 * 104 * This function checks if the used keys in the flow rule are supported by 105 * the device and parses the L2 keys if they match. If unsupported keys are 106 * used, an error message is set in the extended ACK. 107 * 108 * Returns: 0 on success or a negative error code on failure. 109 */ 110 static int ksz9477_flower_parse_key(struct ksz_device *dev, int port, 111 struct netlink_ext_ack *extack, 112 struct flow_rule *rule, 113 unsigned long cookie, u32 prio) 114 { 115 struct flow_dissector *dissector = rule->match.dissector; 116 int ret; 117 118 if (dissector->used_keys & 119 ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | 120 BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | 121 BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) { 122 NL_SET_ERR_MSG_MOD(extack, 123 "Unsupported keys used"); 124 return -EOPNOTSUPP; 125 } 126 127 if (flow_rule_match_has_control_flags(rule, extack)) 128 return -EOPNOTSUPP; 129 130 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) || 131 flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 132 ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule, 133 cookie, prio); 134 if (ret) 135 return ret; 136 } 137 138 return 0; 139 } 140 141 /** 142 * ksz9477_flower_parse_action - Parse flow rule actions for a specified port 143 * on a ksz_device. 144 * @dev: The ksz_device instance. 145 * @port: The port number to parse the flow rule actions for. 146 * @extack: The netlink extended ACK for reporting errors. 147 * @cls: The flow_cls_offload instance containing the flow rule. 148 * @entry_idx: The index of the ACL entry to store the action. 149 * 150 * This function checks if the actions in the flow rule are supported by 151 * the device. Currently, only actions that change priorities are supported. 152 * If unsupported actions are encountered, an error message is set in the 153 * extended ACK. 154 * 155 * Returns: 0 on success or a negative error code on failure. 156 */ 157 static int ksz9477_flower_parse_action(struct ksz_device *dev, int port, 158 struct netlink_ext_ack *extack, 159 struct flow_cls_offload *cls, 160 int entry_idx) 161 { 162 struct flow_rule *rule = flow_cls_offload_flow_rule(cls); 163 struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv; 164 const struct flow_action_entry *act; 165 struct ksz9477_acl_entry *entry; 166 bool prio_force = false; 167 u8 prio_val = 0; 168 int i; 169 170 if (TC_H_MIN(cls->classid)) { 171 NL_SET_ERR_MSG_MOD(extack, "hw_tc is not supported. Use: action skbedit prio"); 172 return -EOPNOTSUPP; 173 } 174 175 flow_action_for_each(i, act, &rule->action) { 176 switch (act->id) { 177 case FLOW_ACTION_PRIORITY: 178 if (act->priority > KSZ9477_MAX_TC) { 179 NL_SET_ERR_MSG_MOD(extack, "Priority value is too high"); 180 return -EOPNOTSUPP; 181 } 182 prio_force = true; 183 prio_val = act->priority; 184 break; 185 default: 186 NL_SET_ERR_MSG_MOD(extack, "action not supported"); 187 return -EOPNOTSUPP; 188 } 189 } 190 191 /* pick entry to store action */ 192 entry = &acl->acles.entries[entry_idx]; 193 194 ksz9477_acl_action_rule_cfg(entry->entry, prio_force, prio_val); 195 ksz9477_acl_processing_rule_set_action(entry->entry, entry_idx); 196 197 return 0; 198 } 199 200 /** 201 * ksz9477_cls_flower_add - Add a flow classification rule for a specified port 202 * on a ksz_device. 203 * @ds: The DSA switch instance. 204 * @port: The port number to add the flow classification rule to. 205 * @cls: The flow_cls_offload instance containing the flow rule. 206 * @ingress: A flag indicating if the rule is applied on the ingress path. 207 * 208 * This function adds a flow classification rule for a specified port on a 209 * ksz_device. It checks if the ACL offloading is supported and parses the flow 210 * keys and actions. If the ACL is not supported, it returns an error. If there 211 * are unprocessed entries, it parses the action for the rule. 212 * 213 * Returns: 0 on success or a negative error code on failure. 214 */ 215 int ksz9477_cls_flower_add(struct dsa_switch *ds, int port, 216 struct flow_cls_offload *cls, bool ingress) 217 { 218 struct flow_rule *rule = flow_cls_offload_flow_rule(cls); 219 struct netlink_ext_ack *extack = cls->common.extack; 220 struct ksz_device *dev = ds->priv; 221 struct ksz9477_acl_priv *acl; 222 int action_entry_idx; 223 int ret; 224 225 acl = dev->ports[port].acl_priv; 226 227 if (!acl) { 228 NL_SET_ERR_MSG_MOD(extack, "ACL offloading is not supported"); 229 return -EOPNOTSUPP; 230 } 231 232 /* A complex rule set can take multiple entries. Use first entry 233 * to store the action. 234 */ 235 action_entry_idx = acl->acles.entries_count; 236 237 ret = ksz9477_flower_parse_key(dev, port, extack, rule, cls->cookie, 238 cls->common.prio); 239 if (ret) 240 return ret; 241 242 ret = ksz9477_flower_parse_action(dev, port, extack, cls, 243 action_entry_idx); 244 if (ret) 245 return ret; 246 247 ret = ksz9477_sort_acl_entries(dev, port); 248 if (ret) 249 return ret; 250 251 return ksz9477_acl_write_list(dev, port); 252 } 253 254 /** 255 * ksz9477_cls_flower_del - Remove a flow classification rule for a specified 256 * port on a ksz_device. 257 * @ds: The DSA switch instance. 258 * @port: The port number to remove the flow classification rule from. 259 * @cls: The flow_cls_offload instance containing the flow rule. 260 * @ingress: A flag indicating if the rule is applied on the ingress path. 261 * 262 * This function removes a flow classification rule for a specified port on a 263 * ksz_device. It checks if the ACL is initialized, and if not, returns an 264 * error. If the ACL is initialized, it removes entries with the specified 265 * cookie and rewrites the ACL list. 266 * 267 * Returns: 0 on success or a negative error code on failure. 268 */ 269 int ksz9477_cls_flower_del(struct dsa_switch *ds, int port, 270 struct flow_cls_offload *cls, bool ingress) 271 { 272 unsigned long cookie = cls->cookie; 273 struct ksz_device *dev = ds->priv; 274 struct ksz9477_acl_priv *acl; 275 276 acl = dev->ports[port].acl_priv; 277 278 if (!acl) 279 return -EOPNOTSUPP; 280 281 ksz9477_acl_remove_entries(dev, port, &acl->acles, cookie); 282 283 return ksz9477_acl_write_list(dev, port); 284 } 285