From ca069229880ba47ba100b62ed34c0c226355b0c6 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Fri, 13 Nov 2009 11:08:32 -0800 Subject: [PATCH] ofproto: Add selective Flow Removed messages and flow deletes (OpenFlow 0.9) In OpenFlow 0.9, flow "expiration" messages are sent when flows are explicitly removed by a delete action. As such, the message is renamed from Flow Expired to Flow Removed. This commit adds that support as well as supporting the ability to choose sending these messages on a per flow basis. NOTE: OVS at this point is not wire-compatible with OpenFlow 0.9 until the final commit in this OpenFlow 0.9 set. --- include/openflow/openflow.h | 25 ++++++----- lib/learning-switch.c | 5 +-- lib/ofp-print.c | 44 +++++++++---------- ofproto/ofproto.c | 85 ++++++++++++++++++++----------------- utilities/ovs-ofctl.8.in | 7 +-- utilities/ovs-ofctl.c | 2 - 6 files changed, 82 insertions(+), 86 deletions(-) diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h index a9199c71..511cdb71 100644 --- a/include/openflow/openflow.h +++ b/include/openflow/openflow.h @@ -95,7 +95,7 @@ enum ofp_type { /* Asynchronous messages. */ OFPT_PACKET_IN, /* Async message */ - OFPT_FLOW_EXPIRED, /* Async message */ + OFPT_FLOW_REMOVED, /* Async message */ OFPT_PORT_STATUS, /* Async message */ /* Controller command messages. */ @@ -128,9 +128,6 @@ struct ofp_hello { #define OFP_DEFAULT_MISS_SEND_LEN 128 enum ofp_config_flags { - /* Tells datapath to notify the controller of expired flow entries. */ - OFPC_SEND_FLOW_EXP = 1 << 0, - /* Handling of IP fragments. */ OFPC_FRAG_NORMAL = 0 << 1, /* No special handling for fragments. */ OFPC_FRAG_DROP = 1 << 1, /* Drop fragments. */ @@ -560,27 +557,29 @@ struct ofp_flow_mod { }; OFP_ASSERT(sizeof(struct ofp_flow_mod) == 68); -/* Why did this flow expire? */ -enum ofp_flow_expired_reason { - OFPER_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ - OFPER_HARD_TIMEOUT /* Time exceeded hard_timeout. */ +/* Why was this flow removed? */ +enum ofp_flow_removed_reason { + OFPRR_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ + OFPRR_HARD_TIMEOUT, /* Time exceeded hard_timeout. */ + OFPRR_DELETE /* Evicted by a DELETE flow mod. */ }; -/* Flow expiration (datapath -> controller). */ -struct ofp_flow_expired { +/* Flow removed (datapath -> controller). */ +struct ofp_flow_removed { struct ofp_header header; struct ofp_match match; /* Description of fields. */ uint16_t priority; /* Priority level of flow entry. */ - uint8_t reason; /* One of OFPER_*. */ + uint8_t reason; /* One of OFPRR_*. */ uint8_t pad[1]; /* Align to 32-bits. */ uint32_t duration; /* Time flow was alive in seconds. */ - uint8_t pad2[4]; /* Align to 64-bits. */ + uint16_t idle_timeout; /* Idle timeout from original flow mod. */ + uint8_t pad2[6]; /* Align to 64-bits. */ uint64_t packet_count; uint64_t byte_count; }; -OFP_ASSERT(sizeof(struct ofp_flow_expired) == 76); +OFP_ASSERT(sizeof(struct ofp_flow_removed) == 80); /* Values for 'type' in ofp_error_message. These values are immutable: they * will not change in future versions of the protocol (although new values may diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 074a056e..260f5d76 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -283,8 +283,8 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, process_stats_reply }, { - OFPT_FLOW_EXPIRED, - sizeof(struct ofp_flow_expired), + OFPT_FLOW_REMOVED, + sizeof(struct ofp_flow_removed), NULL }, }; @@ -337,7 +337,6 @@ send_features_request(struct lswitch *sw, struct rconn *rconn) /* Send OFPT_SET_CONFIG. */ osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &b); - osc->flags = htons(OFPC_SEND_FLOW_EXP); osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); queue_tx(sw, rconn, b); diff --git a/lib/ofp-print.c b/lib/ofp-print.c index ced57c4f..926531ec 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -582,10 +582,6 @@ ofp_print_switch_config(struct ds *string, const void *oh, uint16_t flags; flags = ntohs(osc->flags); - if (flags & OFPC_SEND_FLOW_EXP) { - flags &= ~OFPC_SEND_FLOW_EXP; - ds_put_format(string, " (sending flow expirations)"); - } if (flags) { ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags); } @@ -739,41 +735,45 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len, default: ds_put_format(string, " cmd:%d ", ntohs(ofm->command)); } - ds_put_format(string, "idle:%d hard:%d pri:%d buf:%#x", + ds_put_format(string, "idle:%d hard:%d pri:%d buf:%#x flags:%"PRIx16" ", ntohs(ofm->idle_timeout), ntohs(ofm->hard_timeout), ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1, - ntohl(ofm->buffer_id)); + ntohl(ofm->buffer_id), ntohs(ofm->flags)); ofp_print_actions(string, ofm->actions, len - offsetof(struct ofp_flow_mod, actions)); ds_put_char(string, '\n'); } -/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'string' +/* Pretty-print the OFPT_FLOW_REMOVED packet of 'len' bytes at 'oh' to 'string' * at the given 'verbosity' level. */ static void -ofp_print_flow_expired(struct ds *string, const void *oh, +ofp_print_flow_removed(struct ds *string, const void *oh, size_t len OVS_UNUSED, int verbosity) { - const struct ofp_flow_expired *ofe = oh; + const struct ofp_flow_removed *ofr = oh; - ofp_print_match(string, &ofe->match, verbosity); + ofp_print_match(string, &ofr->match, verbosity); ds_put_cstr(string, " reason="); - switch (ofe->reason) { - case OFPER_IDLE_TIMEOUT: + switch (ofr->reason) { + case OFPRR_IDLE_TIMEOUT: ds_put_cstr(string, "idle"); break; - case OFPER_HARD_TIMEOUT: + case OFPRR_HARD_TIMEOUT: ds_put_cstr(string, "hard"); break; + case OFPRR_DELETE: + ds_put_cstr(string, "delete"); + break; default: - ds_put_format(string, "**%"PRIu8"**", ofe->reason); + ds_put_format(string, "**%"PRIu8"**", ofr->reason); break; } ds_put_format(string, - " pri%"PRIu16" secs%"PRIu32" pkts%"PRIu64" bytes%"PRIu64"\n", - ofe->match.wildcards ? ntohs(ofe->priority) : (uint16_t)-1, - ntohl(ofe->duration), ntohll(ofe->packet_count), - ntohll(ofe->byte_count)); + " pri%"PRIu16" secs%"PRIu32" idle%"PRIu16" pkts%"PRIu64 + " bytes%"PRIu64"\n", + ofr->match.wildcards ? ntohs(ofr->priority) : (uint16_t)-1, + ntohl(ofr->duration), ntohs(ofr->idle_timeout), + ntohll(ofr->packet_count), ntohll(ofr->byte_count)); } static void @@ -1327,10 +1327,10 @@ static const struct openflow_packet packets[] = { ofp_print_flow_mod, }, { - OFPT_FLOW_EXPIRED, - "flow_expired", - sizeof (struct ofp_flow_expired), - ofp_print_flow_expired, + OFPT_FLOW_REMOVED, + "flow_removed", + sizeof (struct ofp_flow_removed), + ofp_print_flow_removed, }, { OFPT_PORT_MOD, diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 6bdbc73f..f80439c8 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -85,6 +85,7 @@ struct rule { 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 created; /* Creation time. */ uint64_t packet_count; /* Number of packets received. */ @@ -142,7 +143,8 @@ 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); + uint16_t idle_timeout, uint16_t hard_timeout, + 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 *); @@ -155,12 +157,13 @@ 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); struct ofconn { struct list node; struct rconn *rconn; struct pktbuf *pktbuf; - bool send_flow_exp; int miss_send_len; struct rconn_packet_counter *packet_in_counter; @@ -981,7 +984,8 @@ ofproto_add_flow(struct ofproto *p, { struct rule *rule; rule = rule_create(p, NULL, actions, n_actions, - idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0); + idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, + 0, false); cls_rule_from_flow(&rule->cr, flow, wildcards, priority); rule_insert(p, rule, NULL, 0); } @@ -1331,7 +1335,6 @@ ofconn_create(struct ofproto *p, struct rconn *rconn) list_push_back(&p->all_conns, &ofconn->node); ofconn->rconn = rconn; ofconn->pktbuf = NULL; - ofconn->send_flow_exp = false; ofconn->miss_send_len = 0; ofconn->packet_in_counter = rconn_packet_counter_create (); ofconn->reply_counter = rconn_packet_counter_create (); @@ -1393,12 +1396,14 @@ ofconn_wait(struct ofconn *ofconn) 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) + uint16_t idle_timeout, uint16_t hard_timeout, + bool send_flow_removed) { struct rule *rule = xzalloc(sizeof *rule); rule->idle_timeout = idle_timeout; rule->hard_timeout = hard_timeout; rule->used = rule->created = time_msec(); + rule->send_flow_removed = send_flow_removed; rule->super = super; if (super) { list_push_back(&super->list, &rule->list); @@ -1558,7 +1563,8 @@ rule_create_subrule(struct ofproto *ofproto, struct rule *rule, const flow_t *flow) { struct rule *subrule = rule_create(ofproto, rule, NULL, 0, - rule->idle_timeout, rule->hard_timeout); + rule->idle_timeout, rule->hard_timeout, + false); COVERAGE_INC(ofproto_subrule_create); cls_rule_from_flow(&subrule->cr, flow, 0, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX @@ -1877,9 +1883,6 @@ handle_get_config_request(struct ofproto *p, struct ofconn *ofconn, /* Figure out flags. */ dpif_get_drop_frags(p->dpif, &drop_frags); flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL; - if (ofconn->send_flow_exp) { - flags |= OFPC_SEND_FLOW_EXP; - } /* Send reply. */ osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf); @@ -1903,8 +1906,6 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn, } flags = ntohs(osc->flags); - ofconn->send_flow_exp = (flags & OFPC_SEND_FLOW_EXP) != 0; - if (ofconn == p->controller) { switch (flags & OFPC_FRAG_MASK) { case OFPC_FRAG_NORMAL: @@ -2810,7 +2811,8 @@ add_flow(struct ofproto *p, struct ofconn *ofconn, rule = rule_create(p, NULL, (const union ofp_action *) ofm->actions, n_actions, ntohs(ofm->idle_timeout), - ntohs(ofm->hard_timeout)); + ntohs(ofm->hard_timeout), + ofm->flags & htons(OFPFF_SEND_FLOW_REM)); cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority)); error = 0; @@ -2836,6 +2838,8 @@ modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm, } if (command == OFPFC_DELETE) { + long long int now = time_msec(); + send_flow_removed(p, rule, now, OFPRR_DELETE); rule_remove(p, rule); } else { size_t actions_len = n_actions * sizeof *rule->actions; @@ -3224,25 +3228,38 @@ revalidate_rule(struct ofproto *p, struct rule *rule) } static struct ofpbuf * -compose_flow_exp(const struct rule *rule, long long int now, uint8_t reason) +compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason) { - struct ofp_flow_expired *ofe; + struct ofp_flow_removed *ofr; struct ofpbuf *buf; - ofe = make_openflow(sizeof *ofe, OFPT_FLOW_EXPIRED, &buf); - flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match); - ofe->priority = htons(rule->cr.priority); - ofe->reason = reason; - ofe->duration = htonl((now - rule->created) / 1000); - ofe->packet_count = htonll(rule->packet_count); - ofe->byte_count = htonll(rule->byte_count); + ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf); + flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofr->match); + ofr->priority = htons(rule->cr.priority); + ofr->reason = reason; + ofr->duration = htonl((now - rule->created) / 1000); + ofr->idle_timeout = htons(rule->idle_timeout); + ofr->packet_count = htonll(rule->packet_count); + ofr->byte_count = htonll(rule->byte_count); return buf; } static void -send_flow_exp(struct ofproto *p, struct rule *rule, - long long int now, uint8_t reason) +uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule) +{ + assert(rule->installed); + assert(!rule->cr.wc.wildcards); + + if (rule->super) { + rule_remove(ofproto, rule); + } else { + rule_uninstall(ofproto, rule); + } +} +static void +send_flow_removed(struct ofproto *p, struct rule *rule, + long long int now, uint8_t reason) { struct ofconn *ofconn; struct ofconn *prev; @@ -3256,11 +3273,11 @@ send_flow_exp(struct ofproto *p, struct rule *rule, prev = NULL; LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) { - if (ofconn->send_flow_exp && rconn_is_connected(ofconn->rconn)) { + if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn)) { if (prev) { queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter); } else { - buf = compose_flow_exp(rule, now, reason); + buf = compose_flow_removed(rule, now, reason); } prev = ofconn; } @@ -3270,18 +3287,6 @@ send_flow_exp(struct ofproto *p, struct rule *rule, } } -static void -uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule) -{ - assert(rule->installed); - assert(!rule->cr.wc.wildcards); - - if (rule->super) { - rule_remove(ofproto, rule); - } else { - rule_uninstall(ofproto, rule); - } -} static void expire_rule(struct cls_rule *cls_rule, void *p_) @@ -3324,9 +3329,9 @@ expire_rule(struct cls_rule *cls_rule, void *p_) } if (!rule_is_hidden(rule)) { - send_flow_exp(p, rule, now, - (now >= hard_expire - ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT)); + send_flow_removed(p, rule, now, + (now >= hard_expire + ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT)); } rule_remove(p, rule); } diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index c8909805..978dd1e0 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -136,7 +136,7 @@ switch's tables are removed. See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR. .TP -\fBmonitor \fIswitch\fR [\fImiss-len\fR [\fIsend-exp]] +\fBmonitor \fIswitch\fR [\fImiss-len\fR] Connects to \fIswitch\fR and prints to the console all OpenFlow messages received. Usually, \fIswitch\fR should specify a connection named on \fBovs\-openflowd\fR(8)'s \fB-l\fR or \fB--listen\fR command line @@ -149,11 +149,6 @@ OpenFlow reference implementation does not send these messages to the \fBovs\-ofctl monitor\fR client connection unless a nonzero value is specified on this argument. -If \fIsend-exp\fR is specified as \fB1\fR, \fBovs\-ofctl\fR will also -request to be sent flow expiration messages. If this argument is -omitted, or \fB0\fR is specified, then \fRovs\-ofctl\fR will not request -flow expirations. - This command may be useful for debugging switch or controller implementations. diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 9a3f8c70..9dd80b11 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -966,12 +966,10 @@ do_monitor(int argc OVS_UNUSED, char *argv[]) open_vconn(argv[1], &vconn); if (argc > 2) { int miss_send_len = atoi(argv[2]); - int send_flow_exp = argc > 3 ? atoi(argv[3]) : 0; struct ofp_switch_config *osc; struct ofpbuf *buf; osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf); - osc->flags = htons(send_flow_exp ? OFPC_SEND_FLOW_EXP : 0); osc->miss_send_len = htons(miss_send_len); send_openflow_buffer(vconn, buf); } -- 2.30.2