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 parse_resubmit(char *arg, struct ofpbuf *ofpacts) { struct ofpact_resubmit *resubmit; char *in_port_s, *table_s; 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, &resubmit->in_port)) { ovs_fatal(0, "%s: resubmit to unknown port", in_port_s); } } else { resubmit->in_port = OFPP_IN_PORT; } table_s = strsep(&arg, ","); resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 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"); } }
static void parse_resubmit(struct ofpbuf *b, char *arg) { struct nx_action_resubmit *nar; char *in_port_s, *table_s; uint16_t in_port; uint8_t table; 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); } } else { in_port = OFPP_IN_PORT; } table_s = strsep(&arg, ","); table = table_s && table_s[0] ? str_to_u32(table_s) : 255; if (in_port == OFPP_IN_PORT && table == 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 str_to_action(const struct flow *flow, char *str, struct ofpbuf *b) { char *pos, *act, *arg; 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"); } break; } else if (!strcasecmp(act, "CONTROLLER")) { struct ofp_action_output *oao; oao = put_output_action(b, OFPP_CONTROLLER); /* Unless a numeric argument is specified, we send the whole * packet to the controller. */ if (arg[0] && (strspn(arg, "0123456789") == strlen(arg))) { oao->max_len = htons(str_to_u32(arg)); } else { oao->max_len = htons(UINT16_MAX); } } else if (ofputil_port_from_string(act, &port)) { put_output_action(b, port); } else { ovs_fatal(0, "Unknown action: %s", act); } n_actions++; } }
/* 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); }
/* Helper for bundle_parse and bundle_parse_load. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string.*/ static char * WARN_UNUSED_RESULT bundle_parse__(const char *s, char **save_ptr, const char *fields, const char *basis, const char *algorithm, const char *slave_type, const char *dst, const char *slave_delim, struct ofpbuf *ofpacts) { struct ofpact_bundle *bundle; if (!slave_delim) { return xasprintf("%s: not enough arguments to bundle action", s); } if (strcasecmp(slave_delim, "slaves")) { return xasprintf("%s: missing slave delimiter, expected `slaves' " "got `%s'", s, slave_delim); } bundle = ofpact_put_BUNDLE(ofpacts); for (;;) { ofp_port_t slave_port; char *slave; slave = strtok_r(NULL, ", []", save_ptr); if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) { break; } if (!ofputil_port_from_string(slave, &slave_port)) { return xasprintf("%s: bad port number", slave); } ofpbuf_put(ofpacts, &slave_port, sizeof slave_port); bundle = ofpacts->l2; bundle->n_slaves++; } ofpact_update_len(ofpacts, &bundle->ofpact); bundle->basis = atoi(basis); if (!strcasecmp(fields, "eth_src")) { bundle->fields = NX_HASH_FIELDS_ETH_SRC; } else if (!strcasecmp(fields, "symmetric_l4")) { bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4; } else { return xasprintf("%s: unknown fields `%s'", s, fields); } if (!strcasecmp(algorithm, "active_backup")) { bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; } else if (!strcasecmp(algorithm, "hrw")) { bundle->algorithm = NX_BD_ALG_HRW; } else { return xasprintf("%s: unknown algorithm `%s'", s, algorithm); } if (strcasecmp(slave_type, "ofport")) { return xasprintf("%s: unknown slave_type `%s'", s, slave_type); } if (dst) { char *error = mf_parse_subfield(&bundle->dst, dst); if (error) { return error; } } return NULL; }