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;
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_delete_and_null(&rnh->client_list);
+ list_delete_and_null(&rnh->zebra_static_route_list);
+ list_delete_and_null(&rnh->zebra_pseudowire_list);
free_state(rnh->vrf_id, rnh->state, rnh->node);
XFREE(MTYPE_RNH, rnh);
}
}
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);
}
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);
}
}
}
+/* 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:
+ memset(prefix, 0, sizeof(*prefix));
+ 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.
*/
}
/*
- * Determine appropriate route (RE entry) resolving a tracked entry
- * (nexthop or BGP route for import).
+ * Determine appropriate route (RE entry) resolving a tracked BGP route
+ * for BGP route for import.
*/
-static struct route_entry *zebra_rnh_resolve_entry(vrf_id_t vrfid, int family,
- rnh_type_t type,
+static
+struct route_entry *zebra_rnh_resolve_import_entry(vrf_id_t vrfid,
+ int family,
struct route_node *nrn,
struct rnh *rnh,
struct route_node **prn)
if (!rn)
return NULL;
- /* When resolving nexthops, do not resolve via the default route unless
- * 'ip nht resolve-via-default' is configured.
- */
- if ((type == RNH_NEXTHOP_TYPE)
- && (is_default_prefix(&rn->p)
- && !nh_resolve_via_default(rn->p.family)))
- re = NULL;
- else if ((type == RNH_IMPORT_CHECK_TYPE)
- && CHECK_FLAG(rnh->flags, ZEBRA_NHT_EXACT_MATCH)
- && !prefix_same(&nrn->p, &rn->p))
- re = NULL;
- else {
- /* Identify appropriate route entry. */
- RNODE_FOREACH_RE(rn, re)
- {
- if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
- continue;
- if (!CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB))
- continue;
+ /* Unlock route node - we don't need to lock when walking the tree. */
+ route_unlock_node(rn);
- if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) {
- if (re->type == ZEBRA_ROUTE_CONNECT)
- break;
- if (re->type == ZEBRA_ROUTE_NHRP) {
- struct nexthop *nexthop;
- for (nexthop = re->nexthop; nexthop;
- nexthop = nexthop->next)
- if (nexthop->type
- == NEXTHOP_TYPE_IFINDEX)
- break;
- if (nexthop)
- break;
- }
- } else if ((type == RNH_IMPORT_CHECK_TYPE)
- && (re->type == ZEBRA_ROUTE_BGP))
- continue;
- else
- break;
- }
+ if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_EXACT_MATCH) &&
+ !prefix_same(&nrn->p, &rn->p))
+ return NULL;
+
+ /* Identify appropriate route entry. */
+ RNODE_FOREACH_RE(rn, re) {
+ if (!CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) &&
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) &&
+ (re->type != ZEBRA_ROUTE_BGP))
+ break;
}
- /* Need to unlock route node */
- route_unlock_node(rn);
if (re)
*prn = rn;
return re;
/* Evaluate each static route associated with this nexthop. */
for (ALL_LIST_ELEMENTS_RO(rnh->zebra_static_route_list, node,
static_rn)) {
- RNODE_FOREACH_RE(static_rn, sre)
- {
+ RNODE_FOREACH_RE (static_rn, sre) {
if (sre->type != ZEBRA_ROUTE_STATIC)
continue;
}
}
+/*
+ * Determine appropriate route (route entry) resolving a tracked
+ * nexthop.
+ */
+static struct route_entry *zebra_rnh_resolve_nexthop_entry(vrf_id_t vrfid,
+ int family,
+ struct route_node *nrn,
+ struct rnh *rnh,
+ struct route_node **prn)
+{
+ struct route_table *route_table;
+ struct route_node *rn;
+ struct route_entry *re;
+
+ *prn = NULL;
+
+ route_table = zebra_vrf_table(family2afi(family), SAFI_UNICAST, vrfid);
+ if (!route_table)
+ return NULL;
+
+ rn = route_node_match(route_table, &nrn->p);
+ if (!rn)
+ return NULL;
+
+ /* Unlock route node - we don't need to lock when walking the tree. */
+ route_unlock_node(rn);
+
+ /* While resolving nexthops, we may need to walk up the tree from the
+ * most-specific match. Do similar logic as in zebra_rib.c
+ */
+ while (rn) {
+ /* Do not resolve over default route unless allowed &&
+ * match route to be exact if so specified
+ */
+ if (is_default_prefix(&rn->p) &&
+ !rnh_resolve_via_default(rn->p.family))
+ return NULL;
+
+ /* Identify appropriate route entry. */
+ RNODE_FOREACH_RE(rn, re) {
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
+ continue;
+ if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
+ continue;
+
+ if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) {
+ if ((re->type == ZEBRA_ROUTE_CONNECT)
+ || (re->type == ZEBRA_ROUTE_STATIC))
+ break;
+ if (re->type == ZEBRA_ROUTE_NHRP) {
+ struct nexthop *nexthop;
+
+ for (nexthop = re->nexthop;
+ nexthop;
+ nexthop = nexthop->next)
+ if (nexthop->type
+ == NEXTHOP_TYPE_IFINDEX)
+ break;
+ if (nexthop)
+ break;
+ }
+ } else
+ break;
+ }
+
+ /* Route entry found, we're done; else, walk up the tree. */
+ if (re) {
+ *prn = rn;
+ return re;
+ }
+
+ if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
+ rn = rn->parent;
+ else
+ return NULL;
+ }
+
+ return NULL;
+}
+
+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
/* 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);
}
}
rnh = nrn->info;
/* Identify route entry (RE) resolving this tracked entry. */
- re = zebra_rnh_resolve_entry(vrfid, family, type, nrn, rnh, &prn);
+ if (type == RNH_IMPORT_CHECK_TYPE)
+ re = zebra_rnh_resolve_import_entry(vrfid, family, nrn,
+ rnh, &prn);
+ else
+ re = zebra_rnh_resolve_nexthop_entry(vrfid, family, nrn, rnh,
+ &prn);
/* If the entry cannot be resolved and that is also the existing state,
* there is nothing further to do.
rnh = nrn->info;
- re = zebra_rnh_resolve_entry(vrfid, family, type, nrn, rnh, &prn);
+ /* Identify route entry (RIB) resolving this tracked entry. */
+ if (type == RNH_IMPORT_CHECK_TYPE)
+ re = zebra_rnh_resolve_import_entry(vrfid, family, nrn,
+ rnh, &prn);
+ else
+ re = zebra_rnh_resolve_nexthop_entry(vrfid, family, nrn, rnh,
+ &prn);
- if (re)
+ if (re) {
UNSET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED);
+ UNSET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
+ }
}
/* Evaluate all tracked entries (nexthops or routes for import into BGP)
state->type = re->type;
state->distance = re->distance;
state->metric = re->metric;
+ state->vrf_id = re->vrf_id;
route_entry_copy_nexthops(state, re->nexthop);
rnh->state = state;
if (r1->nexthop_num != r2->nexthop_num)
return 1;
- if (CHECK_FLAG(r1->status, ROUTE_ENTRY_NEXTHOPS_CHANGED))
+ if (CHECK_FLAG(r1->status, ROUTE_ENTRY_NEXTHOPS_CHANGED)
+ || CHECK_FLAG(r1->status, ROUTE_ENTRY_LABELS_CHANGED))
return 1;
return 0;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, vrf_id);
+ zclient_create_header(s, cmd, vrf_id);
stream_putw(s, rn->p.family);
switch (rn->p.family) {
break;
}
if (re) {
+ stream_putc(s, re->type);
+ stream_putw(s, re->instance);
stream_putc(s, re->distance);
stream_putl(s, re->metric);
num = 0;
stream_putc(s, nexthop->type);
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
stream_put_in_addr(s,
&nexthop->gate.ipv4);
stream_putl(s, nexthop->ifindex);
case NEXTHOP_TYPE_IFINDEX:
stream_putl(s, nexthop->ifindex);
break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- stream_put_in_addr(s,
- &nexthop->gate.ipv4);
- stream_putl(s, nexthop->ifindex);
- break;
case NEXTHOP_TYPE_IPV6:
- stream_put(s, &nexthop->gate.ipv6, 16);
- stream_putl(s, nexthop->ifindex);
- break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
stream_put(s, &nexthop->gate.ipv6, 16);
stream_putl(s, nexthop->ifindex);
}
stream_putc_at(s, nump, num);
} else {
+ stream_putc(s, 0); // type
+ stream_putw(s, 0); // instance
stream_putc(s, 0); // distance
stream_putl(s, 0); // metric
stream_putc(s, 0); // nexthops
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");
}