X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-parse.c;h=7b881afcba1d212028df6e4ae0866ddd34403f1a;hb=b676167a6f6f5bad24cf2a198c504d6e1cca080b;hp=f4845f6db020c84dd554932d999c690a9290e75b;hpb=e0edde6fee279cdbbf3c179f5f50adaf0c7c7f1e;p=sliver-openvswitch.git diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index f4845f6db..7b881afcb 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -22,15 +22,15 @@ #include #include -#include "autopath.h" #include "bundle.h" #include "byte-order.h" #include "dynamic-string.h" #include "learn.h" #include "meta-flow.h" -#include "netdev.h" #include "multipath.h" +#include "netdev.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" @@ -41,6 +41,9 @@ VLOG_DEFINE_THIS_MODULE(ofp_parse); +static void ofp_fatal(const char *flow, bool verbose, const char *format, ...) + NO_RETURN; + static uint8_t str_to_table_id(const char *str) { @@ -119,107 +122,73 @@ str_to_ip(const char *str, ovs_be32 *ip) *ip = in_addr.s_addr; } -static struct ofp_action_output * -put_output_action(struct ofpbuf *b, uint16_t port) -{ - struct ofp_action_output *oao; - - oao = ofputil_put_OFPAT10_OUTPUT(b); - oao->port = htons(port); - return oao; -} - static void -parse_enqueue(struct ofpbuf *b, char *arg) +parse_enqueue(char *arg, struct ofpbuf *ofpacts) { char *sp = NULL; char *port = strtok_r(arg, ":q", &sp); char *queue = strtok_r(NULL, "", &sp); - struct ofp_action_enqueue *oae; + struct ofpact_enqueue *enqueue; if (port == NULL || queue == NULL) { ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); } - oae = ofputil_put_OFPAT10_ENQUEUE(b); - oae->port = htons(str_to_u32(port)); - oae->queue_id = htonl(str_to_u32(queue)); + enqueue = ofpact_put_ENQUEUE(ofpacts); + enqueue->port = str_to_u32(port); + enqueue->queue = str_to_u32(queue); } static void -parse_output(struct ofpbuf *b, char *arg) +parse_output(char *arg, struct ofpbuf *ofpacts) { if (strchr(arg, '[')) { - struct nx_action_output_reg *naor; - struct mf_subfield src; + struct ofpact_output_reg *output_reg; - mf_parse_subfield(&src, arg); - - naor = ofputil_put_NXAST_OUTPUT_REG(b); - naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits); - naor->src = htonl(src.field->nxm_header); - naor->max_len = htons(UINT16_MAX); + output_reg = ofpact_put_OUTPUT_REG(ofpacts); + mf_parse_subfield(&output_reg->src, arg); + output_reg->max_len = UINT16_MAX; } else { - put_output_action(b, str_to_u32(arg)); + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(ofpacts); + output->port = str_to_u32(arg); + output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; } } static void -parse_resubmit(struct ofpbuf *b, char *arg) +parse_resubmit(char *arg, struct ofpbuf *ofpacts) { - struct nx_action_resubmit *nar; + struct ofpact_resubmit *resubmit; char *in_port_s, *table_s; - uint16_t in_port; - uint8_t table; + + resubmit = ofpact_put_RESUBMIT(ofpacts); in_port_s = strsep(&arg, ","); if (in_port_s && in_port_s[0]) { - if (!ofputil_port_from_string(in_port_s, &in_port)) { - in_port = str_to_u32(in_port_s); + if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { + ovs_fatal(0, "%s: resubmit to unknown port", in_port_s); } } else { - in_port = OFPP_IN_PORT; + resubmit->in_port = OFPP_IN_PORT; } table_s = strsep(&arg, ","); - table = table_s && table_s[0] ? str_to_u32(table_s) : 255; + resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255; - if (in_port == OFPP_IN_PORT && table == 255) { + if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " " on resubmit"); } - - if (in_port != OFPP_IN_PORT && table == 255) { - nar = ofputil_put_NXAST_RESUBMIT(b); - } else { - nar = ofputil_put_NXAST_RESUBMIT_TABLE(b); - nar->table = table; - } - nar->in_port = htons(in_port); } static void -parse_set_tunnel(struct ofpbuf *b, const char *arg) +parse_note(const char *arg, struct ofpbuf *ofpacts) { - uint64_t tun_id = str_to_u64(arg); - if (tun_id > UINT32_MAX) { - ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id); - } else { - ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id); - } -} + struct ofpact_note *note; -static void -parse_note(struct ofpbuf *b, const char *arg) -{ - size_t start_ofs = b->size; - struct nx_action_note *nan; - int remainder; - size_t len; - - nan = ofputil_put_NXAST_NOTE(b); - - b->size -= sizeof nan->note; + note = ofpact_put_NOTE(ofpacts); while (*arg != '\0') { uint8_t byte; bool ok; @@ -235,32 +204,27 @@ parse_note(struct ofpbuf *b, const char *arg) if (!ok) { ovs_fatal(0, "bad hex digit in `note' argument"); } - ofpbuf_put(b, &byte, 1); + ofpbuf_put(ofpacts, &byte, 1); - arg += 2; - } + note = ofpacts->l2; + note->length++; - len = b->size - start_ofs; - remainder = len % OFP_ACTION_ALIGN; - if (remainder) { - ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder); + arg += 2; } - nan = (struct nx_action_note *)((char *)b->data + start_ofs); - nan->len = htons(b->size - start_ofs); + ofpact_update_len(ofpacts, ¬e->ofpact); } static void parse_fin_timeout(struct ofpbuf *b, char *arg) { - struct nx_action_fin_timeout *naft; + struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); char *key, *value; - naft = ofputil_put_NXAST_FIN_TIMEOUT(b); while (ofputil_parse_key_value(&arg, &key, &value)) { if (!strcmp(key, "idle_timeout")) { - naft->fin_idle_timeout = htons(str_to_u16(value, key)); + oft->fin_idle_timeout = str_to_u16(value, key); } else if (!strcmp(key, "hard_timeout")) { - naft->fin_hard_timeout = htons(str_to_u16(value, key)); + oft->fin_hard_timeout = str_to_u16(value, key); } else { ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); } @@ -298,179 +262,459 @@ parse_controller(struct ofpbuf *b, char *arg) } if (reason == OFPR_ACTION && controller_id == 0) { - put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len); + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(b); + output->port = OFPP_CONTROLLER; + output->max_len = max_len; + } else { + struct ofpact_controller *controller; + + controller = ofpact_put_CONTROLLER(b); + controller->max_len = max_len; + controller->reason = reason; + controller->controller_id = controller_id; + } +} + +static void +parse_noargs_dec_ttl(struct ofpbuf *b) +{ + struct ofpact_cnt_ids *ids; + uint16_t id = 0; + + ids = ofpact_put_DEC_TTL(b); + ofpbuf_put(b, &id, sizeof id); + ids = b->l2; + ids->n_controllers++; + ofpact_update_len(b, &ids->ofpact); +} + +static void +parse_dec_ttl(struct ofpbuf *b, char *arg) +{ + if (*arg == '\0') { + parse_noargs_dec_ttl(b); } else { - struct nx_action_controller *nac; + struct ofpact_cnt_ids *ids; + char *cntr; + + ids = ofpact_put_DEC_TTL(b); + ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; + for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; + cntr = strtok_r(NULL, ", ", &arg)) { + uint16_t id = atoi(cntr); + + ofpbuf_put(b, &id, sizeof id); + ids = b->l2; + ids->n_controllers++; + } + if (!ids->n_controllers) { + ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller " + "id."); + } + ofpact_update_len(b, &ids->ofpact); + } +} - nac = ofputil_put_NXAST_CONTROLLER(b); - nac->max_len = htons(max_len); - nac->reason = reason; - nac->controller_id = htons(controller_id); +static void +set_field_parse(const char *arg, struct ofpbuf *ofpacts) +{ + char *orig = xstrdup(arg); + struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts); + char *value; + char *delim; + char *key; + const struct mf_field *mf; + const char *error; + union mf_value mf_value; + + value = orig; + delim = strstr(orig, "->"); + if (!delim) { + ovs_fatal(0, "%s: missing `->'", orig); } + if (strlen(delim) <= strlen("->")) { + ovs_fatal(0, "%s: missing field name following `->'", orig); + } + + key = delim + strlen("->"); + mf = mf_from_name(key); + if (!mf) { + ovs_fatal(0, "%s is not valid oxm field name", key); + } + if (!mf->writable) { + ovs_fatal(0, "%s is not allowed to set", key); + } + + delim[0] = '\0'; + error = mf_parse_value(mf, value, &mf_value); + if (error) { + ovs_fatal(0, "%s", error); + } + if (!mf_is_value_valid(mf, &mf_value)) { + ovs_fatal(0, "%s is not valid valid for field %s", value, key); + } + ofpact_set_field_init(load, mf, &mf_value); + free(orig); +} + +static void +parse_metadata(struct ofpbuf *b, char *arg) +{ + struct ofpact_metadata *om; + char *mask = strchr(arg, '/'); + + om = ofpact_put_WRITE_METADATA(b); + + if (mask) { + *mask = '\0'; + om->mask = htonll(str_to_u64(mask + 1)); + } else { + om->mask = htonll(UINT64_MAX); + } + + om->metadata = htonll(str_to_u64(arg)); } static void parse_named_action(enum ofputil_action_code code, const struct flow *flow, - struct ofpbuf *b, char *arg) + char *arg, struct ofpbuf *ofpacts) { - struct ofp_action_dl_addr *oada; - struct ofp_action_vlan_pcp *oavp; - struct ofp_action_vlan_vid *oavv; - struct ofp_action_nw_addr *oana; - struct ofp_action_tp_port *oata; + struct ofpact_tunnel *tunnel; + uint16_t vid; + uint16_t ethertype; + ovs_be32 ip; + uint8_t pcp; + uint8_t tos; switch (code) { + case OFPUTIL_ACTION_INVALID: + NOT_REACHED(); + case OFPUTIL_OFPAT10_OUTPUT: - parse_output(b, arg); + case OFPUTIL_OFPAT11_OUTPUT: + parse_output(arg, ofpacts); break; case OFPUTIL_OFPAT10_SET_VLAN_VID: - oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b); - oavv->vlan_vid = htons(str_to_u32(arg)); + case OFPUTIL_OFPAT11_SET_VLAN_VID: + vid = str_to_u32(arg); + if (vid & ~VLAN_VID_MASK) { + ovs_fatal(0, "%s: not a valid VLAN VID", arg); + } + ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; break; case OFPUTIL_OFPAT10_SET_VLAN_PCP: - oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b); - oavp->vlan_pcp = str_to_u32(arg); + case OFPUTIL_OFPAT11_SET_VLAN_PCP: + pcp = str_to_u32(arg); + if (pcp & ~7) { + ovs_fatal(0, "%s: not a valid VLAN PCP", arg); + } + ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; + break; + + case OFPUTIL_OFPAT12_SET_FIELD: + set_field_parse(arg, ofpacts); break; case OFPUTIL_OFPAT10_STRIP_VLAN: - ofputil_put_OFPAT10_STRIP_VLAN(b); + case OFPUTIL_OFPAT11_POP_VLAN: + ofpact_put_STRIP_VLAN(ofpacts); + break; + + case OFPUTIL_OFPAT11_PUSH_VLAN: + ethertype = str_to_u16(arg, "ethertype"); + if (ethertype != ETH_TYPE_VLAN_8021Q) { + /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ + ovs_fatal(0, "%s: not a valid VLAN ethertype", arg); + } + ofpact_put_PUSH_VLAN(ofpacts); break; + case OFPUTIL_OFPAT11_SET_QUEUE: + ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); + break; + + case OFPUTIL_OFPAT10_SET_DL_SRC: + case OFPUTIL_OFPAT11_SET_DL_SRC: + str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); + break; + case OFPUTIL_OFPAT10_SET_DL_DST: - oada = ofputil_put_action(code, b); - str_to_mac(arg, oada->dl_addr); + case OFPUTIL_OFPAT11_SET_DL_DST: + str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); break; case OFPUTIL_OFPAT10_SET_NW_SRC: + case OFPUTIL_OFPAT11_SET_NW_SRC: + str_to_ip(arg, &ip); + ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; + break; + case OFPUTIL_OFPAT10_SET_NW_DST: - oana = ofputil_put_action(code, b); - str_to_ip(arg, &oana->nw_addr); + case OFPUTIL_OFPAT11_SET_NW_DST: + str_to_ip(arg, &ip); + ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; break; case OFPUTIL_OFPAT10_SET_NW_TOS: - ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg); + case OFPUTIL_OFPAT11_SET_NW_TOS: + tos = str_to_u32(arg); + if (tos & ~IP_DSCP_MASK) { + ovs_fatal(0, "%s: not a valid TOS", arg); + } + ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; break; + case OFPUTIL_OFPAT11_DEC_NW_TTL: + NOT_REACHED(); + case OFPUTIL_OFPAT10_SET_TP_SRC: + case OFPUTIL_OFPAT11_SET_TP_SRC: + ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); + break; + case OFPUTIL_OFPAT10_SET_TP_DST: - oata = ofputil_put_action(code, b); - oata->tp_port = htons(str_to_u32(arg)); + case OFPUTIL_OFPAT11_SET_TP_DST: + ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); break; case OFPUTIL_OFPAT10_ENQUEUE: - parse_enqueue(b, arg); + parse_enqueue(arg, ofpacts); break; case OFPUTIL_NXAST_RESUBMIT: - parse_resubmit(b, arg); + parse_resubmit(arg, ofpacts); break; case OFPUTIL_NXAST_SET_TUNNEL: - parse_set_tunnel(b, arg); + case OFPUTIL_NXAST_SET_TUNNEL64: + tunnel = ofpact_put_SET_TUNNEL(ofpacts); + tunnel->ofpact.compat = code; + tunnel->tun_id = str_to_u64(arg); + break; + + case OFPUTIL_NXAST_WRITE_METADATA: + parse_metadata(ofpacts, arg); break; case OFPUTIL_NXAST_SET_QUEUE: - ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg)); + ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); break; case OFPUTIL_NXAST_POP_QUEUE: - ofputil_put_NXAST_POP_QUEUE(b); + ofpact_put_POP_QUEUE(ofpacts); break; case OFPUTIL_NXAST_REG_MOVE: - nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg); + nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); break; case OFPUTIL_NXAST_REG_LOAD: - nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg); + nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); break; case OFPUTIL_NXAST_NOTE: - parse_note(b, arg); - break; - - case OFPUTIL_NXAST_SET_TUNNEL64: - ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg)); + parse_note(arg, ofpacts); break; case OFPUTIL_NXAST_MULTIPATH: - multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg); - break; - - case OFPUTIL_NXAST_AUTOPATH: - autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg); + multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); break; case OFPUTIL_NXAST_BUNDLE: - bundle_parse(b, arg); + bundle_parse(arg, ofpacts); break; case OFPUTIL_NXAST_BUNDLE_LOAD: - bundle_parse_load(b, arg); + bundle_parse_load(arg, ofpacts); break; case OFPUTIL_NXAST_RESUBMIT_TABLE: case OFPUTIL_NXAST_OUTPUT_REG: + case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: NOT_REACHED(); case OFPUTIL_NXAST_LEARN: - learn_parse(b, arg, flow); + learn_parse(arg, flow, ofpacts); break; case OFPUTIL_NXAST_EXIT: - ofputil_put_NXAST_EXIT(b); + ofpact_put_EXIT(ofpacts); break; case OFPUTIL_NXAST_DEC_TTL: - ofputil_put_NXAST_DEC_TTL(b); + parse_dec_ttl(ofpacts, arg); + break; + + case OFPUTIL_OFPAT11_DEC_MPLS_TTL: + case OFPUTIL_NXAST_DEC_MPLS_TTL: + ofpact_put_DEC_MPLS_TTL(ofpacts); break; case OFPUTIL_NXAST_FIN_TIMEOUT: - parse_fin_timeout(b, arg); + parse_fin_timeout(ofpacts, arg); break; case OFPUTIL_NXAST_CONTROLLER: - parse_controller(b, arg); + parse_controller(ofpacts, arg); + break; + + case OFPUTIL_OFPAT11_PUSH_MPLS: + case OFPUTIL_NXAST_PUSH_MPLS: + ofpact_put_PUSH_MPLS(ofpacts)->ethertype = + htons(str_to_u16(arg, "push_mpls")); + break; + + case OFPUTIL_OFPAT11_POP_MPLS: + case OFPUTIL_NXAST_POP_MPLS: + ofpact_put_POP_MPLS(ofpacts)->ethertype = + htons(str_to_u16(arg, "pop_mpls")); break; } } +static bool +str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg, + struct ofpbuf *ofpacts, int n_actions) +{ + int code = ofputil_action_code_from_name(act); + if (code >= 0) { + parse_named_action(code, flow, arg, ofpacts); + } else if (!strcasecmp(act, "drop")) { + if (n_actions) { + ovs_fatal(0, "Drop actions must not be preceded by other " + "actions"); + } else if (ofputil_parse_key_value(&pos, &act, &arg)) { + ovs_fatal(0, "Drop actions must not be followed by other " + "actions"); + } + return false; + } else { + uint16_t port; + if (ofputil_port_from_string(act, &port)) { + ofpact_put_OUTPUT(ofpacts)->port = port; + } else { + ovs_fatal(0, "Unknown action: %s", act); + } + } + + return true; +} + static void -str_to_action(const struct flow *flow, char *str, struct ofpbuf *b) +str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) { char *pos, *act, *arg; + enum ofperr error; int n_actions; pos = str; n_actions = 0; while (ofputil_parse_key_value(&pos, &act, &arg)) { - uint16_t port; - int code; - - code = ofputil_action_code_from_name(act); - if (code >= 0) { - parse_named_action(code, flow, b, arg); - } else if (!strcasecmp(act, "drop")) { - /* A drop action in OpenFlow occurs by just not setting - * an action. */ - if (n_actions) { - ovs_fatal(0, "Drop actions must not be preceded by other " - "actions"); - } else if (ofputil_parse_key_value(&pos, &act, &arg)) { - ovs_fatal(0, "Drop actions must not be followed by other " - "actions"); - } + if (!str_to_ofpact__(flow, pos, act, arg, ofpacts, n_actions)) { break; - } else if (ofputil_port_from_string(act, &port)) { - put_output_action(b, port); + } + n_actions++; + } + + error = ofpacts_verify(ofpacts->data, ofpacts->size); + if (error) { + ovs_fatal(0, "Incorrect action ordering"); + } + + ofpact_pad(ofpacts); +} + +static void +parse_named_instruction(enum ovs_instruction_type type, + char *arg, struct ofpbuf *ofpacts) +{ + enum ofperr error; + + switch (type) { + case OVSINST_OFPIT11_APPLY_ACTIONS: + NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */ + break; + + case OVSINST_OFPIT11_WRITE_ACTIONS: + /* XXX */ + ovs_fatal(0, "instruction write-actions is not supported yet"); + break; + + case OVSINST_OFPIT11_CLEAR_ACTIONS: + ofpact_put_CLEAR_ACTIONS(ofpacts); + break; + + case OVSINST_OFPIT11_WRITE_METADATA: + parse_metadata(ofpacts, arg); + break; + + case OVSINST_OFPIT11_GOTO_TABLE: { + struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); + char *table_s = strsep(&arg, ","); + if (!table_s || !table_s[0]) { + ovs_fatal(0, "instruction goto-table needs table id"); + } + ogt->table_id = str_to_table_id(table_s); + break; + } + } + + /* If write_metadata is specified as an action AND an instruction, ofpacts + could be invalid. */ + error = ofpacts_verify(ofpacts->data, ofpacts->size); + if (error) { + ovs_fatal(0, "Incorrect instruction ordering"); + } +} + +static void +str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) +{ + char *pos, *inst, *arg; + int type; + const char *prev_inst = NULL; + int prev_type = -1; + int n_actions = 0; + + pos = str; + while (ofputil_parse_key_value(&pos, &inst, &arg)) { + type = ofpact_instruction_type_from_name(inst); + if (type < 0) { + if (!str_to_ofpact__(flow, pos, inst, arg, ofpacts, n_actions)) { + break; + } + + type = OVSINST_OFPIT11_APPLY_ACTIONS; + if (prev_type == type) { + n_actions++; + continue; + } + } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) { + ovs_fatal(0, "%s isn't supported. Just write actions then " + "it is interpreted as apply_actions", inst); } else { - ovs_fatal(0, "Unknown action: %s", act); + parse_named_instruction(type, arg, ofpacts); + } + + if (type == prev_type) { + ovs_fatal(0, "instruction can be specified at most once: %s", + inst); + } + if (type <= prev_type) { + ovs_fatal(0, "Instruction %s must be specified before %s", + inst, prev_inst); } + + prev_inst = inst; + prev_type = type; n_actions++; } + ofpact_pad(ofpacts); } struct protocol { @@ -493,6 +737,9 @@ parse_protocol(const char *name, const struct protocol **p_out) { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, + { "rarp", ETH_TYPE_RARP, 0}, + { "mpls", ETH_TYPE_MPLS, 0 }, + { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, }; const struct protocol *p; @@ -520,7 +767,7 @@ ofp_fatal(const char *flow, bool verbose, const char *format, ...) } static void -parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule) +parse_field(const struct mf_field *mf, const char *s, struct match *match) { union mf_value value, mask; char *error; @@ -530,7 +777,7 @@ parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule) ovs_fatal(0, "%s", error); } - mf_set(mf, &value, &mask, rule); + mf_set(mf, &value, &mask, match); } /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man @@ -586,15 +833,22 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, NOT_REACHED(); } - cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY); + match_init_catchall(&fm->match); + fm->priority = OFP_DEFAULT_PRIORITY; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); + if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { + /* For modify, by default, don't update the cookie. */ + fm->new_cookie = htonll(UINT64_MAX); + } else{ + fm->new_cookie = htonll(0); + } fm->table_id = 0xff; fm->command = command; fm->idle_timeout = OFP_FLOW_PERMANENT; fm->hard_timeout = OFP_FLOW_PERMANENT; fm->buffer_id = UINT32_MAX; - fm->out_port = OFPP_NONE; + fm->out_port = OFPP_ANY; fm->flags = 0; if (fields & F_ACTIONS) { act_str = strstr(string, "action"); @@ -615,14 +869,20 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, const struct protocol *p; if (parse_protocol(name, &p)) { - cls_rule_set_dl_type(&fm->cr, htons(p->dl_type)); + match_set_dl_type(&fm->match, htons(p->dl_type)); if (p->nw_proto) { - cls_rule_set_nw_proto(&fm->cr, p->nw_proto); + match_set_nw_proto(&fm->match, p->nw_proto); } } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { fm->flags |= OFPFF_SEND_FLOW_REM; } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { fm->flags |= OFPFF_CHECK_OVERLAP; + } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { + fm->flags |= OFPFF12_RESET_COUNTS; + } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { + fm->flags |= OFPFF13_NO_PKT_COUNTS; + } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { + fm->flags |= OFPFF13_NO_BYT_COUNTS; } else { char *value; @@ -634,31 +894,43 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, if (!strcmp(name, "table")) { fm->table_id = str_to_table_id(value); } else if (!strcmp(name, "out_port")) { - fm->out_port = atoi(value); + if (!ofputil_port_from_string(name, &fm->out_port)) { + ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port", + name); + } } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { - fm->cr.priority = str_to_u16(value, name); + fm->priority = str_to_u16(value, name); } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { fm->idle_timeout = str_to_u16(value, name); } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { fm->hard_timeout = str_to_u16(value, name); } else if (!strcmp(name, "cookie")) { char *mask = strchr(value, '/'); + if (mask) { + /* A mask means we're searching for a cookie. */ if (command == OFPFC_ADD) { ofp_fatal(str_, verbose, "flow additions cannot use " "a cookie mask"); } *mask = '\0'; + fm->cookie = htonll(str_to_u64(value)); fm->cookie_mask = htonll(str_to_u64(mask+1)); } else { - fm->cookie_mask = htonll(UINT64_MAX); + /* No mask means that the cookie is being set. */ + if (command != OFPFC_ADD && command != OFPFC_MODIFY + && command != OFPFC_MODIFY_STRICT) { + ofp_fatal(str_, verbose, "cannot set cookie"); + } + fm->new_cookie = htonll(str_to_u64(value)); } - fm->cookie = htonll(str_to_u64(value)); } else if (mf_from_name(name)) { - parse_field(mf_from_name(name), value, &fm->cr); + parse_field(mf_from_name(name), value, &fm->match); } else if (!strcmp(name, "duration") || !strcmp(name, "n_packets") - || !strcmp(name, "n_bytes")) { + || !strcmp(name, "n_bytes") + || !strcmp(name, "idle_age") + || !strcmp(name, "hard_age")) { /* Ignore these, so that users can feed the output of * "ovs-ofctl dump-flows" back into commands that parse * flows. */ @@ -667,31 +939,100 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, } } } + if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX) + && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { + /* On modifies without a mask, we are supposed to add a flow if + * one does not exist. If a cookie wasn't been specified, use a + * default of zero. */ + fm->new_cookie = htonll(0); + } if (fields & F_ACTIONS) { - struct ofpbuf actions; + struct ofpbuf ofpacts; - ofpbuf_init(&actions, sizeof(union ofp_action)); - str_to_action(&fm->cr.flow, act_str, &actions); - fm->actions = ofpbuf_steal_data(&actions); - fm->n_actions = actions.size / sizeof(union ofp_action); + ofpbuf_init(&ofpacts, 32); + str_to_inst_ofpacts(&fm->match.flow, act_str, &ofpacts); + fm->ofpacts_len = ofpacts.size; + fm->ofpacts = ofpbuf_steal_data(&ofpacts); } else { - fm->actions = NULL; - fm->n_actions = 0; + fm->ofpacts_len = 0; + fm->ofpacts = NULL; } free(string); } +/* Convert 'str_' (as described in the documentation for the "monitor" command + * in the ovs-ofctl man page) into 'fmr'. */ +void +parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, + const char *str_) +{ + static uint32_t id; + + char *string = xstrdup(str_); + char *save_ptr = NULL; + char *name; + + fmr->id = id++; + fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY + | NXFMF_OWN | NXFMF_ACTIONS); + fmr->out_port = OFPP_NONE; + fmr->table_id = 0xff; + match_init_catchall(&fmr->match); + + for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; + name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { + const struct protocol *p; + + if (!strcmp(name, "!initial")) { + fmr->flags &= ~NXFMF_INITIAL; + } else if (!strcmp(name, "!add")) { + fmr->flags &= ~NXFMF_ADD; + } else if (!strcmp(name, "!delete")) { + fmr->flags &= ~NXFMF_DELETE; + } else if (!strcmp(name, "!modify")) { + fmr->flags &= ~NXFMF_MODIFY; + } else if (!strcmp(name, "!actions")) { + fmr->flags &= ~NXFMF_ACTIONS; + } else if (!strcmp(name, "!own")) { + fmr->flags &= ~NXFMF_OWN; + } else if (parse_protocol(name, &p)) { + match_set_dl_type(&fmr->match, htons(p->dl_type)); + if (p->nw_proto) { + match_set_nw_proto(&fmr->match, p->nw_proto); + } + } else { + char *value; + + value = strtok_r(NULL, ", \t\r\n", &save_ptr); + if (!value) { + ovs_fatal(0, "%s: field %s missing value", str_, name); + } + + if (!strcmp(name, "table")) { + fmr->table_id = str_to_table_id(value); + } else if (!strcmp(name, "out_port")) { + fmr->out_port = atoi(value); + } else if (mf_from_name(name)) { + parse_field(mf_from_name(name), value, &fmr->match); + } else { + ovs_fatal(0, "%s: unknown keyword %s", str_, name); + } + } + } + free(string); +} + /* Parses 's' as a set of OpenFlow actions and appends the actions to * 'actions'. * * Prints an error on stderr and aborts the program if 's' syntax is * invalid. */ void -parse_ofp_actions(const char *s_, struct ofpbuf *actions) +parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) { char *s = xstrdup(s_); - str_to_action(NULL, s, actions); + str_to_ofpacts(NULL, s, ofpacts); free(s); } @@ -701,15 +1042,15 @@ void parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, uint16_t command, bool verbose) { - struct cls_rule rule_copy; + struct match match_copy; parse_ofp_str(fm, command, string, verbose); - /* Normalize a copy of the rule. This ensures that non-normalized flows + /* Normalize a copy of the match. This ensures that non-normalized flows * get logged but doesn't affect what gets sent to the switch, so that the * switch can do whatever it likes with the flow. */ - rule_copy = fm->cr; - ofputil_normalize_rule(&rule_copy); + match_copy = fm->match; + ofputil_normalize_match(&match_copy); } void @@ -751,7 +1092,88 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, fsr->aggregate = aggregate; fsr->cookie = fm.cookie; fsr->cookie_mask = fm.cookie_mask; - fsr->match = fm.cr; + fsr->match = fm.match; fsr->out_port = fm.out_port; fsr->table_id = fm.table_id; } + +/* Parses a specification of a flow from 's' into 'flow'. 's' must take the + * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a + * mf_field. Fields must be specified in a natural order for satisfying + * prerequisites. + * + * Returns NULL on success, otherwise a malloc()'d string that explains the + * problem. */ +char * +parse_ofp_exact_flow(struct flow *flow, const char *s) +{ + char *pos, *key, *value_s; + char *error = NULL; + char *copy; + + memset(flow, 0, sizeof *flow); + + pos = copy = xstrdup(s); + while (ofputil_parse_key_value(&pos, &key, &value_s)) { + const struct protocol *p; + if (parse_protocol(key, &p)) { + if (flow->dl_type) { + error = xasprintf("%s: Ethernet type set multiple times", s); + goto exit; + } + flow->dl_type = htons(p->dl_type); + + if (p->nw_proto) { + if (flow->nw_proto) { + error = xasprintf("%s: network protocol set " + "multiple times", s); + goto exit; + } + flow->nw_proto = p->nw_proto; + } + } else { + const struct mf_field *mf; + union mf_value value; + char *field_error; + + mf = mf_from_name(key); + if (!mf) { + error = xasprintf("%s: unknown field %s", s, key); + goto exit; + } + + if (!mf_are_prereqs_ok(mf, flow)) { + error = xasprintf("%s: prerequisites not met for setting %s", + s, key); + goto exit; + } + + if (!mf_is_zero(mf, flow)) { + error = xasprintf("%s: field %s set multiple times", s, key); + goto exit; + } + + field_error = mf_parse_value(mf, value_s, &value); + if (field_error) { + error = xasprintf("%s: bad value for %s (%s)", + s, key, field_error); + free(field_error); + goto exit; + } + + mf_set_flow_value(mf, &value, flow); + } + } + + if (!flow->in_port) { + flow->in_port = OFPP_NONE; + } + +exit: + free(copy); + + if (error) { + memset(flow, 0, sizeof *flow); + } + return error; +}