/* 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); }
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); }
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man * page) into 'fm' for sending the specified flow_mod 'command' to a switch. * If 'actions' is specified, an action must be in 'string' and may be expanded * or reallocated. * * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* * constant for 'command'. To parse syntax for an OFPST_FLOW or * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ void parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, bool verbose) { enum { F_OUT_PORT = 1 << 0, F_ACTIONS = 1 << 1, F_TIMEOUT = 1 << 3, F_PRIORITY = 1 << 4, F_FLAGS = 1 << 5, } fields; char *string = xstrdup(str_); char *save_ptr = NULL; char *act_str = NULL; char *name; switch (command) { case -1: fields = F_OUT_PORT; break; case OFPFC_ADD: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; break; case OFPFC_DELETE: fields = F_OUT_PORT; break; case OFPFC_DELETE_STRICT: fields = F_OUT_PORT | F_PRIORITY; break; case OFPFC_MODIFY: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; break; case OFPFC_MODIFY_STRICT: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; break; default: NOT_REACHED(); } 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_ANY; fm->flags = 0; if (fields & F_ACTIONS) { act_str = strstr(string, "action"); if (!act_str) { ofp_fatal(str_, verbose, "must specify an action"); } *act_str = '\0'; act_str = strchr(act_str + 1, '='); if (!act_str) { ofp_fatal(str_, verbose, "must specify an action"); } act_str++; } 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 (parse_protocol(name, &p)) { match_set_dl_type(&fm->match, htons(p->dl_type)); if (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; value = strtok_r(NULL, ", \t\r\n", &save_ptr); if (!value) { ofp_fatal(str_, verbose, "field %s missing value", name); } if (!strcmp(name, "table")) { fm->table_id = str_to_u8(value, name); } else if (!strcmp(name, "out_port")) { 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->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 { /* 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)); } } else if (mf_from_name(name)) { parse_field(mf_from_name(name), value, &fm->match); } else if (!strcmp(name, "duration") || !strcmp(name, "n_packets") || !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. */ } else { ofp_fatal(str_, verbose, "unknown keyword %s", name); } } } 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 ofpacts; enum ofperr err; ofpbuf_init(&ofpacts, 32); 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, 0); if (err) { exit(EXIT_FAILURE); } } else { fm->ofpacts_len = 0; fm->ofpacts = NULL; } free(string); }
/* 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.ofp_port) { flow->in_port.ofp_port = OFPP_NONE; } exit: free(copy); if (error) { memset(flow, 0, sizeof *flow); } return error; }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_spec(const char *orig, char *name, char *value, struct ofpact_learn_spec *spec) { if (mf_from_name(name)) { const struct mf_field *dst = mf_from_name(name); union mf_value imm; char *error; error = mf_parse_value(dst, value, &imm); if (error) { return error; } spec->n_bits = dst->n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; memset(&spec->src_imm, 0, sizeof spec->src_imm); memcpy(&spec->src_imm.u8[sizeof spec->src_imm - dst->n_bytes], &imm, dst->n_bytes); spec->dst_type = NX_LEARN_DST_MATCH; spec->dst.field = dst; spec->dst.ofs = 0; spec->dst.n_bits = dst->n_bits; } else if (strchr(name, '[')) { /* Parse destination and check prerequisites. */ char *error; error = mf_parse_subfield(&spec->dst, name); if (error) { return error; } if (!mf_nxm_header(spec->dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", orig, name); } /* Parse source and check prerequisites. */ if (value[0] != '\0') { error = mf_parse_subfield(&spec->src, value); if (error) { return error; } if (spec->src.n_bits != spec->dst.n_bits) { return xasprintf("%s: bit widths of %s (%u) and %s (%u) " "differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_MATCH; } else if (!strcmp(name, "load")) { if (value[strcspn(value, "[-")] == '-') { char *error = learn_parse_load_immediate(value, spec); if (error) { return error; } } else { struct ofpact_reg_move move; char *error; error = nxm_parse_reg_move(&move, value); if (error) { return error; } spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { char *error = mf_parse_subfield(&spec->src, value); if (error) { return error; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { return xasprintf("%s: unknown keyword %s", orig, name); } return NULL; }
static void learn_parse_spec(const char *orig, char *name, char *value, struct ofpact_learn_spec *spec) { if (mf_from_name(name)) { const struct mf_field *dst = mf_from_name(name); union mf_value imm; char *error; error = mf_parse_value(dst, value, &imm); if (error) { ovs_fatal(0, "%s", error); } spec->n_bits = dst->n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; memset(&spec->src_imm, 0, sizeof spec->src_imm); memcpy(&spec->src_imm.u8[sizeof spec->src_imm - dst->n_bytes], &imm, dst->n_bytes); spec->dst_type = NX_LEARN_DST_MATCH; spec->dst.field = dst; spec->dst.ofs = 0; spec->dst.n_bits = dst->n_bits; } else if (strchr(name, '[')) { /* Parse destination and check prerequisites. */ if (mf_parse_subfield(&spec->dst, name)[0] != '\0') { ovs_fatal(0, "%s: syntax error after NXM field name `%s'", orig, name); } /* Parse source and check prerequisites. */ if (value[0] != '\0') { if (mf_parse_subfield(&spec->src, value)[0] != '\0') { ovs_fatal(0, "%s: syntax error after NXM field name `%s'", orig, value); } if (spec->src.n_bits != spec->dst.n_bits) { ovs_fatal(0, "%s: bit widths of %s (%u) and %s (%u) differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_MATCH; } else if (!strcmp(name, "load")) { if (value[strcspn(value, "[-")] == '-') { learn_parse_load_immediate(value, spec); } else { struct ofpact_reg_move move; nxm_parse_reg_move(&move, value); spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { if (mf_parse_subfield(&spec->src, value)[0] != '\0') { ovs_fatal(0, "%s: syntax error after NXM field name `%s'", orig, name); } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { ovs_fatal(0, "%s: unknown keyword %s", orig, name); } }
static void process_packet_in(struct lswitch *sw, const struct ofp_header *oh) { struct ofputil_packet_in pi; uint32_t buffer_id; uint64_t ofpacts_stub[64]; struct ofpbuf ofpacts; struct ofputil_packet_out po; enum ofperr error; uint8_t icmp_packet[128]; const struct mf_field *mf; const struct mf_field *mf_id; union mf_value sf_value, sf_mask; union mf_value sf_value_id, sf_mask_id; error = ofputil_decode_packet_in(oh, true, &pi, NULL, &buffer_id, NULL); if (error) { VLOG_WARN_RL(&rl, "failed to decode packet-in: %s", ofperr_to_string(error)); return; } /* Ignore packets sent via output to OFPP_CONTROLLER. This library never * uses such an action. You never know what experiments might be going on, * though, and it seems best not to interfere with them. */ if (pi.reason != OFPR_ACTION || buffer_id != UINT32_MAX) { return; } struct ethhdr *old_pkt = pi.packet; struct iphdr * old_ipv4 = (struct iphdr*)(old_pkt + 1); uint16_t old_tot_len = ntohs(old_ipv4->tot_len); uint8_t header_len = old_ipv4->ihl*4; if (pi.packet_len < header_len + sizeof(struct ethhdr) + 8 || old_tot_len < header_len + 8) return; if (old_pkt->h_proto != htons(ETH_P_IP)) return; //VLOG_INFO("AFTER MATCH"); uint32_t icmp_packet_len = sizeof(struct ethhdr) + sizeof(struct iphdr) + header_len + sizeof(struct icmphdr) + 8; icmp_unexpect_ttl(icmp_packet, &pi, header_len, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0); ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); mf_id = mf_from_name("tun_id"); if (!mf_id) return; sf_value_id.be64 = pi.flow_metadata.flow.tunnel.tun_id; sf_mask_id.be64 = OVS_BE64_MAX; ofpact_put_set_field(&ofpacts, mf_id, &sf_value_id, &sf_mask_id); mf = mf_from_name("tun_dst"); if (!mf) return; sf_value.be32 = pi.flow_metadata.flow.tunnel.ip_src; sf_mask.be32 = OVS_BE32_MAX; ofpact_put_set_field(&ofpacts, mf, &sf_value, &sf_mask); ofpact_put_OUTPUT(&ofpacts)->port = OFPP_IN_PORT; /* Prepare packet_out in case we need one. */ po.buffer_id = buffer_id; po.packet = icmp_packet; po.packet_len = icmp_packet_len; po.in_port = pi.flow_metadata.flow.in_port.ofp_port; po.ofpacts = ofpacts.data; po.ofpacts_len = ofpacts.size; queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol)); //VLOG_INFO("AFTER MATCH last"); }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_spec(const char *orig, char *name, char *value, struct ofpact_learn_spec *spec, struct ofpbuf *ofpacts, struct match *match) { if (mf_from_name(name)) { const struct mf_field *dst = mf_from_name(name); union mf_value imm; char *error; error = mf_parse_value(dst, value, &imm); if (error) { return error; } spec->n_bits = dst->n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; spec->dst_type = NX_LEARN_DST_MATCH; spec->dst.field = dst; spec->dst.ofs = 0; spec->dst.n_bits = dst->n_bits; /* Update 'match' to allow for satisfying destination * prerequisites. */ mf_set_value(dst, &imm, match, NULL); /* Push value last, as this may reallocate 'spec'! */ uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(dst->n_bytes)); memcpy(src_imm, &imm, dst->n_bytes); } else if (strchr(name, '[')) { /* Parse destination and check prerequisites. */ char *error; error = mf_parse_subfield(&spec->dst, name); if (error) { return error; } if (!mf_nxm_header(spec->dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", orig, name); } /* Parse source and check prerequisites. */ if (value[0] != '\0') { error = mf_parse_subfield(&spec->src, value); if (error) { return error; } if (spec->src.n_bits != spec->dst.n_bits) { return xasprintf("%s: bit widths of %s (%u) and %s (%u) " "differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_MATCH; } else if (!strcmp(name, "load")) { if (value[strcspn(value, "[-")] == '-') { char *error = learn_parse_load_immediate(value, spec, ofpacts); if (error) { return error; } } else { struct ofpact_reg_move move; char *error; error = nxm_parse_reg_move(&move, value); if (error) { return error; } spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { char *error = mf_parse_subfield(&spec->src, value); if (error) { return error; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { return xasprintf("%s: unknown keyword %s", orig, name); } return NULL; }
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man * page) into 'fm' for sending the specified flow_mod 'command' to a switch. * If 'actions' is specified, an action must be in 'string' and may be expanded * or reallocated. * * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* * constant for 'command'. To parse syntax for an OFPST_FLOW or * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ void parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, bool verbose) { enum { F_OUT_PORT = 1 << 0, F_ACTIONS = 1 << 1, F_TIMEOUT = 1 << 3, F_PRIORITY = 1 << 4 } fields; char *string = xstrdup(str_); char *save_ptr = NULL; char *act_str = NULL; char *name; switch (command) { case -1: fields = F_OUT_PORT; break; case OFPFC_ADD: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY; break; case OFPFC_DELETE: fields = F_OUT_PORT; break; case OFPFC_DELETE_STRICT: fields = F_OUT_PORT | F_PRIORITY; break; case OFPFC_MODIFY: fields = F_ACTIONS; break; case OFPFC_MODIFY_STRICT: fields = F_ACTIONS | F_PRIORITY; break; default: NOT_REACHED(); } cls_rule_init_catchall(&fm->cr, 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->flags = 0; if (fields & F_ACTIONS) { act_str = strstr(string, "action"); if (!act_str) { ofp_fatal(str_, verbose, "must specify an action"); } *act_str = '\0'; act_str = strchr(act_str + 1, '='); if (!act_str) { ofp_fatal(str_, verbose, "must specify an action"); } act_str++; } 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 (parse_protocol(name, &p)) { cls_rule_set_dl_type(&fm->cr, htons(p->dl_type)); if (p->nw_proto) { cls_rule_set_nw_proto(&fm->cr, p->nw_proto); } } 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, "table")) { fm->table_id = str_to_table_id(value); } else if (!strcmp(name, "out_port")) { fm->out_port = atoi(value); } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { fm->cr.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 { /* 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)); } } else if (mf_from_name(name)) { parse_field(mf_from_name(name), value, &fm->cr); } else if (!strcmp(name, "duration") || !strcmp(name, "n_packets") || !strcmp(name, "n_bytes")) { /* Ignore these, so that users can feed the output of * "ovs-ofctl dump-flows" back into commands that parse * flows. */ } else { ofp_fatal(str_, verbose, "unknown keyword %s", name); } } } 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; 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); } else { fm->actions = NULL; fm->n_actions = 0; } free(string); }