1 /* Copyright (c) 2015, 2016 Nicira, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include "byte-order.h"
23 #include "openflow/openflow.h"
24 #include "openvswitch/dynamic-string.h"
25 #include "openvswitch/match.h"
26 #include "openvswitch/ofp-actions.h"
27 #include "openvswitch/ofp-msgs.h"
28 #include "openvswitch/ofp-parse.h"
29 #include "openvswitch/ofp-print.h"
30 #include "openvswitch/ofp-util.h"
31 #include "openvswitch/ofpbuf.h"
32 #include "openvswitch/vlog.h"
33 #include "ovn-controller.h"
34 #include "ovn/lib/actions.h"
37 #include "socket-util.h"
39 #include "vswitch-idl.h"
41 VLOG_DEFINE_THIS_MODULE(ofctrl);
43 /* An OpenFlow flow. */
46 struct hmap_node hmap_node;
52 struct ofpact *ofpacts;
56 static uint32_t ovn_flow_hash(const struct ovn_flow *);
57 static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table,
58 const struct ovn_flow *target);
59 static char *ovn_flow_to_string(const struct ovn_flow *);
60 static void ovn_flow_log(const struct ovn_flow *, const char *action);
61 static void ovn_flow_destroy(struct ovn_flow *);
63 static ovs_be32 queue_msg(struct ofpbuf *);
64 static void queue_flow_mod(struct ofputil_flow_mod *);
66 /* OpenFlow connection to the switch. */
67 static struct rconn *swconn;
69 static void queue_group_mod(struct ofputil_group_mod *);
71 /* Last seen sequence number for 'swconn'. When this differs from
72 * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
73 static unsigned int seqno;
75 /* Connection state machine. */
78 STATE(S_TLV_TABLE_REQUESTED) \
79 STATE(S_TLV_TABLE_MOD_SENT) \
80 STATE(S_CLEAR_FLOWS) \
83 #define STATE(NAME) NAME,
89 static enum ofctrl_state state;
91 /* Transaction IDs for messages in flight to the switch. */
92 static ovs_be32 xid, xid2;
94 /* Counter for in-flight OpenFlow messages on 'swconn'. We only send a new
95 * round of flow table modifications to the switch when the counter falls to
96 * zero, to avoid unbounded buffering. */
97 static struct rconn_packet_counter *tx_counter;
99 /* Flow table of "struct ovn_flow"s, that holds the flow table currently
100 * installed in the switch. */
101 static struct hmap installed_flows;
103 /* A reference to the group_table. */
104 static struct group_table *groups;
106 /* MFF_* field ID for our Geneve option. In S_TLV_TABLE_MOD_SENT, this is
107 * the option we requested (we don't know whether we obtained it yet). In
108 * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */
109 static enum mf_field_id mff_ovn_geneve;
111 static void ovn_flow_table_clear(struct hmap *flow_table);
112 static void ovn_flow_table_destroy(struct hmap *flow_table);
114 static void ovn_group_table_clear(struct group_table *group_table,
117 static void ofctrl_recv(const struct ofp_header *, enum ofptype);
122 swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
123 tx_counter = rconn_packet_counter_create();
124 hmap_init(&installed_flows);
127 /* S_NEW, for a new connection.
129 * Sends NXT_TLV_TABLE_REQUEST and transitions to
130 * S_TLV_TABLE_REQUESTED. */
135 struct ofpbuf *buf = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_REQUEST,
136 rconn_get_version(swconn), 0);
137 xid = queue_msg(buf);
138 state = S_TLV_TABLE_REQUESTED;
142 recv_S_NEW(const struct ofp_header *oh OVS_UNUSED,
143 enum ofptype type OVS_UNUSED)
148 /* S_TLV_TABLE_REQUESTED, when NXT_TLV_TABLE_REQUEST has been sent
149 * and we're waiting for a reply.
151 * If we receive an NXT_TLV_TABLE_REPLY:
153 * - If it contains our tunnel metadata option, assign its field ID to
154 * mff_ovn_geneve and transition to S_CLEAR_FLOWS.
156 * - Otherwise, if there is an unused tunnel metadata field ID, send
157 * NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST, and transition to
158 * S_TLV_TABLE_MOD_SENT.
160 * - Otherwise, log an error, disable Geneve, and transition to
163 * If we receive an OFPT_ERROR:
165 * - Log an error, disable Geneve, and transition to S_CLEAR_FLOWS. */
168 run_S_TLV_TABLE_REQUESTED(void)
173 recv_S_TLV_TABLE_REQUESTED(const struct ofp_header *oh, enum ofptype type)
175 if (oh->xid != xid) {
176 ofctrl_recv(oh, type);
177 } else if (type == OFPTYPE_NXT_TLV_TABLE_REPLY) {
178 struct ofputil_tlv_table_reply reply;
179 enum ofperr error = ofputil_decode_tlv_table_reply(oh, &reply);
181 VLOG_ERR("failed to decode TLV table request (%s)",
182 ofperr_to_string(error));
186 const struct ofputil_tlv_map *map;
187 uint64_t md_free = UINT64_MAX;
188 BUILD_ASSERT(TUN_METADATA_NUM_OPTS == 64);
190 LIST_FOR_EACH (map, list_node, &reply.mappings) {
191 if (map->option_class == OVN_GENEVE_CLASS
192 && map->option_type == OVN_GENEVE_TYPE
193 && map->option_len == OVN_GENEVE_LEN) {
194 if (map->index >= TUN_METADATA_NUM_OPTS) {
195 VLOG_ERR("desired Geneve tunnel option 0x%"PRIx16","
196 "%"PRIu8",%"PRIu8" already in use with "
197 "unsupported index %"PRIu16,
198 map->option_class, map->option_type,
199 map->option_len, map->index);
202 mff_ovn_geneve = MFF_TUN_METADATA0 + map->index;
203 state = S_CLEAR_FLOWS;
208 if (map->index < TUN_METADATA_NUM_OPTS) {
209 md_free &= ~(UINT64_C(1) << map->index);
213 VLOG_DBG("OVN Geneve option not found");
215 VLOG_ERR("no Geneve options free for use by OVN");
219 unsigned int index = rightmost_1bit_idx(md_free);
220 mff_ovn_geneve = MFF_TUN_METADATA0 + index;
221 struct ofputil_tlv_map tm;
222 tm.option_class = OVN_GENEVE_CLASS;
223 tm.option_type = OVN_GENEVE_TYPE;
224 tm.option_len = OVN_GENEVE_LEN;
227 struct ofputil_tlv_table_mod ttm;
228 ttm.command = NXTTMC_ADD;
229 ovs_list_init(&ttm.mappings);
230 ovs_list_push_back(&ttm.mappings, &tm.list_node);
232 xid = queue_msg(ofputil_encode_tlv_table_mod(OFP13_VERSION, &ttm));
233 xid2 = queue_msg(ofputil_encode_barrier_request(OFP13_VERSION));
234 state = S_TLV_TABLE_MOD_SENT;
235 } else if (type == OFPTYPE_ERROR) {
236 VLOG_ERR("switch refused to allocate Geneve option (%s)",
237 ofperr_to_string(ofperr_decode_msg(oh, NULL)));
240 char *s = ofp_to_string(oh, ntohs(oh->length), 1);
241 VLOG_ERR("unexpected reply to TLV table request (%s)",
250 state = S_CLEAR_FLOWS;
253 /* S_TLV_TABLE_MOD_SENT, when NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST
254 * have been sent and we're waiting for a reply to one or the other.
256 * If we receive an OFPT_ERROR:
258 * - If the error is NXTTMFC_ALREADY_MAPPED or NXTTMFC_DUP_ENTRY, we
259 * raced with some other controller. Transition to S_NEW.
261 * - Otherwise, log an error, disable Geneve, and transition to
264 * If we receive OFPT_BARRIER_REPLY:
266 * - Set the tunnel metadata field ID to the one that we requested.
267 * Transition to S_CLEAR_FLOWS.
271 run_S_TLV_TABLE_MOD_SENT(void)
276 recv_S_TLV_TABLE_MOD_SENT(const struct ofp_header *oh, enum ofptype type)
278 if (oh->xid != xid && oh->xid != xid2) {
279 ofctrl_recv(oh, type);
280 } else if (oh->xid == xid2 && type == OFPTYPE_BARRIER_REPLY) {
281 state = S_CLEAR_FLOWS;
282 } else if (oh->xid == xid && type == OFPTYPE_ERROR) {
283 enum ofperr error = ofperr_decode_msg(oh, NULL);
284 if (error == OFPERR_NXTTMFC_ALREADY_MAPPED ||
285 error == OFPERR_NXTTMFC_DUP_ENTRY) {
286 VLOG_INFO("raced with another controller adding "
287 "Geneve option (%s); trying again",
288 ofperr_to_string(error));
291 VLOG_ERR("error adding Geneve option (%s)",
292 ofperr_to_string(error));
296 char *s = ofp_to_string(oh, ntohs(oh->length), 1);
297 VLOG_ERR("unexpected reply to Geneve option allocation request (%s)",
305 state = S_CLEAR_FLOWS;
308 /* S_CLEAR_FLOWS, after we've established a Geneve metadata field ID and it's
309 * time to set up some flows.
311 * Sends an OFPT_TABLE_MOD to clear all flows, then transitions to
315 run_S_CLEAR_FLOWS(void)
317 /* Send a flow_mod to delete all flows. */
318 struct ofputil_flow_mod fm = {
319 .match = MATCH_CATCHALL_INITIALIZER,
320 .table_id = OFPTT_ALL,
321 .command = OFPFC_DELETE,
324 VLOG_DBG("clearing all flows");
326 struct ofputil_group_mod gm;
327 memset(&gm, 0, sizeof gm);
328 gm.command = OFPGC11_DELETE;
329 gm.group_id = OFPG_ALL;
330 gm.command_bucket_id = OFPG15_BUCKET_ALL;
331 ovs_list_init(&gm.buckets);
332 queue_group_mod(&gm);
333 ofputil_bucket_list_destroy(&gm.buckets);
335 /* Clear installed_flows, to match the state of the switch. */
336 ovn_flow_table_clear(&installed_flows);
338 /* Clear existing groups, to match the state of the switch. */
340 ovn_group_table_clear(groups, true);
343 state = S_UPDATE_FLOWS;
347 recv_S_CLEAR_FLOWS(const struct ofp_header *oh, enum ofptype type)
349 ofctrl_recv(oh, type);
352 /* S_UPDATE_FLOWS, for maintaining the flow table over time.
354 * Compare the installed flows to the ones we want. Send OFPT_FLOW_MOD as
357 * This is a terminal state. We only transition out of it if the connection
361 run_S_UPDATE_FLOWS(void)
363 /* Nothing to do here.
365 * Being in this state enables ofctrl_put() to work, however. */
369 recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type)
371 ofctrl_recv(oh, type);
374 /* Runs the OpenFlow state machine against 'br_int', which is local to the
375 * hypervisor on which we are running. Attempts to negotiate a Geneve option
376 * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE. If successful,
377 * returns the MFF_* field ID for the option, otherwise returns 0. */
379 ofctrl_run(const struct ovsrec_bridge *br_int)
383 target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
384 if (strcmp(target, rconn_get_target(swconn))) {
385 VLOG_INFO("%s: connecting to switch", target);
386 rconn_connect(swconn, target, target);
390 rconn_disconnect(swconn);
395 if (!rconn_is_connected(swconn)) {
398 if (seqno != rconn_get_connection_seqno(swconn)) {
399 seqno = rconn_get_connection_seqno(swconn);
403 enum ofctrl_state old_state;
407 #define STATE(NAME) case NAME: run_##NAME(); break;
413 } while (state != old_state);
415 for (int i = 0; state == old_state && i < 50; i++) {
416 struct ofpbuf *msg = rconn_recv(swconn);
421 const struct ofp_header *oh = msg->data;
425 error = ofptype_decode(&type, oh);
428 #define STATE(NAME) case NAME: recv_##NAME(oh, type); break;
435 char *s = ofp_to_string(oh, ntohs(oh->length), 1);
436 VLOG_WARN("could not decode OpenFlow message (%s): %s",
437 ofperr_to_string(error), s);
444 return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS
445 ? mff_ovn_geneve : 0);
451 rconn_run_wait(swconn);
452 rconn_recv_wait(swconn);
458 rconn_destroy(swconn);
459 ovn_flow_table_destroy(&installed_flows);
460 rconn_packet_counter_destroy(tx_counter);
464 queue_msg(struct ofpbuf *msg)
466 const struct ofp_header *oh = msg->data;
467 ovs_be32 xid = oh->xid;
468 rconn_send(swconn, msg, tx_counter);
473 log_openflow_rl(struct vlog_rate_limit *rl, enum vlog_level level,
474 const struct ofp_header *oh, const char *title)
476 if (!vlog_should_drop(&this_module, level, rl)) {
477 char *s = ofp_to_string(oh, ntohs(oh->length), 2);
478 vlog(&this_module, level, "%s: %s", title, s);
484 ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
486 if (type == OFPTYPE_ECHO_REQUEST) {
487 queue_msg(make_echo_reply(oh));
488 } else if (type == OFPTYPE_ERROR) {
489 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
490 log_openflow_rl(&rl, VLL_INFO, oh, "OpenFlow error");
491 } else if (type != OFPTYPE_ECHO_REPLY &&
492 type != OFPTYPE_BARRIER_REPLY &&
493 type != OFPTYPE_PACKET_IN &&
494 type != OFPTYPE_PORT_STATUS &&
495 type != OFPTYPE_FLOW_REMOVED) {
496 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
497 log_openflow_rl(&rl, VLL_DBG, oh, "OpenFlow packet ignored");
501 /* Flow table interface to the rest of ovn-controller. */
503 /* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to
504 * the OpenFlow table numbered 'table_id' with the given 'priority'. The
505 * caller retains ownership of 'match' and 'actions'.
507 * This just assembles the desired flow table in memory. Nothing is actually
508 * sent to the switch until a later call to ofctrl_run().
510 * The caller should initialize its own hmap to hold the flows. */
512 ofctrl_add_flow(struct hmap *desired_flows,
513 uint8_t table_id, uint16_t priority,
514 const struct match *match, const struct ofpbuf *actions)
516 struct ovn_flow *f = xmalloc(sizeof *f);
517 f->table_id = table_id;
518 f->priority = priority;
520 f->ofpacts = xmemdup(actions->data, actions->size);
521 f->ofpacts_len = actions->size;
522 f->hmap_node.hash = ovn_flow_hash(f);
524 if (ovn_flow_lookup(desired_flows, f)) {
525 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
526 if (!VLOG_DROP_INFO(&rl)) {
527 char *s = ovn_flow_to_string(f);
528 VLOG_INFO("dropping duplicate flow: %s", s);
536 hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash);
541 /* Returns a hash of the key in 'f'. */
543 ovn_flow_hash(const struct ovn_flow *f)
545 return hash_2words((f->table_id << 16) | f->priority,
546 match_hash(&f->match, 0));
550 /* Finds and returns an ovn_flow in 'flow_table' whose key is identical to
551 * 'target''s key, or NULL if there is none. */
552 static struct ovn_flow *
553 ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target)
557 HMAP_FOR_EACH_WITH_HASH (f, hmap_node, target->hmap_node.hash,
559 if (f->table_id == target->table_id
560 && f->priority == target->priority
561 && match_equal(&f->match, &target->match)) {
569 ovn_flow_to_string(const struct ovn_flow *f)
571 struct ds s = DS_EMPTY_INITIALIZER;
572 ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
573 ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
574 match_format(&f->match, &s, OFP_DEFAULT_PRIORITY);
575 ds_put_cstr(&s, ", actions=");
576 ofpacts_format(f->ofpacts, f->ofpacts_len, &s);
577 return ds_steal_cstr(&s);
581 ovn_flow_log(const struct ovn_flow *f, const char *action)
583 if (VLOG_IS_DBG_ENABLED()) {
584 char *s = ovn_flow_to_string(f);
585 VLOG_DBG("%s flow: %s", action, s);
591 ovn_flow_destroy(struct ovn_flow *f)
599 /* Flow tables of struct ovn_flow. */
602 ovn_flow_table_clear(struct hmap *flow_table)
605 HMAP_FOR_EACH_POP (f, hmap_node, flow_table) {
611 ovn_flow_table_destroy(struct hmap *flow_table)
613 ovn_flow_table_clear(flow_table);
614 hmap_destroy(flow_table);
617 /* Flow table update. */
620 queue_flow_mod(struct ofputil_flow_mod *fm)
622 fm->buffer_id = UINT32_MAX;
623 fm->out_port = OFPP_ANY;
624 fm->out_group = OFPG_ANY;
625 queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM));
631 /* Finds and returns a group_info in 'existing_groups' whose key is identical
632 * to 'target''s key, or NULL if there is none. */
633 static struct group_info *
634 ovn_group_lookup(struct hmap *exisiting_groups,
635 const struct group_info *target)
637 struct group_info *e;
639 HMAP_FOR_EACH_WITH_HASH(e, hmap_node, target->hmap_node.hash,
641 if (e->group_id == target->group_id) {
648 /* Clear either desired_groups or existing_groups in group_table. */
650 ovn_group_table_clear(struct group_table *group_table, bool existing)
652 struct group_info *g, *next;
653 struct hmap *target_group = existing
654 ? &group_table->existing_groups
655 : &group_table->desired_groups;
657 HMAP_FOR_EACH_SAFE (g, next, hmap_node, target_group) {
658 hmap_remove(target_group, &g->hmap_node);
659 bitmap_set0(group_table->group_ids, g->group_id);
660 ds_destroy(&g->group);
666 queue_group_mod(struct ofputil_group_mod *gm)
668 queue_msg(ofputil_encode_group_mod(OFP13_VERSION, gm));
672 /* Replaces the flow table on the switch, if possible, by the flows in
673 * 'flow_table', which should have been added with ofctrl_add_flow().
674 * Regardless of whether the flow table is updated, this deletes all of the
675 * flows from 'flow_table' and frees them. (The hmap itself isn't
678 * Replaces the group table on the switch, if possible, by the groups in
679 * 'group_table->desired_groups'. Regardless of whether the group table
680 * is updated, this deletes all the groups from the
681 * 'group_table->desired_groups' and frees them. (The hmap itself isn't
684 * This should be called after ofctrl_run() within the main loop. */
686 ofctrl_put(struct hmap *flow_table, struct group_table *group_table)
689 groups = group_table;
692 /* The flow table can be updated if the connection to the switch is up and
693 * in the correct state and not backlogged with existing flow_mods. (Our
694 * criteria for being backlogged appear very conservative, but the socket
695 * between ovn-controller and OVS provides some buffering.) Otherwise,
696 * discard the flows. A solution to either of those problems will cause us
697 * to wake up and retry. */
698 if (state != S_UPDATE_FLOWS
699 || rconn_packet_counter_n_packets(tx_counter)) {
700 ovn_flow_table_clear(flow_table);
701 ovn_group_table_clear(group_table, false);
705 /* Iterate through all the desired groups. If there are new ones,
706 * add them to the switch. */
707 struct group_info *desired;
708 HMAP_FOR_EACH(desired, hmap_node, &group_table->desired_groups) {
709 if (!ovn_group_lookup(&group_table->existing_groups, desired)) {
710 /* Create and install new group. */
711 struct ofputil_group_mod gm;
712 enum ofputil_protocol usable_protocols;
714 struct ds group_string = DS_EMPTY_INITIALIZER;
715 ds_put_format(&group_string, "group_id=%u,%s",
716 desired->group_id, ds_cstr(&desired->group));
718 error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD,
719 ds_cstr(&group_string),
722 queue_group_mod(&gm);
724 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
725 VLOG_ERR_RL(&rl, "new group %s %s", error,
726 ds_cstr(&group_string));
729 ds_destroy(&group_string);
730 ofputil_bucket_list_destroy(&gm.buckets);
734 /* Iterate through all of the installed flows. If any of them are no
735 * longer desired, delete them; if any of them should have different
736 * actions, update them. */
737 struct ovn_flow *i, *next;
738 HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) {
739 struct ovn_flow *d = ovn_flow_lookup(flow_table, i);
741 /* Installed flow is no longer desirable. Delete it from the
742 * switch and from installed_flows. */
743 struct ofputil_flow_mod fm = {
745 .priority = i->priority,
746 .table_id = i->table_id,
747 .command = OFPFC_DELETE_STRICT,
750 ovn_flow_log(i, "removing");
752 hmap_remove(&installed_flows, &i->hmap_node);
755 if (!ofpacts_equal(i->ofpacts, i->ofpacts_len,
756 d->ofpacts, d->ofpacts_len)) {
757 /* Update actions in installed flow. */
758 struct ofputil_flow_mod fm = {
760 .priority = i->priority,
761 .table_id = i->table_id,
762 .ofpacts = d->ofpacts,
763 .ofpacts_len = d->ofpacts_len,
764 .command = OFPFC_MODIFY_STRICT,
767 ovn_flow_log(i, "updating");
769 /* Replace 'i''s actions by 'd''s. */
771 i->ofpacts = d->ofpacts;
772 i->ofpacts_len = d->ofpacts_len;
777 hmap_remove(flow_table, &d->hmap_node);
782 /* The previous loop removed from 'flow_table' all of the flows that are
783 * already installed. Thus, any flows remaining in 'flow_table' need to
784 * be added to the flow table. */
786 HMAP_FOR_EACH_SAFE (d, next, hmap_node, flow_table) {
787 /* Send flow_mod to add flow. */
788 struct ofputil_flow_mod fm = {
790 .priority = d->priority,
791 .table_id = d->table_id,
792 .ofpacts = d->ofpacts,
793 .ofpacts_len = d->ofpacts_len,
794 .command = OFPFC_ADD,
797 ovn_flow_log(d, "adding");
799 /* Move 'd' from 'flow_table' to installed_flows. */
800 hmap_remove(flow_table, &d->hmap_node);
801 hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash);
804 /* Iterate through the installed groups from previous runs. If they
805 * are not needed delete them. */
806 struct group_info *installed, *next_group;
807 HMAP_FOR_EACH_SAFE(installed, next_group, hmap_node,
808 &group_table->existing_groups) {
809 if (!ovn_group_lookup(&group_table->desired_groups, installed)) {
810 /* Delete the group. */
811 struct ofputil_group_mod gm;
812 enum ofputil_protocol usable_protocols;
814 struct ds group_string = DS_EMPTY_INITIALIZER;
815 ds_put_format(&group_string, "group_id=%u", installed->group_id);
817 error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
818 ds_cstr(&group_string),
821 queue_group_mod(&gm);
823 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
824 VLOG_ERR_RL(&rl, "Error deleting group %d: %s",
825 installed->group_id, error);
828 ds_destroy(&group_string);
829 ofputil_bucket_list_destroy(&gm.buckets);
831 /* Remove 'installed' from 'group_table->existing_groups' */
832 hmap_remove(&group_table->existing_groups, &installed->hmap_node);
833 ds_destroy(&installed->group);
835 /* Dealloc group_id. */
836 bitmap_set0(group_table->group_ids, installed->group_id);
841 /* Move the contents of desired_groups to existing_groups. */
842 HMAP_FOR_EACH_SAFE(desired, next_group, hmap_node,
843 &group_table->desired_groups) {
844 hmap_remove(&group_table->desired_groups, &desired->hmap_node);
845 if (!ovn_group_lookup(&group_table->existing_groups, desired)) {
846 hmap_insert(&group_table->existing_groups, &desired->hmap_node,
847 desired->hmap_node.hash);
849 ds_destroy(&desired->group);