/* 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); }
/* 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 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); }
void learn_execute(const struct nx_action_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm) { const void *p, *end; struct ofpbuf actions; cls_rule_init_catchall(&fm->cr, ntohs(learn->priority)); fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = learn->cookie; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = ntohs(learn->idle_timeout); fm->hard_timeout = ntohs(learn->hard_timeout); fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM; fm->actions = NULL; fm->n_actions = 0; ofpbuf_init(&actions, 64); if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct nx_action_fin_timeout *naft; naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions); naft->fin_idle_timeout = learn->fin_idle_timeout; naft->fin_hard_timeout = learn->fin_hard_timeout; } for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) { uint16_t header = ntohs(get_be16(&p)); int n_bits = header & NX_LEARN_N_BITS_MASK; int src_type = header & NX_LEARN_SRC_MASK; int dst_type = header & NX_LEARN_DST_MASK; union mf_subvalue value; struct mf_subfield dst; int chunk, ofs; if (!header) { break; } if (src_type == NX_LEARN_SRC_FIELD) { struct mf_subfield src; get_subfield(n_bits, &p, &src); mf_read_subfield(&src, flow, &value); } else { int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16); memset(&value, 0, sizeof value); bitwise_copy(p, p_bytes, 0, &value, sizeof value, 0, n_bits); p = (const uint8_t *) p + p_bytes; } switch (dst_type) { case NX_LEARN_DST_MATCH: get_subfield(n_bits, &p, &dst); mf_write_subfield(&dst, &value, &fm->cr); break; case NX_LEARN_DST_LOAD: get_subfield(n_bits, &p, &dst); for (ofs = 0; ofs < n_bits; ofs += chunk) { struct nx_action_reg_load *load; chunk = MIN(n_bits - ofs, 64); load = ofputil_put_NXAST_REG_LOAD(&actions); load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk); load->dst = htonl(dst.field->nxm_header); bitwise_copy(&value, sizeof value, ofs, &load->value, sizeof load->value, 0, chunk); } break; case NX_LEARN_DST_OUTPUT: if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7]; } break; } } fm->actions = ofpbuf_steal_data(&actions); fm->n_actions = actions.size / sizeof(struct ofp_action_header); }
void learn_execute(const struct nx_action_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm) { const void *p, *end; struct ofpbuf actions; cls_rule_init_catchall(&fm->cr, ntohs(learn->priority)); fm->cookie = learn->cookie; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = ntohs(learn->idle_timeout); fm->hard_timeout = ntohs(learn->hard_timeout); fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM; fm->actions = NULL; fm->n_actions = 0; ofpbuf_init(&actions, 64); for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) { uint16_t header = ntohs(get_be16(&p)); int n_bits = header & NX_LEARN_N_BITS_MASK; int src_type = header & NX_LEARN_SRC_MASK; int dst_type = header & NX_LEARN_DST_MASK; uint64_t value; struct nx_action_reg_load *load; ovs_be32 dst_field; int dst_ofs; if (!header) { break; } if (src_type == NX_LEARN_SRC_FIELD) { ovs_be32 src_field = get_be32(&p); int src_ofs = ntohs(get_be16(&p)); value = nxm_read_field_bits(src_field, nxm_encode_ofs_nbits(src_ofs, n_bits), flow); } else { value = get_bits(n_bits, &p); } switch (dst_type) { case NX_LEARN_DST_MATCH: dst_field = get_be32(&p); dst_ofs = ntohs(get_be16(&p)); mf_set_subfield(mf_from_nxm_header(ntohl(dst_field)), value, dst_ofs, n_bits, &fm->cr); break; case NX_LEARN_DST_LOAD: dst_field = get_be32(&p); dst_ofs = ntohs(get_be16(&p)); load = ofputil_put_NXAST_REG_LOAD(&actions); load->ofs_nbits = nxm_encode_ofs_nbits(dst_ofs, n_bits); load->dst = dst_field; load->value = htonll(value); break; case NX_LEARN_DST_OUTPUT: ofputil_put_OFPAT_OUTPUT(&actions)->port = htons(value); break; } } fm->actions = ofpbuf_steal_data(&actions); fm->n_actions = actions.size / sizeof(struct ofp_action_header); }