From 2e4f5fcf3f5837de6805ccad58cd852a7cca595d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 7 Dec 2010 12:45:24 -0800 Subject: [PATCH] ofp-util: New abstractions for flow_mod, flow_stats_request. These will be useful for adding Nicira Extended Match support to ovs-ofctl. This commit makes ofproto use the new flow_mod abstraction, but not the new flow and aggregate stats abstraction. The latter takes a bit more infrastructure that I haven't finished yet. --- lib/nx-match.h | 4 + lib/ofp-util.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++ lib/ofp-util.h | 33 ++++++ ofproto/ofproto.c | 142 ++++-------------------- 4 files changed, 327 insertions(+), 122 deletions(-) diff --git a/lib/nx-match.h b/lib/nx-match.h index e02e22b46..ba57f8139 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -71,4 +71,8 @@ void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *); */ #define NXM_MAX_LEN 192 +/* This is my guess at the length of a "typical" nx_match, for use in + * predicting space requirements. */ +#define NXM_TYPICAL_LEN 64 + #endif /* nx-match.h */ diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 11836ccdd..8cf269a4b 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -755,6 +755,276 @@ ofputil_msg_type_code(const struct ofputil_msg_type *type) { return type->code; } + +/* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh', received when the + * current flow format was 'flow_format', into an abstract flow_mod in 'fm'. + * Returns 0 if successful, otherwise an OpenFlow error code. + * + * Does not validate the flow_mod actions. */ +int +ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh, + enum nx_flow_format flow_format) +{ + const struct ofputil_msg_type *type; + struct ofpbuf b; + + b.data = (void *) oh; + b.size = ntohs(oh->length); + + ofputil_decode_msg_type(oh, &type); + if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) { + /* Standard OpenFlow flow_mod. */ + struct ofp_match match, orig_match; + const struct ofp_flow_mod *ofm; + int error; + + /* Dissect the message. */ + ofm = ofpbuf_try_pull(&b, sizeof *ofm); + if (!ofm) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions); + if (error) { + return error; + } + + /* Normalize ofm->match. If normalization actually changes anything, + * then log the differences. */ + match = ofm->match; + match.pad1[0] = match.pad2[0] = 0; + orig_match = match; + normalize_match(&match); + if (memcmp(&match, &orig_match, sizeof orig_match)) { + if (!VLOG_DROP_INFO(&bad_ofmsg_rl)) { + char *old = ofp_match_to_literal_string(&orig_match); + char *new = ofp_match_to_literal_string(&match); + VLOG_INFO("normalization changed ofp_match, details:"); + VLOG_INFO(" pre: %s", old); + VLOG_INFO("post: %s", new); + free(old); + free(new); + } + } + + /* Translate the message. */ + ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), flow_format, + ofm->cookie, &fm->cr); + fm->cookie = ofm->cookie; + fm->command = ntohs(ofm->command); + fm->idle_timeout = ntohs(ofm->idle_timeout); + fm->hard_timeout = ntohs(ofm->hard_timeout); + fm->buffer_id = ntohl(ofm->buffer_id); + fm->out_port = ntohs(ofm->out_port); + fm->flags = ntohs(ofm->flags); + } else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) { + /* Nicira extended flow_mod. */ + const struct nx_flow_mod *nfm; + int error; + + /* Dissect the message. */ + nfm = ofpbuf_try_pull(&b, sizeof *nfm); + if (!nfm) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority), + &fm->cr); + if (error) { + return error; + } + error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions); + if (error) { + return error; + } + + /* Translate the message. */ + fm->cookie = nfm->cookie; + fm->command = ntohs(nfm->command); + fm->idle_timeout = ntohs(nfm->idle_timeout); + fm->hard_timeout = ntohs(nfm->hard_timeout); + fm->buffer_id = ntohl(nfm->buffer_id); + fm->out_port = ntohs(nfm->out_port); + fm->flags = ntohs(nfm->flags); + } else { + NOT_REACHED(); + } + + return 0; +} + +/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to + * 'flow_format' and returns the message. */ +struct ofpbuf * +ofputil_encode_flow_mod(const struct flow_mod *fm, + enum nx_flow_format flow_format) +{ + size_t actions_len = fm->n_actions * sizeof *fm->actions; + struct ofpbuf *msg; + + if (flow_format == NXFF_OPENFLOW10 + || flow_format == NXFF_TUN_ID_FROM_COOKIE) { + struct ofp_flow_mod *ofm; + + msg = ofpbuf_new(sizeof *ofm + actions_len); + ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg); + ofputil_cls_rule_to_match(&fm->cr, flow_format, &ofm->match); + ofm->cookie = fm->cookie; + ofm->command = htons(fm->command); + ofm->idle_timeout = htons(fm->idle_timeout); + ofm->hard_timeout = htons(fm->hard_timeout); + ofm->priority = htons(fm->cr.priority); + ofm->buffer_id = htonl(fm->buffer_id); + ofm->out_port = htons(fm->out_port); + ofm->flags = htons(fm->flags); + } else if (flow_format == NXFF_NXM) { + struct nx_flow_mod *nfm; + int match_len; + + msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len); + put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg); + match_len = nx_put_match(msg, &fm->cr); + + nfm = msg->data; + nfm->cookie = fm->cookie; + nfm->command = htons(fm->command); + nfm->idle_timeout = htons(fm->idle_timeout); + nfm->hard_timeout = htons(fm->hard_timeout); + nfm->priority = htons(fm->cr.priority); + nfm->buffer_id = htonl(fm->buffer_id); + nfm->out_port = htons(fm->out_port); + nfm->flags = htons(fm->flags); + nfm->match_len = htons(match_len); + } else { + NOT_REACHED(); + } + + ofpbuf_put(msg, fm->actions, actions_len); + update_openflow_length(msg); + return msg; +} + +static int +ofputil_decode_ofpst_flow_request(struct flow_stats_request *fsr, + const struct ofp_header *oh, + enum nx_flow_format flow_format, + bool aggregate) +{ + const struct ofp_flow_stats_request *ofsr = ofputil_stats_body(oh); + + fsr->aggregate = aggregate; + ofputil_cls_rule_from_match(&ofsr->match, 0, flow_format, 0, &fsr->match); + fsr->out_port = ntohs(ofsr->out_port); + fsr->table_id = ofsr->table_id; + + return 0; +} + +static int +ofputil_decode_nxst_flow_request(struct flow_stats_request *fsr, + const struct ofp_header *oh, + bool aggregate) +{ + const struct nx_flow_stats_request *nfsr; + struct ofpbuf b; + int error; + + b.data = (void *) oh; + b.size = ntohs(oh->length); + + nfsr = ofpbuf_try_pull(&b, sizeof *nfsr); + if (!nfsr) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match); + if (error) { + return error; + } + if (b.size) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + fsr->aggregate = aggregate; + fsr->out_port = ntohs(nfsr->out_port); + fsr->table_id = nfsr->table_id; + + return 0; +} + +/* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE + * message 'oh', received when the current flow format was 'flow_format', into + * an abstract flow_stats_request in 'fsr'. Returns 0 if successful, otherwise + * an OpenFlow error code. */ +int +ofputil_decode_flow_stats_request(struct flow_stats_request *fsr, + const struct ofp_header *oh, + enum nx_flow_format flow_format) +{ + const struct ofputil_msg_type *type; + struct ofpbuf b; + int code; + + b.data = (void *) oh; + b.size = ntohs(oh->length); + + ofputil_decode_msg_type(oh, &type); + code = ofputil_msg_type_code(type); + switch (code) { + case OFPUTIL_OFPST_FLOW_REQUEST: + return ofputil_decode_ofpst_flow_request(fsr, oh, flow_format, false); + + case OFPUTIL_OFPST_AGGREGATE_REQUEST: + return ofputil_decode_ofpst_flow_request(fsr, oh, flow_format, true); + + case OFPUTIL_NXST_FLOW_REQUEST: + return ofputil_decode_nxst_flow_request(fsr, oh, false); + + case OFPUTIL_NXST_AGGREGATE_REQUEST: + return ofputil_decode_nxst_flow_request(fsr, oh, true); + + default: + /* Hey, the caller lied. */ + NOT_REACHED(); + } +} + +/* Converts abstract flow_stats_request 'fsr' into an OFPST_FLOW, + * OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE message 'oh' according to + * 'flow_format', and returns the message. */ +struct ofpbuf * +ofputil_encode_flow_stats_request(const struct flow_stats_request *fsr, + enum nx_flow_format flow_format) +{ + struct ofpbuf *msg; + + if (flow_format == NXFF_OPENFLOW10 + || flow_format == NXFF_TUN_ID_FROM_COOKIE) { + struct ofp_flow_stats_request *ofsr; + int type; + + BUILD_ASSERT_DECL(sizeof(struct ofp_flow_stats_request) + == sizeof(struct ofp_aggregate_stats_request)); + + type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW; + ofsr = ofputil_make_stats_request(sizeof *ofsr, type, &msg); + ofputil_cls_rule_to_match(&fsr->match, flow_format, &ofsr->match); + ofsr->table_id = fsr->table_id; + ofsr->out_port = htons(fsr->out_port); + } else if (flow_format == NXFF_NXM) { + struct nx_flow_stats_request *nfsr; + int match_len; + + ofputil_make_nxstats_request(sizeof *nfsr, NXST_FLOW, &msg); + match_len = nx_put_match(msg, &fsr->match); + + nfsr = msg->data; + nfsr->out_port = htons(fsr->out_port); + nfsr->match_len = htons(match_len); + nfsr->table_id = fsr->table_id; + } else { + NOT_REACHED(); + } + + return msg; +} /* Returns a string representing the message type of 'type'. The string is the * enumeration constant for the type, e.g. "OFPT_HELLO". For statistics diff --git a/lib/ofp-util.h b/lib/ofp-util.h index f82e083a5..4666a88f3 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -112,6 +112,39 @@ void ofputil_cls_rule_to_match(const struct cls_rule *, enum nx_flow_format, void normalize_match(struct ofp_match *); char *ofp_match_to_literal_string(const struct ofp_match *match); +/* Flow format independent flow_mod. */ +struct flow_mod { + struct cls_rule cr; + ovs_be64 cookie; + uint16_t command; + uint16_t idle_timeout; + uint16_t hard_timeout; + uint32_t buffer_id; + uint16_t out_port; + uint16_t flags; + union ofp_action *actions; + size_t n_actions; +}; + +int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *, + enum nx_flow_format); +struct ofpbuf *ofputil_encode_flow_mod(const struct flow_mod *, + enum nx_flow_format); + +/* Flow stats or aggregate stats request, independent of flow format. */ +struct flow_stats_request { + bool aggregate; /* Aggregate results? */ + struct cls_rule match; + uint16_t out_port; + uint8_t table_id; +}; + +int ofputil_decode_flow_stats_request(struct flow_stats_request *, + const struct ofp_header *, + enum nx_flow_format); +struct ofpbuf *ofputil_encode_flow_stats_request( + const struct flow_stats_request *, enum nx_flow_format); + /* OpenFlow protocol utility functions. */ void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **); void *make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 07f7b5c64..64789656c 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -3804,19 +3804,6 @@ facet_update_stats(struct ofproto *ofproto, struct facet *facet, } } -struct flow_mod { - struct cls_rule cr; - ovs_be64 cookie; - uint16_t command; - uint16_t idle_timeout; - uint16_t hard_timeout; - uint32_t buffer_id; - uint16_t out_port; - uint16_t flags; - union ofp_action *actions; - size_t n_actions; -}; - /* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT * in which no matching flow already exists in the flow table. * @@ -4027,9 +4014,10 @@ delete_flow(struct ofproto *p, struct rule *rule, ovs_be16 out_port) } static int -flow_mod_core(struct ofconn *ofconn, struct flow_mod *fm) +handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *p = ofconn->ofproto; + struct flow_mod fm; int error; error = reject_slave_controller(ofconn, "flow_mod"); @@ -4037,36 +4025,41 @@ flow_mod_core(struct ofconn *ofconn, struct flow_mod *fm) return error; } - error = validate_actions(fm->actions, fm->n_actions, - &fm->cr.flow, p->max_ports); + error = ofputil_decode_flow_mod(&fm, oh, ofconn->flow_format); if (error) { return error; } - /* We do not support the emergency flow cache. It will hopefully - * get dropped from OpenFlow in the near future. */ - if (fm->flags & OFPFF_EMERG) { + /* We do not support the emergency flow cache. It will hopefully get + * dropped from OpenFlow in the near future. */ + if (fm.flags & OFPFF_EMERG) { /* There isn't a good fit for an error code, so just state that the * flow table is full. */ return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL); } - switch (fm->command) { + error = validate_actions(fm.actions, fm.n_actions, + &fm.cr.flow, p->max_ports); + if (error) { + return error; + } + + switch (fm.command) { case OFPFC_ADD: - return add_flow(ofconn, fm); + return add_flow(ofconn, &fm); case OFPFC_MODIFY: - return modify_flows_loose(ofconn, fm); + return modify_flows_loose(ofconn, &fm); case OFPFC_MODIFY_STRICT: - return modify_flow_strict(ofconn, fm); + return modify_flow_strict(ofconn, &fm); case OFPFC_DELETE: - delete_flows_loose(p, fm); + delete_flows_loose(p, &fm); return 0; case OFPFC_DELETE_STRICT: - delete_flow_strict(p, fm); + delete_flow_strict(p, &fm); return 0; default: @@ -4074,101 +4067,6 @@ flow_mod_core(struct ofconn *ofconn, struct flow_mod *fm) } } -static int -handle_ofpt_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) -{ - struct ofp_match orig_match; - struct ofp_flow_mod *ofm; - struct flow_mod fm; - struct ofpbuf b; - int error; - - b.data = (void *) oh; - b.size = ntohs(oh->length); - - /* Dissect the message. */ - ofm = ofpbuf_try_pull(&b, sizeof *ofm); - if (!ofm) { - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - error = ofputil_pull_actions(&b, b.size, &fm.actions, &fm.n_actions); - if (error) { - return error; - } - - /* Normalize ofm->match. If normalization actually changes anything, then - * log the differences. */ - ofm->match.pad1[0] = ofm->match.pad2[0] = 0; - orig_match = ofm->match; - normalize_match(&ofm->match); - if (memcmp(&ofm->match, &orig_match, sizeof orig_match)) { - static struct vlog_rate_limit normal_rl = VLOG_RATE_LIMIT_INIT(1, 1); - if (!VLOG_DROP_INFO(&normal_rl)) { - char *old = ofp_match_to_literal_string(&orig_match); - char *new = ofp_match_to_literal_string(&ofm->match); - VLOG_INFO("%s: normalization changed ofp_match, details:", - rconn_get_name(ofconn->rconn)); - VLOG_INFO(" pre: %s", old); - VLOG_INFO("post: %s", new); - free(old); - free(new); - } - } - - /* Translate the message. */ - ofputil_cls_rule_from_match(&ofm->match, ntohs(ofm->priority), - ofconn->flow_format, ofm->cookie, &fm.cr); - fm.cookie = ofm->cookie; - fm.command = ntohs(ofm->command); - fm.idle_timeout = ntohs(ofm->idle_timeout); - fm.hard_timeout = ntohs(ofm->hard_timeout); - fm.buffer_id = ntohl(ofm->buffer_id); - fm.out_port = ntohs(ofm->out_port); - fm.flags = ntohs(ofm->flags); - - /* Execute the command. */ - return flow_mod_core(ofconn, &fm); -} - -static int -handle_nxt_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) -{ - struct nx_flow_mod *nfm; - struct flow_mod fm; - struct ofpbuf b; - int error; - - b.data = (void *) oh; - b.size = ntohs(oh->length); - - /* Dissect the message. */ - nfm = ofpbuf_try_pull(&b, sizeof *nfm); - if (!nfm) { - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority), - &fm.cr); - if (error) { - return error; - } - error = ofputil_pull_actions(&b, b.size, &fm.actions, &fm.n_actions); - if (error) { - return error; - } - - /* Translate the message. */ - fm.cookie = nfm->cookie; - fm.command = ntohs(nfm->command); - fm.idle_timeout = ntohs(nfm->idle_timeout); - fm.hard_timeout = ntohs(nfm->hard_timeout); - fm.buffer_id = ntohl(nfm->buffer_id); - fm.out_port = ntohs(nfm->out_port); - fm.flags = ntohs(nfm->flags); - - /* Execute the command. */ - return flow_mod_core(ofconn, &fm); -} - static int handle_tun_id_from_cookie(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -4284,7 +4182,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) return handle_port_mod(ofconn, oh); case OFPUTIL_OFPT_FLOW_MOD: - return handle_ofpt_flow_mod(ofconn, oh); + return handle_flow_mod(ofconn, oh); case OFPUTIL_OFPT_BARRIER_REQUEST: return handle_barrier_request(ofconn, oh); @@ -4308,7 +4206,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) return handle_nxt_set_flow_format(ofconn, oh); case OFPUTIL_NXT_FLOW_MOD: - return handle_nxt_flow_mod(ofconn, oh); + return handle_flow_mod(ofconn, oh); /* OpenFlow statistics requests. */ case OFPUTIL_OFPST_DESC_REQUEST: -- 2.43.0