X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fofp-parse.c;h=5ef3892ad893aa8bfbd4c7b587eb55f76ef10241;hb=303721ee82a2e985435d9fc82f8644fcd38a6fe0;hp=eaaa8ba15675b284cf6efe1687c0de2bc0162e3e;hpb=1cb20095c3c933aca1e7607ad2dd6ed9933b359e;p=cascardo%2Fovs.git diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index eaaa8ba15..5ef3892ad 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. + * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ #include #include #include +#include #include "byte-order.h" #include "dynamic-string.h" @@ -168,6 +169,20 @@ str_to_ip(const char *str, ovs_be32 *ip) return NULL; } +/* Parses 'str' as a conntrack helper into 'alg'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +char * OVS_WARN_UNUSED_RESULT +str_to_connhelper(const char *str, uint16_t *alg) +{ + if (!strcmp(str, "ftp")) { + *alg = IPPORT_FTP; + return NULL; + } + return xasprintf("invalid conntrack helper \"%s\"", str); +} + struct protocol { const char *name; uint16_t dl_type; @@ -221,9 +236,15 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match, union mf_value value, mask; char *error; + if (!*s) { + /* If there's no string, we're just trying to match on the + * existence of the field, so use a no-op value. */ + s = "0/0"; + } + error = mf_parse(mf, s, &value, &mask); if (!error) { - *usable_protocols &= mf_set(mf, &value, &mask, match); + *usable_protocols &= mf_set(mf, &value, &mask, match, &error); } return error; } @@ -254,9 +275,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, F_PRIORITY = 1 << 4, F_FLAGS = 1 << 5, } fields; - char *save_ptr = NULL; char *act_str = NULL; - char *name; + char *name, *value; *usable_protocols = OFPUTIL_P_ANY; @@ -312,35 +332,29 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, OVS_NOT_REACHED(); } - match_init_catchall(&fm->match); - fm->priority = OFP_DEFAULT_PRIORITY; - fm->cookie = htonll(0); - fm->cookie_mask = htonll(0); + *fm = (struct ofputil_flow_mod) { + .match = MATCH_CATCHALL_INITIALIZER, + .priority = OFP_DEFAULT_PRIORITY, + .table_id = 0xff, + .command = command, + .buffer_id = UINT32_MAX, + .out_port = OFPP_ANY, + .out_group = OFPG_ANY, + .delete_reason = OFPRR_DELETE, + }; + /* For modify, by default, don't update the cookie. */ if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { - /* For modify, by default, don't update the cookie. */ fm->new_cookie = OVS_BE64_MAX; - } else{ - fm->new_cookie = htonll(0); } - fm->modify_cookie = false; - 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; - fm->importance = 0; - fm->out_group = OFPG11_ANY; - fm->delete_reason = OFPRR_DELETE; + if (fields & F_ACTIONS) { act_str = extract_actions(string); if (!act_str) { return xstrdup("must specify an action"); } } - for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; - name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { + + while (ofputil_parse_key_value(&string, &name, &value)) { const struct protocol *p; char *error = NULL; @@ -366,25 +380,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, || !strcmp(name, "allow_hidden_fields")) { /* ignore these fields. */ } else if (mf_from_name(name)) { - char *value; - - value = strtok_r(NULL, ", \t\r\n", &save_ptr); - if (!value) { - /* If there's no value, we're just trying to match on the - * existence of the field, so use a no-op value. */ - value = "0/0"; - } - error = parse_field(mf_from_name(name), value, &fm->match, usable_protocols); - if (error) { - return error; - } } else { - char *value; - - value = strtok_r(NULL, ", \t\r\n", &save_ptr); - if (!value) { + if (!*value) { return xasprintf("field %s missing value", name); } @@ -398,6 +397,12 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, error = xasprintf("%s is not a valid OpenFlow port", value); } + } else if (fields & F_OUT_PORT && !strcmp(name, "out_group")) { + *usable_protocols &= OFPUTIL_P_OF11_UP; + if (!ofputil_group_from_string(value, &fm->out_group)) { + error = xasprintf("%s is not a valid OpenFlow group", + value); + } } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { uint16_t priority = 0; @@ -450,10 +455,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, } else { error = xasprintf("unknown keyword %s", name); } + } - if (error) { - return error; - } + if (error) { + return error; } } /* Check for usable protocol interdependencies between match fields. */ @@ -776,8 +781,7 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, enum ofputil_protocol *usable_protocols) { static atomic_count id = ATOMIC_COUNT_INIT(0); - char *save_ptr = NULL; - char *name; + char *name, *value; fmr->id = atomic_count_inc(&id); @@ -787,9 +791,9 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, 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)) { + while (ofputil_parse_key_value(&string, &name, &value)) { const struct protocol *p; + char *error = NULL; if (!strcmp(name, "!initial")) { fmr->flags &= ~NXFMF_INITIAL; @@ -808,33 +812,26 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, if (p->nw_proto) { match_set_nw_proto(&fmr->match, p->nw_proto); } + } else if (mf_from_name(name)) { + error = parse_field(mf_from_name(name), value, &fmr->match, + usable_protocols); } else { - char *value; - - value = strtok_r(NULL, ", \t\r\n", &save_ptr); - if (!value) { + if (!*value) { return xasprintf("%s: field %s missing value", str_, name); } if (!strcmp(name, "table")) { - char *error = str_to_u8(value, "table", &fmr->table_id); - if (error) { - return error; - } + error = str_to_u8(value, "table", &fmr->table_id); } else if (!strcmp(name, "out_port")) { fmr->out_port = u16_to_ofp(atoi(value)); - } else if (mf_from_name(name)) { - char *error; - - error = parse_field(mf_from_name(name), value, &fmr->match, - usable_protocols); - if (error) { - return error; - } } else { return xasprintf("%s: unknown keyword %s", str_, name); } } + + if (error) { + return error; + } } return NULL; } @@ -883,6 +880,61 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, return error; } +/* Convert 'setting' (as described for the "mod-table" command + * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and + * 'tm->table_vacancy->vacancy_down' threshold values. + * For the two threshold values, value of vacancy_up is always greater + * than value of vacancy_down. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +char * OVS_WARN_UNUSED_RESULT +parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting) +{ + char *save_ptr = NULL; + char *vac_up, *vac_down; + char *value = xstrdup(setting); + char *ret_msg; + int vacancy_up, vacancy_down; + + strtok_r(value, ":", &save_ptr); + vac_down = strtok_r(NULL, ",", &save_ptr); + if (!vac_down) { + ret_msg = xasprintf("Vacancy down value missing"); + goto exit; + } + if (!str_to_int(vac_down, 0, &vacancy_down) || + vacancy_down < 0 || vacancy_down > 100) { + ret_msg = xasprintf("Invalid vacancy down value \"%s\"", vac_down); + goto exit; + } + vac_up = strtok_r(NULL, ",", &save_ptr); + if (!vac_up) { + ret_msg = xasprintf("Vacancy up value missing"); + goto exit; + } + if (!str_to_int(vac_up, 0, &vacancy_up) || + vacancy_up < 0 || vacancy_up > 100) { + ret_msg = xasprintf("Invalid vacancy up value \"%s\"", vac_up); + goto exit; + } + if (vacancy_down > vacancy_up) { + ret_msg = xasprintf("Invalid vacancy range, vacancy up should be " + "greater than vacancy down (%s)", + ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE)); + goto exit; + } + + free(value); + tm->table_vacancy.vacancy_down = vacancy_down; + tm->table_vacancy.vacancy_up = vacancy_up; + return NULL; + +exit: + free(value); + return ret_msg; +} + /* Convert 'table_id' and 'setting' (as described for the "mod-table" command * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a * switch. @@ -909,13 +961,13 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, tm->miss = OFPUTIL_TABLE_MISS_DEFAULT; tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; tm->eviction_flags = UINT32_MAX; - + tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; + tm->table_vacancy.vacancy_down = 0; + tm->table_vacancy.vacancy_up = 0; + tm->table_vacancy.vacancy = 0; /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod. - * Only OpenFlow 1.4+ can configure eviction via table_mod. - * - * (OpenFlow 1.4+ can also configure vacancy events via table_mod, but OVS - * doesn't support those yet and they're also logically a per-OpenFlow - * session setting so it wouldn't make sense to support them here anyway.) + * Only OpenFlow 1.4+ can configure eviction and vacancy events + * via table_mod. */ if (!strcmp(setting, "controller")) { tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER; @@ -932,6 +984,16 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, } else if (!strcmp(setting, "noevict")) { tm->eviction = OFPUTIL_TABLE_EVICTION_OFF; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); + } else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) { + tm->vacancy = OFPUTIL_TABLE_VACANCY_ON; + *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); + char *error = parse_ofp_table_vacancy(tm, setting); + if (error) { + return error; + } + } else if (!strcmp(setting, "novacancy")) { + tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF; + *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); } else { return xasprintf("invalid table_mod setting %s", setting); } @@ -989,6 +1051,7 @@ parse_ofp_flow_mod_file(const char *file_name, int command, error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, &usable); if (error) { + char *err_msg; size_t i; for (i = 0; i < *n_fms; i++) { @@ -1003,7 +1066,9 @@ parse_ofp_flow_mod_file(const char *file_name, int command, fclose(stream); } - return xasprintf("%s:%d: %s", file_name, line_number, error); + err_msg = xasprintf("%s:%d: %s", file_name, line_number, error); + free(error); + return err_msg; } *usable_protocols &= usable; /* Each line can narrow the set. */ *n_fms += 1; @@ -1168,7 +1233,7 @@ parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type, bucket->weight = group_type == OFPGT11_SELECT ? 1 : 0; bucket->bucket_id = OFPG15_BUCKET_ALL; bucket->watch_port = OFPP_ANY; - bucket->watch_group = OFPG11_ANY; + bucket->watch_group = OFPG_ANY; ds_init(&actions); @@ -1231,24 +1296,20 @@ static char * OVS_WARN_UNUSED_RESULT parse_select_group_field(char *s, struct field_array *fa, enum ofputil_protocol *usable_protocols) { - char *save_ptr = NULL; - char *name; + char *name, *value_str; - for (name = strtok_r(s, "=, \t\r\n", &save_ptr); name; - name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { + while (ofputil_parse_key_value(&s, &name, &value_str)) { const struct mf_field *mf = mf_from_name(name); if (mf) { char *error; - const char *value_str; union mf_value value; if (bitmap_is_set(fa->used.bm, mf->id)) { return xasprintf("%s: duplicate field", name); } - value_str = strtok_r(NULL, ", \t\r\n", &save_ptr); - if (value_str) { + if (*value_str) { error = mf_parse_value(mf, value_str, &value); if (error) { return error; @@ -1300,10 +1361,8 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command, F_COMMAND_BUCKET_ID = 1 << 2, F_COMMAND_BUCKET_ID_ALL = 1 << 3, } fields; - char *save_ptr = NULL; bool had_type = false; bool had_command_bucket_id = false; - char *name; struct ofputil_bucket *bucket; char *error = NULL; @@ -1361,16 +1420,9 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command, } /* Parse everything before the buckets. */ - for (name = strtok_r(string, "=, \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) { - error = xasprintf("field %s missing value", name); - goto out; - } - + char *pos = string; + char *name, *value; + while (ofputil_parse_key_value(&pos, &name, &value)) { if (!strcmp(name, "command_bucket_id")) { if (!(fields & F_COMMAND_BUCKET_ID)) { error = xstrdup("command bucket id is not needed"); @@ -1621,30 +1673,30 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command, } char * OVS_WARN_UNUSED_RESULT -parse_ofp_geneve_table_mod_str(struct ofputil_geneve_table_mod *gtm, +parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *ttm, uint16_t command, const char *s, enum ofputil_protocol *usable_protocols) { *usable_protocols = OFPUTIL_P_NXM_OXM_ANY; - gtm->command = command; - list_init(>m->mappings); + ttm->command = command; + list_init(&ttm->mappings); while (*s) { - struct ofputil_geneve_map *map = xmalloc(sizeof *map); + struct ofputil_tlv_map *map = xmalloc(sizeof *map); int n; if (*s == ',') { s++; } - list_push_back(>m->mappings, &map->list_node); + list_push_back(&ttm->mappings, &map->list_node); if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}->tun_metadata%"SCNi16"%n", &map->option_class, &map->option_type, &map->option_len, &map->index, &n)) { - ofputil_uninit_geneve_table(>m->mappings); - return xstrdup("invalid geneve mapping"); + ofputil_uninit_tlv_table(&ttm->mappings); + return xstrdup("invalid tlv mapping"); } s += n;