> On 28.10.2016, at 19:20, Rafael Zalamena <[email protected]> wrote:
> 
> 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;
> 

Yes, adding the validation step is the right approach!

And this approach is what we also do in switchd (and iked's ikev2 parser).

> Other small fixes:

Small fixes should go in separately:

> - Simplify OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET() macro;

OK

> - Change swofp_flow_table_add() malloc behaviour to be like all the rest
>   of the code (don't wait for memory);

OK

> - swofp_flow_entry_put_instructions() doesn't need a pointer to an mbuf
>   pointer, we only read it;

OK

> - 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;

OK

> - Return more accurate error messages: some error messages were being sent
>   with the wrong type/code combination;

OK

> - Removed swofp_validate_flow_action_set_field() as it was incorporated
>   in action validation;
> 

This is part of the validation diff.

Could you commit the small fixes first and send an updated validation diff?

> 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;
> 

Sure

> 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.
> 

Full ack,

a) it is a very important design decision that switch(4) does NOT depend
on switchd(8). This is not 100% true at the moment (as switch(4) doesn't fully
behave like a normal socket, eg. it only supports full single packet read/write 
I/O),
but it should be possible to connect any user space controller to /dev/switch0,
eg. by using a tool like socat to connect it to ryu instead of switchd.

b) The kernel should never "trust" user land to do the right thing. Crashing it 
from
user input via /dev/switch* would be a bad thing. And we cannot even guarantee
that switchd(8) is absolutely safe.

> ok?
> 

Small fixes OK first, see above.

Reyk

> 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