This diff teaches switch(4) how to do more validations on dynamic input
field types, like: ofp_match (has N oxms), ofp_action_header (might be
followed by N actions) and ofp_instruction (might have N actions inside).

This is important because the internal switch structures reuse the ofp_match
and friends from the packet and blindly trusts it to be correct, so to be
able to do that we must ensure that we are receiving them correctly.
We need to pay special attention to the fields that these macros use:
 - OFP_OXM_FOREACH;
 - OFP_ACTION_FOREACH;
 - OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET;
 - OFP_FLOW_MOD_INSTRUCTON_FOREACH;
 - OFP_I_ACTIONS_FOREACH;
 - OFP_BUCKETS_FOREACH;

Other small fixes:
 - Simplify OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET() macro;
 - Change swofp_flow_table_add() malloc behaviour to be like all the rest
   of the code (don't wait for memory);
 - swofp_flow_entry_put_instructions() doesn't need a pointer to an mbuf
   pointer, we only read it;
 - Change the validation function parameters: we can't check for non-errors
   using the value 0, because the spec actually uses it for some errors.
   Instead use int pointer to get error value and use the return value only
   to find out if the validation succeeded or not;
 - Return more accurate error messages: some error messages were being sent
   with the wrong type/code combination;
 - Removed swofp_validate_flow_action_set_field() as it was incorporated
   in action validation;

TODO for next diffs:
 - We still need to code the validation for group messages, but since I
   haven't started using it yet, I didn't touch it;
 - We still need to implement validation for some specific OXM errors:
   duplicated OXM, invalid wildcard, invalid value or missing prereq;
 - swofp_validate_buckets() could use validate_action() with some effort;

Even though switchd(8) does packet validation (both on input and output),
we should commit this to enable other vendors/software to use the OpenBSD
switch(4) directly, otherwise we will need more effort to implement this
for switchd(8) relaying and force people to use switchd(8) needlessly.

ok?

Index: net/ofp.h
===================================================================
RCS file: /home/obsdcvs/src/sys/net/ofp.h,v
retrieving revision 1.2
diff -u -p -r1.2 ofp.h
--- net/ofp.h   30 Sep 2016 12:40:00 -0000      1.2
+++ net/ofp.h   25 Oct 2016 08:50:14 -0000
@@ -95,7 +95,7 @@ struct ofp_hello_element_versionbitmap {
 
 /* Ports */
 #define OFP_PORT_MAX           0xffffff00      /* Maximum number of physical 
ports */
-#define        OFP_PORT_INPUT          0xfffffff8      /* Send back to input 
port */
+#define        OFP_PORT_INPUT          0xfffffff8      /* Send back to input 
port */
 #define OFP_PORT_FLOWTABLE     0xfffffff9      /* Perform actions in flow 
table */
 #define OFP_PORT_NORMAL                0xfffffffa      /* Let switch decide */
 #define OFP_PORT_FLOOD         0xfffffffb      /* All non-block ports except 
input */
@@ -179,9 +179,9 @@ struct ofp_switch_features {
 
 /* Switch capabilities */
 #define OFP_SWCAP_FLOW_STATS           0x1     /* Flow statistics */
-#define OFP_SWCAP_TABLE_STATS                  0x2     /* Table statistics */
-#define OFP_SWCAP_PORT_STATS                   0x4     /* Port statistics */
-#define OFP_SWCAP_GROUP_STATS          0x8     /* Group statistics */
+#define OFP_SWCAP_TABLE_STATS          0x2     /* Table statistics */
+#define OFP_SWCAP_PORT_STATS           0x4     /* Port statistics */
+#define OFP_SWCAP_GROUP_STATS          0x8     /* Group statistics */
 #define OFP_SWCAP_IP_REASM             0x20    /* Can reassemble IP frags */
 #define OFP_SWCAP_QUEUE_STATS          0x40    /* Queue statistics */
 #define OFP_SWCAP_ARP_MATCH_IP         0x80    /* Match IP addresses in ARP 
pkts */
@@ -314,15 +314,15 @@ struct ofp_action_mpls_ttl {
 struct ofp_action_push {
        uint16_t        ap_type;
        uint16_t        ap_len;
-       uint16_t        ap_ethertype;
-       uint8_t         pad[2];
+       uint16_t        ap_ethertype;
+       uint8_t         ap_pad[2];
 } __packed;
 
 struct ofp_action_pop_mpls {
        uint16_t        apm_type;
        uint16_t        apm_len;
        uint16_t        apm_ethertype;
-       uint8_t         pad[2];
+       uint8_t         apm_pad[2];
 } __packed;
 
 struct ofp_action_group {
@@ -342,6 +342,12 @@ struct ofp_action_set_field {
        uint16_t        asf_type;
        uint16_t        asf_len;
        uint8_t         asf_field[4];
+} __packed;
+
+struct ofp_action_set_queue {
+       uint16_t        asq_type;
+       uint16_t        asq_len;
+       uint32_t        asq_queue_id;
 } __packed;
 
 /* Packet-Out Message */
Index: net/switchofp.c
===================================================================
RCS file: /home/obsdcvs/src/sys/net/switchofp.c,v
retrieving revision 1.18
diff -u -p -r1.18 switchofp.c
--- net/switchofp.c     28 Oct 2016 09:01:49 -0000      1.18
+++ net/switchofp.c     28 Oct 2016 14:48:49 -0000
@@ -209,8 +209,13 @@ int         swofp_flow_cmp_strict(struct swofp_
 int     swofp_flow_filter(struct swofp_flow_entry *, uint64_t, uint64_t,
            uint32_t, uint32_t);
 void    swofp_flow_timeout(struct switch_softc *);
-int     swofp_validate_flow_match(struct ofp_match *);
-int     swofp_validate_flow_instruction(struct ofp_instruction *);
+int     swofp_validate_oxm(struct ofp_ox_match *, int *);
+int     swofp_validate_flow_match(struct ofp_match *, int *);
+int     swofp_validate_flow_instruction(struct ofp_instruction *, size_t,
+           int *);
+int     swofp_validate_action(struct ofp_action_header *, size_t, int *);
+int     swofp_flow_entry_put_instructions(struct mbuf *,
+           struct swofp_flow_entry *, int *);
 
 /*
  * OpenFlow protocol compare oxm
@@ -388,9 +393,9 @@ for ((curr) = (head); (caddr_t)curr < ((
 /*
  * Get instruction offset in ofp_flow_mod
  */
-#define OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET(ofm)                               
\
-(offsetof(struct ofp_flow_mod, fm_match) + ntohs((ofm)->fm_match.om_length) +  
\
-    (((0x8 - (ntohs((ofm)->fm_match.om_length) % 0x8)) & 0x7)))
+#define OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET(ofm)               \
+       (offsetof(struct ofp_flow_mod, fm_match) +              \
+           OFP_ALIGN(ntohs((ofm)->fm_match.om_length)))
 
 /*
  * Instructions "FOREACH" in ofp_flow_mod. You can get header using
@@ -1238,9 +1243,10 @@ swofp_flow_table_add(struct switch_softc
        if ((swft = swofp_flow_table_lookup(sc, table_id)) != NULL)
                return (swft);
 
-       new = malloc(sizeof(*new), M_DEVBUF, M_WAITOK|M_ZERO);
-       new->swft_table_id = table_id;
+       if ((new = malloc(sizeof(*new), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
+               return (NULL);
 
+       new->swft_table_id = table_id;
        TAILQ_FOREACH(swft, &ofs->swofs_table_list, swft_table_next) {
                if (table_id < swft->swft_table_id)
                        break;
@@ -1804,75 +1810,240 @@ swofp_ox_cmp_ether_addr(struct ofp_ox_ma
        }
 }
 
-
-/* TODO: validation for match */
 int
-swofp_validate_flow_match(struct ofp_match *om)
+swofp_validate_oxm(struct ofp_ox_match *oxm, int *err)
 {
-       struct ofp_oxm_class *handler;
-       struct ofp_ox_match *oxm;
+       struct ofp_oxm_class    *handler;
+       int                      length, hasmask;
+       int                      neededlen;
 
-       OFP_OXM_FOREACH(om, ntohs(om->om_length), oxm) {
-               handler = swofp_lookup_oxm_handler(oxm);
-               if (handler == NULL)
-                       return (OFP_ERRMATCH_BAD_FIELD);
-               if (handler->oxm_match == NULL)
-                       return (OFP_ERRMATCH_BAD_FIELD);
+       hasmask = OFP_OXM_GET_HASMASK(oxm);
+       length = oxm->oxm_length;
+
+       handler = swofp_lookup_oxm_handler(oxm);
+       if (handler == NULL || handler->oxm_match == NULL) {
+               *err = OFP_ERRMATCH_BAD_FIELD;
+               return (-1);
+       }
+
+       neededlen = (hasmask) ?
+           (handler->oxm_len * 2) : (handler->oxm_len);
+       if (oxm->oxm_length != neededlen) {
+               *err = OFP_ERRMATCH_BAD_LEN;
+               return (-1);
        }
 
        return (0);
 }
 
 int
-swofp_validate_flow_action_set_field(struct ofp_action_set_field *oasf)
+swofp_validate_flow_match(struct ofp_match *om, int *err)
 {
-       struct ofp_ox_match     *oxm;
-       struct ofp_oxm_class    *handler;
-
-       oxm = (struct ofp_ox_match *)oasf->asf_field;
+       struct ofp_ox_match *oxm;
 
-       handler = swofp_lookup_oxm_handler(oxm);
-       if (handler == NULL)
-               return (OFP_ERRACTION_SET_TYPE);
-       if (handler->oxm_set == NULL)
-               return (OFP_ERRACTION_SET_TYPE);
+       /*
+        * TODO this function is missing checks for:
+        * - OFP_ERRMATCH_BAD_TAG;
+        * - OFP_ERRMATCH_BAD_VALUE;
+        * - OFP_ERRMATCH_BAD_MASK;
+        * - OFP_ERRMATCH_BAD_PREREQ;
+        * - OFP_ERRMATCH_DUP_FIELD;
+        */
+       OFP_OXM_FOREACH(om, ntohs(om->om_length), oxm) {
+               if (swofp_validate_oxm(oxm, err))
+                       return (*err);
+       }
 
        return (0);
 }
 
-/* TODO: validation for instruction */
 int
-swofp_validate_flow_instruction(struct ofp_instruction *oi)
+swofp_validate_flow_instruction(struct ofp_instruction *oi, size_t total,
+    int *err)
 {
        struct ofp_action_header        *oah;
        struct ofp_instruction_actions  *oia;
-       int                              error = 0;
+       int                              ilen;
+
+       ilen = ntohs(oi->i_len);
+       /* Check for bigger than packet or smaller than header. */
+       if (ilen > total || ilen < sizeof(*oi)) {
+               *err = OFP_ERRINST_BAD_LEN;
+               return (-1);
+       }
 
        switch (ntohs(oi->i_type)) {
        case OFP_INSTRUCTION_T_GOTO_TABLE:
+               if (ilen != sizeof(struct ofp_instruction_goto_table)) {
+                       *err = OFP_ERRINST_BAD_LEN;
+                       return (-1);
+               }
+               break;
        case OFP_INSTRUCTION_T_WRITE_META:
+               if (ilen != sizeof(struct ofp_instruction_write_metadata)) {
+                       *err = OFP_ERRINST_BAD_LEN;
+                       return (-1);
+               }
+               break;
        case OFP_INSTRUCTION_T_METER:
-       case OFP_INSTRUCTION_T_EXPERIMENTER:
+               if (ilen != sizeof(struct ofp_instruction_meter)) {
+                       *err = OFP_ERRINST_BAD_LEN;
+                       return (-1);
+               }
                break;
+
        case OFP_INSTRUCTION_T_WRITE_ACTIONS:
        case OFP_INSTRUCTION_T_CLEAR_ACTIONS:
-               break;
        case OFP_INSTRUCTION_T_APPLY_ACTIONS:
+               if (ilen < sizeof(*oia)) {
+                       *err = OFP_ERRINST_BAD_LEN;
+                       return (-1);
+               }
+
                oia = (struct ofp_instruction_actions *)oi;
-               OFP_I_ACTIONS_FOREACH(oia, oah) {
-                       if (ntohs(oah->ah_type) == OFP_ACTION_SET_FIELD) {
-                               error = swofp_validate_flow_action_set_field(
-                                   (struct ofp_action_set_field *)oah);
-                               if (error)
-                                       break;
+
+               /* Validate actions before iterating over them. */
+               oah = (struct ofp_action_header *)
+                   ((uint8_t *)oia + sizeof(*oia));
+               if (swofp_validate_action(oah, ilen - sizeof(*oia), err))
+                       return (-1);
+               break;
+
+       case OFP_INSTRUCTION_T_EXPERIMENTER:
+               /* FALLTHROUGH */
+       default:
+               *err = OFP_ERRINST_UNKNOWN_INST;
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+swofp_validate_action(struct ofp_action_header *ah, size_t ahtotal, int *err)
+{
+       struct ofp_action_handler       *oah;
+       struct ofp_ox_match             *oxm;
+       uint8_t                         *dptr;
+       int                              ahtype, ahlen, oxmlen;
+
+       /* No actions. */
+       if (ahtotal == 0)
+               return (0);
+
+       /* Check if we have at least the first header. */
+       if (ahtotal < sizeof(*ah)) {
+               *err = OFP_ERRACTION_LEN;
+               return (-1);
+       }
+
+ parse_next_action:
+       ahtype = ntohs(ah->ah_type);
+       ahlen = ntohs(ah->ah_len);
+       if (ahlen < sizeof(*ah) || ahlen > ahtotal) {
+               *err = OFP_ERRACTION_LEN;
+               return (-1);
+       }
+
+       switch (ahtype) {
+       case OFP_ACTION_OUTPUT:
+               if (ahlen != sizeof(struct ofp_action_output)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_GROUP:
+               if (ahlen != sizeof(struct ofp_action_group)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_SET_QUEUE:
+               /* TODO missing struct in ofp.h. */
+               break;
+       case OFP_ACTION_SET_MPLS_TTL:
+               if (ahlen != sizeof(struct ofp_action_mpls_ttl)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_SET_NW_TTL:
+               if (ahlen != sizeof(struct ofp_action_nw_ttl)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_COPY_TTL_OUT:
+       case OFP_ACTION_COPY_TTL_IN:
+       case OFP_ACTION_DEC_MPLS_TTL:
+       case OFP_ACTION_POP_VLAN:
+               if (ahlen != sizeof(struct ofp_action_header)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_PUSH_VLAN:
+       case OFP_ACTION_PUSH_MPLS:
+       case OFP_ACTION_PUSH_PBB:
+               if (ahlen != sizeof(struct ofp_action_push)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_POP_MPLS:
+               if (ahlen != sizeof(struct ofp_action_pop_mpls)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+               break;
+       case OFP_ACTION_SET_FIELD:
+               if (ahlen < sizeof(struct ofp_action_set_field)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+
+               oxmlen = ahlen - sizeof(struct ofp_action_set_field);
+               if (oxmlen < sizeof(*oxm)) {
+                       *err = OFP_ERRACTION_LEN;
+                       return (-1);
+               }
+
+               dptr = (uint8_t *)ah;
+               dptr += sizeof(struct ofp_action_set_field);
+               while (oxmlen) {
+                       oxm = (struct ofp_ox_match *)dptr;
+                       if (swofp_validate_oxm(oxm, err)) {
+                               if (*err == OFP_ERRMATCH_BAD_LEN)
+                                       *err = OFP_ERRACTION_SET_LEN;
+                               else
+                                       *err = OFP_ERRACTION_SET_TYPE;
+
+                               return (-1);
                        }
+
+                       dptr += sizeof(*oxm) + oxm->oxm_length;
+                       oxmlen -= sizeof(*oxm) + oxm->oxm_length;
                }
                break;
+
        default:
-               error = EINVAL;
+               /* Unknown/unsupported action. */
+               *err = OFP_ERRACTION_TYPE;
+               return (-1);
        }
 
-       return (error);
+       oah = swofp_lookup_action_handler(ahtype);
+       /* Unknown/unsupported action. */
+       if (oah == NULL) {
+               *err = OFP_ERRACTION_TYPE;
+               return (-1);
+       }
+
+       ahtotal -= min(ahlen, ahtotal);
+       if (ahtotal)
+               goto parse_next_action;
+
+       return (0);
 }
 
 int
@@ -4565,15 +4736,15 @@ swofp_send_flow_removed(struct switch_so
  * OpenFlow protocol FLOW MOD message handlers
  */
 int
-swofp_flow_entry_put_instructions(struct mbuf **m,
-    struct swofp_flow_entry *swfe)
+swofp_flow_entry_put_instructions(struct mbuf *m,
+    struct swofp_flow_entry *swfe, int *err)
 {
        struct ofp_flow_mod     *ofm;
        struct ofp_instruction  *oi;
        caddr_t                  inst;
-       int                      start, len, off, error;
+       int                      start, len, off;
 
-       ofm = mtod((*m), struct ofp_flow_mod *);
+       ofm = mtod(m, struct ofp_flow_mod *);
 
        /*
         * Clear instructions from flow entry. It's necessary to only modify
@@ -4585,43 +4756,72 @@ swofp_flow_entry_put_instructions(struct
        start = OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET(ofm);
        len = ntohs(ofm->fm_oh.oh_length) - start;
        for (off = start; off < start + len; off += ntohs(oi->i_len)) {
-               oi = (struct ofp_instruction *)(mtod(*m, caddr_t) + off);
-
-               if ((error = swofp_validate_flow_instruction(oi)))
-                       goto failed;
+               oi = (struct ofp_instruction *)(mtod(m, caddr_t) + off);
+               if (swofp_validate_flow_instruction(oi,
+                   len - (off - start), err))
+                       return (-1);
 
                if ((inst = malloc(ntohs(oi->i_len), M_DEVBUF,
-                           M_DONTWAIT|M_ZERO)) == NULL) {
-                               error = OFP_ERRFLOWMOD_UNKNOWN;
-                               goto failed;
+                   M_DONTWAIT|M_ZERO)) == NULL) {
+                       *err = OFP_ERRINST_EPERM;
+                       return (-1);
                }
                memcpy(inst, oi, ntohs(oi->i_len));
 
                switch (ntohs(oi->i_type)) {
                case OFP_INSTRUCTION_T_GOTO_TABLE:
+                       if (swfe->swfe_goto_table)
+                               free(swfe->swfe_goto_table, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
                        swfe->swfe_goto_table =
                            (struct ofp_instruction_goto_table *)inst;
                        break;
                case OFP_INSTRUCTION_T_WRITE_META:
+                       if (swfe->swfe_write_metadata)
+                               free(swfe->swfe_write_metadata, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
                        swfe->swfe_write_metadata =
                            (struct ofp_instruction_write_metadata *)inst;
                        break;
                case OFP_INSTRUCTION_T_WRITE_ACTIONS:
+                       if (swfe->swfe_write_actions)
+                               free(swfe->swfe_write_actions, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
                        swfe->swfe_write_actions =
                            (struct ofp_instruction_actions *)inst;
                        break;
                case OFP_INSTRUCTION_T_APPLY_ACTIONS:
+                       if (swfe->swfe_apply_actions)
+                               free(swfe->swfe_apply_actions, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
                        swfe->swfe_apply_actions =
                            (struct ofp_instruction_actions *)inst;
                        break;
                case OFP_INSTRUCTION_T_CLEAR_ACTIONS:
+                       if (swfe->swfe_clear_actions)
+                               free(swfe->swfe_clear_actions, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
                        swfe->swfe_clear_actions =
                            (struct ofp_instruction_actions *)inst;
                        break;
                case OFP_INSTRUCTION_T_METER:
-                       swfe->swfe_meter = (struct ofp_instruction_meter *)inst;
+                       if (swfe->swfe_meter)
+                               free(swfe->swfe_meter, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
+                       swfe->swfe_meter =
+                           (struct ofp_instruction_meter *)inst;
                        break;
                case OFP_INSTRUCTION_T_EXPERIMENTER:
+                       if (swfe->swfe_experimenter)
+                               free(swfe->swfe_experimenter, M_DEVBUF,
+                                   ntohs(oi->i_len));
+
                        swfe->swfe_experimenter =
                            (struct ofp_instruction_experimenter *)inst;
                        break;
@@ -4632,9 +4832,6 @@ swofp_flow_entry_put_instructions(struct
        }
 
        return (0);
-
- failed:
-       return (error);
 }
 
 int
@@ -4645,7 +4842,8 @@ swofp_flow_mod_cmd_add(struct switch_sof
        struct ofp_match                *om;
        struct swofp_flow_entry         *swfe, *old_swfe;
        struct swofp_flow_table         *swft;
-       int                              error;
+       int                              error, omlen;
+       uint16_t                         etype = OFP_ERRTYPE_FLOW_MOD_FAILED;
 
        oh = mtod(m, struct ofp_header *);
        ofm = mtod(m, struct ofp_flow_mod *);
@@ -4662,8 +4860,30 @@ swofp_flow_mod_cmd_add(struct switch_sof
                goto ofp_error;
        }
 
-       if ((swft = swofp_flow_table_lookup(sc, ofm->fm_table_id)) == NULL)
-               swft = swofp_flow_table_add(sc, ofm->fm_table_id);
+       omlen = ntohs(om->om_length);
+       /*
+        * 1) ofp_match header must have at least its own size,
+        *    otherwise memcpy() will fail later;
+        * 2) OXM filters can't be bigger than the packet.
+        */
+       if (omlen < sizeof(*om) &&
+           omlen >= (m->m_len - sizeof(*ofm))) {
+               etype = OFP_ERRTYPE_BAD_REQUEST;
+               error = OFP_ERRREQ_LEN;
+               goto ofp_error;
+       }
+
+       /* Validate that the OXM are in-place and correct. */
+       if (swofp_validate_flow_match(om, &error)) {
+               etype = OFP_ERRTYPE_BAD_MATCH;
+               goto ofp_error;
+       }
+
+       swft = swofp_flow_table_add(sc, ofm->fm_table_id);
+       if (swft == NULL) {
+               error = OFP_ERRFLOWMOD_TABLE_ID;
+               goto ofp_error;
+       }
 
        if ((old_swfe = swofp_flow_search_by_table(swft, om,
            ntohs(ofm->fm_priority)))) {
@@ -4687,26 +4907,25 @@ swofp_flow_mod_cmd_add(struct switch_sof
        nanouptime(&swfe->swfe_installed_time);
        nanouptime(&swfe->swfe_idle_time);
 
-       if ((error = swofp_validate_flow_match(om)))
-               goto ofp_error;
-
-       if ((swfe->swfe_match = malloc(ntohs(om->om_length), M_DEVBUF,
+       if ((swfe->swfe_match = malloc(omlen, M_DEVBUF,
            M_DONTWAIT|M_ZERO)) == NULL) {
                error = OFP_ERRFLOWMOD_UNKNOWN;
                goto ofp_error_free_flow;
        }
-       memcpy(swfe->swfe_match, om, ntohs(om->om_length));
+       memcpy(swfe->swfe_match, om, omlen);
 
        /*
         * If the ofp_match structure is empty and priority is zero, then
         * this is a special flow type called table-miss which is the last
         * flow to match.
         */
-       if (ntohs(om->om_length) == sizeof(*om) && swfe->swfe_priority == 0)
+       if (omlen == sizeof(*om) && swfe->swfe_priority == 0)
                swfe->swfe_tablemiss = 1;
 
-       if ((error = swofp_flow_entry_put_instructions(&m, swfe)))
+       if (swofp_flow_entry_put_instructions(m, swfe, &error)) {
+               etype = OFP_ERRTYPE_BAD_INSTRUCTION;
                goto ofp_error_free_flow;
+       }
 
        if (old_swfe) {
                if (!(ntohs(ofm->fm_flags) & OFP_FLOWFLAG_RESET_COUNTS)) {
@@ -4730,7 +4949,7 @@ swofp_flow_mod_cmd_add(struct switch_sof
  ofp_error_free_flow:
        swofp_flow_entry_free(&swfe);
  ofp_error:
-       swofp_send_error(sc, m, OFP_ERRTYPE_FLOW_MOD_FAILED, error);
+       swofp_send_error(sc, m, etype, error);
        return (0);
 }
 
@@ -4743,7 +4962,8 @@ swofp_flow_mod_cmd_common_modify(struct 
        struct ofp_match                *om;
        struct swofp_flow_entry         *swfe;
        struct swofp_flow_table         *swft;
-       int                              error;
+       int                              error, omlen;
+       uint16_t                         etype = OFP_ERRTYPE_FLOW_MOD_FAILED;
 
        oh = mtod(m, struct ofp_header *);
        ofm = mtod(m, struct ofp_flow_mod *);
@@ -4760,6 +4980,25 @@ swofp_flow_mod_cmd_common_modify(struct 
                goto ofp_error;
        }
 
+       omlen = ntohs(om->om_length);
+       /*
+        * 1) ofp_match header must have at least its own size,
+        *    otherwise memcpy() will fail later;
+        * 2) OXM filters can't be bigger than the packet.
+        */
+       if (omlen < sizeof(*om) &&
+           omlen >= (m->m_len - sizeof(*ofm))) {
+               etype = OFP_ERRTYPE_BAD_MATCH;
+               error = OFP_ERRMATCH_BAD_LEN;
+               goto ofp_error;
+       }
+
+       /* Validate that the OXM are in-place and correct. */
+       if (swofp_validate_flow_match(om, &error)) {
+               etype = OFP_ERRTYPE_BAD_MATCH;
+               goto ofp_error;
+       }
+
        if ((swft = swofp_flow_table_lookup(sc, ofm->fm_table_id)) == NULL) {
                error = OFP_ERRFLOWMOD_TABLE_ID;
                goto ofp_error;
@@ -4777,7 +5016,7 @@ swofp_flow_mod_cmd_common_modify(struct 
                    ntohl(ofm->fm_out_group)))
                        continue;
 
-               if ((error = swofp_flow_entry_put_instructions(&m, swfe))) {
+               if (swofp_flow_entry_put_instructions(m, swfe, &error)) {
                        /*
                         * If error occurs in swofp_flow_entry_put_instructions,
                         * the flow entry might be half-way modified. So the
@@ -4785,6 +5024,7 @@ swofp_flow_mod_cmd_common_modify(struct 
                         */
                        swofp_flow_entry_delete(sc, swft, swfe,
                            OFP_FLOWREM_REASON_DELETE);
+                       etype = OFP_ERRTYPE_BAD_INSTRUCTION;
                        goto ofp_error;
                }
 
@@ -4798,7 +5038,7 @@ swofp_flow_mod_cmd_common_modify(struct 
        return (0);
 
  ofp_error:
-       swofp_send_error(sc, m, OFP_ERRTYPE_FLOW_MOD_FAILED, error);
+       swofp_send_error(sc, m, etype, error);
        return (0);
 }
 
@@ -4823,10 +5063,30 @@ swofp_flow_mod_cmd_common_delete(struct 
        struct ofp_flow_mod     *ofm;
        struct ofp_match        *om;
        struct swofp_flow_table *swft;
+       int                      error, omlen;
+       uint16_t                 etype = OFP_ERRTYPE_FLOW_MOD_FAILED;
 
        ofm = (struct ofp_flow_mod *)(mtod(m, caddr_t));
        om = &ofm->fm_match;
 
+       omlen = ntohs(om->om_length);
+       /*
+        * 1) ofp_match header must have at least its own size,
+        *    otherwise memcpy() will fail later;
+        * 2) OXM filters can't be bigger than the packet.
+        */
+       if (omlen < sizeof(*om) &&
+           omlen >= (m->m_len - sizeof(*ofm))) {
+               error = OFP_ERRFLOWMOD_UNKNOWN;
+               goto ofp_error;
+       }
+
+       /* Validate that the OXM are in-place and correct. */
+       if (swofp_validate_flow_match(om, &error)) {
+               etype = OFP_ERRTYPE_BAD_MATCH;
+               goto ofp_error;
+       }
+
        TAILQ_FOREACH(swft, &ofs->swofs_table_list, swft_table_next) {
                if ((ofm->fm_table_id != OFP_TABLE_ID_ALL) &&
                    (ofm->fm_table_id != swft->swft_table_id))
@@ -4842,6 +5102,10 @@ swofp_flow_mod_cmd_common_delete(struct 
 
        m_freem(m);
        return (0);
+
+ ofp_error:
+       swofp_send_error(sc, m, etype, error);
+       return (-1);
 }
 
 int
@@ -5067,7 +5331,7 @@ swofp_recv_packet_out(struct switch_soft
        struct ofp_packet_out           *pout;
        struct ofp_action_header        *ah;
        struct mbuf                     *mc = NULL, *mcn;
-       int                              al_start, al_len, off;
+       int                              al_start, al_len, off, error;
        struct switch_flow_classify      swfcl = {};
        struct swofp_pipline_desc        swpld = { .swpld_swfcl = &swfcl };
 
@@ -5084,6 +5348,15 @@ swofp_recv_packet_out(struct switch_soft
        pout = mtod(m, struct ofp_packet_out *);
 
        al_start = offsetof(struct ofp_packet_out, pout_actions);
+
+       /* Validate actions before anything else. */
+       ah = (struct ofp_action_header *)
+           ((uint8_t *)pout + sizeof(*pout));
+       if (swofp_validate_action(ah, al_len, &error)) {
+               swofp_send_error(sc, m, OFP_ERRTYPE_BAD_REQUEST, error);
+               return (EINVAL);
+       }
+
        if (pout->pout_buffer_id == OFP_PKTOUT_NO_BUFFER) {
                /*
                 * It's not necessary to deep copy at here because it's done

Reply via email to