This diff teaches switchd(8) how to validate flow_mod messages, more
specifically the flow instructions and actions. The oxm validations
were already implemented so we get them for free here.

ok?

Index: sys/net/ofp.h
===================================================================
RCS file: /cvs/src/sys/net/ofp.h,v
retrieving revision 1.2
diff -u -p -r1.2 ofp.h
--- sys/net/ofp.h       30 Sep 2016 12:40:00 -0000      1.2
+++ sys/net/ofp.h       12 Oct 2016 15:36:30 -0000
@@ -315,14 +315,14 @@ struct ofp_action_push {
        uint16_t        ap_type;
        uint16_t        ap_len;
        uint16_t        ap_ethertype;
-       uint8_t         pad[2];
+       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: usr.sbin/switchd/ofp13.c
===================================================================
RCS file: /cvs/src/usr.sbin/switchd/ofp13.c,v
retrieving revision 1.20
diff -u -p -r1.20 ofp13.c
--- usr.sbin/switchd/ofp13.c    12 Oct 2016 15:18:56 -0000      1.20
+++ usr.sbin/switchd/ofp13.c    12 Oct 2016 15:36:30 -0000
@@ -54,6 +54,12 @@ int   ofp13_echo_request(struct switchd *
 int     ofp13_validate_error(struct switchd *,
            struct sockaddr_storage *, struct sockaddr_storage *,
            struct ofp_header *, struct ibuf *);
+int     ofp13_validate_action(struct switchd *, struct ofp_header *,
+           struct ibuf *, off_t *, struct ofp_action_header *);
+int     ofp13_validate_instruction(struct switchd *, struct ofp_header *,
+           struct ibuf *, off_t *, struct ofp_instruction *);
+int     ofp13_validate_flow_mod(struct switchd *, struct sockaddr_storage *,
+           struct sockaddr_storage *, struct ofp_header *, struct ibuf *);
 int     ofp13_validate_oxm_basic(struct ibuf *, off_t, int, uint8_t);
 int     ofp13_validate_oxm(struct switchd *, struct ofp_ox_match *,
            struct ofp_header *, struct ibuf *, off_t);
@@ -121,7 +127,7 @@ struct ofp_callback ofp13_callbacks[] = 
        { OFP_T_FLOW_REMOVED,           ofp13_flow_removed, NULL },
        { OFP_T_PORT_STATUS,            NULL, NULL },
        { OFP_T_PACKET_OUT,             NULL, ofp13_validate_packet_out },
-       { OFP_T_FLOW_MOD,               NULL, NULL },
+       { OFP_T_FLOW_MOD,               NULL, ofp13_validate_flow_mod },
        { OFP_T_GROUP_MOD,              NULL, NULL },
        { OFP_T_PORT_MOD,               NULL, NULL },
        { OFP_T_TABLE_MOD,              NULL, NULL },
@@ -501,6 +507,274 @@ ofp13_validate_packet_out(struct switchd
                if (pout->pout_buffer_id == (uint32_t)-1)
                        break;
                off += ntohs(ah->ah_len);
+       }
+
+       return (0);
+}
+
+int
+ofp13_validate_action(struct switchd *sc, struct ofp_header *oh,
+    struct ibuf *ibuf, off_t *off, struct ofp_action_header *ah)
+{
+       struct ofp_action_output        *ao;
+       struct ofp_action_mpls_ttl      *amt;
+       struct ofp_action_push          *ap;
+       struct ofp_action_pop_mpls      *apm;
+       struct ofp_action_group         *ag;
+       struct ofp_action_nw_ttl        *ant;
+       struct ofp_action_set_field     *asf;
+       struct ofp_action_set_queue     *asq;
+       struct ofp_ox_match             *oxm;
+       int                              len, type;
+       off_t                            moff;
+
+       type = ntohs(ah->ah_type);
+       len = ntohs(ah->ah_len);
+       switch (type) {
+       case OFP_ACTION_OUTPUT:
+               if ((ao = ibuf_seek(ibuf, *off, sizeof(*ao))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d port %u max_len %d",
+                   print_map(type, ofp_action_map), len, ntohl(ao->ao_port),
+                   ntohs(ao->ao_max_len));
+               break;
+       case OFP_ACTION_SET_MPLS_TTL:
+               if ((amt = ibuf_seek(ibuf, *off, sizeof(*amt))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d ttl %d",
+                   print_map(type, ofp_action_map), len, amt->amt_ttl);
+               break;
+       case OFP_ACTION_PUSH_VLAN:
+       case OFP_ACTION_PUSH_MPLS:
+       case OFP_ACTION_PUSH_PBB:
+               if ((ap = ibuf_seek(ibuf, *off, sizeof(*ap))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d ethertype %#04x",
+                   print_map(type, ofp_action_map), len,
+                   ntohs(ap->ap_ethertype));
+               break;
+       case OFP_ACTION_POP_MPLS:
+               if ((apm = ibuf_seek(ibuf, *off, sizeof(*apm))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d ethertype %#04x",
+                   print_map(type, ofp_action_map), len,
+                   ntohs(apm->apm_ethertype));
+               break;
+       case OFP_ACTION_SET_QUEUE:
+               if ((asq = ibuf_seek(ibuf, *off, sizeof(*asq))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d queue_id %u",
+                   print_map(type, ofp_action_map), len,
+                   ntohl(asq->asq_queue_id));
+               break;
+       case OFP_ACTION_GROUP:
+               if ((ag = ibuf_seek(ibuf, *off, sizeof(*ag))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d group_id %u",
+                   print_map(type, ofp_action_map), len,
+                   ntohl(ag->ag_group_id));
+               break;
+       case OFP_ACTION_SET_NW_TTL:
+               if ((ant = ibuf_seek(ibuf, *off, sizeof(*ant))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\t\taction %s len %d ttl %d",
+                   print_map(type, ofp_action_map), len, ant->ant_ttl);
+               break;
+       case OFP_ACTION_SET_FIELD:
+               if ((asf = ibuf_seek(ibuf, *off, sizeof(*asf))) == NULL)
+                       return (-1);
+
+               moff = *off + sizeof(*asf) - sizeof(asf->asf_field);
+               *off += len;
+               log_debug("\t\taction %s len %d",
+                   print_map(type, ofp_action_map), len);
+
+               len -= sizeof(*asf) - sizeof(asf->asf_field);
+               while (len) {
+                       if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm)))
+                           == NULL)
+                               return (-1);
+                       if (ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1)
+                               return (-1);
+
+                       len -= sizeof(*oxm) - oxm->oxm_length;
+                       moff += sizeof(*oxm) - oxm->oxm_length;
+               }
+               break;
+
+       default:
+               /* Generic header without information. */
+               *off += len;
+               log_debug("\t\taction %s len %d",
+                   print_map(type, ofp_action_map), len);
+               break;
+       }
+
+       return (0);
+}
+
+int
+ofp13_validate_instruction(struct switchd *sc, struct ofp_header *oh,
+    struct ibuf *ibuf, off_t *off, struct ofp_instruction *i)
+{
+       struct ofp_instruction_actions          *ia;
+       struct ofp_instruction_goto_table       *igt;
+       struct ofp_instruction_write_metadata   *iwm;
+       struct ofp_instruction_meter            *im;
+       struct ofp_action_header                *ah;
+       int                                      type, len;
+       off_t                                    oldoff, diff;
+
+       type = ntohs(i->i_type);
+       len = ntohs(i->i_len);
+
+       switch (type) {
+       case OFP_INSTRUCTION_T_GOTO_TABLE:
+               if ((igt = ibuf_seek(ibuf, *off, sizeof(*igt))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\tinstruction %s length %d table_id %d",
+                   print_map(type, ofp_instruction_t_map), len,
+                   igt->igt_table_id);
+               break;
+       case OFP_INSTRUCTION_T_WRITE_META:
+               if ((iwm = ibuf_seek(ibuf, *off, sizeof(*iwm))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\tinstruction %s length %d "
+                   "metadata %llu mask %llu",
+                   print_map(type, ofp_instruction_t_map), len,
+                   be64toh(iwm->iwm_metadata),
+                   be64toh(iwm->iwm_metadata_mask));
+               break;
+       case OFP_INSTRUCTION_T_METER:
+               if ((im = ibuf_seek(ibuf, *off, sizeof(*im))) == NULL)
+                       return (-1);
+
+               *off += len;
+               log_debug("\tinstruction %s length %d meter_id %d",
+                   print_map(type, ofp_instruction_t_map), len,
+                   im->im_meter_id);
+               break;
+       case OFP_INSTRUCTION_T_WRITE_ACTIONS:
+       case OFP_INSTRUCTION_T_CLEAR_ACTIONS:
+       case OFP_INSTRUCTION_T_APPLY_ACTIONS:
+               if ((ia = ibuf_seek(ibuf, *off, sizeof(*ia))) == NULL)
+                       return (-1);
+
+               log_debug("\tinstruction %s length %d",
+                   print_map(type, ofp_instruction_t_map), len);
+
+               *off += sizeof(*ia);
+               len -= sizeof(*ia);
+               while (len) {
+                       oldoff = *off;
+                       if ((ah = ibuf_seek(ibuf, *off, sizeof(*ah))) == NULL ||
+                           ofp13_validate_action(sc, oh, ibuf, off, ah) == -1)
+                               return (-1);
+
+                       diff = *off - oldoff;
+                       /*
+                        * Loop prevention:
+                        * If the packet is malformed don't let an infinite
+                        * loop happen. This is caused by length fields set
+                        * to zero.
+                        */
+                       if (diff == 0)
+                               break;
+
+                       len -= diff;
+               }
+               break;
+       default:
+               log_debug("\tinstruction %s length %d",
+                   print_map(type, ofp_instruction_t_map), len);
+               *off += len;
+               break;
+       }
+
+       return (0);
+}
+
+int
+ofp13_validate_flow_mod(struct switchd *sc,
+    struct sockaddr_storage *src, struct sockaddr_storage *dst,
+    struct ofp_header *oh, struct ibuf *ibuf)
+{
+       struct ofp_flow_mod             *fm;
+       struct ofp_match                *om;
+       struct ofp_instruction          *i;
+       struct ofp_ox_match             *oxm;
+       off_t                            off, moff, offdiff;
+       int                              matchlen, matchtype, left;
+
+       off = 0;
+       if ((fm = ibuf_seek(ibuf, off, sizeof(*fm))) == NULL)
+               return (-1);
+
+       log_debug("\tcommand %s table %d timeout (idle %d hard %d) "
+           "priority %d buffer_id %u out_port %u out_group %u "
+           "flags %#04x cookie %llu mask %llu",
+           print_map(fm->fm_command, ofp_flowcmd_map),
+           fm->fm_table_id, ntohs(fm->fm_idle_timeout),
+           ntohs(fm->fm_hard_timeout), ntohs(fm->fm_priority),
+           ntohl(fm->fm_buffer_id), ntohl(fm->fm_out_port),
+           ntohl(fm->fm_out_group), ntohs(fm->fm_flags),
+           be64toh(fm->fm_cookie), be64toh(fm->fm_cookie_mask));
+
+       off += offsetof(struct ofp_flow_mod, fm_match);
+
+       om = &fm->fm_match;
+       matchtype = ntohs(om->om_type);
+       matchlen = ntohs(om->om_length);
+
+       moff = off + sizeof(*om);
+       off += OFP_ALIGN(matchlen);
+
+       matchlen -= sizeof(*om);
+       while (matchlen) {
+               if ((oxm = ibuf_seek(ibuf, moff, sizeof(*oxm))) == NULL ||
+                   ofp13_validate_oxm(sc, oxm, oh, ibuf, moff) == -1)
+                       return (-1);
+               moff += sizeof(*oxm) + oxm->oxm_length;
+               matchlen -= sizeof(*oxm) + oxm->oxm_length;
+       }
+
+       left = ntohs(oh->oh_length) - off;
+       moff = off;
+       while (left) {
+               if ((i = ibuf_seek(ibuf, moff, sizeof(*i))) == NULL ||
+                   ofp13_validate_instruction(sc, oh, ibuf, &moff, i) == -1)
+                       return (-1);
+
+               offdiff = moff - off;
+               /*
+                * Loop prevention:
+                * If the packet is malformed don't let an infinite
+                * loop happen. This is caused by length fields set
+                * to zero.
+                */
+               if (offdiff == 0)
+                       break;
+
+               left -= offdiff;
+               off = moff;
        }
 
        return (0);

Reply via email to