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 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; }
/* 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 learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = OFP_FLOW_PERMANENT; learn->hard_timeout = OFP_FLOW_PERMANENT; learn->priority = OFP_DEFAULT_PRIORITY; learn->table_id = 1; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = strtoull(value, NULL, 0); } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->frame; learn->n_specs++; error = learn_parse_spec(orig, name, value, spec); if (error) { return error; } /* Update 'match' to allow for satisfying destination * prerequisites. */ if (spec->src_type == NX_LEARN_SRC_IMMEDIATE && spec->dst_type == NX_LEARN_DST_MATCH) { mf_write_subfield(&spec->dst, &spec->src_imm, &match); } } } ofpact_update_len(ofpacts, &learn->ofpact); return NULL; }
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++; } }
/* 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__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = OFP_FLOW_PERMANENT; learn->hard_timeout = OFP_FLOW_PERMANENT; learn->priority = OFP_DEFAULT_PRIORITY; learn->table_id = 1; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else if (!strcmp(name, "send_flow_rem")) { learn->flags |= NX_LEARN_F_SEND_FLOW_REM; } else if (!strcmp(name, "delete_learned")) { learn->flags |= NX_LEARN_F_DELETE_LEARNED; } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); error = learn_parse_spec(orig, name, value, spec, ofpacts, &match); if (error) { return error; } learn = ofpacts->header; } } ofpact_finish_LEARN(ofpacts, &learn); return NULL; }
static void parse_fin_timeout(struct ofpbuf *b, char *arg) { struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); char *key, *value; while (ofputil_parse_key_value(&arg, &key, &value)) { if (!strcmp(key, "idle_timeout")) { oft->fin_idle_timeout = str_to_u16(value, key); } else if (!strcmp(key, "hard_timeout")) { oft->fin_hard_timeout = str_to_u16(value, key); } else { ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); } } }
static void parse_controller(struct ofpbuf *b, char *arg) { enum ofp_packet_in_reason reason = OFPR_ACTION; uint16_t controller_id = 0; uint16_t max_len = UINT16_MAX; if (!arg[0]) { /* Use defaults. */ } else if (strspn(arg, "0123456789") == strlen(arg)) { max_len = str_to_u16(arg, "max_len"); } else { char *name, *value; while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "reason")) { if (!ofputil_packet_in_reason_from_string(value, &reason)) { ovs_fatal(0, "unknown reason \"%s\"", value); } } else if (!strcmp(name, "max_len")) { max_len = str_to_u16(value, "max_len"); } else if (!strcmp(name, "id")) { controller_id = str_to_u16(value, "id"); } else { ovs_fatal(0, "unknown key \"%s\" parsing controller action", name); } } } if (reason == OFPR_ACTION && controller_id == 0) { 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 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 = ovs_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 { 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); }
static void 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)) { if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) { break; } n_actions++; } error = ofpacts_verify(ofpacts->data, ofpacts->size); if (error) { ovs_fatal(0, "Incorrect action ordering"); } ofpact_pad(ofpacts); }
/* 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; }
/* Parses 'arg' as a set of arguments to the "learn" action and appends a * matching NXAST_LEARN action to 'b'. The format parsed is described in * ovs-ofctl(8). * * Prints an error on stderr and aborts the program if 'arg' syntax is invalid. * * If 'flow' is nonnull, then it should be the flow from a cls_rule that is * the matching rule for the learning action. This helps to better validate * the action's arguments. * * Modifies 'arg'. */ void learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow) { char *orig = xstrdup(arg); char *name, *value; enum ofperr error; size_t learn_ofs; size_t len; struct nx_action_learn *learn; struct cls_rule rule; learn_ofs = b->size; learn = ofputil_put_NXAST_LEARN(b); learn->idle_timeout = htons(OFP_FLOW_PERMANENT); learn->hard_timeout = htons(OFP_FLOW_PERMANENT); learn->priority = htons(OFP_DEFAULT_PRIORITY); learn->cookie = htonll(0); learn->flags = htons(0); learn->table_id = 1; cls_rule_init_catchall(&rule, 0); while (ofputil_parse_key_value(&arg, &name, &value)) { learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { ovs_fatal(0, "%s: table id 255 not valid for `learn' action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = htons(atoi(value)); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = htons(atoi(value)); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = htons(atoi(value)); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = htons(atoi(value)); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = htons(atoi(value)); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else { struct learn_spec spec; learn_parse_spec(orig, name, value, &spec); /* Check prerequisites. */ if (spec.src_type == NX_LEARN_SRC_FIELD && flow && !mf_are_prereqs_ok(spec.src.field, flow)) { ovs_fatal(0, "%s: cannot specify source field %s because " "prerequisites are not satisfied", orig, spec.src.field->name); } if ((spec.dst_type == NX_LEARN_DST_MATCH || spec.dst_type == NX_LEARN_DST_LOAD) && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) { ovs_fatal(0, "%s: cannot specify destination field %s because " "prerequisites are not satisfied", orig, spec.dst.field->name); } /* Update 'rule' to allow for satisfying destination * prerequisites. */ if (spec.src_type == NX_LEARN_SRC_IMMEDIATE && spec.dst_type == NX_LEARN_DST_MATCH) { mf_write_subfield(&spec.dst, &spec.src_imm, &rule); } /* Output the flow_mod_spec. */ put_u16(b, spec.n_bits | spec.src_type | spec.dst_type); if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) { int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2; int ofs = sizeof spec.src_imm - n_bytes; ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes); } else { put_u32(b, spec.src.field->nxm_header); put_u16(b, spec.src.ofs); } if (spec.dst_type == NX_LEARN_DST_MATCH || spec.dst_type == NX_LEARN_DST_LOAD) { put_u32(b, spec.dst.field->nxm_header); put_u16(b, spec.dst.ofs); } else { assert(spec.dst_type == NX_LEARN_DST_OUTPUT); } } } put_u16(b, 0); len = b->size - learn_ofs; if (len % 8) { ofpbuf_put_zeros(b, 8 - len % 8); } learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); learn->len = htons(b->size - learn_ofs); /* In theory the above should have caught any errors, but... */ if (flow) { error = learn_check(learn, flow); if (error) { ovs_fatal(0, "%s: %s", orig, ofperr_to_string(error)); } } free(orig); }