X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=9cb3f91095000f8a41636ac5392a8754459597cd;hb=fa8b054f50e6f669a1cc4c41ada0f1fdad03e9dd;hp=ab9a835e6161eafe242e5760bd9bff568023674d;hpb=3c8552c1db717d2018adbc5e17cbf2583f0a6639;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index ab9a835e6..9cb3f9109 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -24,6 +24,7 @@ #include #include #include +#include "byte-order.h" #include "classifier.h" #include "coverage.h" #include "discovery.h" @@ -36,6 +37,7 @@ #include "mac-learning.h" #include "netdev.h" #include "netflow.h" +#include "nx-match.h" #include "odp-util.h" #include "ofp-print.h" #include "ofp-util.h" @@ -58,18 +60,11 @@ #include "unixctl.h" #include "vconn.h" #include "vlog.h" -#include "xtoxll.h" -VLOG_DEFINE_THIS_MODULE(ofproto) +VLOG_DEFINE_THIS_MODULE(ofproto); #include "sflow_api.h" -enum { - TABLEID_HASH = 0, - TABLEID_CLASSIFIER = 1 -}; - - struct ofport { struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */ struct netdev *netdev; @@ -89,12 +84,11 @@ static int xlate_actions(const union ofp_action *in, size_t n_in, struct rule { struct cls_rule cr; - uint64_t flow_cookie; /* Controller-issued identifier. - (Kept in network-byte order.) */ + ovs_be64 flow_cookie; /* Controller-issued identifier. */ uint16_t idle_timeout; /* In seconds from time of last use. */ uint16_t hard_timeout; /* In seconds from time of creation. */ bool send_flow_removed; /* Send a flow removed message? */ - long long int used; /* Last-used time (0 if never used). */ + long long int used; /* Time last used; time created if not used. */ long long int created; /* Creation time. */ uint64_t packet_count; /* Number of packets received. */ uint64_t byte_count; /* Number of bytes received. */ @@ -155,7 +149,7 @@ rule_is_hidden(const struct rule *rule) static struct rule *rule_create(struct ofproto *, struct rule *super, const union ofp_action *, size_t n_actions, uint16_t idle_timeout, uint16_t hard_timeout, - uint64_t flow_cookie, bool send_flow_removed); + ovs_be64 flow_cookie, bool send_flow_removed); static void rule_free(struct rule *); static void rule_destroy(struct ofproto *, struct rule *); static struct rule *rule_from_cls_rule(const struct cls_rule *); @@ -168,8 +162,7 @@ static void rule_install(struct ofproto *, struct rule *, struct rule *displaced_rule); static void rule_uninstall(struct ofproto *, struct rule *); static void rule_post_uninstall(struct ofproto *, struct rule *); -static void send_flow_removed(struct ofproto *p, struct rule *rule, - long long int now, uint8_t reason); +static void send_flow_removed(struct ofproto *, struct rule *, uint8_t reason); /* ofproto supports two kinds of OpenFlow connections: * @@ -217,6 +210,7 @@ struct ofconn { struct list node; /* In struct ofproto's "all_conns" list. */ struct rconn *rconn; /* OpenFlow connection. */ enum ofconn_type type; /* Type. */ + int flow_format; /* One of NXFF_*. */ /* OFPT_PACKET_IN related data. */ struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */ @@ -251,7 +245,7 @@ BUILD_ASSERT_DECL(OFPR_ACTION == _ODPL_ACTION_NR); static struct ofconn *ofconn_create(struct ofproto *, struct rconn *, enum ofconn_type); static void ofconn_destroy(struct ofconn *); -static void ofconn_run(struct ofconn *, struct ofproto *); +static void ofconn_run(struct ofconn *); static void ofconn_wait(struct ofconn *); static bool ofconn_receives_async_msgs(const struct ofconn *); static char *ofconn_make_name(const struct ofproto *, const char *target); @@ -297,7 +291,6 @@ struct ofproto { bool need_revalidate; long long int next_expiration; struct tag_set revalidate_set; - bool tun_id_from_cookie; /* OpenFlow connections. */ struct hmap controllers; /* Controller "struct ofconn"s. */ @@ -333,8 +326,7 @@ static void revalidate_cb(struct cls_rule *rule_, void *p_); static void handle_odp_msg(struct ofproto *, struct ofpbuf *); -static void handle_openflow(struct ofconn *, struct ofproto *, - struct ofpbuf *); +static void handle_openflow(struct ofconn *, struct ofpbuf *); static struct ofport *get_port(const struct ofproto *, uint16_t odp_port); static void update_port(struct ofproto *, const char *devname); @@ -1100,7 +1092,7 @@ ofproto_run1(struct ofproto *p) } LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &p->all_conns) { - ofconn_run(ofconn, p); + ofconn_run(ofconn); } /* Fail-open maintenance. Do this after processing the ofconns since @@ -1306,29 +1298,32 @@ ofproto_send_packet(struct ofproto *p, const struct flow *flow, return 0; } +/* Adds a flow to the OpenFlow flow table in 'p' that matches 'cls_rule' and + * performs the 'n_actions' actions in 'actions'. The new flow will not + * timeout. + * + * If cls_rule->priority is in the range of priorities supported by OpenFlow + * (0...65535, inclusive) then the flow will be visible to OpenFlow + * controllers; otherwise, it will be hidden. + * + * The caller retains ownership of 'cls_rule' and 'actions'. */ void -ofproto_add_flow(struct ofproto *p, const struct flow *flow, - uint32_t wildcards, unsigned int priority, - const union ofp_action *actions, size_t n_actions, - int idle_timeout) +ofproto_add_flow(struct ofproto *p, const struct cls_rule *cls_rule, + const union ofp_action *actions, size_t n_actions) { struct rule *rule; - rule = rule_create(p, NULL, actions, n_actions, - idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, - 0, 0, false); - cls_rule_from_flow(flow, wildcards, priority, &rule->cr); + rule = rule_create(p, NULL, actions, n_actions, 0, 0, 0, false); + rule->cr = *cls_rule; rule_insert(p, rule, NULL, 0); } void -ofproto_delete_flow(struct ofproto *ofproto, const struct flow *flow, - uint32_t wildcards, unsigned int priority) +ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target) { struct rule *rule; rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls, - flow, wildcards, - priority)); + target)); if (rule) { rule_remove(ofproto, rule); } @@ -1372,6 +1367,8 @@ reinit_ports(struct ofproto *p) size_t n_odp_ports; size_t i; + COVERAGE_INC(ofproto_reinit_ports); + svec_init(&devnames); HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { svec_add (&devnames, (char *) ofport->opp.name); @@ -1396,7 +1393,6 @@ make_ofport(const struct odp_port *odp_port) enum netdev_flags flags; struct ofport *ofport; struct netdev *netdev; - bool carrier; int error; memset(&netdev_options, 0, sizeof netdev_options); @@ -1424,8 +1420,7 @@ make_ofport(const struct odp_port *odp_port) netdev_get_flags(netdev, &flags); ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN; - netdev_get_carrier(netdev, &carrier); - ofport->opp.state = carrier ? 0 : OFPPS_LINK_DOWN; + ofport->opp.state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN; netdev_get_features(netdev, &ofport->opp.curr, &ofport->opp.advertised, @@ -1640,6 +1635,7 @@ ofconn_create(struct ofproto *p, struct rconn *rconn, enum ofconn_type type) list_push_back(&p->all_conns, &ofconn->node); ofconn->rconn = rconn; ofconn->type = type; + ofconn->flow_format = NXFF_OPENFLOW10; ofconn->role = NX_ROLE_OTHER; ofconn->packet_in_counter = rconn_packet_counter_create (); ofconn->pktbuf = NULL; @@ -1666,8 +1662,9 @@ ofconn_destroy(struct ofconn *ofconn) } static void -ofconn_run(struct ofconn *ofconn, struct ofproto *p) +ofconn_run(struct ofconn *ofconn) { + struct ofproto *p = ofconn->ofproto; int iteration; size_t i; @@ -1704,7 +1701,7 @@ ofconn_run(struct ofconn *ofconn, struct ofproto *p) if (p->fail_open) { fail_open_maybe_recover(p->fail_open); } - handle_openflow(ofconn, p, of_msg); + handle_openflow(ofconn, of_msg); ofpbuf_delete(of_msg); } } @@ -1845,7 +1842,7 @@ static struct rule * rule_create(struct ofproto *ofproto, struct rule *super, const union ofp_action *actions, size_t n_actions, uint16_t idle_timeout, uint16_t hard_timeout, - uint64_t flow_cookie, bool send_flow_removed) + ovs_be64 flow_cookie, bool send_flow_removed) { struct rule *rule = xzalloc(sizeof *rule); rule->idle_timeout = idle_timeout; @@ -1906,7 +1903,7 @@ rule_destroy(struct ofproto *ofproto, struct rule *rule) } static bool -rule_has_out_port(const struct rule *rule, uint16_t out_port) +rule_has_out_port(const struct rule *rule, ovs_be16 out_port) { const union ofp_action *oa; struct actions_iterator i; @@ -2063,9 +2060,14 @@ rule_create_subrule(struct ofproto *ofproto, struct rule *rule, rule->idle_timeout, rule->hard_timeout, 0, false); COVERAGE_INC(ofproto_subrule_create); - cls_rule_from_flow(flow, 0, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX - : rule->cr.priority), &subrule->cr); - classifier_insert_exact(&ofproto->cls, &subrule->cr); + cls_rule_init_exact(flow, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX + : rule->cr.priority), + &subrule->cr); + + if (classifier_insert(&ofproto->cls, &subrule->cr)) { + /* Can't happen, */ + NOT_REACHED(); + } return subrule; } @@ -2299,34 +2301,15 @@ queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn, } } -static void -send_error(const struct ofconn *ofconn, const struct ofp_header *oh, - int error, const void *data, size_t len) -{ - struct ofpbuf *buf; - struct ofp_error_msg *oem; - - if (!(error >> 16)) { - VLOG_WARN_RL(&rl, "not sending bad error code %d to controller", - error); - return; - } - - COVERAGE_INC(ofproto_error); - oem = make_openflow_xid(len + sizeof *oem, OFPT_ERROR, - oh ? oh->xid : 0, &buf); - oem->type = htons((unsigned int) error >> 16); - oem->code = htons(error & 0xffff); - memcpy(oem->data, data, len); - queue_tx(buf, ofconn, ofconn->reply_counter); -} - static void send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh, int error) { - size_t oh_length = ntohs(oh->length); - send_error(ofconn, oh, error, oh, MIN(oh_length, 64)); + struct ofpbuf *buf = make_ofp_error_msg(error, oh); + if (buf) { + COVERAGE_INC(ofproto_error); + queue_tx(buf, ofconn, ofconn->reply_counter); + } } static void @@ -2350,15 +2333,14 @@ handle_echo_request(struct ofconn *ofconn, struct ofp_header *oh) } static int -handle_features_request(struct ofproto *p, struct ofconn *ofconn, - struct ofp_header *oh) +handle_features_request(struct ofconn *ofconn, struct ofp_header *oh) { struct ofp_switch_features *osf; struct ofpbuf *buf; struct ofport *port; osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf); - osf->datapath_id = htonll(p->datapath_id); + osf->datapath_id = htonll(ofconn->ofproto->datapath_id); osf->n_buffers = htonl(pktbuf_capacity()); osf->n_tables = 2; osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS | @@ -2376,7 +2358,7 @@ handle_features_request(struct ofproto *p, struct ofconn *ofconn, (1u << OFPAT_SET_TP_DST) | (1u << OFPAT_ENQUEUE)); - HMAP_FOR_EACH (port, hmap_node, &p->ports) { + HMAP_FOR_EACH (port, hmap_node, &ofconn->ofproto->ports) { hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp)); } @@ -2385,8 +2367,7 @@ handle_features_request(struct ofproto *p, struct ofconn *ofconn, } static int -handle_get_config_request(struct ofproto *p, struct ofconn *ofconn, - struct ofp_header *oh) +handle_get_config_request(struct ofconn *ofconn, struct ofp_header *oh) { struct ofpbuf *buf; struct ofp_switch_config *osc; @@ -2394,7 +2375,7 @@ handle_get_config_request(struct ofproto *p, struct ofconn *ofconn, bool drop_frags; /* Figure out flags. */ - dpif_get_drop_frags(p->dpif, &drop_frags); + dpif_get_drop_frags(ofconn->ofproto->dpif, &drop_frags); flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL; /* Send reply. */ @@ -2407,8 +2388,7 @@ handle_get_config_request(struct ofproto *p, struct ofconn *ofconn, } static int -handle_set_config(struct ofproto *p, struct ofconn *ofconn, - struct ofp_switch_config *osc) +handle_set_config(struct ofconn *ofconn, struct ofp_switch_config *osc) { uint16_t flags; int error; @@ -2422,10 +2402,10 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn, if (ofconn->type == OFCONN_PRIMARY && ofconn->role != NX_ROLE_SLAVE) { switch (flags & OFPC_FRAG_MASK) { case OFPC_FRAG_NORMAL: - dpif_set_drop_frags(p->dpif, false); + dpif_set_drop_frags(ofconn->ofproto->dpif, false); break; case OFPC_FRAG_DROP: - dpif_set_drop_frags(p->dpif, true); + dpif_set_drop_frags(ofconn->ofproto->dpif, true); break; default: VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")", @@ -2496,7 +2476,8 @@ static struct rule * lookup_valid_rule(struct ofproto *ofproto, const struct flow *flow) { struct rule *rule; - rule = rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow)); + rule = rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow, + CLS_INC_ALL)); /* The rule we found might not be valid, since we could be in need of * revalidation. If it is not valid, don't return it. */ @@ -2694,6 +2675,36 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx, = priority; } +static void +xlate_set_dl_tci(struct action_xlate_ctx *ctx) +{ + ovs_be16 dl_vlan = ctx->flow.dl_vlan; + uint8_t dl_vlan_pcp = ctx->flow.dl_vlan_pcp; + + if (dl_vlan == htons(OFP_VLAN_NONE)) { + odp_actions_add(ctx->out, ODPAT_STRIP_VLAN); + } else { + union odp_action *oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI); + oa->dl_tci.tci = htons(ntohs(dl_vlan & htons(VLAN_VID_MASK)) + | (dl_vlan_pcp << VLAN_PCP_SHIFT) + | VLAN_CFI); + } +} + +static void +xlate_reg_move_action(struct action_xlate_ctx *ctx, + const struct nx_action_reg_move *narm) +{ + ovs_be16 old_vlan = ctx->flow.dl_vlan; + uint8_t old_pcp = ctx->flow.dl_vlan_pcp; + + nxm_execute_reg_move(narm, &ctx->flow); + + if (ctx->flow.dl_vlan != old_vlan || ctx->flow.dl_vlan_pcp != old_pcp) { + xlate_set_dl_tci(ctx); + } +} + static void xlate_nicira_action(struct action_xlate_ctx *ctx, const struct nx_action_header *nah) @@ -2732,6 +2743,15 @@ xlate_nicira_action(struct action_xlate_ctx *ctx, odp_actions_add(ctx->out, ODPAT_POP_PRIORITY); break; + case NXAST_REG_MOVE: + xlate_reg_move_action(ctx, (const struct nx_action_reg_move *) nah); + break; + + case NXAST_REG_LOAD: + nxm_execute_reg_load((const struct nx_action_reg_load *) nah, + &ctx->flow); + break; + /* If you add a new action here that modifies flow data, don't forget to * update the flow key in ctx->flow at the same time. */ @@ -2767,23 +2787,19 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPAT_SET_VLAN_VID: - oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI); - oa->dl_tci.tci = ia->vlan_vid.vlan_vid; - oa->dl_tci.tci |= htons(ctx->flow.dl_vlan_pcp << VLAN_PCP_SHIFT); ctx->flow.dl_vlan = ia->vlan_vid.vlan_vid; + xlate_set_dl_tci(ctx); break; case OFPAT_SET_VLAN_PCP: - oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI); - oa->dl_tci.tci = htons(ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT); - oa->dl_tci.tci |= ctx->flow.dl_vlan; ctx->flow.dl_vlan_pcp = ia->vlan_pcp.vlan_pcp; + xlate_set_dl_tci(ctx); break; case OFPAT_STRIP_VLAN: - odp_actions_add(ctx->out, ODPAT_STRIP_VLAN); ctx->flow.dl_vlan = htons(OFP_VLAN_NONE); ctx->flow.dl_vlan_pcp = 0; + xlate_set_dl_tci(ctx); break; case OFPAT_SET_DL_SRC: @@ -2888,18 +2904,14 @@ xlate_actions(const union ofp_action *in, size_t n_in, * error message code (composed with ofp_mkerr()) for the caller to propagate * upward. Otherwise, returns 0. * - * 'oh' is used to make log messages more informative. */ + * The log message mentions 'msg_type'. */ static int -reject_slave_controller(struct ofconn *ofconn, const struct ofp_header *oh) +reject_slave_controller(struct ofconn *ofconn, const const char *msg_type) { if (ofconn->type == OFCONN_PRIMARY && ofconn->role == NX_ROLE_SLAVE) { static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5); - char *type_name; - - type_name = ofp_message_type_to_string(oh->type); VLOG_WARN_RL(&perm_rl, "rejecting %s message from slave controller", - type_name); - free(type_name); + msg_type); return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); } else { @@ -2908,29 +2920,42 @@ reject_slave_controller(struct ofconn *ofconn, const struct ofp_header *oh) } static int -handle_packet_out(struct ofproto *p, struct ofconn *ofconn, - struct ofp_header *oh) +handle_packet_out(struct ofconn *ofconn, struct ofp_header *oh) { + struct ofproto *p = ofconn->ofproto; struct ofp_packet_out *opo; struct ofpbuf payload, *buffer; - struct odp_actions actions; + union ofp_action *ofp_actions; + struct odp_actions odp_actions; + struct ofpbuf request; struct flow flow; - int n_actions; + size_t n_ofp_actions; uint16_t in_port; int error; - error = reject_slave_controller(ofconn, oh); + COVERAGE_INC(ofproto_packet_out); + + error = reject_slave_controller(ofconn, "OFPT_PACKET_OUT"); if (error) { return error; } - error = check_ofp_packet_out(oh, &payload, &n_actions, p->max_ports); + /* Get ofp_packet_out. */ + request.data = oh; + request.size = ntohs(oh->length); + opo = ofpbuf_try_pull(&request, offsetof(struct ofp_packet_out, actions)); + if (!opo) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + /* Get actions. */ + error = ofputil_pull_actions(&request, ntohs(opo->actions_len), + &ofp_actions, &n_ofp_actions); if (error) { return error; } - opo = (struct ofp_packet_out *) oh; - COVERAGE_INC(ofproto_packet_out); + /* Get payload. */ if (opo->buffer_id != htonl(UINT32_MAX)) { error = pktbuf_retrieve(ofconn->pktbuf, ntohl(opo->buffer_id), &buffer, &in_port); @@ -2939,19 +2964,28 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn, } payload = *buffer; } else { + payload = request; buffer = NULL; } - flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow); - error = xlate_actions((const union ofp_action *) opo->actions, n_actions, - &flow, p, &payload, &actions, NULL, NULL, NULL); + /* Extract flow, check actions. */ + flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)), + &flow); + error = validate_actions(ofp_actions, n_ofp_actions, &flow, p->max_ports); if (error) { - return error; + goto exit; } - dpif_execute(p->dpif, actions.actions, actions.n_actions, &payload); - ofpbuf_delete(buffer); + /* Send. */ + error = xlate_actions(ofp_actions, n_ofp_actions, &flow, p, &payload, + &odp_actions, NULL, NULL, NULL); + if (!error) { + dpif_execute(p->dpif, odp_actions.actions, odp_actions.n_actions, + &payload); + } +exit: + ofpbuf_delete(buffer); return 0; } @@ -2981,14 +3015,14 @@ update_port_config(struct ofproto *p, struct ofport *port, } static int -handle_port_mod(struct ofproto *p, struct ofconn *ofconn, - struct ofp_header *oh) +handle_port_mod(struct ofconn *ofconn, struct ofp_header *oh) { + struct ofproto *p = ofconn->ofproto; const struct ofp_port_mod *opm; struct ofport *port; int error; - error = reject_slave_controller(ofconn, oh); + error = reject_slave_controller(ofconn, "OFPT_PORT_MOD"); if (error) { return error; } @@ -3013,7 +3047,7 @@ handle_port_mod(struct ofproto *p, struct ofconn *ofconn, } static struct ofpbuf * -make_stats_reply(uint32_t xid, uint16_t type, size_t body_len) +make_ofp_stats_reply(ovs_be32 xid, ovs_be16 type, size_t body_len) { struct ofp_stats_reply *osr; struct ofpbuf *msg; @@ -3026,34 +3060,72 @@ make_stats_reply(uint32_t xid, uint16_t type, size_t body_len) } static struct ofpbuf * -start_stats_reply(const struct ofp_stats_request *request, size_t body_len) +start_ofp_stats_reply(const struct ofp_stats_request *request, size_t body_len) { - return make_stats_reply(request->header.xid, request->type, body_len); + return make_ofp_stats_reply(request->header.xid, request->type, body_len); } static void * -append_stats_reply(size_t nbytes, struct ofconn *ofconn, struct ofpbuf **msgp) +append_ofp_stats_reply(size_t nbytes, struct ofconn *ofconn, + struct ofpbuf **msgp) { struct ofpbuf *msg = *msgp; assert(nbytes <= UINT16_MAX - sizeof(struct ofp_stats_reply)); if (nbytes + msg->size > UINT16_MAX) { struct ofp_stats_reply *reply = msg->data; reply->flags = htons(OFPSF_REPLY_MORE); - *msgp = make_stats_reply(reply->header.xid, reply->type, nbytes); + *msgp = make_ofp_stats_reply(reply->header.xid, reply->type, nbytes); queue_tx(msg, ofconn, ofconn->reply_counter); } return ofpbuf_put_uninit(*msgp, nbytes); } +static struct ofpbuf * +make_nxstats_reply(ovs_be32 xid, ovs_be32 subtype, size_t body_len) +{ + struct nicira_stats_msg *nsm; + struct ofpbuf *msg; + + msg = ofpbuf_new(MIN(sizeof *nsm + body_len, UINT16_MAX)); + nsm = put_openflow_xid(sizeof *nsm, OFPT_STATS_REPLY, xid, msg); + nsm->type = htons(OFPST_VENDOR); + nsm->flags = htons(0); + nsm->vendor = htonl(NX_VENDOR_ID); + nsm->subtype = htonl(subtype); + return msg; +} + +static struct ofpbuf * +start_nxstats_reply(const struct nicira_stats_msg *request, size_t body_len) +{ + return make_nxstats_reply(request->header.xid, request->subtype, body_len); +} + +static void +append_nxstats_reply(size_t nbytes, struct ofconn *ofconn, + struct ofpbuf **msgp) +{ + struct ofpbuf *msg = *msgp; + assert(nbytes <= UINT16_MAX - sizeof(struct nicira_stats_msg)); + if (nbytes + msg->size > UINT16_MAX) { + struct nicira_stats_msg *reply = msg->data; + reply->flags = htons(OFPSF_REPLY_MORE); + *msgp = make_nxstats_reply(reply->header.xid, reply->subtype, nbytes); + queue_tx(msg, ofconn, ofconn->reply_counter); + } + ofpbuf_prealloc_tailroom(*msgp, nbytes); +} + static int -handle_desc_stats_request(struct ofproto *p, struct ofconn *ofconn, - struct ofp_stats_request *request) +handle_desc_stats_request(struct ofconn *ofconn, + struct ofp_stats_request *request) { + struct ofproto *p = ofconn->ofproto; struct ofp_desc_stats *ods; struct ofpbuf *msg; - msg = start_stats_reply(request, sizeof *ods); - ods = append_stats_reply(sizeof *ods, ofconn, &msg); + msg = start_ofp_stats_reply(request, sizeof *ods); + ods = append_ofp_stats_reply(sizeof *ods, ofconn, &msg); memset(ods, 0, sizeof *ods); ovs_strlcpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc); ovs_strlcpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc); @@ -3066,49 +3138,33 @@ handle_desc_stats_request(struct ofproto *p, struct ofconn *ofconn, } static int -handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn, +handle_table_stats_request(struct ofconn *ofconn, struct ofp_stats_request *request) { + struct ofproto *p = ofconn->ofproto; struct ofp_table_stats *ots; struct ofpbuf *msg; - struct odp_stats dpstats; - int n_exact, n_subrules, n_wild; struct rule *rule; + int n_rules; - msg = start_stats_reply(request, sizeof *ots * 2); + msg = start_ofp_stats_reply(request, sizeof *ots * 2); - /* Count rules of various kinds. */ - n_subrules = 0; + /* Count rules other than subrules. */ + n_rules = classifier_count(&p->cls); CLASSIFIER_FOR_EACH_EXACT_RULE (rule, cr, &p->cls) { if (rule->super) { - n_subrules++; + n_rules--; } } - n_exact = classifier_count_exact(&p->cls) - n_subrules; - n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls); - - /* Hash table. */ - dpif_get_dp_stats(p->dpif, &dpstats); - ots = append_stats_reply(sizeof *ots, ofconn, &msg); - memset(ots, 0, sizeof *ots); - ots->table_id = TABLEID_HASH; - strcpy(ots->name, "hash"); - ots->wildcards = htonl(0); - ots->max_entries = htonl(dpstats.max_capacity); - ots->active_count = htonl(n_exact); - ots->lookup_count = htonll(dpstats.n_frags + dpstats.n_hit + - dpstats.n_missed); - ots->matched_count = htonll(dpstats.n_hit); /* XXX */ /* Classifier table. */ - ots = append_stats_reply(sizeof *ots, ofconn, &msg); + ots = append_ofp_stats_reply(sizeof *ots, ofconn, &msg); memset(ots, 0, sizeof *ots); - ots->table_id = TABLEID_CLASSIFIER; strcpy(ots->name, "classifier"); - ots->wildcards = p->tun_id_from_cookie ? htonl(OVSFW_ALL) - : htonl(OFPFW_ALL); - ots->max_entries = htonl(65536); - ots->active_count = htonl(n_wild); + ots->wildcards = (ofconn->flow_format == NXFF_OPENFLOW10 + ? htonl(OFPFW_ALL) : htonl(OVSFW_ALL)); + ots->max_entries = htonl(1024 * 1024); /* An arbitrary big number. */ + ots->active_count = htonl(n_rules); ots->lookup_count = htonll(0); /* XXX */ ots->matched_count = htonll(0); /* XXX */ @@ -3128,7 +3184,7 @@ append_port_stat(struct ofport *port, struct ofconn *ofconn, * netdev_get_stats() will log errors. */ netdev_get_stats(port->netdev, &stats); - ops = append_stats_reply(sizeof *ops, ofconn, msgp); + ops = append_ofp_stats_reply(sizeof *ops, ofconn, msgp); ops->port_no = htons(port->opp.port_no); memset(ops->pad, 0, sizeof ops->pad); ops->rx_packets = htonll(stats.rx_packets); @@ -3146,10 +3202,10 @@ append_port_stat(struct ofport *port, struct ofconn *ofconn, } static int -handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, - struct ofp_stats_request *osr, +handle_port_stats_request(struct ofconn *ofconn, struct ofp_stats_request *osr, size_t arg_size) { + struct ofproto *p = ofconn->ofproto; struct ofp_port_stats_request *psr; struct ofp_port_stats *ops; struct ofpbuf *msg; @@ -3160,7 +3216,7 @@ handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, } psr = (struct ofp_port_stats_request *) osr->body; - msg = start_stats_reply(osr, sizeof *ops * 16); + msg = start_ofp_stats_reply(osr, sizeof *ops * 16); if (psr->port_no != htons(OFPP_NONE)) { port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no))); if (port) { @@ -3177,9 +3233,8 @@ handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, } struct flow_stats_cbdata { - struct ofproto *ofproto; struct ofconn *ofconn; - uint16_t out_port; + ovs_be16 out_port; struct ofpbuf *msg; }; @@ -3237,6 +3292,14 @@ query_stats(struct ofproto *p, struct rule *rule, *byte_countp = byte_count; } +static void +calc_flow_duration(long long int start, ovs_be32 *sec, ovs_be32 *nsec) +{ + long long int msecs = time_msec() - start; + *sec = htonl(msecs / 1000); + *nsec = htonl((msecs % 1000) * (1000 * 1000)); +} + static void flow_stats_cb(struct cls_rule *rule_, void *cbdata_) { @@ -3245,9 +3308,6 @@ flow_stats_cb(struct cls_rule *rule_, void *cbdata_) struct ofp_flow_stats *ofs; uint64_t packet_count, byte_count; size_t act_len, len; - long long int tdiff = time_msec() - rule->created; - uint32_t sec = tdiff / 1000; - uint32_t msec = tdiff - (sec * 1000); if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) { return; @@ -3256,16 +3316,15 @@ flow_stats_cb(struct cls_rule *rule_, void *cbdata_) act_len = sizeof *rule->actions * rule->n_actions; len = offsetof(struct ofp_flow_stats, actions) + act_len; - query_stats(cbdata->ofproto, rule, &packet_count, &byte_count); + query_stats(cbdata->ofconn->ofproto, rule, &packet_count, &byte_count); - ofs = append_stats_reply(len, cbdata->ofconn, &cbdata->msg); + ofs = append_ofp_stats_reply(len, cbdata->ofconn, &cbdata->msg); ofs->length = htons(len); - ofs->table_id = rule->cr.wc.wildcards ? TABLEID_CLASSIFIER : TABLEID_HASH; + ofs->table_id = 0; ofs->pad = 0; flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, - cbdata->ofproto->tun_id_from_cookie, &ofs->match); - ofs->duration_sec = htonl(sec); - ofs->duration_nsec = htonl(msec * 1000000); + cbdata->ofconn->flow_format, &ofs->match); + calc_flow_duration(rule->created, &ofs->duration_sec, &ofs->duration_nsec); ofs->cookie = rule->flow_cookie; ofs->priority = htons(rule->cr.priority); ofs->idle_timeout = htons(rule->idle_timeout); @@ -3281,16 +3340,12 @@ flow_stats_cb(struct cls_rule *rule_, void *cbdata_) static int table_id_to_include(uint8_t table_id) { - return (table_id == TABLEID_HASH ? CLS_INC_EXACT - : table_id == TABLEID_CLASSIFIER ? CLS_INC_WILD - : table_id == 0xff ? CLS_INC_ALL - : 0); + return table_id == 0 || table_id == 0xff ? CLS_INC_ALL : 0; } static int -handle_flow_stats_request(struct ofproto *p, struct ofconn *ofconn, - const struct ofp_stats_request *osr, - size_t arg_size) +handle_flow_stats_request(struct ofconn *ofconn, + const struct ofp_stats_request *osr, size_t arg_size) { struct ofp_flow_stats_request *fsr; struct flow_stats_cbdata cbdata; @@ -3302,18 +3357,84 @@ handle_flow_stats_request(struct ofproto *p, struct ofconn *ofconn, fsr = (struct ofp_flow_stats_request *) osr->body; COVERAGE_INC(ofproto_flows_req); - cbdata.ofproto = p; cbdata.ofconn = ofconn; cbdata.out_port = fsr->out_port; - cbdata.msg = start_stats_reply(osr, 1024); - cls_rule_from_match(&fsr->match, 0, false, 0, &target); - classifier_for_each_match(&p->cls, &target, + cbdata.msg = start_ofp_stats_reply(osr, 1024); + cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0, &target); + classifier_for_each_match(&ofconn->ofproto->cls, &target, table_id_to_include(fsr->table_id), flow_stats_cb, &cbdata); queue_tx(cbdata.msg, ofconn, ofconn->reply_counter); return 0; } +static void +nx_flow_stats_cb(struct cls_rule *rule_, void *cbdata_) +{ + struct rule *rule = rule_from_cls_rule(rule_); + struct flow_stats_cbdata *cbdata = cbdata_; + struct nx_flow_stats *nfs; + uint64_t packet_count, byte_count; + size_t act_len, start_len; + + if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) { + return; + } + + query_stats(cbdata->ofconn->ofproto, rule, &packet_count, &byte_count); + + act_len = sizeof *rule->actions * rule->n_actions; + + start_len = cbdata->msg->size; + append_nxstats_reply(sizeof *nfs + NXM_MAX_LEN + act_len, + cbdata->ofconn, &cbdata->msg); + nfs = ofpbuf_put_uninit(cbdata->msg, sizeof *nfs); + nfs->table_id = 0; + nfs->pad = 0; + calc_flow_duration(rule->created, &nfs->duration_sec, &nfs->duration_nsec); + nfs->cookie = rule->flow_cookie; + nfs->priority = htons(rule->cr.priority); + nfs->idle_timeout = htons(rule->idle_timeout); + nfs->hard_timeout = htons(rule->hard_timeout); + nfs->match_len = htons(nx_put_match(cbdata->msg, &rule->cr)); + memset(nfs->pad2, 0, sizeof nfs->pad2); + nfs->packet_count = htonll(packet_count); + nfs->byte_count = htonll(byte_count); + if (rule->n_actions > 0) { + ofpbuf_put(cbdata->msg, rule->actions, act_len); + } + nfs->length = htons(cbdata->msg->size - start_len); +} + +static int +handle_nxst_flow(struct ofconn *ofconn, struct ofpbuf *b) +{ + struct nx_flow_stats_request *nfsr; + struct flow_stats_cbdata cbdata; + struct cls_rule target; + int error; + + /* Dissect the message. */ + 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, &target); + if (error) { + return error; + } + + COVERAGE_INC(ofproto_flows_req); + cbdata.ofconn = ofconn; + cbdata.out_port = nfsr->out_port; + cbdata.msg = start_nxstats_reply(&nfsr->nsm, 1024); + classifier_for_each_match(&ofconn->ofproto->cls, &target, + table_id_to_include(nfsr->table_id), + nx_flow_stats_cb, &cbdata); + queue_tx(cbdata.msg, ofconn, ofconn->reply_counter); + return 0; +} + struct flow_stats_ds_cbdata { struct ofproto *ofproto; struct ds *results; @@ -3336,7 +3457,7 @@ flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_) query_stats(cbdata->ofproto, rule, &packet_count, &byte_count); flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, - cbdata->ofproto->tun_id_from_cookie, &match); + NXFF_OPENFLOW10, &match); ds_put_format(results, "duration=%llds, ", (time_msec() - rule->created) / 1000); @@ -3367,14 +3488,14 @@ ofproto_get_all_flows(struct ofproto *p, struct ds *results) cbdata.ofproto = p; cbdata.results = results; - cls_rule_from_match(&match, 0, false, 0, &target); + cls_rule_from_match(&match, 0, NXFF_OPENFLOW10, 0, &target); classifier_for_each_match(&p->cls, &target, CLS_INC_ALL, flow_stats_ds_cb, &cbdata); } struct aggregate_stats_cbdata { struct ofproto *ofproto; - uint16_t out_port; + ovs_be16 out_port; uint64_t packet_count; uint64_t byte_count; uint32_t n_flows; @@ -3398,42 +3519,84 @@ aggregate_stats_cb(struct cls_rule *rule_, void *cbdata_) cbdata->n_flows++; } +static void +query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target, + ovs_be16 out_port, uint8_t table_id, + struct ofp_aggregate_stats_reply *oasr) +{ + struct aggregate_stats_cbdata cbdata; + + COVERAGE_INC(ofproto_agg_request); + cbdata.ofproto = ofproto; + cbdata.out_port = out_port; + cbdata.packet_count = 0; + cbdata.byte_count = 0; + cbdata.n_flows = 0; + classifier_for_each_match(&ofproto->cls, target, + table_id_to_include(table_id), + aggregate_stats_cb, &cbdata); + + oasr->flow_count = htonl(cbdata.n_flows); + oasr->packet_count = htonll(cbdata.packet_count); + oasr->byte_count = htonll(cbdata.byte_count); + memset(oasr->pad, 0, sizeof oasr->pad); +} + static int -handle_aggregate_stats_request(struct ofproto *p, struct ofconn *ofconn, +handle_aggregate_stats_request(struct ofconn *ofconn, const struct ofp_stats_request *osr, size_t arg_size) { - struct ofp_aggregate_stats_request *asr; + struct ofp_aggregate_stats_request *request; struct ofp_aggregate_stats_reply *reply; - struct aggregate_stats_cbdata cbdata; struct cls_rule target; struct ofpbuf *msg; - if (arg_size != sizeof *asr) { + if (arg_size != sizeof *request) { return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); } - asr = (struct ofp_aggregate_stats_request *) osr->body; + request = (struct ofp_aggregate_stats_request *) osr->body; - COVERAGE_INC(ofproto_agg_request); - cbdata.ofproto = p; - cbdata.out_port = asr->out_port; - cbdata.packet_count = 0; - cbdata.byte_count = 0; - cbdata.n_flows = 0; - cls_rule_from_match(&asr->match, 0, false, 0, &target); - classifier_for_each_match(&p->cls, &target, - table_id_to_include(asr->table_id), - aggregate_stats_cb, &cbdata); + cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0, &target); - msg = start_stats_reply(osr, sizeof *reply); - reply = append_stats_reply(sizeof *reply, ofconn, &msg); - reply->flow_count = htonl(cbdata.n_flows); - reply->packet_count = htonll(cbdata.packet_count); - reply->byte_count = htonll(cbdata.byte_count); + msg = start_ofp_stats_reply(osr, sizeof *reply); + reply = append_ofp_stats_reply(sizeof *reply, ofconn, &msg); + query_aggregate_stats(ofconn->ofproto, &target, request->out_port, + request->table_id, reply); queue_tx(msg, ofconn, ofconn->reply_counter); return 0; } +static int +handle_nxst_aggregate(struct ofconn *ofconn, struct ofpbuf *b) +{ + struct nx_aggregate_stats_request *request; + struct ofp_aggregate_stats_reply *reply; + struct cls_rule target; + struct ofpbuf *buf; + int error; + + /* Dissect the message. */ + request = ofpbuf_try_pull(b, sizeof *request); + if (!request) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = nx_pull_match(b, ntohs(request->match_len), 0, &target); + if (error) { + return error; + } + + /* Reply. */ + COVERAGE_INC(ofproto_flows_req); + buf = start_nxstats_reply(&request->nsm, sizeof *reply); + reply = ofpbuf_put_uninit(buf, sizeof *reply); + query_aggregate_stats(ofconn->ofproto, &target, request->out_port, + request->table_id, reply); + queue_tx(buf, ofconn, ofconn->reply_counter); + + return 0; +} + struct queue_stats_cbdata { struct ofconn *ofconn; struct ofport *ofport; @@ -3446,7 +3609,7 @@ put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id, { struct ofp_queue_stats *reply; - reply = append_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg); + reply = append_ofp_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg); reply->port_no = htons(cbdata->ofport->opp.port_no); memset(reply->pad, 0, sizeof reply->pad); reply->queue_id = htonl(queue_id); @@ -3483,10 +3646,11 @@ handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id, } static int -handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn, +handle_queue_stats_request(struct ofconn *ofconn, const struct ofp_stats_request *osr, size_t arg_size) { + struct ofproto *ofproto = ofconn->ofproto; struct ofp_queue_stats_request *qsr; struct queue_stats_cbdata cbdata; struct ofport *port; @@ -3501,7 +3665,7 @@ handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn, COVERAGE_INC(ofproto_queue_req); cbdata.ofconn = ofconn; - cbdata.msg = start_stats_reply(osr, 128); + cbdata.msg = start_ofp_stats_reply(osr, 128); port_no = ntohs(qsr->port_no); queue_id = ntohl(qsr->queue_id); @@ -3524,8 +3688,45 @@ handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn, } static int -handle_stats_request(struct ofproto *p, struct ofconn *ofconn, - struct ofp_header *oh) +handle_vendor_stats_request(struct ofconn *ofconn, + struct ofp_stats_request *osr, size_t arg_size) +{ + struct nicira_stats_msg *nsm; + struct ofpbuf b; + ovs_be32 vendor; + + if (arg_size < 4) { + VLOG_WARN_RL(&rl, "truncated vendor stats request body"); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + memcpy(&vendor, osr->body, sizeof vendor); + if (vendor != htonl(NX_VENDOR_ID)) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + } + + if (ntohs(osr->header.length) < sizeof(struct nicira_stats_msg)) { + VLOG_WARN_RL(&rl, "truncated Nicira stats request"); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + nsm = (struct nicira_stats_msg *) osr; + b.data = nsm; + b.size = ntohs(nsm->header.length); + switch (ntohl(nsm->subtype)) { + case NXST_FLOW: + return handle_nxst_flow(ofconn, &b); + + case NXST_AGGREGATE: + return handle_nxst_aggregate(ofconn, &b); + + default: + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); + } +} + +static int +handle_stats_request(struct ofconn *ofconn, struct ofp_header *oh) { struct ofp_stats_request *osr; size_t arg_size; @@ -3540,25 +3741,25 @@ handle_stats_request(struct ofproto *p, struct ofconn *ofconn, switch (ntohs(osr->type)) { case OFPST_DESC: - return handle_desc_stats_request(p, ofconn, osr); + return handle_desc_stats_request(ofconn, osr); case OFPST_FLOW: - return handle_flow_stats_request(p, ofconn, osr, arg_size); + return handle_flow_stats_request(ofconn, osr, arg_size); case OFPST_AGGREGATE: - return handle_aggregate_stats_request(p, ofconn, osr, arg_size); + return handle_aggregate_stats_request(ofconn, osr, arg_size); case OFPST_TABLE: - return handle_table_stats_request(p, ofconn, osr); + return handle_table_stats_request(ofconn, osr); case OFPST_PORT: - return handle_port_stats_request(p, ofconn, osr, arg_size); + return handle_port_stats_request(ofconn, osr, arg_size); case OFPST_QUEUE: - return handle_queue_stats_request(p, ofconn, osr, arg_size); + return handle_queue_stats_request(ofconn, osr, arg_size); case OFPST_VENDOR: - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + return handle_vendor_stats_request(ofconn, osr, arg_size); default: return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT); @@ -3597,46 +3798,50 @@ update_stats(struct ofproto *ofproto, struct rule *rule, } } +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. * * Adds the flow specified by 'ofm', which is followed by 'n_actions' - * ofp_actions, to 'p''s flow table. Returns 0 on success or an OpenFlow error - * code as encoded by ofp_mkerr() on failure. + * ofp_actions, to ofconn->ofproto's flow table. Returns 0 on success or an + * OpenFlow error code as encoded by ofp_mkerr() on failure. * * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id, * if any. */ static int -add_flow(struct ofproto *p, struct ofconn *ofconn, - const struct ofp_flow_mod *ofm, size_t n_actions) +add_flow(struct ofconn *ofconn, struct flow_mod *fm) { + struct ofproto *p = ofconn->ofproto; struct ofpbuf *packet; struct rule *rule; uint16_t in_port; int error; - if (ofm->flags & htons(OFPFF_CHECK_OVERLAP)) { - struct flow flow; - uint32_t wildcards; - - flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie, - &flow, &wildcards); - if (classifier_rule_overlaps(&p->cls, &flow, wildcards, - ntohs(ofm->priority))) { - return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP); - } + if (fm->flags & OFPFF_CHECK_OVERLAP + && classifier_rule_overlaps(&p->cls, &fm->cr)) { + return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP); } - rule = rule_create(p, NULL, (const union ofp_action *) ofm->actions, - n_actions, ntohs(ofm->idle_timeout), - ntohs(ofm->hard_timeout), ofm->cookie, - ofm->flags & htons(OFPFF_SEND_FLOW_REM)); - cls_rule_from_match(&ofm->match, ntohs(ofm->priority), - p->tun_id_from_cookie, ofm->cookie, &rule->cr); + rule = rule_create(p, NULL, fm->actions, fm->n_actions, + fm->idle_timeout, fm->hard_timeout, fm->cookie, + fm->flags & OFPFF_SEND_FLOW_REM); + rule->cr = fm->cr; error = 0; - if (ofm->buffer_id != htonl(UINT32_MAX)) { - error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id), + if (fm->buffer_id != UINT32_MAX) { + error = pktbuf_retrieve(ofconn->pktbuf, fm->buffer_id, &packet, &in_port); } else { packet = NULL; @@ -3648,39 +3853,31 @@ add_flow(struct ofproto *p, struct ofconn *ofconn, } static struct rule * -find_flow_strict(struct ofproto *p, const struct ofp_flow_mod *ofm) +find_flow_strict(struct ofproto *p, const struct flow_mod *fm) { - uint32_t wildcards; - struct flow flow; - - flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie, - &flow, &wildcards); - return rule_from_cls_rule(classifier_find_rule_exactly( - &p->cls, &flow, wildcards, - ntohs(ofm->priority))); + return rule_from_cls_rule(classifier_find_rule_exactly(&p->cls, &fm->cr)); } static int -send_buffered_packet(struct ofproto *ofproto, struct ofconn *ofconn, - struct rule *rule, const struct ofp_flow_mod *ofm) +send_buffered_packet(struct ofconn *ofconn, + struct rule *rule, uint32_t buffer_id) { struct ofpbuf *packet; uint16_t in_port; struct flow flow; int error; - if (ofm->buffer_id == htonl(UINT32_MAX)) { + if (buffer_id == UINT32_MAX) { return 0; } - error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id), - &packet, &in_port); + error = pktbuf_retrieve(ofconn->pktbuf, buffer_id, &packet, &in_port); if (error) { return error; } flow_extract(packet, 0, in_port, &flow); - rule_execute(ofproto, rule, packet, &flow); + rule_execute(ofconn->ofproto, rule, packet, &flow); return 0; } @@ -3689,13 +3886,12 @@ send_buffered_packet(struct ofproto *ofproto, struct ofconn *ofconn, struct modify_flows_cbdata { struct ofproto *ofproto; - const struct ofp_flow_mod *ofm; - size_t n_actions; + const struct flow_mod *fm; struct rule *match; }; -static int modify_flow(struct ofproto *, const struct ofp_flow_mod *, - size_t n_actions, struct rule *); +static int modify_flow(struct ofproto *, const struct flow_mod *, + struct rule *); static void modify_flows_cb(struct cls_rule *, void *cbdata_); /* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code as @@ -3704,30 +3900,24 @@ static void modify_flows_cb(struct cls_rule *, void *cbdata_); * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id, * if any. */ static int -modify_flows_loose(struct ofproto *p, struct ofconn *ofconn, - const struct ofp_flow_mod *ofm, size_t n_actions) +modify_flows_loose(struct ofconn *ofconn, struct flow_mod *fm) { struct modify_flows_cbdata cbdata; - struct cls_rule target; - cbdata.ofproto = p; - cbdata.ofm = ofm; - cbdata.n_actions = n_actions; + cbdata.ofproto = ofconn->ofproto; + cbdata.fm = fm; cbdata.match = NULL; - cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie, - &target); - - classifier_for_each_match(&p->cls, &target, CLS_INC_ALL, + classifier_for_each_match(&ofconn->ofproto->cls, &fm->cr, CLS_INC_ALL, modify_flows_cb, &cbdata); if (cbdata.match) { /* This credits the packet to whichever flow happened to happened to * match last. That's weird. Maybe we should do a lookup for the * flow that actually matches the packet? Who knows. */ - send_buffered_packet(p, ofconn, cbdata.match, ofm); + send_buffered_packet(ofconn, cbdata.match, fm->buffer_id); return 0; } else { - return add_flow(p, ofconn, ofm, n_actions); + return add_flow(ofconn, fm); } } @@ -3737,15 +3927,15 @@ modify_flows_loose(struct ofproto *p, struct ofconn *ofconn, * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id, * if any. */ static int -modify_flow_strict(struct ofproto *p, struct ofconn *ofconn, - struct ofp_flow_mod *ofm, size_t n_actions) +modify_flow_strict(struct ofconn *ofconn, struct flow_mod *fm) { - struct rule *rule = find_flow_strict(p, ofm); + struct ofproto *p = ofconn->ofproto; + struct rule *rule = find_flow_strict(p, fm); if (rule && !rule_is_hidden(rule)) { - modify_flow(p, ofm, n_actions, rule); - return send_buffered_packet(p, ofconn, rule, ofm); + modify_flow(p, fm, rule); + return send_buffered_packet(ofconn, rule, fm->buffer_id); } else { - return add_flow(p, ofconn, ofm, n_actions); + return add_flow(ofconn, fm); } } @@ -3758,7 +3948,7 @@ modify_flows_cb(struct cls_rule *rule_, void *cbdata_) if (!rule_is_hidden(rule)) { cbdata->match = rule; - modify_flow(cbdata->ofproto, cbdata->ofm, cbdata->n_actions, rule); + modify_flow(cbdata->ofproto, cbdata->fm, rule); } } @@ -3767,24 +3957,23 @@ modify_flows_cb(struct cls_rule *rule_, void *cbdata_) * the rule's actions to match those in 'ofm' (which is followed by 'n_actions' * ofp_action[] structures). */ static int -modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm, - size_t n_actions, struct rule *rule) +modify_flow(struct ofproto *p, const struct flow_mod *fm, struct rule *rule) { - size_t actions_len = n_actions * sizeof *rule->actions; + size_t actions_len = fm->n_actions * sizeof *rule->actions; - rule->flow_cookie = ofm->cookie; + rule->flow_cookie = fm->cookie; /* If the actions are the same, do nothing. */ - if (n_actions == rule->n_actions - && (!n_actions || !memcmp(ofm->actions, rule->actions, actions_len))) - { + if (fm->n_actions == rule->n_actions + && (!fm->n_actions + || !memcmp(fm->actions, rule->actions, actions_len))) { return 0; } /* Replace actions. */ free(rule->actions); - rule->actions = n_actions ? xmemdup(ofm->actions, actions_len) : NULL; - rule->n_actions = n_actions; + rule->actions = fm->n_actions ? xmemdup(fm->actions, actions_len) : NULL; + rule->n_actions = fm->n_actions; /* Make sure that the datapath gets updated properly. */ if (rule->cr.wc.wildcards) { @@ -3801,36 +3990,32 @@ modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm, struct delete_flows_cbdata { struct ofproto *ofproto; - uint16_t out_port; + ovs_be16 out_port; }; static void delete_flows_cb(struct cls_rule *, void *cbdata_); -static void delete_flow(struct ofproto *, struct rule *, uint16_t out_port); +static void delete_flow(struct ofproto *, struct rule *, ovs_be16 out_port); /* Implements OFPFC_DELETE. */ static void -delete_flows_loose(struct ofproto *p, const struct ofp_flow_mod *ofm) +delete_flows_loose(struct ofproto *p, const struct flow_mod *fm) { struct delete_flows_cbdata cbdata; - struct cls_rule target; cbdata.ofproto = p; - cbdata.out_port = ofm->out_port; - - cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie, - &target); + cbdata.out_port = htons(fm->out_port); - classifier_for_each_match(&p->cls, &target, CLS_INC_ALL, + classifier_for_each_match(&p->cls, &fm->cr, CLS_INC_ALL, delete_flows_cb, &cbdata); } /* Implements OFPFC_DELETE_STRICT. */ static void -delete_flow_strict(struct ofproto *p, struct ofp_flow_mod *ofm) +delete_flow_strict(struct ofproto *p, struct flow_mod *fm) { - struct rule *rule = find_flow_strict(p, ofm); + struct rule *rule = find_flow_strict(p, fm); if (rule) { - delete_flow(p, rule, ofm->out_port); + delete_flow(p, rule, htons(fm->out_port)); } } @@ -3853,7 +4038,7 @@ delete_flows_cb(struct cls_rule *rule_, void *cbdata_) * 'out_port' is htons(OFPP_NONE) or if 'rule' actually outputs to the * specified 'out_port'. */ static void -delete_flow(struct ofproto *p, struct rule *rule, uint16_t out_port) +delete_flow(struct ofproto *p, struct rule *rule, ovs_be16 out_port) { if (rule_is_hidden(rule)) { return; @@ -3863,37 +4048,81 @@ delete_flow(struct ofproto *p, struct rule *rule, uint16_t out_port) return; } - send_flow_removed(p, rule, time_msec(), OFPRR_DELETE); + send_flow_removed(p, rule, OFPRR_DELETE); rule_remove(p, rule); } static int -handle_flow_mod(struct ofproto *p, struct ofconn *ofconn, - struct ofp_flow_mod *ofm) +flow_mod_core(struct ofconn *ofconn, struct flow_mod *fm) { - struct ofp_match orig_match; - size_t n_actions; + struct ofproto *p = ofconn->ofproto; int error; - error = reject_slave_controller(ofconn, &ofm->header); + error = reject_slave_controller(ofconn, "flow_mod"); if (error) { return error; } - error = check_ofp_message_array(&ofm->header, OFPT_FLOW_MOD, sizeof *ofm, - sizeof *ofm->actions, &n_actions); + + error = validate_actions(fm->actions, fm->n_actions, + &fm->cr.flow, p->max_ports); if (error) { return error; } /* We do not support the emergency flow cache. It will hopefully * get dropped from OpenFlow in the near future. */ - if (ofm->flags & htons(OFPFF_EMERG)) { + 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); } - /* Normalize ofp->match. If normalization actually changes anything, then + switch (fm->command) { + case OFPFC_ADD: + return add_flow(ofconn, fm); + + case OFPFC_MODIFY: + return modify_flows_loose(ofconn, fm); + + case OFPFC_MODIFY_STRICT: + return modify_flow_strict(ofconn, fm); + + case OFPFC_DELETE: + delete_flows_loose(p, fm); + return 0; + + case OFPFC_DELETE_STRICT: + delete_flow_strict(p, fm); + return 0; + + default: + return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND); + } +} + +static int +handle_ofpt_flow_mod(struct ofconn *ofconn, 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 = 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; @@ -3912,41 +4141,62 @@ handle_flow_mod(struct ofproto *p, struct ofconn *ofconn, } } - if (!ofm->match.wildcards) { - ofm->priority = htons(UINT16_MAX); - } + /* Translate the message. */ + 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); - error = validate_actions((const union ofp_action *) ofm->actions, - n_actions, p->max_ports); - if (error) { - return error; - } - - switch (ntohs(ofm->command)) { - case OFPFC_ADD: - return add_flow(p, ofconn, ofm, n_actions); + /* Execute the command. */ + return flow_mod_core(ofconn, &fm); +} - case OFPFC_MODIFY: - return modify_flows_loose(p, ofconn, ofm, n_actions); +static int +handle_nxt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh) +{ + struct nx_flow_mod *nfm; + struct flow_mod fm; + struct ofpbuf b; + int error; - case OFPFC_MODIFY_STRICT: - return modify_flow_strict(p, ofconn, ofm, n_actions); + b.data = oh; + b.size = ntohs(oh->length); - case OFPFC_DELETE: - delete_flows_loose(p, ofm); - return 0; + /* 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; + } - case OFPFC_DELETE_STRICT: - delete_flow_strict(p, ofm); - return 0; + /* 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); - default: - return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND); - } + /* Execute the command. */ + return flow_mod_core(ofconn, &fm); } static int -handle_tun_id_from_cookie(struct ofproto *p, struct nxt_tun_id_cookie *msg) +handle_tun_id_from_cookie(struct ofconn *ofconn, struct nxt_tun_id_cookie *msg) { int error; @@ -3955,13 +4205,12 @@ handle_tun_id_from_cookie(struct ofproto *p, struct nxt_tun_id_cookie *msg) return error; } - p->tun_id_from_cookie = !!msg->set; + ofconn->flow_format = msg->set ? NXFF_TUN_ID_FROM_COOKIE : NXFF_OPENFLOW10; return 0; } static int -handle_role_request(struct ofproto *ofproto, - struct ofconn *ofconn, struct nicira_header *msg) +handle_role_request(struct ofconn *ofconn, struct nicira_header *msg) { struct nx_role_request *nrr; struct nx_role_request *reply; @@ -3993,7 +4242,7 @@ handle_role_request(struct ofproto *ofproto, if (role == NX_ROLE_MASTER) { struct ofconn *other; - HMAP_FOR_EACH (other, hmap_node, &ofproto->controllers) { + HMAP_FOR_EACH (other, hmap_node, &ofconn->ofproto->controllers) { if (other->role == NX_ROLE_MASTER) { other->role = NX_ROLE_SLAVE; } @@ -4001,10 +4250,8 @@ handle_role_request(struct ofproto *ofproto, } ofconn->role = role; - reply = make_openflow_xid(sizeof *reply, OFPT_VENDOR, msg->header.xid, - &buf); - reply->nxh.vendor = htonl(NX_VENDOR_ID); - reply->nxh.subtype = htonl(NXT_ROLE_REPLY); + reply = make_nxmsg_xid(sizeof *reply, NXT_ROLE_REPLY, msg->header.xid, + &buf); reply->role = htonl(role); queue_tx(buf, ofconn, ofconn->reply_counter); @@ -4012,8 +4259,32 @@ handle_role_request(struct ofproto *ofproto, } static int -handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg) +handle_nxt_set_flow_format(struct ofconn *ofconn, + struct nxt_set_flow_format *msg) { + uint32_t format; + int error; + + error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg); + if (error) { + return error; + } + + format = ntohl(msg->format); + if (format == NXFF_OPENFLOW10 + || format == NXFF_TUN_ID_FROM_COOKIE + || format == NXFF_NXM) { + ofconn->flow_format = format; + return 0; + } else { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); + } +} + +static int +handle_vendor(struct ofconn *ofconn, void *msg) +{ + struct ofproto *p = ofconn->ofproto; struct ofp_vendor_header *ovh = msg; struct nicira_header *nh; @@ -4040,10 +4311,16 @@ handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg) msg); case NXT_TUN_ID_FROM_COOKIE: - return handle_tun_id_from_cookie(p, msg); + return handle_tun_id_from_cookie(ofconn, msg); case NXT_ROLE_REQUEST: - return handle_role_request(p, ofconn, msg); + return handle_role_request(ofconn, msg); + + case NXT_SET_FLOW_FORMAT: + return handle_nxt_set_flow_format(ofconn, msg); + + case NXT_FLOW_MOD: + return handle_nxt_flow_mod(ofconn, &ovh->header); } return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); @@ -4063,8 +4340,7 @@ handle_barrier_request(struct ofconn *ofconn, struct ofp_header *oh) } static void -handle_openflow(struct ofconn *ofconn, struct ofproto *p, - struct ofpbuf *ofp_msg) +handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg) { struct ofp_header *oh = ofp_msg->data; int error; @@ -4080,35 +4356,35 @@ handle_openflow(struct ofconn *ofconn, struct ofproto *p, break; case OFPT_FEATURES_REQUEST: - error = handle_features_request(p, ofconn, oh); + error = handle_features_request(ofconn, oh); break; case OFPT_GET_CONFIG_REQUEST: - error = handle_get_config_request(p, ofconn, oh); + error = handle_get_config_request(ofconn, oh); break; case OFPT_SET_CONFIG: - error = handle_set_config(p, ofconn, ofp_msg->data); + error = handle_set_config(ofconn, ofp_msg->data); break; case OFPT_PACKET_OUT: - error = handle_packet_out(p, ofconn, ofp_msg->data); + error = handle_packet_out(ofconn, ofp_msg->data); break; case OFPT_PORT_MOD: - error = handle_port_mod(p, ofconn, oh); + error = handle_port_mod(ofconn, oh); break; case OFPT_FLOW_MOD: - error = handle_flow_mod(p, ofconn, ofp_msg->data); + error = handle_ofpt_flow_mod(ofconn, ofp_msg->data); break; case OFPT_STATS_REQUEST: - error = handle_stats_request(p, ofconn, oh); + error = handle_stats_request(ofconn, oh); break; case OFPT_VENDOR: - error = handle_vendor(p, ofconn, ofp_msg->data); + error = handle_vendor(ofconn, ofp_msg->data); break; case OFPT_BARRIER_REQUEST: @@ -4297,13 +4573,15 @@ ofproto_update_used(struct ofproto *p) for (i = 0; i < n_flows; i++) { struct odp_flow *f = &flows[i]; + struct cls_rule target; struct rule *rule; struct flow flow; odp_flow_key_to_flow(&f->key, &flow); + cls_rule_init_exact(&flow, UINT16_MAX, &target); - rule = rule_from_cls_rule( - classifier_find_rule_exactly(&p->cls, &flow, 0, UINT16_MAX)); + rule = rule_from_cls_rule(classifier_find_rule_exactly(&p->cls, + &target)); if (rule && rule->installed) { update_time(p, rule, &f->stats); @@ -4510,7 +4788,7 @@ rule_expire(struct cls_rule *cls_rule, void *cbdata_) /* Get rid of the rule. */ if (!rule_is_hidden(rule)) { - send_flow_removed(cbdata->ofproto, rule, now, + send_flow_removed(cbdata->ofproto, rule, (now >= hard_expire ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT)); } @@ -4539,7 +4817,8 @@ revalidate_rule(struct ofproto *p, struct rule *rule) COVERAGE_INC(ofproto_revalidate_rule); if (rule->super) { struct rule *super; - super = rule_from_cls_rule(classifier_lookup_wild(&p->cls, flow)); + super = rule_from_cls_rule(classifier_lookup(&p->cls, flow, + CLS_INC_WILD)); if (!super) { rule_remove(p, rule); return false; @@ -4550,8 +4829,7 @@ revalidate_rule(struct ofproto *p, struct rule *rule) rule->super = super; rule->hard_timeout = super->hard_timeout; rule->idle_timeout = super->idle_timeout; - rule->created = super->created; - rule->used = 0; + rule->created = rule->used = super->created; } } @@ -4560,23 +4838,19 @@ revalidate_rule(struct ofproto *p, struct rule *rule) } static struct ofpbuf * -compose_flow_removed(struct ofproto *p, const struct rule *rule, - long long int now, uint8_t reason) +compose_ofp_flow_removed(struct ofconn *ofconn, const struct rule *rule, + uint8_t reason) { struct ofp_flow_removed *ofr; struct ofpbuf *buf; - long long int tdiff = now - rule->created; - uint32_t sec = tdiff / 1000; - uint32_t msec = tdiff - (sec * 1000); ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf); - flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, p->tun_id_from_cookie, + flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, ofconn->flow_format, &ofr->match); ofr->cookie = rule->flow_cookie; ofr->priority = htons(rule->cr.priority); ofr->reason = reason; - ofr->duration_sec = htonl(sec); - ofr->duration_nsec = htonl(msec * 1000000); + calc_flow_duration(rule->created, &ofr->duration_sec, &ofr->duration_nsec); ofr->idle_timeout = htons(rule->idle_timeout); ofr->packet_count = htonll(rule->packet_count); ofr->byte_count = htonll(rule->byte_count); @@ -4584,38 +4858,56 @@ compose_flow_removed(struct ofproto *p, const struct rule *rule, return buf; } +static struct ofpbuf * +compose_nx_flow_removed(const struct rule *rule, uint8_t reason) +{ + struct nx_flow_removed *nfr; + struct ofpbuf *buf; + int match_len; + + nfr = make_nxmsg(sizeof *nfr, NXT_FLOW_REMOVED, &buf); + + match_len = nx_put_match(buf, &rule->cr); + + nfr->cookie = rule->flow_cookie; + nfr->priority = htons(rule->cr.priority); + nfr->reason = reason; + calc_flow_duration(rule->created, &nfr->duration_sec, &nfr->duration_nsec); + nfr->idle_timeout = htons(rule->idle_timeout); + nfr->match_len = htons(match_len); + nfr->packet_count = htonll(rule->packet_count); + nfr->byte_count = htonll(rule->byte_count); + + return buf; +} + static void -send_flow_removed(struct ofproto *p, struct rule *rule, - long long int now, uint8_t reason) +send_flow_removed(struct ofproto *p, struct rule *rule, uint8_t reason) { struct ofconn *ofconn; - struct ofconn *prev; - struct ofpbuf *buf = NULL; if (!rule->send_flow_removed) { return; } - /* We limit the maximum number of queued flow expirations it by accounting - * them under the counter for replies. That works because preventing - * OpenFlow requests from being processed also prevents new flows from - * being added (and expiring). (It also prevents processing OpenFlow - * requests that would not add new flows, so it is imperfect.) */ - - prev = NULL; LIST_FOR_EACH (ofconn, node, &p->all_conns) { - if (rconn_is_connected(ofconn->rconn) - && ofconn_receives_async_msgs(ofconn)) { - if (prev) { - queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter); - } else { - buf = compose_flow_removed(p, rule, now, reason); - } - prev = ofconn; + struct ofpbuf *msg; + + if (!rconn_is_connected(ofconn->rconn) + || !ofconn_receives_async_msgs(ofconn)) { + continue; } - } - if (prev) { - queue_tx(buf, prev, prev->reply_counter); + + msg = (ofconn->flow_format == NXFF_NXM + ? compose_nx_flow_removed(rule, reason) + : compose_ofp_flow_removed(ofconn, rule, reason)); + + /* Account flow expirations under ofconn->reply_counter, the counter + * for replies to OpenFlow requests. That works because preventing + * OpenFlow requests from being processed also prevents new flows from + * being added (and expiring). (It also prevents processing OpenFlow + * requests that would not add new flows, so it is imperfect.) */ + queue_tx(msg, ofconn, ofconn->reply_counter); } }