From 731a75fef1a5dbae813b02aa795d386ec2106428 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 30 Jun 2017 12:26:04 -0300 Subject: [PATCH] zebra: add nexthop tracking for pseudowires If the remote end of a pseudowire becomes unreachable (no route or an unlabeled route), then it must be uninstalled. In the same way, when the remote end becomes reachable, the pseudowire must be installed. Signed-off-by: Renato Westphal --- zebra/zebra_pw.c | 9 +++++- zebra/zebra_pw.h | 1 + zebra/zebra_rnh.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-- zebra/zebra_rnh.h | 4 +++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 143224254b..35d8c84da1 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -27,6 +27,7 @@ #include "zebra/debug.h" #include "zebra/rib.h" #include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_pw.h" @@ -78,6 +79,9 @@ void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id, pw->ifname, zebra_route_string(pw->protocol)); + /* remove nexthop tracking */ + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + /* uninstall */ if (pw->status == PW_STATUS_UP) hook_call(pw_uninstall, pw); @@ -94,6 +98,8 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, uint32_t remote_label, uint8_t flags, union pw_protocol_fields *data) { + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + pw->ifindex = ifindex; pw->type = type; pw->af = af; @@ -104,7 +110,7 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, pw->data = *data; if (pw->enabled) - zebra_pw_update(pw); + zebra_register_rnh_pseudowire(pw->vrf_id, pw); else zebra_pw_uninstall(pw); } @@ -120,6 +126,7 @@ void zebra_pw_update(struct zebra_pw *pw) { if (zebra_pw_check_reachability(pw) < 0) { zebra_pw_uninstall(pw); + /* wait for NHT and try again later */ } else { /* * Install or reinstall the pseudowire (e.g. to update diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h index 62f45db5cd..382eaeaa4f 100644 --- a/zebra/zebra_pw.h +++ b/zebra/zebra_pw.h @@ -43,6 +43,7 @@ struct zebra_pw { int status; uint8_t protocol; struct zserv *client; + struct rnh *rnh; struct thread *install_retry_timer; }; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 8ab46f683c..65698f30ae 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -126,6 +126,7 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type) rnh->client_list = list_new(); rnh->vrf_id = vrfid; rnh->zebra_static_route_list = list_new(); + rnh->zebra_pseudowire_list = list_new(); route_lock_node(rn); rn->info = rnh; rnh->node = rn; @@ -161,6 +162,7 @@ void zebra_free_rnh(struct rnh *rnh) rnh->flags |= ZEBRA_NHT_DELETED; list_free(rnh->client_list); list_free(rnh->zebra_static_route_list); + list_free(rnh->zebra_pseudowire_list); free_state(rnh->vrf_id, rnh->state, rnh->node); XFREE(MTYPE_RNH, rnh); } @@ -210,7 +212,8 @@ void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, } listnode_delete(rnh->client_list, client); if (list_isempty(rnh->client_list) - && list_isempty(rnh->zebra_static_route_list)) + && list_isempty(rnh->zebra_static_route_list) + && list_isempty(rnh->zebra_pseudowire_list)) zebra_delete_rnh(rnh, type); } @@ -237,7 +240,8 @@ void zebra_deregister_rnh_static_nh(vrf_id_t vrf_id, struct prefix *nh, listnode_delete(rnh->zebra_static_route_list, static_rn); if (list_isempty(rnh->client_list) - && list_isempty(rnh->zebra_static_route_list)) + && list_isempty(rnh->zebra_static_route_list) + && list_isempty(rnh->zebra_pseudowire_list)) zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE); } @@ -284,6 +288,58 @@ void zebra_deregister_rnh_static_nexthops(vrf_id_t vrf_id, } } +/* XXX move this utility function elsewhere? */ +static void addr2hostprefix(int af, const union g_addr *addr, + struct prefix *prefix) +{ + switch (af) { + case AF_INET: + prefix->family = AF_INET; + prefix->prefixlen = IPV4_MAX_BITLEN; + prefix->u.prefix4 = addr->ipv4; + break; + case AF_INET6: + prefix->family = AF_INET6; + prefix->prefixlen = IPV6_MAX_BITLEN; + prefix->u.prefix6 = addr->ipv6; + break; + default: + zlog_warn("%s: unknown address family %d", __func__, af); + break; + } +} + +void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) +{ + struct prefix nh; + struct rnh *rnh; + + addr2hostprefix(pw->af, &pw->nexthop, &nh); + rnh = zebra_add_rnh(&nh, vrf_id, RNH_NEXTHOP_TYPE); + if (rnh && !listnode_lookup(rnh->zebra_pseudowire_list, pw)) { + listnode_add(rnh->zebra_pseudowire_list, pw); + pw->rnh = rnh; + zebra_evaluate_rnh(vrf_id, pw->af, 1, RNH_NEXTHOP_TYPE, &nh); + } +} + +void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) +{ + struct rnh *rnh; + + rnh = pw->rnh; + if (!rnh) + return; + + listnode_delete(rnh->zebra_pseudowire_list, pw); + pw->rnh = NULL; + + if (list_isempty(rnh->client_list) + && list_isempty(rnh->zebra_static_route_list) + && list_isempty(rnh->zebra_pseudowire_list)) + zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE); +} + /* Apply the NHT route-map for a client to the route (and nexthops) * resolving a NH. */ @@ -595,6 +651,15 @@ static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family, } } +static void zebra_rnh_process_pseudowires(vrf_id_t vrfid, struct rnh *rnh) +{ + struct zebra_pw *pw; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(rnh->zebra_pseudowire_list, node, pw)) + zebra_pw_update(pw); +} + /* * See if a tracked nexthop entry has undergone any change, and if so, * take appropriate action; this involves notifying any clients and/or @@ -636,6 +701,9 @@ static void zebra_rnh_eval_nexthop_entry(vrf_id_t vrfid, int family, int force, /* Process static routes attached to this nexthop */ zebra_rnh_process_static_routes(vrfid, family, nrn, rnh, prn, rnh->state); + + /* Process pseudowires attached to this nexthop */ + zebra_rnh_process_pseudowires(vrfid, rnh); } } @@ -1003,5 +1071,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty) if (!list_isempty(rnh->zebra_static_route_list)) vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); + if (!list_isempty(rnh->zebra_pseudowire_list)) + vty_out(vty, " zebra[pseudowires]"); vty_out(vty, "\n"); } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index f8d89ec8ca..7e183684da 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -42,6 +42,8 @@ struct rnh { struct list * zebra_static_route_list; /* static routes dependent on this NH */ + struct list + *zebra_pseudowire_list; /* pseudowires dependent on this NH */ struct route_node *node; int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */ @@ -67,6 +69,8 @@ extern void zebra_deregister_rnh_static_nexthops(vrf_id_t, struct route_node *rn); extern void zebra_deregister_rnh_static_nh(vrf_id_t, struct prefix *, struct route_node *); +extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *); +extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, rnh_type_t type); extern void zebra_evaluate_rnh(vrf_id_t vrfid, int family, int force, -- 2.39.5