/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
VLOG_DEFINE_THIS_MODULE(ofp_parse);
+static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
+ NO_RETURN;
+
static uint8_t
str_to_table_id(const char *str)
{
{
struct ofp_action_output *oao;
- oao = ofputil_put_OFPAT_OUTPUT(b);
+ oao = ofputil_put_OFPAT10_OUTPUT(b);
oao->port = htons(port);
return oao;
}
ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
}
- oae = ofputil_put_OFPAT_ENQUEUE(b);
+ oae = ofputil_put_OFPAT10_ENQUEUE(b);
oae->port = htons(str_to_u32(port));
oae->queue_id = htonl(str_to_u32(queue));
}
nan->len = htons(b->size - start_ofs);
}
+static void
+parse_fin_timeout(struct ofpbuf *b, char *arg)
+{
+ struct nx_action_fin_timeout *naft;
+ char *key, *value;
+
+ naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
+ while (ofputil_parse_key_value(&arg, &key, &value)) {
+ if (!strcmp(key, "idle_timeout")) {
+ naft->fin_idle_timeout = htons(str_to_u16(value, key));
+ } else if (!strcmp(key, "hard_timeout")) {
+ naft->fin_hard_timeout = htons(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) {
+ put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len);
+ } else {
+ struct nx_action_controller *nac;
+
+ nac = ofputil_put_NXAST_CONTROLLER(b);
+ nac->max_len = htons(max_len);
+ nac->reason = reason;
+ nac->controller_id = htons(controller_id);
+ }
+}
+
static void
parse_named_action(enum ofputil_action_code code, const struct flow *flow,
struct ofpbuf *b, char *arg)
struct ofp_action_tp_port *oata;
switch (code) {
- case OFPUTIL_OFPAT_OUTPUT:
+ case OFPUTIL_OFPAT10_OUTPUT:
parse_output(b, arg);
break;
- case OFPUTIL_OFPAT_SET_VLAN_VID:
- oavv = ofputil_put_OFPAT_SET_VLAN_VID(b);
+ case OFPUTIL_OFPAT10_SET_VLAN_VID:
+ oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b);
oavv->vlan_vid = htons(str_to_u32(arg));
break;
- case OFPUTIL_OFPAT_SET_VLAN_PCP:
- oavp = ofputil_put_OFPAT_SET_VLAN_PCP(b);
+ case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+ oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b);
oavp->vlan_pcp = str_to_u32(arg);
break;
- case OFPUTIL_OFPAT_STRIP_VLAN:
- ofputil_put_OFPAT_STRIP_VLAN(b);
+ case OFPUTIL_OFPAT10_STRIP_VLAN:
+ ofputil_put_OFPAT10_STRIP_VLAN(b);
break;
- case OFPUTIL_OFPAT_SET_DL_SRC:
- case OFPUTIL_OFPAT_SET_DL_DST:
+ case OFPUTIL_OFPAT10_SET_DL_SRC:
+ case OFPUTIL_OFPAT10_SET_DL_DST:
oada = ofputil_put_action(code, b);
str_to_mac(arg, oada->dl_addr);
break;
- case OFPUTIL_OFPAT_SET_NW_SRC:
- case OFPUTIL_OFPAT_SET_NW_DST:
+ case OFPUTIL_OFPAT10_SET_NW_SRC:
+ case OFPUTIL_OFPAT10_SET_NW_DST:
oana = ofputil_put_action(code, b);
str_to_ip(arg, &oana->nw_addr);
break;
- case OFPUTIL_OFPAT_SET_NW_TOS:
- ofputil_put_OFPAT_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
+ case OFPUTIL_OFPAT10_SET_NW_TOS:
+ ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
break;
- case OFPUTIL_OFPAT_SET_TP_SRC:
- case OFPUTIL_OFPAT_SET_TP_DST:
+ case OFPUTIL_OFPAT10_SET_TP_SRC:
+ case OFPUTIL_OFPAT10_SET_TP_DST:
oata = ofputil_put_action(code, b);
oata->tp_port = htons(str_to_u32(arg));
break;
- case OFPUTIL_OFPAT_ENQUEUE:
+ case OFPUTIL_OFPAT10_ENQUEUE:
parse_enqueue(b, arg);
break;
case OFPUTIL_NXAST_DEC_TTL:
ofputil_put_NXAST_DEC_TTL(b);
break;
+
+ case OFPUTIL_NXAST_FIN_TIMEOUT:
+ parse_fin_timeout(b, arg);
+ break;
+
+ case OFPUTIL_NXAST_CONTROLLER:
+ parse_controller(b, arg);
+ break;
}
}
"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 {
}
/* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
- * (one of OFPFC_*) and appends the parsed OpenFlow message to 'packets'.
- * '*cur_format' should initially contain the flow format currently configured
- * on the connection; this function will add a message to change the flow
- * format and update '*cur_format', if this is necessary to add the parsed
- * flow. */
+ * (one of OFPFC_*) into 'fm'. */
void
-parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
- bool *flow_mod_table_id, char *string, uint16_t command,
- bool verbose)
+parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
+ uint16_t command, bool verbose)
{
- enum nx_flow_format min_format, next_format;
struct cls_rule rule_copy;
- struct ofpbuf *ofm;
- struct ofputil_flow_mod fm;
- parse_ofp_str(&fm, command, string, verbose);
-
- min_format = ofputil_min_flow_format(&fm.cr);
- if (command != OFPFC_ADD && fm.cookie_mask != htonll(0)) {
- min_format = NXFF_NXM;
- }
- next_format = MAX(*cur_format, min_format);
- if (next_format != *cur_format) {
- struct ofpbuf *sff = ofputil_make_set_flow_format(next_format);
- list_push_back(packets, &sff->list_node);
- *cur_format = next_format;
- }
+ parse_ofp_str(fm, command, string, verbose);
/* Normalize a copy of the rule. This ensures that non-normalized flows
* get logged but doesn't affect what gets sent to the switch, so that the
* switch can do whatever it likes with the flow. */
- rule_copy = fm.cr;
- ofputil_normalize_rule(&rule_copy, next_format);
-
- if (fm.table_id != 0xff && !*flow_mod_table_id) {
- struct ofpbuf *sff = ofputil_make_flow_mod_table_id(true);
- list_push_back(packets, &sff->list_node);
- *flow_mod_table_id = true;
- }
-
- ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id);
- list_push_back(packets, &ofm->list_node);
+ rule_copy = fm->cr;
+ ofputil_normalize_rule(&rule_copy);
}
-/* Similar to parse_ofp_flow_mod_str(), except that the string is read from
- * 'stream' and the command is always OFPFC_ADD. Returns false if end-of-file
- * is reached before reading a flow, otherwise true. */
-bool
-parse_ofp_flow_mod_file(struct list *packets,
- enum nx_flow_format *cur, bool *flow_mod_table_id,
- FILE *stream, uint16_t command)
+void
+parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
+ struct ofputil_flow_mod **fms, size_t *n_fms)
{
+ size_t allocated_fms;
+ FILE *stream;
struct ds s;
- bool ok;
+ stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
+ if (stream == NULL) {
+ ovs_fatal(errno, "%s: open", file_name);
+ }
+
+ allocated_fms = *n_fms;
ds_init(&s);
- ok = ds_get_preprocessed_line(&s, stream) == 0;
- if (ok) {
- parse_ofp_flow_mod_str(packets, cur, flow_mod_table_id,
- ds_cstr(&s), command, true);
+ while (!ds_get_preprocessed_line(&s, stream)) {
+ if (*n_fms >= allocated_fms) {
+ *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
+ }
+ parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false);
+ *n_fms += 1;
}
ds_destroy(&s);
- return ok;
+ if (stream != stdin) {
+ fclose(stream);
+ }
}
void
parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
- bool aggregate, char *string)
+ bool aggregate, const char *string)
{
struct ofputil_flow_mod fm;
fsr->out_port = fm.out_port;
fsr->table_id = fm.table_id;
}
+
+/* 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);
+ }
+ }
+
+exit:
+ free(copy);
+
+ if (error) {
+ memset(flow, 0, sizeof *flow);
+ }
+ return error;
+}