diff options
Diffstat (limited to 'zebra/zebra_dplane.c')
| -rw-r--r-- | zebra/zebra_dplane.c | 306 |
1 files changed, 305 insertions, 1 deletions
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index af54c3b5c7..d1b28227c3 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -122,6 +122,33 @@ struct dplane_pw_info { }; /* + * Interface/prefix info for the dataplane + */ +struct dplane_intf_info { + + char ifname[INTERFACE_NAMSIZ]; + ifindex_t ifindex; + + uint32_t metric; + uint32_t flags; + +#define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */ +#define DPLANE_INTF_SECONDARY (1 << 1) +#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; + + /* Dest address, for p2p, or broadcast prefix */ + struct prefix dest_prefix; + + char *label; + char label_buf[32]; +}; + +/* * The context block used to exchange info about route updates across * the boundary between the zebra main context (and pthread) and the * dataplane layer (and pthread). @@ -152,11 +179,12 @@ struct zebra_dplane_ctx { vrf_id_t zd_vrf_id; uint32_t zd_table_id; - /* Support info for either route or LSP update */ + /* Support info for different kinds of updates */ union { struct dplane_route_info rinfo; zebra_lsp_t lsp; struct dplane_pw_info pw; + struct dplane_intf_info intf; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -266,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 */ @@ -303,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 @@ -409,6 +443,16 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) } break; + 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; } @@ -549,6 +593,14 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_SYS_ROUTE_DELETE: ret = "SYS_ROUTE_DEL"; break; + + case DPLANE_OP_ADDR_INSTALL: + ret = "ADDR_INSTALL"; + break; + case DPLANE_OP_ADDR_UNINSTALL: + ret = "ADDR_UNINSTALL"; + break; + } return ret; @@ -868,6 +920,90 @@ dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx) return &(ctx->u.pw.nhg); } +/* Accessors for interface information */ +const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.ifname; +} + +ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.ifindex; +} + +uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.metric; +} + +/* Is interface addr p2p? */ +bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED); +} + +bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(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) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.intf.prefix); +} + +bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST); +} + +const struct prefix *dplane_ctx_get_intf_dest( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST) + return &(ctx->u.intf.dest_prefix); + else + return NULL; +} + +bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_HAS_LABEL); +} + +const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.label; +} + /* * End of dplane context accessors */ @@ -1494,6 +1630,140 @@ done: } /* + * Enqueue interface address add for the dataplane. + */ +enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp, + const struct connected *ifc) +{ +#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); +} + +/* + * Enqueue interface address remove/uninstall for the dataplane. + */ +enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, + const struct connected *ifc) +{ + 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; +} + +/* * Handler for 'show dplane' */ int dplane_show_helper(struct vty *vty, bool detailed) @@ -1877,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) @@ -1925,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: |
