#include <config.h>
-#include "ofp-parse.h"
-
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <netinet/in.h>
#include "byte-order.h"
-#include "dynamic-string.h"
+#include "dp-packet.h"
#include "learn.h"
-#include "meta-flow.h"
#include "multipath.h"
#include "netdev.h"
#include "nx-match.h"
-#include "ofp-actions.h"
-#include "ofp-util.h"
-#include "ofpbuf.h"
#include "openflow/openflow.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-parse.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vconn.h"
#include "ovs-thread.h"
#include "packets.h"
#include "simap.h"
#include "socket-util.h"
-#include "openvswitch/vconn.h"
+#include "util.h"
/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
*
.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) {
return error;
}
+/* Parse a string representation of a OFPT_PACKET_OUT to '*po'. If successful,
+ * both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */
+static char * OVS_WARN_UNUSED_RESULT
+parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
+ enum ofputil_protocol *usable_protocols)
+{
+ enum ofputil_protocol action_usable_protocols;
+ uint64_t stub[256 / 8];
+ struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
+ struct dp_packet *packet = NULL;
+ char *act_str = NULL;
+ char *name, *value;
+ char *error = NULL;
+
+ *usable_protocols = OFPUTIL_P_ANY;
+
+ *po = (struct ofputil_packet_out) {
+ .buffer_id = UINT32_MAX,
+ .in_port = OFPP_CONTROLLER,
+ };
+
+ act_str = extract_actions(string);
+
+ while (ofputil_parse_key_value(&string, &name, &value)) {
+ if (!*value) {
+ error = xasprintf("field %s missing value", name);
+ goto out;
+ }
+
+ if (!strcmp(name, "in_port")) {
+ if (!ofputil_port_from_string(value, &po->in_port)) {
+ error = xasprintf("%s is not a valid OpenFlow port", value);
+ goto out;
+ }
+ if (ofp_to_u16(po->in_port) > ofp_to_u16(OFPP_MAX)
+ && po->in_port != OFPP_LOCAL
+ && po->in_port != OFPP_NONE
+ && po->in_port != OFPP_CONTROLLER) {
+ error = xasprintf(
+ "%s is not a valid OpenFlow port for PACKET_OUT",
+ value);
+ goto out;
+ }
+ } else if (!strcmp(name, "packet")) {
+ const char *error_msg = eth_from_hex(value, &packet);
+ if (error_msg) {
+ error = xasprintf("%s: %s", name, error_msg);
+ goto out;
+ }
+ } else {
+ error = xasprintf("unknown keyword %s", name);
+ goto out;
+ }
+ }
+
+ if (!packet || !dp_packet_size(packet)) {
+ error = xstrdup("must specify packet");
+ goto out;
+ }
+
+ if (act_str) {
+ error = ofpacts_parse_actions(act_str, &ofpacts,
+ &action_usable_protocols);
+ *usable_protocols &= action_usable_protocols;
+ if (error) {
+ goto out;
+ }
+ }
+ po->ofpacts_len = ofpacts.size;
+ po->ofpacts = ofpbuf_steal_data(&ofpacts);
+
+ po->packet_len = dp_packet_size(packet);
+ po->packet = dp_packet_steal_data(packet);
+out:
+ ofpbuf_uninit(&ofpacts);
+ dp_packet_delete(packet);
+ return error;
+}
+
+/* Convert 'str_' (as described in the Packet-Out Syntax section of the
+ * ovs-ofctl man page) into 'po' for sending a OFPT_PACKET_OUT message to a
+ * switch. Returns the set of usable protocols in '*usable_protocols'.
+ *
+ * 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_packet_out_str(struct ofputil_packet_out *po, const char *str_,
+ enum ofputil_protocol *usable_protocols)
+{
+ char *string = xstrdup(str_);
+ char *error;
+
+ error = parse_ofp_packet_out_str__(po, string, usable_protocols);
+ if (error) {
+ po->ofpacts = NULL;
+ po->ofpacts_len = 0;
+ }
+
+ free(string);
+ return error;
+}
+
static char * OVS_WARN_UNUSED_RESULT
parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
struct ofpbuf *bands, int command,
{
char *save_ptr = NULL;
char *vac_up, *vac_down;
- char *value = strdup(setting);
+ 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) {
- return xasprintf("Vacancy down value missing");
+ ret_msg = xasprintf("Vacancy down value missing");
+ goto exit;
}
if (!str_to_int(vac_down, 0, &vacancy_down) ||
vacancy_down < 0 || vacancy_down > 100) {
- return xasprintf("Invalid vacancy down value \"%s\"", vac_down);
+ ret_msg = xasprintf("Invalid vacancy down value \"%s\"", vac_down);
+ goto exit;
}
vac_up = strtok_r(NULL, ",", &save_ptr);
if (!vac_up) {
- return xasprintf("Vacancy up value missing");
+ ret_msg = xasprintf("Vacancy up value missing");
+ goto exit;
}
if (!str_to_int(vac_up, 0, &vacancy_up) ||
vacancy_up < 0 || vacancy_up > 100) {
- return xasprintf("Invalid vacancy up value \"%s\"", vac_up);
+ ret_msg = xasprintf("Invalid vacancy up value \"%s\"", vac_up);
+ goto exit;
}
if (vacancy_down > vacancy_up) {
- return xasprintf("Invalid vacancy range, vacancy up should be greater"
- " than vacancy down ""(%s)",
- ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE));
+ 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
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++) {
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;
/* 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. If 'mask' is specified, fills the mask field for each of the
+ * prerequisites. If 'wc' is specified, masks the field in 'wc' for each of the
* field specified in flow. If the map, 'names_portno' is specfied, converts
* the in_port name into port no while setting the 'flow'.
*
* Returns NULL on success, otherwise a malloc()'d string that explains the
* problem. */
char *
-parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s,
+parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
+ const struct tun_table *tun_table, const char *s,
const struct simap *portno_names)
{
char *pos, *key, *value_s;
char *copy;
memset(flow, 0, sizeof *flow);
- if (mask) {
- memset(mask, 0, sizeof *mask);
+ if (wc) {
+ memset(wc, 0, sizeof *wc);
}
+ flow->tunnel.metadata.tab = tun_table;
pos = copy = xstrdup(s);
while (ofputil_parse_key_value(&pos, &key, &value_s)) {
goto exit;
}
flow->dl_type = htons(p->dl_type);
- if (mask) {
- mask->dl_type = OVS_BE16_MAX;
+ if (wc) {
+ wc->masks.dl_type = OVS_BE16_MAX;
}
if (p->nw_proto) {
goto exit;
}
flow->nw_proto = p->nw_proto;
- if (mask) {
- mask->nw_proto = UINT8_MAX;
+ if (wc) {
+ wc->masks.nw_proto = UINT8_MAX;
}
}
} else {
goto exit;
}
- if (!mf_are_prereqs_ok(mf, flow)) {
+ if (!mf_are_prereqs_ok(mf, flow, NULL)) {
error = xasprintf("%s: prerequisites not met for setting %s",
s, key);
goto exit;
&& simap_contains(portno_names, value_s)) {
flow->in_port.ofp_port = u16_to_ofp(
simap_get(portno_names, value_s));
- if (mask) {
- mask->in_port.ofp_port = u16_to_ofp(ntohs(OVS_BE16_MAX));
+ if (wc) {
+ wc->masks.in_port.ofp_port
+ = u16_to_ofp(ntohs(OVS_BE16_MAX));
}
} else {
field_error = mf_parse_value(mf, value_s, &value);
}
mf_set_flow_value(mf, &value, flow);
- if (mask) {
- mf_mask_field(mf, mask);
+ if (wc) {
+ mf_mask_field(mf, wc);
}
}
}
if (error) {
memset(flow, 0, sizeof *flow);
- if (mask) {
- memset(mask, 0, sizeof *mask);
+ if (wc) {
+ memset(wc, 0, sizeof *wc);
}
}
return error;
}
static char * OVS_WARN_UNUSED_RESULT
-parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
+parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
char *string,
enum ofputil_protocol *usable_protocols)
{
*usable_protocols = OFPUTIL_P_OF11_UP;
+ if (command == -2) {
+ size_t len;
+
+ string += strspn(string, " \t\r\n"); /* Skip white space. */
+ len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
+
+ if (!strncmp(string, "add", len)) {
+ command = OFPGC11_ADD;
+ } else if (!strncmp(string, "delete", len)) {
+ command = OFPGC11_DELETE;
+ } else if (!strncmp(string, "modify", len)) {
+ command = OFPGC11_MODIFY;
+ } else if (!strncmp(string, "add_or_mod", len)) {
+ command = OFPGC11_ADD_OR_MOD;
+ } else if (!strncmp(string, "insert_bucket", len)) {
+ command = OFPGC15_INSERT_BUCKET;
+ } else if (!strncmp(string, "remove_bucket", len)) {
+ command = OFPGC15_REMOVE_BUCKET;
+ } else {
+ len = 0;
+ command = OFPGC11_ADD;
+ }
+ string += len;
+ }
+
switch (command) {
case OFPGC11_ADD:
fields = F_GROUP_TYPE | F_BUCKETS;
fields = F_GROUP_TYPE | F_BUCKETS;
break;
+ case OFPGC11_ADD_OR_MOD:
+ fields = F_GROUP_TYPE | F_BUCKETS;
+ break;
+
case OFPGC15_INSERT_BUCKET:
fields = F_BUCKETS | F_COMMAND_BUCKET_ID;
*usable_protocols &= OFPUTIL_P_OF15_UP;
gm->command = command;
gm->group_id = OFPG_ANY;
gm->command_bucket_id = OFPG15_BUCKET_ALL;
- list_init(&gm->buckets);
+ ovs_list_init(&gm->buckets);
if (command == OFPGC11_DELETE && string[0] == '\0') {
gm->group_id = OFPG_ALL;
return NULL;
goto out;
}
+ /* Exclude fields for non "hash" selection method. */
+ if (strcmp(gm->props.selection_method, "hash") &&
+ gm->props.fields.values_size) {
+ error = xstrdup("fields may only be specified with \"selection_method=hash\"");
+ goto out;
+ }
+ /* Exclude selection_method_param if no selection_method is given. */
+ if (gm->props.selection_method[0] == 0
+ && gm->props.selection_method_param != 0) {
+ error = xstrdup("selection_method_param is only allowed with \"selection_method\"");
+ goto out;
+ }
if (fields & F_COMMAND_BUCKET_ID) {
if (!(fields & F_COMMAND_BUCKET_ID_ALL || had_command_bucket_id)) {
error = xstrdup("must specify a command bucket id");
free(bucket);
goto out;
}
- list_push_back(&gm->buckets, &bucket->list_node);
+ ovs_list_push_back(&gm->buckets, &bucket->list_node);
if (gm->type != OFPGT11_SELECT && bucket->weight) {
error = xstrdup("Only select groups can have bucket weights.");
bkt_str = next_bkt_str;
}
- if (gm->type == OFPGT11_INDIRECT && !list_is_short(&gm->buckets)) {
+ if (gm->type == OFPGT11_INDIRECT && !ovs_list_is_short(&gm->buckets)) {
error = xstrdup("Indirect groups can have at most one bucket.");
goto out;
}
return NULL;
out:
- ofputil_bucket_list_destroy(&gm->buckets);
+ ofputil_uninit_group_mod(gm);
return error;
}
+/* If 'command' is given as -2, each line may start with a command name ("add",
+ * "modify", "add_or_mod", "delete", "insert_bucket", or "remove_bucket"). A
+ * missing command name is treated as "add".
+ */
char * OVS_WARN_UNUSED_RESULT
-parse_ofp_group_mod_str(struct ofputil_group_mod *gm, uint16_t command,
+parse_ofp_group_mod_str(struct ofputil_group_mod *gm, int command,
const char *str_,
enum ofputil_protocol *usable_protocols)
{
char *error = parse_ofp_group_mod_str__(gm, command, string,
usable_protocols);
free(string);
-
- if (error) {
- ofputil_bucket_list_destroy(&gm->buckets);
- }
return error;
}
+/* If 'command' is given as -2, each line may start with a command name ("add",
+ * "modify", "add_or_mod", "delete", "insert_bucket", or "remove_bucket"). A
+ * missing command name is treated as "add".
+ */
char * OVS_WARN_UNUSED_RESULT
-parse_ofp_group_mod_file(const char *file_name, uint16_t command,
+parse_ofp_group_mod_file(const char *file_name, int command,
struct ofputil_group_mod **gms, size_t *n_gms,
enum ofputil_protocol *usable_protocols)
{
new_gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
for (i = 0; i < *n_gms; i++) {
- list_moved(&new_gms[i].buckets, &(*gms)[i].buckets);
+ ovs_list_moved(&new_gms[i].buckets, &(*gms)[i].buckets);
}
*gms = new_gms;
}
size_t i;
for (i = 0; i < *n_gms; i++) {
- ofputil_bucket_list_destroy(&(*gms)[i].buckets);
+ ofputil_uninit_group_mod(&(*gms)[i]);
}
free(*gms);
*gms = NULL;
return NULL;
}
+/* Opens file 'file_name' and reads each line as a flow_mod or a group_mod,
+ * depending on the first keyword on each line. Stores each flow and group
+ * mods in '*bms', an array allocated on the caller's behalf, and the number of
+ * messages in '*n_bms'.
+ *
+ * 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_bundle_file(const char *file_name,
+ struct ofputil_bundle_msg **bms, size_t *n_bms,
+ enum ofputil_protocol *usable_protocols)
+{
+ size_t allocated_bms;
+ char *error = NULL;
+ int line_number;
+ FILE *stream;
+ struct ds ds;
+
+ *usable_protocols = OFPUTIL_P_ANY;
+
+ *bms = NULL;
+ *n_bms = 0;
+
+ stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
+ if (stream == NULL) {
+ return xasprintf("%s: open failed (%s)",
+ file_name, ovs_strerror(errno));
+ }
+
+ allocated_bms = *n_bms;
+ ds_init(&ds);
+ line_number = 0;
+ while (!ds_get_preprocessed_line(&ds, stream, &line_number)) {
+ enum ofputil_protocol usable;
+ char *s = ds_cstr(&ds);
+ size_t len;
+
+ if (*n_bms >= allocated_bms) {
+ struct ofputil_bundle_msg *new_bms;
+
+ new_bms = x2nrealloc(*bms, &allocated_bms, sizeof **bms);
+ for (size_t i = 0; i < *n_bms; i++) {
+ if (new_bms[i].type == OFPTYPE_GROUP_MOD) {
+ ovs_list_moved(&new_bms[i].gm.buckets,
+ &(*bms)[i].gm.buckets);
+ }
+ }
+ *bms = new_bms;
+ }
+
+ s += strspn(s, " \t\r\n"); /* Skip white space. */
+ len = strcspn(s, ", \t\r\n"); /* Get length of the first token. */
+
+ if (!strncmp(s, "flow", len)) {
+ s += len;
+ error = parse_ofp_flow_mod_str(&(*bms)[*n_bms].fm, s, -2, &usable);
+ if (error) {
+ break;
+ }
+ (*bms)[*n_bms].type = OFPTYPE_FLOW_MOD;
+ } else if (!strncmp(s, "group", len)) {
+ s += len;
+ error = parse_ofp_group_mod_str(&(*bms)[*n_bms].gm, -2, s,
+ &usable);
+ if (error) {
+ break;
+ }
+ (*bms)[*n_bms].type = OFPTYPE_GROUP_MOD;
+ } else if (!strncmp(s, "packet-out", len)) {
+ s += len;
+ error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s, &usable);
+ if (error) {
+ break;
+ }
+ (*bms)[*n_bms].type = OFPTYPE_PACKET_OUT;
+ } else {
+ error = xasprintf("Unsupported bundle message type: %.*s",
+ (int)len, s);
+ break;
+ }
+
+ *usable_protocols &= usable; /* Each line can narrow the set. */
+ *n_bms += 1;
+ }
+
+ ds_destroy(&ds);
+ if (stream != stdin) {
+ fclose(stream);
+ }
+
+ if (error) {
+ char *err_msg = xasprintf("%s:%d: %s", file_name, line_number, error);
+ free(error);
+
+ ofputil_free_bundle_msgs(*bms, *n_bms);
+ *bms = NULL;
+ *n_bms = 0;
+ return err_msg;
+ }
+ return NULL;
+}
+
char * OVS_WARN_UNUSED_RESULT
parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *ttm,
uint16_t command, const char *s,
*usable_protocols = OFPUTIL_P_NXM_OXM_ANY;
ttm->command = command;
- list_init(&ttm->mappings);
+ ovs_list_init(&ttm->mappings);
while (*s) {
struct ofputil_tlv_map *map = xmalloc(sizeof *map);
s++;
}
- list_push_back(&ttm->mappings, &map->list_node);
+ ovs_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,