1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
/* Copyright (c) 2024, Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "openvswitch/hmap.h"
#include "openvswitch/rconn.h"
#include "openvswitch/dynamic-string.h"
#include "openvswitch/ofp-ct.h"
#include "openvswitch/ofp-util.h"
#include "openvswitch/ofp-msgs.h"
#include "openvswitch/vlog.h"
#include "lib/ovn-sb-idl.h"
#include "ovn/features.h"
#include "acl-ids.h"
VLOG_DEFINE_THIS_MODULE(acl_ids);
enum acl_id_state {
/* The ID exists in the SB DB. */
ACTIVE,
/* The ID has been removed from the DB and needs to have its conntrack
* entries flushed.
*/
SB_DELETED,
/* We have sent the conntrack flush request to OVS for this ACL ID. */
FLUSHING,
};
struct acl_id {
int64_t id;
struct uuid uuid;
enum acl_id_state state;
struct hmap_node hmap_node;
ovs_be32 xid;
ovs_be32 barrier_xid;
int flush_count;
};
struct tracked_acl_ids {
struct hmap ids;
};
static struct acl_id *
find_tracked_acl_id(struct tracked_acl_ids *tracked_ids,
const struct sbrec_acl_id *sb_id)
{
uint32_t hash = hash_2words(uuid_hash(&sb_id->header_.uuid),
hash_uint64(sb_id->id));
struct acl_id *acl_id;
HMAP_FOR_EACH_WITH_HASH (acl_id, hmap_node, hash, &tracked_ids->ids) {
if (acl_id->id == sb_id->id &&
uuid_equals(&sb_id->header_.uuid, &acl_id->uuid)) {
return acl_id;
}
}
return NULL;
}
static void
acl_id_destroy(struct acl_id *acl_id)
{
free(acl_id);
}
void *
en_acl_id_init(struct engine_node *node OVS_UNUSED,
struct engine_arg *arg OVS_UNUSED)
{
struct tracked_acl_ids *ids = xzalloc(sizeof *ids);
hmap_init(&ids->ids);
return ids;
}
enum engine_node_state
en_acl_id_run(struct engine_node *node, void *data)
{
if (!ovs_feature_is_supported(OVS_CT_LABEL_FLUSH_SUPPORT)) {
return EN_UNCHANGED;
}
const struct sbrec_acl_id_table *sb_acl_id_table =
EN_OVSDB_GET(engine_get_input("SB_acl_id", node));
const struct sbrec_acl_id *sb_id;
struct tracked_acl_ids *ids = data;
struct acl_id *id;
/* Pre-mark each active ID as SB_DELETED. */
HMAP_FOR_EACH (id, hmap_node, &ids->ids) {
if (id->state == ACTIVE) {
id->state = SB_DELETED;
}
}
SBREC_ACL_ID_TABLE_FOR_EACH (sb_id, sb_acl_id_table) {
id = find_tracked_acl_id(ids, sb_id);
if (!id) {
id = xzalloc(sizeof *id);
id->id = sb_id->id;
id->uuid = sb_id->header_.uuid;
uint32_t hash = hash_2words(uuid_hash(&sb_id->header_.uuid),
hash_uint64(sb_id->id));
hmap_insert(&ids->ids, &id->hmap_node, hash);
}
id->state = ACTIVE;
}
return EN_UPDATED;
}
void
en_acl_id_cleanup(void *data)
{
struct tracked_acl_ids *tracked_ids = data;
struct acl_id *id;
HMAP_FOR_EACH_POP (id, hmap_node, &tracked_ids->ids) {
acl_id_destroy(id);
}
hmap_destroy(&tracked_ids->ids);
}
bool
en_acl_id_is_valid(struct engine_node *node OVS_UNUSED)
{
return true;
}
void
acl_ids_handle_barrier_reply(struct tracked_acl_ids *tracked_acl_ids,
ovs_be32 barrier_xid)
{
/* Since ofctrl_run() runs before engine_run(), there is a chance that
* tracked_acl_ids may be NULL.
*/
if (!tracked_acl_ids) {
return;
}
struct acl_id *acl_id;
HMAP_FOR_EACH_SAFE (acl_id, hmap_node, &tracked_acl_ids->ids) {
if (acl_id->state != FLUSHING || acl_id->barrier_xid != barrier_xid) {
continue;
}
hmap_remove(&tracked_acl_ids->ids, &acl_id->hmap_node);
acl_id_destroy(acl_id);
}
}
#define MAX_FLUSHES 3
bool
acl_ids_handle_non_barrier_reply(const struct ofp_header *oh,
enum ofptype type,
struct tracked_acl_ids *tracked_acl_ids)
{
/* Since ofctrl_run() runs before engine_run(), there is a chance that
* tracked_acl_ids may be NULL.
*/
if (!tracked_acl_ids) {
return false;
}
if (type != OFPTYPE_ERROR) {
return false;
}
struct acl_id *acl_id;
bool handled = false;
HMAP_FOR_EACH_SAFE (acl_id, hmap_node, &tracked_acl_ids->ids) {
if (acl_id->xid != oh->xid) {
continue;
}
handled = true;
/* Uh oh! It looks like one of the flushes failed :(
* Let's find this particular one and move its state
* back to SB_DELETED so we can retry the flush. Of
* course, if this is a naughty little ID and has
* been flushed unsuccessfully too many times, we'll
* delete it since we are unlikely to be able to
* successfully flush it.
*/
acl_id->xid = 0;
acl_id->barrier_xid = 0;
acl_id->flush_count++;
if (acl_id->flush_count >= MAX_FLUSHES) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rl, "Failed to flush conntrack entry for ACL id "
"%"PRId64".", acl_id->id);
hmap_remove(&tracked_acl_ids->ids, &acl_id->hmap_node);
acl_id_destroy(acl_id);
} else {
acl_id->state = SB_DELETED;
}
break;
}
return handled;
}
void
acl_ids_flush_expired(struct tracked_acl_ids *acl_ids, int rconn_version,
struct ovs_list *msgs)
{
ovs_u128 mask = {
/* ct_labels.label BITS[80-95] */
.u64.hi = 0xffff0000,
};
struct acl_id *acl_id;
HMAP_FOR_EACH (acl_id, hmap_node, &acl_ids->ids) {
if (acl_id->state != SB_DELETED) {
continue;
}
ovs_u128 ct_id = {
.u64.hi = acl_id->id << 16,
};
VLOG_DBG("Flushing conntrack entry for ACL id %"PRId64".", acl_id->id);
struct ofp_ct_match match = {
.labels = ct_id,
.labels_mask = mask,
};
struct ofpbuf *msg = ofp_ct_match_encode(&match, NULL,
rconn_version);
const struct ofp_header *oh = msg->data;
acl_id->xid = oh->xid;
acl_id->state = FLUSHING;
ovs_list_push_back(msgs, &msg->list_node);
}
}
void
acl_ids_record_barrier_xid(struct tracked_acl_ids *acl_ids,
ovs_be32 barrier_xid)
{
struct acl_id *acl_id;
HMAP_FOR_EACH (acl_id, hmap_node, &acl_ids->ids) {
if (acl_id->state == FLUSHING && !acl_id->barrier_xid) {
acl_id->barrier_xid = barrier_xid;
}
}
}
|