mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-03 12:38:53 +00:00
Merge pull request #8731 from mjstapp/fix_pw_backups
zebra: Fix pseudowires with backup nexthops
This commit is contained in:
commit
a7c91c4246
@ -401,7 +401,7 @@ extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
|
|||||||
bool fromkernel);
|
bool fromkernel);
|
||||||
|
|
||||||
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
|
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
|
||||||
union g_addr *addr,
|
const union g_addr *addr,
|
||||||
struct route_node **rn_out);
|
struct route_node **rn_out);
|
||||||
extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id,
|
extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id,
|
||||||
struct in_addr addr,
|
struct in_addr addr,
|
||||||
|
@ -157,12 +157,17 @@ struct dplane_pw_info {
|
|||||||
int af;
|
int af;
|
||||||
int status;
|
int status;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
uint32_t nhg_id;
|
||||||
union g_addr dest;
|
union g_addr dest;
|
||||||
mpls_label_t local_label;
|
mpls_label_t local_label;
|
||||||
mpls_label_t remote_label;
|
mpls_label_t remote_label;
|
||||||
|
|
||||||
/* Nexthops */
|
/* Nexthops that are valid and installed */
|
||||||
struct nexthop_group nhg;
|
struct nexthop_group fib_nhg;
|
||||||
|
|
||||||
|
/* Primary and backup nexthop sets, copied from the resolving route. */
|
||||||
|
struct nexthop_group primary_nhg;
|
||||||
|
struct nexthop_group backup_nhg;
|
||||||
|
|
||||||
union pw_protocol_fields fields;
|
union pw_protocol_fields fields;
|
||||||
};
|
};
|
||||||
@ -664,11 +669,21 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
|
|||||||
case DPLANE_OP_PW_INSTALL:
|
case DPLANE_OP_PW_INSTALL:
|
||||||
case DPLANE_OP_PW_UNINSTALL:
|
case DPLANE_OP_PW_UNINSTALL:
|
||||||
/* Free allocated nexthops */
|
/* Free allocated nexthops */
|
||||||
if (ctx->u.pw.nhg.nexthop) {
|
if (ctx->u.pw.fib_nhg.nexthop) {
|
||||||
/* This deals with recursive nexthops too */
|
/* This deals with recursive nexthops too */
|
||||||
nexthops_free(ctx->u.pw.nhg.nexthop);
|
nexthops_free(ctx->u.pw.fib_nhg.nexthop);
|
||||||
|
|
||||||
ctx->u.pw.nhg.nexthop = NULL;
|
ctx->u.pw.fib_nhg.nexthop = NULL;
|
||||||
|
}
|
||||||
|
if (ctx->u.pw.primary_nhg.nexthop) {
|
||||||
|
nexthops_free(ctx->u.pw.primary_nhg.nexthop);
|
||||||
|
|
||||||
|
ctx->u.pw.primary_nhg.nexthop = NULL;
|
||||||
|
}
|
||||||
|
if (ctx->u.pw.backup_nhg.nexthop) {
|
||||||
|
nexthops_free(ctx->u.pw.backup_nhg.nexthop);
|
||||||
|
|
||||||
|
ctx->u.pw.backup_nhg.nexthop = NULL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1630,7 +1645,23 @@ dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx)
|
|||||||
{
|
{
|
||||||
DPLANE_CTX_VALID(ctx);
|
DPLANE_CTX_VALID(ctx);
|
||||||
|
|
||||||
return &(ctx->u.pw.nhg);
|
return &(ctx->u.pw.fib_nhg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nexthop_group *
|
||||||
|
dplane_ctx_get_pw_primary_nhg(const struct zebra_dplane_ctx *ctx)
|
||||||
|
{
|
||||||
|
DPLANE_CTX_VALID(ctx);
|
||||||
|
|
||||||
|
return &(ctx->u.pw.primary_nhg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nexthop_group *
|
||||||
|
dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx)
|
||||||
|
{
|
||||||
|
DPLANE_CTX_VALID(ctx);
|
||||||
|
|
||||||
|
return &(ctx->u.pw.backup_nhg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Accessors for interface information */
|
/* Accessors for interface information */
|
||||||
@ -2461,12 +2492,14 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
|
|||||||
enum dplane_op_e op,
|
enum dplane_op_e op,
|
||||||
struct zebra_pw *pw)
|
struct zebra_pw *pw)
|
||||||
{
|
{
|
||||||
|
int ret = EINVAL;
|
||||||
struct prefix p;
|
struct prefix p;
|
||||||
afi_t afi;
|
afi_t afi;
|
||||||
struct route_table *table;
|
struct route_table *table;
|
||||||
struct route_node *rn;
|
struct route_node *rn;
|
||||||
struct route_entry *re;
|
struct route_entry *re;
|
||||||
const struct nexthop_group *nhg;
|
const struct nexthop_group *nhg;
|
||||||
|
struct nexthop *nh, *newnh, *last_nh;
|
||||||
|
|
||||||
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
|
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
|
||||||
zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u",
|
zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u",
|
||||||
@ -2509,31 +2542,83 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
|
|||||||
|
|
||||||
afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6;
|
afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6;
|
||||||
table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id);
|
table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id);
|
||||||
if (table) {
|
if (table == NULL)
|
||||||
rn = route_node_match(table, &p);
|
goto done;
|
||||||
if (rn) {
|
|
||||||
RNODE_FOREACH_RE(rn, re) {
|
|
||||||
if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (re) {
|
rn = route_node_match(table, &p);
|
||||||
nhg = rib_get_fib_nhg(re);
|
if (rn == NULL)
|
||||||
if (nhg && nhg->nexthop)
|
goto done;
|
||||||
copy_nexthops(&(ctx->u.pw.nhg.nexthop),
|
|
||||||
nhg->nexthop, NULL);
|
|
||||||
|
|
||||||
/* Include any installed backup nexthops */
|
re = NULL;
|
||||||
nhg = rib_get_fib_backup_nhg(re);
|
RNODE_FOREACH_RE(rn, re) {
|
||||||
if (nhg && nhg->nexthop)
|
if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
|
||||||
copy_nexthops(&(ctx->u.pw.nhg.nexthop),
|
break;
|
||||||
nhg->nexthop, NULL);
|
|
||||||
}
|
|
||||||
route_unlock_node(rn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AOK;
|
if (re) {
|
||||||
|
/* We'll capture a 'fib' list of nexthops that meet our
|
||||||
|
* criteria: installed, and labelled.
|
||||||
|
*/
|
||||||
|
nhg = rib_get_fib_nhg(re);
|
||||||
|
last_nh = NULL;
|
||||||
|
|
||||||
|
if (nhg && nhg->nexthop) {
|
||||||
|
for (ALL_NEXTHOPS_PTR(nhg, nh)) {
|
||||||
|
if (!CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
|
||||||
|
|| CHECK_FLAG(nh->flags,
|
||||||
|
NEXTHOP_FLAG_RECURSIVE)
|
||||||
|
|| nh->nh_label == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
newnh = nexthop_dup(nh, NULL);
|
||||||
|
|
||||||
|
if (last_nh)
|
||||||
|
NEXTHOP_APPEND(last_nh, newnh);
|
||||||
|
else
|
||||||
|
ctx->u.pw.fib_nhg.nexthop = newnh;
|
||||||
|
last_nh = newnh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Include any installed backup nexthops also. */
|
||||||
|
nhg = rib_get_fib_backup_nhg(re);
|
||||||
|
if (nhg && nhg->nexthop) {
|
||||||
|
for (ALL_NEXTHOPS_PTR(nhg, nh)) {
|
||||||
|
if (!CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
|
||||||
|
|| CHECK_FLAG(nh->flags,
|
||||||
|
NEXTHOP_FLAG_RECURSIVE)
|
||||||
|
|| nh->nh_label == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
newnh = nexthop_dup(nh, NULL);
|
||||||
|
|
||||||
|
if (last_nh)
|
||||||
|
NEXTHOP_APPEND(last_nh, newnh);
|
||||||
|
else
|
||||||
|
ctx->u.pw.fib_nhg.nexthop = newnh;
|
||||||
|
last_nh = newnh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy primary nexthops; recursive info is included too */
|
||||||
|
assert(re->nhe != NULL); /* SA warning */
|
||||||
|
copy_nexthops(&(ctx->u.pw.primary_nhg.nexthop),
|
||||||
|
re->nhe->nhg.nexthop, NULL);
|
||||||
|
ctx->u.pw.nhg_id = re->nhe->id;
|
||||||
|
|
||||||
|
/* Copy backup nexthop info, if present */
|
||||||
|
if (re->nhe->backup_info && re->nhe->backup_info->nhe) {
|
||||||
|
copy_nexthops(&(ctx->u.pw.backup_nhg.nexthop),
|
||||||
|
re->nhe->backup_info->nhe->nhg.nexthop,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
route_unlock_node(rn);
|
||||||
|
|
||||||
|
ret = AOK;
|
||||||
|
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -437,6 +437,10 @@ const union pw_protocol_fields *dplane_ctx_get_pw_proto(
|
|||||||
const struct zebra_dplane_ctx *ctx);
|
const struct zebra_dplane_ctx *ctx);
|
||||||
const struct nexthop_group *dplane_ctx_get_pw_nhg(
|
const struct nexthop_group *dplane_ctx_get_pw_nhg(
|
||||||
const struct zebra_dplane_ctx *ctx);
|
const struct zebra_dplane_ctx *ctx);
|
||||||
|
const struct nexthop_group *
|
||||||
|
dplane_ctx_get_pw_primary_nhg(const struct zebra_dplane_ctx *ctx);
|
||||||
|
const struct nexthop_group *
|
||||||
|
dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx);
|
||||||
|
|
||||||
/* Accessors for interface information */
|
/* Accessors for interface information */
|
||||||
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);
|
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);
|
||||||
|
@ -54,6 +54,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, FEC, "MPLS FEC object");
|
|||||||
DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object");
|
DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object");
|
||||||
|
|
||||||
int mpls_enabled;
|
int mpls_enabled;
|
||||||
|
bool mpls_pw_reach_strict; /* Strict reachability checking */
|
||||||
|
|
||||||
/* static function declarations */
|
/* static function declarations */
|
||||||
|
|
||||||
@ -3977,6 +3978,7 @@ void zebra_mpls_init_tables(struct zebra_vrf *zvrf)
|
|||||||
void zebra_mpls_init(void)
|
void zebra_mpls_init(void)
|
||||||
{
|
{
|
||||||
mpls_enabled = 0;
|
mpls_enabled = 0;
|
||||||
|
mpls_pw_reach_strict = false;
|
||||||
|
|
||||||
if (mpls_kernel_init() < 0) {
|
if (mpls_kernel_init() < 0) {
|
||||||
flog_warn(EC_ZEBRA_MPLS_SUPPORT_DISABLED,
|
flog_warn(EC_ZEBRA_MPLS_SUPPORT_DISABLED,
|
||||||
|
@ -576,6 +576,7 @@ static inline int mpls_should_lsps_be_processed(struct route_node *rn)
|
|||||||
|
|
||||||
/* Global variables. */
|
/* Global variables. */
|
||||||
extern int mpls_enabled;
|
extern int mpls_enabled;
|
||||||
|
extern bool mpls_pw_reach_strict; /* Strict pseudowire reachability checking */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -458,6 +458,9 @@ int mpls_kernel_init(void)
|
|||||||
|
|
||||||
kr_state.rtseq = 1;
|
kr_state.rtseq = 1;
|
||||||
|
|
||||||
|
/* Strict pseudowire reachability checking required for obsd */
|
||||||
|
mpls_pw_reach_strict = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
126
zebra/zebra_pw.c
126
zebra/zebra_pw.c
@ -48,7 +48,7 @@ static int zebra_pw_enabled(struct zebra_pw *);
|
|||||||
static void zebra_pw_install(struct zebra_pw *);
|
static void zebra_pw_install(struct zebra_pw *);
|
||||||
static void zebra_pw_uninstall(struct zebra_pw *);
|
static void zebra_pw_uninstall(struct zebra_pw *);
|
||||||
static int zebra_pw_install_retry(struct thread *);
|
static int zebra_pw_install_retry(struct thread *);
|
||||||
static int zebra_pw_check_reachability(struct zebra_pw *);
|
static int zebra_pw_check_reachability(const struct zebra_pw *);
|
||||||
static void zebra_pw_update_status(struct zebra_pw *, int);
|
static void zebra_pw_update_status(struct zebra_pw *, int);
|
||||||
|
|
||||||
static inline int zebra_pw_compare(const struct zebra_pw *a,
|
static inline int zebra_pw_compare(const struct zebra_pw *a,
|
||||||
@ -243,14 +243,79 @@ static void zebra_pw_update_status(struct zebra_pw *pw, int status)
|
|||||||
zsend_pw_update(pw->client, pw);
|
zsend_pw_update(pw->client, pw);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int zebra_pw_check_reachability(struct zebra_pw *pw)
|
static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw,
|
||||||
|
struct route_entry *re)
|
||||||
{
|
{
|
||||||
struct route_entry *re;
|
const struct nexthop *nexthop;
|
||||||
struct nexthop *nexthop;
|
const struct nexthop_group *nhg;
|
||||||
|
bool found_p = false;
|
||||||
|
bool fail_p = false;
|
||||||
|
|
||||||
/* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
|
/* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
|
||||||
|
|
||||||
/* find route to the remote end of the pseudowire */
|
/* All active nexthops must be labelled; look at
|
||||||
|
* primary and backup fib lists, in case there's been
|
||||||
|
* a backup nexthop activation.
|
||||||
|
*/
|
||||||
|
nhg = rib_get_fib_nhg(re);
|
||||||
|
if (nhg && nhg->nexthop) {
|
||||||
|
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
|
||||||
|
if (nexthop->nh_label != NULL)
|
||||||
|
found_p = true;
|
||||||
|
else {
|
||||||
|
fail_p = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail_p)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
nhg = rib_get_fib_backup_nhg(re);
|
||||||
|
if (nhg && nhg->nexthop) {
|
||||||
|
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
|
||||||
|
if (nexthop->nh_label != NULL)
|
||||||
|
found_p = true;
|
||||||
|
else {
|
||||||
|
fail_p = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
if (fail_p || !found_p) {
|
||||||
|
if (IS_ZEBRA_DEBUG_PW)
|
||||||
|
zlog_debug("%s: unlabeled route for %s",
|
||||||
|
__func__, pw->ifname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zebra_pw_check_reachability(const struct zebra_pw *pw)
|
||||||
|
{
|
||||||
|
struct route_entry *re;
|
||||||
|
const struct nexthop *nexthop;
|
||||||
|
const struct nexthop_group *nhg;
|
||||||
|
bool found_p = false;
|
||||||
|
|
||||||
|
/* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
|
||||||
|
|
||||||
|
/* Find route to the remote end of the pseudowire */
|
||||||
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
|
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
|
||||||
&pw->nexthop, NULL);
|
&pw->nexthop, NULL);
|
||||||
if (!re) {
|
if (!re) {
|
||||||
@ -260,19 +325,52 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Stricter checking for some OSes (OBSD, e.g.) */
|
||||||
* Need to ensure that there's a label binding for all nexthops.
|
if (mpls_pw_reach_strict)
|
||||||
* Otherwise, ECMP for this route could render the pseudowire unusable.
|
return zebra_pw_check_reachability_strict(pw, re);
|
||||||
|
|
||||||
|
/* There must be at least one installed labelled nexthop;
|
||||||
|
* look at primary and backup fib lists, in case there's been
|
||||||
|
* a backup nexthop activation.
|
||||||
*/
|
*/
|
||||||
for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
|
nhg = rib_get_fib_nhg(re);
|
||||||
if (!nexthop->nh_label) {
|
if (nhg && nhg->nexthop) {
|
||||||
if (IS_ZEBRA_DEBUG_PW)
|
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
|
||||||
zlog_debug("%s: unlabeled route for %s",
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
|
||||||
__func__, pw->ifname);
|
continue;
|
||||||
return -1;
|
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
|
||||||
|
nexthop->nh_label != NULL) {
|
||||||
|
found_p = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (found_p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nhg = rib_get_fib_backup_nhg(re);
|
||||||
|
if (nhg && nhg->nexthop) {
|
||||||
|
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
|
||||||
|
nexthop->nh_label != NULL) {
|
||||||
|
found_p = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_p) {
|
||||||
|
if (IS_ZEBRA_DEBUG_PW)
|
||||||
|
zlog_debug("%s: unlabeled route for %s",
|
||||||
|
__func__, pw->ifname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +332,8 @@ void rib_handle_nhg_replace(struct nhg_hash_entry *old_entry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
|
struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
|
||||||
union g_addr *addr, struct route_node **rn_out)
|
const union g_addr *addr,
|
||||||
|
struct route_node **rn_out)
|
||||||
{
|
{
|
||||||
struct prefix p;
|
struct prefix p;
|
||||||
struct route_table *table;
|
struct route_table *table;
|
||||||
|
Loading…
Reference in New Issue
Block a user