@@ -1,10 +1,155 @@
/* SPDX-License-Identifier: GPL-2.0 */
+#include <arpa/inet.h>
#include <linux/bpf.h>
+#include <linux/in.h>
+#include <stddef.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_endian.h>
+
+#define ETH_ALEN 6
+#define VIF_ADDR_FLAG_ETH_SET 0x01
+#define VIF_ADDR_FLAG_IP_SET 0x02
+
+#define ETH_P_IP 0x0800
+
+#define OVN_CHECK_PORT_SEC_MAC 0x00000001
+#define OVN_CHECK_PORT_SEC_MAC_IP 0x00000002
+
+
+struct bpf_map_def SEC("maps") ovn_vif_map = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(__u32),
+ .value_size = sizeof(__u32),
+ .max_entries = 1,
+};
+
+/* Map for mac port security table*/
+struct bpf_map_def SEC("maps") port_sec_mac_table = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(__u64),
+ .value_size = sizeof(__u8),
+ .max_entries = 4,
+};
+
+struct lpm_mac_ip_key {
+ struct bpf_lpm_trie_key trie_key;
+ __u8 data[10]; /* 6 bytes for mac, 4 bytes for ip */
+};
+
+/* Map for mac + ip4 port security table*/
+struct bpf_map_def SEC("maps") port_sec_mac_ip_table = {
+ .type = BPF_MAP_TYPE_LPM_TRIE,
+ .key_size = sizeof(struct lpm_mac_ip_key),
+ .value_size = sizeof(__u8),
+ .max_entries = 4,
+ .map_flags = BPF_F_NO_PREALLOC,
+};
+
+static inline __u64
+eth_addr_to_uint64(unsigned char ethaddr[ETH_ALEN]) {
+ return ((uint64_t) ethaddr[0] << 40) |
+ ((uint64_t) ethaddr[1] << 32) |
+ ((uint64_t) ethaddr[2] << 24) |
+ ((uint64_t) ethaddr[3] << 16) |
+ ((uint64_t) ethaddr[4] << 8) |
+ ((uint64_t) ethaddr[5] << 0);
+}
SEC("xdp")
int xdp_ovn_vif(struct xdp_md *xdp)
{
+ __u32 key = 0;
+
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ struct ethhdr *eth = data;
+
+ int ret = XDP_PASS;
+
+ if (data + sizeof(*eth) > data_end) {
+ return XDP_DROP;
+ }
+
+ __u32 *vif_flags = bpf_map_lookup_elem(&ovn_vif_map, &key);
+ if (!vif_flags || !(*vif_flags)) {
+ /* No checks to be done. */
+ return XDP_PASS;
+ }
+
+ if (eth->h_source[0] & 0x01) {
+ /* Multicast eth src. Drop it. */
+ return XDP_DROP;
+ }
+
+ if (eth->h_proto == htons(ETH_P_ARP)) {
+ __u8 ps_check_pass = 0;
+ __u64 src_mac;
+
+ src_mac = eth_addr_to_uint64(eth->h_source);
+ if (bpf_map_lookup_elem(&port_sec_mac_table, &src_mac)) {
+ ps_check_pass = 1;
+ }
+
+ if (!ps_check_pass) {
+ return XDP_DROP;
+ }
+
+ /* TODO. Inspect the arp header and check if arp.sha is allowed or not. */
+ return XDP_PASS;
+ } else if (eth->h_proto == htons(ETH_P_IP)) {
+ __u8 ps_check_pass = 0;
+ if (*vif_flags & OVN_CHECK_PORT_SEC_MAC_IP) {
+ struct iphdr *iph;
+ struct lpm_mac_ip_key key = {
+ .trie_key = {
+ .prefixlen = 80,
+ },
+ };
+
+ iph = (struct iphdr *)(eth + 1);
+ if ((void *)(iph + 1) > data_end) {
+ return XDP_DROP;
+ }
+
+ key.data[0] = eth->h_source[0];
+ key.data[1] = eth->h_source[1];
+ key.data[2] = eth->h_source[2];
+ key.data[3] = eth->h_source[3];
+ key.data[4] = eth->h_source[4];
+ key.data[5] = eth->h_source[5];
+
+ key.data[6] = iph->saddr & 0xff;
+ key.data[7] = (iph->saddr >> 8) & 0xff;
+ key.data[8] = (iph->saddr >> 16) & 0xff;
+ key.data[9] = (iph->saddr >> 24) & 0xff;
+
+ __u8 *v;
+ v = bpf_map_lookup_elem(&port_sec_mac_ip_table, &key);
+ if (v && *v) {
+ ps_check_pass = 1;
+ }
+ }
+
+ if (!ps_check_pass && (*vif_flags & OVN_CHECK_PORT_SEC_MAC)) {
+ __u64 src_mac;
+ __u8 *v;
+
+ src_mac = eth_addr_to_uint64(eth->h_source);
+ v = bpf_map_lookup_elem(&port_sec_mac_table, &src_mac);
+ if (v && *v) {
+ ps_check_pass = 1;
+ }
+ }
+
+ if (!ps_check_pass) {
+ return XDP_DROP;
+ }
+ }
+
return XDP_PASS;
}
@@ -174,6 +174,8 @@ OVS_CHECK_PRAGMA_MESSAGE
OVN_CHECK_OVS
OVN_CHECK_VIF_PLUG_PROVIDER
OVN_ENABLE_VIF_PLUG
+OVN_CHECK_BPF
+OVN_CHECK_XDP
OVS_CTAGS_IDENTIFIERS
AC_SUBST([OVS_CFLAGS])
AC_SUBST([OVS_LDFLAGS])
@@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \
controller/ovsport.h \
controller/ovsport.c \
controller/vif-plug.h \
- controller/vif-plug.c
+ controller/vif-plug.c \
+ controller/xdp.c \
+ controller/xdp.h
controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la
man_MANS += controller/ovn-controller.8
@@ -36,6 +36,7 @@
#include "lport.h"
#include "ovn-controller.h"
#include "patch.h"
+#include "xdp.h"
VLOG_DEFINE_THIS_MODULE(binding);
@@ -77,6 +78,7 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_status);
+ ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ifindex);
ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
@@ -539,8 +541,6 @@ static struct binding_lport *local_binding_add_lport(
struct local_binding *,
const struct sbrec_port_binding *,
enum en_lport_type);
-static struct binding_lport *local_binding_get_primary_lport(
- struct local_binding *);
static struct binding_lport *local_binding_get_first_lport(
struct local_binding *lbinding);
static struct binding_lport *local_binding_get_primary_or_localport_lport(
@@ -807,6 +807,27 @@ binding_dump_local_bindings(struct local_binding_data *lbinding_data,
free(nodes);
}
+/* Returns the primary binding lport if present in lbinding's
+ * binding lports list. A binding lport is considered primary
+ * if binding lport's type is LP_VIF and the name matches
+ * with the 'lbinding'.
+ */
+struct binding_lport *
+local_binding_get_primary_lport(struct local_binding *lbinding)
+{
+ if (!lbinding) {
+ return NULL;
+ }
+
+ struct binding_lport *b_lport = local_binding_get_first_lport(lbinding);
+ if (b_lport && b_lport->type == LP_VIF &&
+ !strcmp(lbinding->name, b_lport->name)) {
+ return b_lport;
+ }
+
+ return NULL;
+}
+
static bool
is_lport_vif(const struct sbrec_port_binding *pb)
{
@@ -2636,26 +2657,6 @@ local_binding_get_first_lport(struct local_binding *lbinding)
return NULL;
}
-/* Returns the primary binding lport if present in lbinding's
- * binding lports list. A binding lport is considered primary
- * if binding lport's type is LP_VIF and the name matches
- * with the 'lbinding'.
- */
-static struct binding_lport *
-local_binding_get_primary_lport(struct local_binding *lbinding)
-{
- if (!lbinding) {
- return NULL;
- }
-
- struct binding_lport *b_lport = local_binding_get_first_lport(lbinding);
- if (b_lport && b_lport->type == LP_VIF &&
- !strcmp(lbinding->name, b_lport->name)) {
- return b_lport;
- }
-
- return NULL;
-}
static struct binding_lport *
local_binding_get_primary_or_localport_lport(struct local_binding *lbinding)
@@ -214,4 +214,11 @@ struct binding_lport {
size_t n_port_security;
};
+/* Returns the primary binding lport if present in lbinding's
+ * binding lports list. A binding lport is considered primary
+ * if binding lport's type is LP_VIF and the name matches
+ * with the 'lbinding'.
+ */
+struct binding_lport *local_binding_get_primary_lport(struct local_binding *);
+
#endif /* controller/binding.h */
@@ -76,6 +76,7 @@
#include "stopwatch.h"
#include "lib/inc-proc-eng.h"
#include "hmapx.h"
+#include "xdp.h"
VLOG_DEFINE_THIS_MODULE(main);
@@ -1417,7 +1418,6 @@ en_runtime_data_run(struct engine_node *node, void *data)
}
binding_run(&b_ctx_in, &b_ctx_out);
-
engine_set_node_state(node, EN_UPDATED);
}
@@ -3207,6 +3207,77 @@ flow_output_lflow_output_handler(struct engine_node *node,
return true;
}
+struct ed_type_xdp {
+ struct shash xdp_lports;
+};
+
+static void *
+en_xdp_init(struct engine_node *node OVS_UNUSED,
+ struct engine_arg *arg OVS_UNUSED)
+{
+ struct ed_type_xdp *xdp = xzalloc(sizeof *xdp);
+ ovn_xdp_init(&xdp->xdp_lports);
+ return xdp;
+}
+
+static void
+en_xdp_cleanup(void *data)
+{
+ struct ed_type_xdp *xdp = data;
+ ovn_xdp_destroy(&xdp->xdp_lports);
+}
+
+static void
+en_xdp_run(struct engine_node *node, void *data)
+{
+ struct ed_type_runtime_data *rt_data =
+ engine_get_input_data("runtime_data", node);
+
+ struct ed_type_xdp *xdp_data = data;
+ ovn_xdp_run(&rt_data->lbinding_data.bindings, &rt_data->local_lports,
+ &xdp_data->xdp_lports);
+ engine_set_node_state(node, EN_UPDATED);
+}
+
+static bool
+xdp_runtime_data_handler(struct engine_node *node, void *data)
+{
+ struct ed_type_runtime_data *rt_data =
+ engine_get_input_data("runtime_data", node);
+
+ /* There is no tracked data. Fall back to full recompute of xdp. */
+ if (!rt_data->tracked) {
+ return false;
+ }
+
+ struct ed_type_xdp *xdp_data = data;
+
+ struct hmap *tracked_dp_bindings = &rt_data->tracked_dp_bindings;
+ struct tracked_datapath *tdp;
+ HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) {
+ if (tdp->tracked_type != TRACKED_RESOURCE_UPDATED) {
+ /* Fall back to full recompute when a local datapath
+ * is added or deleted. */
+ return false;
+ }
+
+ struct shash_node *shash_node;
+ SHASH_FOR_EACH (shash_node, &tdp->lports) {
+ struct tracked_lport *lport = shash_node->data;
+ bool removed =
+ lport->tracked_type == TRACKED_RESOURCE_REMOVED ? true: false;
+ if (!ovn_xdp_handle_lport(
+ lport->pb, removed, &rt_data->lbinding_data.bindings,
+ &xdp_data->xdp_lports)) {
+ return false;
+ }
+ }
+ }
+
+ engine_set_node_state(node, EN_UPDATED);
+ return true;
+}
+
struct ovn_controller_exit_args {
bool *exiting;
bool *restart;
@@ -3459,6 +3530,7 @@ main(int argc, char *argv[])
ENGINE_NODE_WITH_CLEAR_TRACK_DATA(addr_sets, "addr_sets");
ENGINE_NODE_WITH_CLEAR_TRACK_DATA(port_groups, "port_groups");
ENGINE_NODE(northd_internal_version, "northd_internal_version");
+ ENGINE_NODE(xdp, "xdp");
#define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
SB_NODES
@@ -3601,6 +3673,11 @@ main(int argc, char *argv[])
flow_output_lflow_output_handler);
engine_add_input(&en_flow_output, &en_pflow_output,
flow_output_pflow_output_handler);
+ engine_add_input(&en_flow_output, &en_xdp,
+ engine_noop_handler);
+
+ engine_add_input(&en_xdp, &en_runtime_data,
+ xdp_runtime_data_handler);
struct engine_arg engine_arg = {
.sb_idl = ovnsb_idl_loop.idl,
new file mode 100644
@@ -0,0 +1,389 @@
+/* Copyright (c) 2022 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 <unistd.h>
+
+/* library headers */
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#include <xdp/libxdp.h>
+
+/* OVS includes. */
+#include "lib/vswitch-idl.h"
+#include "openvswitch/vlog.h"
+
+/* OVN includes. */
+#include "binding.h"
+#include "lib/ovn-dirs.h"
+#include "lib/ovn-util.h"
+#include "lib/ovn-sb-idl.h"
+#include "xdp.h"
+
+VLOG_DEFINE_THIS_MODULE(xdp);
+
+#define MAX_ERRNO 4095
+#define STRERR_BUFSIZE 1024
+
+#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
+
+struct lpm_mac_ip_key {
+ struct bpf_lpm_trie_key trie_key;
+ __u8 data[10]; /* 6 bytes for mac, 4 bytes for ip */
+};
+
+struct xdp_lport {
+ int ifindex;
+ bool bpf_attached;
+ bool bpf_attach_failed;
+
+ struct xdp_program *prog;
+ int vif_map_fd;
+ int port_sec_mac_map_fd;
+ int port_sec_mac_ip_map_fd;
+
+ struct lport_addresses *ps_addrs; /* Port security addresses. */
+ unsigned int n_ps_addrs;
+};
+
+static void xdp_handle_lport(const char *lport, struct shash *local_bindings,
+ struct shash *xdp_lports);
+static struct xdp_lport *xdp_lport_find(struct shash *xdp_lports,
+ const char *lport);
+static void xdp_lport_attach_prog(struct xdp_lport *);
+static void xdp_lport_update_maps(struct xdp_lport *, bool allow);
+static void xdp_lport_detach_prog(struct xdp_lport *);
+static void xdp_lport_update_sb_bpf(struct xdp_lport *,
+ const struct sbrec_port_binding *);
+static struct xdp_lport *xdp_lport_create(
+ struct shash *xdp_lports, const struct binding_lport *);
+static void xdp_lport_destroy(struct xdp_lport *);
+
+
+void
+ovn_xdp_init(struct shash *xdp_lports)
+{
+ shash_init(xdp_lports);
+}
+
+void
+ovn_xdp_run(struct shash *local_bindings, struct sset *local_lports,
+ struct shash *xdp_lports)
+{
+ struct shash_node *node, *next;
+ SHASH_FOR_EACH_SAFE (node, next, xdp_lports) {
+ if (!sset_contains(local_lports, node->name)) {
+ struct xdp_lport *xdp_lport = node->data;
+ shash_delete(xdp_lports, node);
+ xdp_lport_destroy(xdp_lport);
+ }
+ }
+
+ const char *lport;
+ SSET_FOR_EACH (lport, local_lports) {
+ xdp_handle_lport(lport, local_bindings, xdp_lports);
+ }
+}
+
+bool
+ovn_xdp_handle_lport(const struct sbrec_port_binding *pb, bool removed,
+ struct shash *local_bindings, struct shash *xdp_lports)
+{
+ if (removed) {
+ struct xdp_lport *xdp_lport =
+ shash_find_and_delete(xdp_lports, pb->logical_port);
+ if (xdp_lport) {
+ xdp_lport_destroy(xdp_lport);
+ }
+ } else {
+ xdp_handle_lport(pb->logical_port, local_bindings, xdp_lports);
+ }
+
+ return true;
+}
+
+void
+ovn_xdp_destroy(struct shash *xdp_lports)
+{
+ struct shash_node *node, *next;
+ SHASH_FOR_EACH_SAFE (node, next, xdp_lports) {
+ struct xdp_lport *xdp_lport = node->data;
+ shash_delete(xdp_lports, node);
+ xdp_lport_destroy(xdp_lport);
+ }
+}
+
+/* static functions. */
+
+static void
+xdp_handle_lport(const char *lport, struct shash *local_bindings,
+ struct shash *xdp_lports)
+{
+ struct local_binding *lbinding = local_binding_find(local_bindings, lport);
+ if (!lbinding) {
+ return;
+ }
+
+ struct binding_lport *b_lport = local_binding_get_primary_lport(lbinding);
+ if (!b_lport) {
+ return;
+ }
+
+ int ifindex = lbinding->iface->n_ifindex ? lbinding->iface->ifindex[0]
+ : -1;
+ struct xdp_lport *xdp_lport = xdp_lport_find(xdp_lports, lport);
+ if (xdp_lport && ifindex < 1) {
+ /* The interface has been deleted. */
+ shash_find_and_delete(xdp_lports, lport);
+ xdp_lport_destroy(xdp_lport);
+ return;
+ }
+
+ if (ifindex < 1) {
+ return;
+ }
+
+ if (!xdp_lport) {
+ xdp_lport = xdp_lport_create(xdp_lports, b_lport);
+ if (!xdp_lport) {
+ return;
+ }
+ } else {
+ if (xdp_lport->bpf_attached) {
+ xdp_lport_update_maps(xdp_lport, false);
+ }
+ xdp_lport_update_sb_bpf(xdp_lport, b_lport->pb);
+ }
+
+ xdp_lport_attach_prog(xdp_lport);
+ if (xdp_lport->bpf_attached) {
+ xdp_lport_update_maps(xdp_lport, true);
+ }
+}
+
+static void
+xdp_lport_attach_prog(struct xdp_lport *xdp_lport)
+{
+ if (xdp_lport->bpf_attached || xdp_lport->bpf_attach_failed) {
+ return;
+ }
+
+ /* Detach any xdp program if it is already attached. */
+ xdp_lport_detach_prog(xdp_lport);
+
+ char *ovn_xdp_file = xasprintf("%s/ovn_xdp.o", ovn_pkgdatadir());
+ struct xdp_program *prog =
+ xdp_program__open_file(ovn_xdp_file, "xdp", NULL);
+ free(ovn_xdp_file);
+
+ int err = libxdp_get_error(prog);
+ char errmsg[STRERR_BUFSIZE];
+ if (err) {
+ libxdp_strerror(err, errmsg, sizeof(errmsg));
+ VLOG_ERR("ovn_xdp_attach: failed to open xdp program: %s", errmsg);
+ return;
+ }
+
+ err = xdp_program__attach(prog, xdp_lport->ifindex, XDP_MODE_NATIVE, 0);
+
+ if (err) {
+ libxdp_strerror(err, errmsg, sizeof(errmsg));
+ VLOG_ERR("ovn_xdp_attach: failed to attach xdp program: %s", errmsg);
+ xdp_lport->bpf_attach_failed = true;
+ return;
+ }
+
+ struct bpf_object *obj = xdp_program__bpf_obj(prog);
+ struct bpf_map *map = bpf_object__find_map_by_name(obj, "ovn_vif_map");
+
+ xdp_lport->vif_map_fd = map ? bpf_map__fd(map) : -1;
+
+ map = bpf_object__find_map_by_name(obj, "port_sec_mac_table");
+ xdp_lport->port_sec_mac_map_fd = map ? bpf_map__fd(map) : -1;
+
+ map = bpf_object__find_map_by_name(obj, "port_sec_mac_ip_table");
+ xdp_lport->port_sec_mac_ip_map_fd = map ? bpf_map__fd(map) : -1;
+
+ xdp_lport->bpf_attached = true;
+ xdp_lport->bpf_attach_failed = false;
+ xdp_lport->prog = prog;
+}
+
+
+static void
+xdp_lport_update_maps(struct xdp_lport *xdp_lport, bool allow)
+{
+ if (!xdp_lport->bpf_attached || xdp_lport->vif_map_fd < 1 ||
+ xdp_lport->port_sec_mac_map_fd < 1 ||
+ xdp_lport->port_sec_mac_ip_map_fd < 1) {
+ return;
+ }
+
+ uint32_t ovn_xdp_checks = 0;
+ for (size_t i = 0 ; i < xdp_lport->n_ps_addrs; i++) {
+ uint8_t ps_only_l2 = 1;
+ struct lport_addresses *ps = &xdp_lport->ps_addrs[i];
+
+ if (ps->n_ipv4_addrs) {
+ struct lpm_mac_ip_key key;
+ key.data[0] = ps->ea.ea[0];
+ key.data[1] = ps->ea.ea[1];
+ key.data[2] = ps->ea.ea[2];
+ key.data[3] = ps->ea.ea[3];
+ key.data[4] = ps->ea.ea[4];
+ key.data[5] = ps->ea.ea[5];
+
+ for (size_t j = 0; j < ps->n_ipv4_addrs; j++) {
+ key.trie_key.prefixlen = ps->ipv4_addrs[j].plen + 48;
+
+ uint32_t addr = (OVS_FORCE uint32_t)ps->ipv4_addrs[j].addr;
+ key.data[6] = addr & 0xff;
+ key.data[7] = (addr >> 8) & 0xff;
+ key.data[8] = (addr >> 16) & 0xff;
+ key.data[9] = (addr >> 24) & 0xff;
+
+ uint8_t v = allow ? 1 : 0;
+ if (bpf_map_update_elem(xdp_lport->port_sec_mac_ip_map_fd,
+ &key, &v, 0) < 0) {
+ VLOG_ERR("ovn_xdp_attach: failed to update port_sec_mac_ip_table");
+ return;
+ }
+ ovn_xdp_checks |= OVN_CHECK_PORT_SEC_MAC_IP;
+ }
+ ps_only_l2 = 0;
+ }
+
+ ovn_xdp_checks |= OVN_CHECK_PORT_SEC_MAC;
+ uint64_t mac = eth_addr_to_uint64(ps->ea);
+
+ if (bpf_map_update_elem(xdp_lport->port_sec_mac_map_fd, &mac,
+ &ps_only_l2, 0) < 0) {
+ VLOG_ERR("ovn_xdp_attach: failed to update port_sec_mac_table");
+ return;
+ }
+ }
+
+ int key = 0;
+ if (bpf_map_update_elem(xdp_lport->vif_map_fd, &key,
+ &ovn_xdp_checks, 0) < 0) {
+ VLOG_ERR("ovn_xdp_attach: failed to update ovn_check_map");
+ }
+}
+
+static void
+xdp_lport_detach_prog(struct xdp_lport *xdp_lport)
+{
+ if (xdp_lport->bpf_attached && xdp_lport->prog) {
+ if (xdp_lport->vif_map_fd > 0) {
+ close(xdp_lport->vif_map_fd);
+ xdp_lport->vif_map_fd = -1;
+ }
+
+ if (xdp_lport->port_sec_mac_map_fd > 0) {
+ close(xdp_lport->port_sec_mac_map_fd);
+ xdp_lport->port_sec_mac_map_fd = -1;
+ }
+
+ if (xdp_lport->port_sec_mac_ip_map_fd > 0) {
+ close(xdp_lport->port_sec_mac_ip_map_fd);
+ xdp_lport->port_sec_mac_ip_map_fd = -1;
+ }
+
+ xdp_program__detach(xdp_lport->prog, xdp_lport->ifindex, XDP_MODE_NATIVE, 0);
+ xdp_program__close(xdp_lport->prog);
+ xdp_lport->bpf_attached = false;
+ xdp_lport->bpf_attach_failed = false;
+ xdp_lport->prog = NULL;
+ } else {
+ struct xdp_multiprog *mp = NULL;
+ mp = xdp_multiprog__get_from_ifindex(xdp_lport->ifindex);
+
+ if (!mp || IS_ERR_VALUE((unsigned long) mp)) {
+ return;
+ }
+
+ if (xdp_multiprog__detach(mp)) {
+ VLOG_ERR("ovn_xdp_detach: failed to detach xdp program");
+ return;
+ }
+
+ xdp_multiprog__close(mp);
+ }
+}
+
+static struct xdp_lport *
+xdp_lport_find(struct shash *xdp_lports, const char *lport)
+{
+ return shash_find_data(xdp_lports, lport);
+}
+
+static void
+xdp_lport_update_sb_bpf(struct xdp_lport *xdp_lp,
+ const struct sbrec_port_binding *sb_pb)
+{
+ if (xdp_lp->n_ps_addrs) {
+ for (size_t i = 0; i < xdp_lp->n_ps_addrs; i++) {
+ destroy_lport_addresses(&xdp_lp->ps_addrs[i]);
+ }
+ free(xdp_lp->ps_addrs);
+ xdp_lp->ps_addrs = NULL;
+ xdp_lp->n_ps_addrs = 0;
+ }
+
+ xdp_lp->ps_addrs =
+ xmalloc(sizeof *xdp_lp->ps_addrs * sb_pb->n_port_security);
+ for (size_t i = 0; i < sb_pb->n_port_security; i++) {
+ if (!extract_lsp_addresses(
+ sb_pb->port_security[i],
+ &xdp_lp->ps_addrs[xdp_lp->n_ps_addrs])) {
+ static struct vlog_rate_limit rl
+ = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_INFO_RL(&rl, "invalid syntax '%s' in lport port security."
+ " No MAC address found", sb_pb->port_security[i]);
+ continue;
+ }
+ xdp_lp->n_ps_addrs++;
+ }
+}
+
+static struct xdp_lport *
+xdp_lport_create(struct shash *xdp_lports, const struct binding_lport *b_lport)
+{
+ struct xdp_lport *xdp_lport = xzalloc(sizeof(*xdp_lport));
+ ovs_assert(b_lport->lbinding->iface->n_ifindex);
+ xdp_lport->ifindex = b_lport->lbinding->iface->ifindex[0];
+ shash_add(xdp_lports, b_lport->lbinding->name, xdp_lport);
+
+ if (b_lport->pb && b_lport->pb->n_port_security) {
+ xdp_lport_update_sb_bpf(xdp_lport, b_lport->pb);
+ }
+
+ xdp_lport->vif_map_fd = -1;
+ xdp_lport->port_sec_mac_map_fd = -1;
+ xdp_lport->port_sec_mac_ip_map_fd = -1;
+
+ return xdp_lport;
+}
+
+static void
+xdp_lport_destroy(struct xdp_lport *xdp_lport)
+{
+ xdp_lport_detach_prog(xdp_lport);
+ for (size_t i = 0; i < xdp_lport->n_ps_addrs; i++) {
+ destroy_lport_addresses(&xdp_lport->ps_addrs[i]);
+ }
+ free(xdp_lport->ps_addrs);
+ free(xdp_lport);
+}
new file mode 100644
@@ -0,0 +1,41 @@
+/* Copyright (c) 2022 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.
+ */
+
+#ifndef OVN_XDP_H
+#define OVN_XDP_H 1
+
+#define ETH_ALEN 6
+
+struct vif_addr {
+ uint8_t flags;
+ uint8_t eth_addr[ETH_ALEN];
+ ovs_be32 ip_addr;
+};
+
+#define OVN_CHECK_PORT_SEC_MAC 0x00000001
+#define OVN_CHECK_PORT_SEC_MAC_IP 0x00000002
+
+struct sbrec_port_binding;
+struct local_binding;
+struct shash;
+
+void ovn_xdp_init(struct shash *);
+void ovn_xdp_destroy(struct shash *);
+void ovn_xdp_run(struct shash *local_bindings, struct sset *local_lports,
+ struct shash *xdp_lports);
+bool ovn_xdp_handle_lport(const struct sbrec_port_binding *pb, bool removed,
+ struct shash *local_bindings,
+ struct shash *xdp_lports);
+#endif
@@ -592,3 +592,23 @@ AC_DEFUN([OVS_CHECK_DDLOG_FAST_BUILD],
if $ddlog_fast_build; then
DDLOG_EXTRA_RUSTFLAGS="-C opt-level=z"
fi])
+
+dnl Checks for libbpf.
+AC_DEFUN([OVN_CHECK_BPF],
+ [AC_CHECK_LIB(bpf, bpf_load_program, [HAVE_BPF=yes], [HAVE_BPF=no])
+ if test "$HAVE_BPF" = yes; then
+ AC_DEFINE([HAVE_BPF], [1], [Define to 1 if bpf is detected.])
+ LIBS="$LIBS -lbpf"
+ fi
+ AM_CONDITIONAL([HAVE_BPF], [test "$HAVE_BPF" = yes])
+ AC_SUBST([HAVE_BPF])])
+
+dnl Checks for libxdp.
+AC_DEFUN([OVN_CHECK_XDP],
+ [AC_CHECK_LIB(xdp, xdp_program__attach, [HAVE_XDP=yes], [HAVE_XDP=no])
+ if test "$HAVE_XDP" = yes; then
+ AC_DEFINE([HAVE_XDP], [1], [Define to 1 if xdp is detected.])
+ LIBS="$LIBS -lxdp"
+ fi
+ AM_CONDITIONAL([HAVE_XDP], [test "$HAVE_XDP" = yes])
+ AC_SUBST([HAVE_XDP])])
@@ -265,6 +265,7 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \
controller/ovsport.$(OBJEXT) \
controller/patch.$(OBJEXT) \
controller/vif-plug.$(OBJEXT) \
+ controller/xdp.$(OBJEXT) \
northd/ipam.$(OBJEXT)
# Python tests.