diff options
author | Mark Stapp <mjs@voltanet.io> | 2019-01-16 19:40:31 +0100 |
---|---|---|
committer | Mark Stapp <mjs@voltanet.io> | 2019-04-22 19:49:27 +0200 |
commit | 6416880328557628f11dbc7a4ba96087a50fc0f4 (patch) | |
tree | b234222a11a1a2307594dd4eca63966c17445e6b | |
parent | zebra: add interface-address info for dataplane (diff) | |
download | frr-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.c | 69 | ||||
-rw-r--r-- | zebra/interface.c | 22 | ||||
-rw-r--r-- | zebra/ioctl.c | 14 | ||||
-rw-r--r-- | zebra/ioctl_solaris.c | 8 | ||||
-rw-r--r-- | zebra/rt.h | 4 | ||||
-rw-r--r-- | zebra/zebra_dplane.c | 180 |
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: |