net: microchip: sparx5: Let VCAP API validate added key- and actionfields
authorSteen Hegelund <steen.hegelund@microchip.com>
Wed, 9 Nov 2022 11:41:14 +0000 (12:41 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Nov 2022 10:39:12 +0000 (10:39 +0000)
Add support for validating keyfields and actionfields when they are added
to a VCAP rule.
We need to ensure that the field is not already present and that the field
is in the key- or actionset, if the client has added a key- or actionset to
the rule at this point.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/vcap/vcap_api.c

index 37122ba..73ec774 100644 (file)
@@ -790,6 +790,13 @@ const char *vcap_keyfield_name(struct vcap_control *vctrl,
 }
 EXPORT_SYMBOL_GPL(vcap_keyfield_name);
 
+/* map action field id to a string with the action name */
+static const char *vcap_actionfield_name(struct vcap_control *vctrl,
+                                        enum vcap_action_field action)
+{
+       return vctrl->stats->actionfield_names[action];
+}
+
 /* Return the keyfield that matches a key in a keyset */
 static const struct vcap_field *
 vcap_find_keyset_keyfield(struct vcap_control *vctrl,
@@ -1162,14 +1169,60 @@ static void vcap_copy_from_client_keyfield(struct vcap_rule *rule,
        memcpy(&field->data, data, sizeof(field->data));
 }
 
+/* Check if the keyfield is already in the rule */
+static bool vcap_keyfield_unique(struct vcap_rule *rule,
+                                enum vcap_key_field key)
+{
+       struct vcap_rule_internal *ri = to_intrule(rule);
+       const struct vcap_client_keyfield *ckf;
+
+       list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list)
+               if (ckf->ctrl.key == key)
+                       return false;
+       return true;
+}
+
+/* Check if the keyfield is in the keyset */
+static bool vcap_keyfield_match_keyset(struct vcap_rule *rule,
+                                      enum vcap_key_field key)
+{
+       struct vcap_rule_internal *ri = to_intrule(rule);
+       enum vcap_keyfield_set keyset = rule->keyset;
+       enum vcap_type vt = ri->admin->vtype;
+       const struct vcap_field *fields;
+
+       /* the field is accepted if the rule has no keyset yet */
+       if (keyset == VCAP_KFS_NO_VALUE)
+               return true;
+       fields = vcap_keyfields(ri->vctrl, vt, keyset);
+       if (!fields)
+               return false;
+       /* if there is a width there is a way */
+       return fields[key].width > 0;
+}
+
 static int vcap_rule_add_key(struct vcap_rule *rule,
                             enum vcap_key_field key,
                             enum vcap_field_type ftype,
                             struct vcap_client_keyfield_data *data)
 {
+       struct vcap_rule_internal *ri = to_intrule(rule);
        struct vcap_client_keyfield *field;
 
-       /* More validation will be added here later */
+       if (!vcap_keyfield_unique(rule, key)) {
+               pr_warn("%s:%d: keyfield %s is already in the rule\n",
+                       __func__, __LINE__,
+                       vcap_keyfield_name(ri->vctrl, key));
+               return -EINVAL;
+       }
+
+       if (!vcap_keyfield_match_keyset(rule, key)) {
+               pr_err("%s:%d: keyfield %s does not belong in the rule keyset\n",
+                      __func__, __LINE__,
+                      vcap_keyfield_name(ri->vctrl, key));
+               return -EINVAL;
+       }
+
        field = kzalloc(sizeof(*field), GFP_KERNEL);
        if (!field)
                return -ENOMEM;
@@ -1262,14 +1315,60 @@ static void vcap_copy_from_client_actionfield(struct vcap_rule *rule,
        memcpy(&field->data, data, sizeof(field->data));
 }
 
+/* Check if the actionfield is already in the rule */
+static bool vcap_actionfield_unique(struct vcap_rule *rule,
+                                   enum vcap_action_field act)
+{
+       struct vcap_rule_internal *ri = to_intrule(rule);
+       const struct vcap_client_actionfield *caf;
+
+       list_for_each_entry(caf, &ri->data.actionfields, ctrl.list)
+               if (caf->ctrl.action == act)
+                       return false;
+       return true;
+}
+
+/* Check if the actionfield is in the actionset */
+static bool vcap_actionfield_match_actionset(struct vcap_rule *rule,
+                                            enum vcap_action_field action)
+{
+       enum vcap_actionfield_set actionset = rule->actionset;
+       struct vcap_rule_internal *ri = to_intrule(rule);
+       enum vcap_type vt = ri->admin->vtype;
+       const struct vcap_field *fields;
+
+       /* the field is accepted if the rule has no actionset yet */
+       if (actionset == VCAP_AFS_NO_VALUE)
+               return true;
+       fields = vcap_actionfields(ri->vctrl, vt, actionset);
+       if (!fields)
+               return false;
+       /* if there is a width there is a way */
+       return fields[action].width > 0;
+}
+
 static int vcap_rule_add_action(struct vcap_rule *rule,
                                enum vcap_action_field action,
                                enum vcap_field_type ftype,
                                struct vcap_client_actionfield_data *data)
 {
+       struct vcap_rule_internal *ri = to_intrule(rule);
        struct vcap_client_actionfield *field;
 
-       /* More validation will be added here later */
+       if (!vcap_actionfield_unique(rule, action)) {
+               pr_warn("%s:%d: actionfield %s is already in the rule\n",
+                       __func__, __LINE__,
+                       vcap_actionfield_name(ri->vctrl, action));
+               return -EINVAL;
+       }
+
+       if (!vcap_actionfield_match_actionset(rule, action)) {
+               pr_err("%s:%d: actionfield %s does not belong in the rule actionset\n",
+                      __func__, __LINE__,
+                      vcap_actionfield_name(ri->vctrl, action));
+               return -EINVAL;
+       }
+
        field = kzalloc(sizeof(*field), GFP_KERNEL);
        if (!field)
                return -ENOMEM;