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