X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-parse.c;h=da36f88596b48c9b6613412ea207e9690289b405;hb=7431e17196fdb1c3189d67e3aeed4adeab4cf479;hp=6236e5006a4f561678da890c944bb18eb4469ac6;hpb=f25d0cf3c366d92042269a4f787f19c741c2530c;p=sliver-openvswitch.git diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 6236e5006..da36f8859 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ #include #include -#include "autopath.h" #include "bundle.h" #include "byte-order.h" #include "dynamic-string.h" @@ -46,14 +45,14 @@ static void ofp_fatal(const char *flow, bool verbose, const char *format, ...) NO_RETURN; static uint8_t -str_to_table_id(const char *str) +str_to_u8(const char *str, const char *name) { - int table_id; + int value; - if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) { - ovs_fatal(0, "invalid table \"%s\"", str); + if (!str_to_int(str, 10, &value) || value < 0 || value > 255) { + ovs_fatal(0, "invalid %s \"%s\"", name, str); } - return table_id; + return value; } static uint16_t @@ -136,7 +135,7 @@ parse_enqueue(char *arg, struct ofpbuf *ofpacts) } enqueue = ofpact_put_ENQUEUE(ofpacts); - enqueue->port = str_to_u32(port); + enqueue->port = u16_to_ofp(str_to_u32(port)); enqueue->queue = str_to_u32(queue); } @@ -153,7 +152,7 @@ parse_output(char *arg, struct ofpbuf *ofpacts) struct ofpact_output *output; output = ofpact_put_OUTPUT(ofpacts); - output->port = str_to_u32(arg); + output->port = u16_to_ofp(str_to_u32(arg)); output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; } } @@ -169,7 +168,7 @@ parse_resubmit(char *arg, struct ofpbuf *ofpacts) in_port_s = strsep(&arg, ","); if (in_port_s && in_port_s[0]) { if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { - resubmit->in_port = str_to_u32(in_port_s); + ovs_fatal(0, "%s: resubmit to unknown port", in_port_s); } } else { resubmit->in_port = OFPP_IN_PORT; @@ -279,11 +278,152 @@ parse_controller(struct ofpbuf *b, char *arg) } static void -parse_named_action(enum ofputil_action_code code, const struct flow *flow, +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 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); + } +} + +static void +parse_set_mpls_ttl(struct ofpbuf *b, const char *arg) +{ + struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b); + + if (*arg == '\0') { + ovs_fatal(0, "parse_set_mpls_ttl: expected ttl."); + } + + mpls_ttl->ttl = atoi(arg); +} + +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_sample(struct ofpbuf *b, char *arg) +{ + struct ofpact_sample *os = ofpact_put_SAMPLE(b); + char *key, *value; + + while (ofputil_parse_key_value(&arg, &key, &value)) { + if (!strcmp(key, "probability")) { + os->probability = str_to_u16(value, "probability"); + if (os->probability == 0) { + ovs_fatal(0, "invalid probability value \"%s\"", value); + } + } else if (!strcmp(key, "collector_set_id")) { + os->collector_set_id = str_to_u32(value); + } else if (!strcmp(key, "obs_domain_id")) { + os->obs_domain_id = str_to_u32(value); + } else if (!strcmp(key, "obs_point_id")) { + os->obs_point_id = str_to_u32(value); + } else { + ovs_fatal(0, "invalid key \"%s\" in \"sample\" argument", + key); + } + } + if (os->probability == 0) { + ovs_fatal(0, "non-zero \"probability\" must be specified on sample"); + } +} + +static void +parse_named_action(enum ofputil_action_code code, char *arg, struct ofpbuf *ofpacts) { struct ofpact_tunnel *tunnel; uint16_t vid; + uint16_t ethertype; ovs_be32 ip; uint8_t pcp; uint8_t tos; @@ -293,10 +433,12 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, NOT_REACHED(); case OFPUTIL_OFPAT10_OUTPUT: + case OFPUTIL_OFPAT11_OUTPUT: parse_output(arg, ofpacts); break; case OFPUTIL_OFPAT10_SET_VLAN_VID: + 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); @@ -305,6 +447,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, break; case OFPUTIL_OFPAT10_SET_VLAN_PCP: + case OFPUTIL_OFPAT11_SET_VLAN_PCP: pcp = str_to_u32(arg); if (pcp & ~7) { ovs_fatal(0, "%s: not a valid VLAN PCP", arg); @@ -312,29 +455,53 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, 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: + 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: + 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: + 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: + 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); @@ -342,11 +509,16 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, 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: + case OFPUTIL_OFPAT11_SET_TP_DST: ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); break; @@ -365,6 +537,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, tunnel->tun_id = str_to_u64(arg); break; + case OFPUTIL_NXAST_WRITE_METADATA: + parse_metadata(ofpacts, arg); + break; + case OFPUTIL_NXAST_SET_QUEUE: ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); break; @@ -389,10 +565,6 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); break; - case OFPUTIL_NXAST_AUTOPATH: - autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg); - break; - case OFPUTIL_NXAST_BUNDLE: bundle_parse(arg, ofpacts); break; @@ -403,10 +575,11 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, 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(arg, flow, ofpacts); + learn_parse(arg, ofpacts); break; case OFPUTIL_NXAST_EXIT: @@ -414,7 +587,17 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, break; case OFPUTIL_NXAST_DEC_TTL: - ofpact_put_DEC_TTL(ofpacts); + parse_dec_ttl(ofpacts, arg); + break; + + case OFPUTIL_NXAST_SET_MPLS_TTL: + case OFPUTIL_OFPAT11_SET_MPLS_TTL: + parse_set_mpls_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: @@ -424,38 +607,171 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, case OFPUTIL_NXAST_CONTROLLER: 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; + + case OFPUTIL_NXAST_STACK_PUSH: + nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); + break; + case OFPUTIL_NXAST_STACK_POP: + nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); + break; + + case OFPUTIL_NXAST_SAMPLE: + parse_sample(ofpacts, arg); + break; } } +static bool +str_to_ofpact__(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, 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 { + ofp_port_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_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) +str_to_ofpacts(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, 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"); - } + if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) { break; - } else if (ofputil_port_from_string(act, &port)) { - ofpact_put_OUTPUT(ofpacts)->port = 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_OFPIT13_METER: + ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg); + 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_u8(table_s, "table"); + 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(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__(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); @@ -481,6 +797,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; @@ -508,7 +827,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; @@ -518,7 +837,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 @@ -574,7 +893,8 @@ 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) { @@ -588,7 +908,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, 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"); @@ -609,14 +929,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; @@ -626,11 +952,14 @@ 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); + fm->table_id = str_to_u8(value, name); } else if (!strcmp(name, "out_port")) { - fm->out_port = atoi(value); + if (!ofputil_port_from_string(value, &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")) { @@ -656,10 +985,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, fm->new_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. */ @@ -677,11 +1008,19 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, } if (fields & F_ACTIONS) { struct ofpbuf ofpacts; + enum ofperr err; ofpbuf_init(&ofpacts, 32); - str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts); + str_to_inst_ofpacts(act_str, &ofpacts); fm->ofpacts_len = ofpacts.size; fm->ofpacts = ofpbuf_steal_data(&ofpacts); + + err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow, + OFPP_MAX); + if (err) { + exit(EXIT_FAILURE); + } + } else { fm->ofpacts_len = 0; fm->ofpacts = NULL; @@ -690,6 +1029,260 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, free(string); } +/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man + * page) into 'mm' for sending the specified meter_mod 'command' to a switch. + */ +void +parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, + int command, bool verbose) +{ + enum { + F_METER = 1 << 0, + F_FLAGS = 1 << 1, + F_BANDS = 1 << 2, + } fields; + char *string = xstrdup(str_); + char *save_ptr = NULL; + char *band_str = NULL; + char *name; + + switch (command) { + case -1: + fields = F_METER; + break; + + case OFPMC13_ADD: + fields = F_METER | F_FLAGS | F_BANDS; + break; + + case OFPMC13_DELETE: + fields = F_METER; + break; + + case OFPMC13_MODIFY: + fields = F_METER | F_FLAGS | F_BANDS; + break; + + default: + NOT_REACHED(); + } + + mm->command = command; + mm->meter.meter_id = 0; + mm->meter.flags = 0; + if (fields & F_BANDS) { + band_str = strstr(string, "band"); + if (!band_str) { + ofp_fatal(str_, verbose, "must specify bands"); + } + *band_str = '\0'; + + band_str = strchr(band_str + 1, '='); + if (!band_str) { + ofp_fatal(str_, verbose, "must specify bands"); + } + + band_str++; + } + for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; + name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { + + if (fields & F_FLAGS && !strcmp(name, "kbps")) { + mm->meter.flags |= OFPMF13_KBPS; + } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { + mm->meter.flags |= OFPMF13_PKTPS; + } else if (fields & F_FLAGS && !strcmp(name, "burst")) { + mm->meter.flags |= OFPMF13_BURST; + } else if (fields & F_FLAGS && !strcmp(name, "stats")) { + mm->meter.flags |= OFPMF13_STATS; + } else { + char *value; + + value = strtok_r(NULL, ", \t\r\n", &save_ptr); + if (!value) { + ofp_fatal(str_, verbose, "field %s missing value", name); + } + + if (!strcmp(name, "meter")) { + if (!strcmp(value, "all")) { + mm->meter.meter_id = OFPM13_ALL; + } else if (!strcmp(value, "controller")) { + mm->meter.meter_id = OFPM13_CONTROLLER; + } else if (!strcmp(value, "slowpath")) { + mm->meter.meter_id = OFPM13_SLOWPATH; + } else { + mm->meter.meter_id = str_to_u32(value); + if (mm->meter.meter_id > OFPM13_MAX) { + ofp_fatal(str_, verbose, "invalid value for %s", name); + } + } + } else { + ofp_fatal(str_, verbose, "unknown keyword %s", name); + } + } + } + if (fields & F_METER && !mm->meter.meter_id) { + ofp_fatal(str_, verbose, "must specify 'meter'"); + } + if (fields & F_FLAGS && !mm->meter.flags) { + ofp_fatal(str_, verbose, + "meter must specify either 'kbps' or 'pktps'"); + } + + if (fields & F_BANDS) { + struct ofpbuf bands; + uint16_t n_bands = 0; + struct ofputil_meter_band *band = NULL; + int i; + + ofpbuf_init(&bands, 64); + + for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; + name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { + + char *value; + + value = strtok_r(NULL, ", \t\r\n", &save_ptr); + if (!value) { + ofp_fatal(str_, verbose, "field %s missing value", name); + } + + if (!strcmp(name, "type")) { + /* Start a new band */ + band = ofpbuf_put_zeros(&bands, sizeof *band); + n_bands++; + + if (!strcmp(value, "drop")) { + band->type = OFPMBT13_DROP; + } else if (!strcmp(value, "dscp_remark")) { + band->type = OFPMBT13_DSCP_REMARK; + } else { + ofp_fatal(str_, verbose, "field %s unknown value %s", name, + value); + } + } else if (!band || !band->type) { + ofp_fatal(str_, verbose, + "band must start with the 'type' keyword"); + } else if (!strcmp(name, "rate")) { + band->rate = str_to_u32(value); + } else if (!strcmp(name, "burst_size")) { + band->burst_size = str_to_u32(value); + } else if (!strcmp(name, "prec_level")) { + band->prec_level = str_to_u8(value, name); + } else { + ofp_fatal(str_, verbose, "unknown keyword %s", name); + } + } + /* validate bands */ + if (!n_bands) { + ofp_fatal(str_, verbose, "meter must have bands"); + } + + mm->meter.n_bands = n_bands; + mm->meter.bands = ofpbuf_steal_data(&bands); + + for (i = 0; i < n_bands; ++i) { + band = &mm->meter.bands[i]; + + if (!band->type) { + ofp_fatal(str_, verbose, "band must have 'type'"); + } + if (band->type == OFPMBT13_DSCP_REMARK) { + if (!band->prec_level) { + ofp_fatal(str_, verbose, "'dscp_remark' band must have" + " 'prec_level'"); + } + } else { + if (band->prec_level) { + ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have" + " 'prec_level'"); + } + } + if (!band->rate) { + ofp_fatal(str_, verbose, "band must have 'rate'"); + } + if (mm->meter.flags & OFPMF13_BURST) { + if (!band->burst_size) { + ofp_fatal(str_, verbose, "band must have 'burst_size' " + "when 'burst' flag is set"); + } + } else { + if (band->burst_size) { + ofp_fatal(str_, verbose, "band may have 'burst_size' only " + "when 'burst' flag is set"); + } + } + } + } else { + mm->meter.n_bands = 0; + mm->meter.bands = 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_u8(value, name); + } else if (!strcmp(name, "out_port")) { + fmr->out_port = u16_to_ofp(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'. * @@ -699,7 +1292,7 @@ void parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) { char *s = xstrdup(s_); - str_to_ofpacts(NULL, s, ofpacts); + str_to_ofpacts(s, ofpacts); free(s); } @@ -709,15 +1302,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 @@ -759,7 +1352,7 @@ 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; } @@ -832,6 +1425,10 @@ parse_ofp_exact_flow(struct flow *flow, const char *s) } } + if (!flow->in_port.ofp_port) { + flow->in_port.ofp_port = OFPP_NONE; + } + exit: free(copy);