summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Stapp <mjs@voltanet.io>2019-01-16 19:40:31 +0100
committerMark Stapp <mjs@voltanet.io>2019-04-22 19:49:27 +0200
commit6416880328557628f11dbc7a4ba96087a50fc0f4 (patch)
treeb234222a11a1a2307594dd4eca63966c17445e6b
parentzebra: add interface-address info for dataplane (diff)
downloadfrr-6416880328557628f11dbc7a4ba96087a50fc0f4.tar.xz
frr-6416880328557628f11dbc7a4ba96087a50fc0f4.zip
zebra: Use dplane for interface addresses (netlink)
Start using the dataplane for interface-address programming, on netlink platforms. Other platforms just stubbed at this point. Signed-off-by: Mark Stapp <mjs@voltanet.io>
-rw-r--r--zebra/if_netlink.c69
-rw-r--r--zebra/interface.c22
-rw-r--r--zebra/ioctl.c14
-rw-r--r--zebra/ioctl_solaris.c8
-rw-r--r--zebra/rt.h4
-rw-r--r--zebra/zebra_dplane.c180
6 files changed, 284 insertions, 13 deletions
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index b2f470bc8..264fd3c2d 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -894,6 +894,69 @@ static int netlink_address(int cmd, int family, struct interface *ifp,
0);
}
+/* Interface address modification. */
+static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int bytelen;
+ const struct prefix *p;
+ int cmd;
+ const char *label;
+
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg ifa;
+ char buf[NL_PKT_BUF_SIZE];
+ } req;
+
+ p = dplane_ctx_get_intf_addr(ctx);
+ memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
+
+ bytelen = (p->family == AF_INET ? 4 : 16);
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL)
+ cmd = RTM_NEWADDR;
+ else
+ cmd = RTM_DELADDR;
+
+ req.n.nlmsg_type = cmd;
+ req.ifa.ifa_family = p->family;
+
+ req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
+
+ addattr_l(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen);
+
+ if (p->family == AF_INET) {
+ if (dplane_ctx_intf_is_connected(ctx)) {
+ p = dplane_ctx_get_intf_dest(ctx);
+ addattr_l(&req.n, sizeof(req), IFA_ADDRESS,
+ &p->u.prefix, bytelen);
+ } else if (cmd == RTM_NEWADDR &&
+ dplane_ctx_intf_has_dest(ctx)) {
+ p = dplane_ctx_get_intf_dest(ctx);
+ addattr_l(&req.n, sizeof(req), IFA_BROADCAST,
+ &p->u.prefix, bytelen);
+ }
+ }
+
+ /* p is now either address or destination/bcast addr */
+ req.ifa.ifa_prefixlen = p->prefixlen;
+
+ if (dplane_ctx_intf_is_secondary(ctx))
+ SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY);
+
+ if (dplane_ctx_intf_has_label(ctx)) {
+ label = dplane_ctx_get_intf_label(ctx);
+ addattr_l(&req.n, sizeof(req), IFA_LABEL, label,
+ strlen(label) + 1);
+ }
+
+ return netlink_talk_info(netlink_talk_filter, &req.n,
+ dplane_ctx_get_ns(ctx), 0);
+}
+
int kernel_address_add_ipv4(struct interface *ifp, struct connected *ifc)
{
return netlink_address(RTM_NEWADDR, AF_INET, ifp, ifc);
@@ -914,6 +977,12 @@ int kernel_address_delete_ipv6(struct interface *ifp, struct connected *ifc)
return netlink_address(RTM_DELADDR, AF_INET6, ifp, ifc);
}
+enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx)
+{
+ return (netlink_address_ctx(ctx) == 0 ?
+ ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+}
+
int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
int len;
diff --git a/zebra/interface.c b/zebra/interface.c
index 229f9c1da..84815d7cd 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -442,6 +442,7 @@ static void if_addr_wakeup(struct interface *ifp)
struct connected *ifc;
struct prefix *p;
int ret;
+ enum zebra_dplane_result dplane_res;
for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) {
p = ifc->address;
@@ -479,12 +480,13 @@ static void if_addr_wakeup(struct interface *ifp)
if_refresh(ifp);
}
- ret = if_set_prefix(ifp, ifc);
- if (ret < 0) {
+ dplane_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res ==
+ ZEBRA_DPLANE_REQUEST_FAILURE) {
flog_err_sys(
EC_ZEBRA_IFACE_ADDR_ADD_FAILED,
"Can't set interface's address: %s",
- safe_strerror(errno));
+ dplane_res2str(dplane_res));
continue;
}
@@ -2626,6 +2628,7 @@ static int ip_address_install(struct vty *vty, struct interface *ifp,
struct connected *ifc;
struct prefix_ipv4 *p;
int ret;
+ enum zebra_dplane_result dplane_res;
if_data = ifp->info;
@@ -2699,10 +2702,10 @@ static int ip_address_install(struct vty *vty, struct interface *ifp,
if_refresh(ifp);
}
- ret = if_set_prefix(ifp, ifc);
- if (ret < 0) {
+ dplane_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
vty_out(vty, "%% Can't set interface IP address: %s.\n",
- safe_strerror(errno));
+ dplane_res2str(dplane_res));
return CMD_WARNING_CONFIG_FAILED;
}
@@ -2723,6 +2726,7 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp,
struct prefix_ipv4 lp, pp;
struct connected *ifc;
int ret;
+ enum zebra_dplane_result dplane_res;
/* Convert to prefix structure. */
ret = str2prefix_ipv4(addr_str, &lp);
@@ -2767,10 +2771,10 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp,
}
/* This is real route. */
- ret = if_unset_prefix(ifp, ifc);
- if (ret < 0) {
+ dplane_res = dplane_intf_addr_unset(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
vty_out(vty, "%% Can't unset interface IP address: %s.\n",
- safe_strerror(errno));
+ dplane_res2str(dplane_res));
return CMD_WARNING_CONFIG_FAILED;
}
UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index 9499c731e..45fad623e 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.c
@@ -180,6 +180,20 @@ void if_get_mtu(struct interface *ifp)
#endif
}
+/*
+ * TODO -- stub handler for interface address programming via the zebra dplane,
+ * for non-netlink platforms.
+ */
+#ifndef HAVE_NETLINK
+
+enum zebra_dplane_result kernel_address_update_ctx(
+ struct zebra_dplane_ctx *ctx)
+{
+ return -1;
+}
+
+#endif /* !HAVE_NETLINK */
+
#ifdef HAVE_NETLINK
/* Interface address setting via netlink interface. */
int if_set_prefix(struct interface *ifp, struct connected *ifc)
diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c
index c523ee983..e95916c56 100644
--- a/zebra/ioctl_solaris.c
+++ b/zebra/ioctl_solaris.c
@@ -183,6 +183,14 @@ void if_get_mtu(struct interface *ifp)
zebra_interface_up_update(ifp);
}
+/*
+ * Stub for new dataplane interface-address modification path.
+ */
+enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx)
+{
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+}
+
/* Set up interface's address, netmask (and broadcast? ).
Solaris uses ifname:number semantics to set IP address aliases. */
int if_set_prefix(struct interface *ifp, struct connected *ifc)
diff --git a/zebra/rt.h b/zebra/rt.h
index 2c77af2aa..f4b9bcb94 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -54,6 +54,10 @@ extern int kernel_address_add_ipv4(struct interface *, struct connected *);
extern int kernel_address_delete_ipv4(struct interface *, struct connected *);
extern int kernel_address_add_ipv6(struct interface *, struct connected *);
extern int kernel_address_delete_ipv6(struct interface *, struct connected *);
+
+enum zebra_dplane_result kernel_address_update_ctx(
+ struct zebra_dplane_ctx *ctx);
+
extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id);
extern int kernel_interface_set_master(struct interface *master,
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 703072c0f..d1b28227c 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -134,8 +134,9 @@ struct dplane_intf_info {
#define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */
#define DPLANE_INTF_SECONDARY (1 << 1)
-#define DPLANE_INTF_HAS_DEST (1 << 2)
-#define DPLANE_INTF_HAS_LABEL (1 << 3)
+#define DPLANE_INTF_BROADCAST (1 << 2)
+#define DPLANE_INTF_HAS_DEST (1 << 3)
+#define DPLANE_INTF_HAS_LABEL (1 << 4)
/* Interface address/prefix */
struct prefix prefix;
@@ -293,6 +294,9 @@ static struct zebra_dplane_globals {
_Atomic uint32_t dg_pws_in;
_Atomic uint32_t dg_pw_errors;
+ _Atomic uint32_t dg_intf_addrs_in;
+ _Atomic uint32_t dg_intf_addr_errors;
+
_Atomic uint32_t dg_update_yields;
/* Dataplane pthread */
@@ -330,6 +334,9 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
enum dplane_op_e op);
static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
enum dplane_op_e op);
+static enum zebra_dplane_result intf_addr_update_internal(
+ const struct interface *ifp, const struct connected *ifc,
+ enum dplane_op_e op);
/*
* Public APIs
@@ -438,6 +445,14 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
+ /* Maybe free label string, if allocated */
+ if ((*pctx)->u.intf.label != NULL &&
+ (*pctx)->u.intf.label != (*pctx)->u.intf.label_buf) {
+ free((*pctx)->u.intf.label);
+ (*pctx)->u.intf.label = NULL;
+ }
+ break;
+
case DPLANE_OP_NONE:
break;
}
@@ -942,6 +957,13 @@ bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx)
return (ctx->u.intf.flags & DPLANE_INTF_SECONDARY);
}
+bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST);
+}
+
const struct prefix *dplane_ctx_get_intf_addr(
const struct zebra_dplane_ctx *ctx)
{
@@ -1613,7 +1635,31 @@ done:
enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp,
const struct connected *ifc)
{
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+#if !defined(HAVE_NETLINK) && defined(HAVE_STRUCT_IFALIASREQ)
+ /* Extra checks for this OS path. */
+
+ /* Don't configure PtP addresses on broadcast ifs or reverse */
+ if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) {
+ if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("Failed to set intf addr: mismatch p2p and connected");
+
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+ }
+
+ /* Ensure that no existing installed v4 route conflicts with
+ * the new interface prefix. This check must be done in the
+ * zebra pthread context, and any route delete (if needed)
+ * is enqueued before the interface address programming attempt.
+ */
+ if (ifc->address->family == AF_INET) {
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)ifc->address;
+ rib_lookup_and_pushup(p, ifp->vrf_id);
+ }
+#endif
+
+ return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_INSTALL);
}
/*
@@ -1622,7 +1668,99 @@ enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp,
enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
const struct connected *ifc)
{
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_UNINSTALL);
+}
+
+static enum zebra_dplane_result intf_addr_update_internal(
+ const struct interface *ifp, const struct connected *ifc,
+ enum dplane_op_e op)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
+ struct zebra_dplane_ctx *ctx = NULL;
+ struct zebra_ns *zns;
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ char addr_str[PREFIX_STRLEN];
+
+ prefix2str(ifc->address, addr_str, sizeof(addr_str));
+
+ zlog_debug("init intf ctx %s: idx %d, addr %u:%s",
+ dplane_op2str(op), ifp->ifindex, ifp->vrf_id,
+ addr_str);
+ }
+
+ ctx = dplane_ctx_alloc();
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ ctx->zd_vrf_id = ifp->vrf_id;
+
+ zns = zebra_ns_lookup(ifp->vrf_id);
+ dplane_ctx_ns_init(ctx, zns, false);
+
+ /* Init the interface-addr-specific area */
+ memset(&ctx->u.intf, 0, sizeof(ctx->u.intf));
+
+ strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname));
+ ctx->u.intf.ifindex = ifp->ifindex;
+ ctx->u.intf.prefix = *(ifc->address);
+
+ if (if_is_broadcast(ifp))
+ ctx->u.intf.flags |= DPLANE_INTF_BROADCAST;
+
+ if (CONNECTED_PEER(ifc)) {
+ ctx->u.intf.dest_prefix = *(ifc->destination);
+ ctx->u.intf.flags |=
+ (DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST);
+ } else if (ifc->destination) {
+ ctx->u.intf.dest_prefix = *(ifc->destination);
+ ctx->u.intf.flags |= DPLANE_INTF_HAS_DEST;
+ }
+
+ if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
+ ctx->u.intf.flags |= DPLANE_INTF_SECONDARY;
+
+ if (ifc->label) {
+ size_t len;
+
+ ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL;
+
+ /* Use embedded buffer if it's adequate; else allocate. */
+ len = strlen(ifc->label);
+
+ if (len < sizeof(ctx->u.intf.label_buf)) {
+ strncpy(ctx->u.intf.label_buf, ifc->label,
+ sizeof(ctx->u.intf.label_buf));
+ ctx->u.intf.label = ctx->u.intf.label_buf;
+ } else {
+ ctx->u.intf.label = strdup(ifc->label);
+ }
+ }
+
+ ret = dplane_route_enqueue(ctx);
+
+done:
+
+ /* Increment counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ /* Error counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
+ 1, memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
}
/*
@@ -2009,6 +2147,35 @@ kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
}
/*
+ * Handler for kernel-facing interface address updates
+ */
+static enum zebra_dplane_result
+kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result res;
+
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ char dest_str[PREFIX_STRLEN];
+
+ prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
+ sizeof(dest_str));
+
+ zlog_debug("Dplane intf %s, idx %u, addr %s",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx), dest_str);
+ }
+
+ res = kernel_address_update_ctx(ctx);
+
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
+ 1, memory_order_relaxed);
+
+ return res;
+}
+
+/*
* Kernel provider callback
*/
static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
@@ -2057,6 +2224,11 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
res = kernel_dplane_pw_update(ctx);
break;
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ res = kernel_dplane_address_update(ctx);
+ break;
+
/* Ignore system 'notifications' - the kernel already knows */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE: