diff options
Diffstat (limited to '')
-rw-r--r-- | nhrpd/nhrp_route.c | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c new file mode 100644 index 0000000..698c6d0 --- /dev/null +++ b/nhrpd/nhrp_route.c @@ -0,0 +1,524 @@ +/* NHRP routing functions + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "nhrpd.h" +#include "table.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "zclient.h" + +DEFINE_MTYPE_STATIC(NHRPD, NHRP_ROUTE, "NHRP routing entry"); + +static struct zclient *zclient; +static struct route_table *zebra_rib[AFI_MAX]; + +struct route_info { + union sockunion via; + struct interface *ifp; + struct interface *nhrp_ifp; +}; + +static struct route_node *nhrp_route_update_get(const struct prefix *p, + int create) +{ + struct route_node *rn; + afi_t afi = family2afi(PREFIX_FAMILY(p)); + + if (!zebra_rib[afi]) + return NULL; + + if (create) { + rn = route_node_get(zebra_rib[afi], p); + if (!rn->info) { + rn->info = XCALLOC(MTYPE_NHRP_ROUTE, + sizeof(struct route_info)); + route_lock_node(rn); + } + return rn; + } else { + return route_node_lookup(zebra_rib[afi], p); + } +} + +static void nhrp_route_update_put(struct route_node *rn) +{ + struct route_info *ri = rn->info; + + if (!ri->ifp && !ri->nhrp_ifp + && sockunion_is_null(&ri->via)) { + XFREE(MTYPE_NHRP_ROUTE, rn->info); + route_unlock_node(rn); + } + route_unlock_node(rn); +} + +static void nhrp_route_update_zebra(const struct prefix *p, + union sockunion *nexthop, + struct interface *ifp) +{ + struct route_node *rn; + struct route_info *ri; + + rn = nhrp_route_update_get(p, !sockunion_is_null(nexthop) || ifp); + if (rn) { + ri = rn->info; + ri->via = *nexthop; + ri->ifp = ifp; + nhrp_route_update_put(rn); + } +} + +static void nhrp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, reg ? ZEBRA_NHRP_NEIGH_REGISTER : + ZEBRA_NHRP_NEIGH_UNREGISTER, + vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + +void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp) +{ + struct route_node *rn; + struct route_info *ri; + + rn = nhrp_route_update_get(p, ifp != NULL); + if (rn) { + ri = rn->info; + ri->nhrp_ifp = ifp; + nhrp_route_update_put(rn); + } +} + +void nhrp_route_announce(int add, enum nhrp_cache_type type, + const struct prefix *p, struct interface *ifp, + const union sockunion *nexthop, uint32_t mtu) +{ + struct zapi_route api; + struct zapi_nexthop *api_nh; + union sockunion *nexthop_ref = (union sockunion *)nexthop; + + if (zclient->sock < 0) + return; + + memset(&api, 0, sizeof(api)); + api.type = ZEBRA_ROUTE_NHRP; + api.safi = SAFI_UNICAST; + api.vrf_id = VRF_DEFAULT; + api.prefix = *p; + + switch (type) { + case NHRP_CACHE_NEGATIVE: + zapi_route_set_blackhole(&api, BLACKHOLE_REJECT); + ifp = NULL; + nexthop = NULL; + break; + case NHRP_CACHE_DYNAMIC: + case NHRP_CACHE_NHS: + case NHRP_CACHE_STATIC: + /* Regular route, so these are announced + * to other routing daemons */ + break; + default: + SET_FLAG(api.flags, ZEBRA_FLAG_FIB_OVERRIDE); + break; + } + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + api.nexthop_num = 1; + api_nh = &api.nexthops[0]; + api_nh->vrf_id = VRF_DEFAULT; + + switch (api.prefix.family) { + case AF_INET: + if (api.prefix.prefixlen == IPV4_MAX_BITLEN && + nexthop_ref && + memcmp(&nexthop_ref->sin.sin_addr, &api.prefix.u.prefix4, + sizeof(struct in_addr)) == 0) { + nexthop_ref = NULL; + } + if (nexthop_ref) { + api_nh->gate.ipv4 = nexthop_ref->sin.sin_addr; + api_nh->type = NEXTHOP_TYPE_IPV4; + } + if (ifp) { + api_nh->ifindex = ifp->ifindex; + if (api_nh->type == NEXTHOP_TYPE_IPV4) + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + else + api_nh->type = NEXTHOP_TYPE_IFINDEX; + } + break; + case AF_INET6: + if (api.prefix.prefixlen == IPV6_MAX_BITLEN && + nexthop_ref && + memcmp(&nexthop_ref->sin6.sin6_addr, &api.prefix.u.prefix6, + sizeof(struct in6_addr)) == 0) { + nexthop_ref = NULL; + } + if (nexthop_ref) { + api_nh->gate.ipv6 = nexthop_ref->sin6.sin6_addr; + api_nh->type = NEXTHOP_TYPE_IPV6; + } + if (ifp) { + api_nh->ifindex = ifp->ifindex; + if (api_nh->type == NEXTHOP_TYPE_IPV6) + api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; + else + api_nh->type = NEXTHOP_TYPE_IFINDEX; + } + break; + } + if (mtu) { + SET_FLAG(api.message, ZAPI_MESSAGE_MTU); + api.mtu = mtu; + } + + if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) { + char buf[PREFIX_STRLEN]; + + zlog_debug( + "Zebra send: route %s %pFX nexthop %s metric %u count %d dev %s", + add ? "add" : "del", &api.prefix, + nexthop_ref ? inet_ntop(api.prefix.family, + &api_nh->gate, + buf, sizeof(buf)) + : "<onlink>", + api.metric, api.nexthop_num, ifp ? ifp->name : "none"); + } + + zclient_route_send(add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, zclient, + &api); +} + +int nhrp_route_read(ZAPI_CALLBACK_ARGS) +{ + struct zapi_route api; + struct zapi_nexthop *api_nh; + struct interface *ifp = NULL; + union sockunion nexthop_addr; + int added; + + if (zapi_route_decode(zclient->ibuf, &api) < 0) + return -1; + + /* we completely ignore srcdest routes for now. */ + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) + return 0; + + /* ignore our routes */ + if (api.type == ZEBRA_ROUTE_NHRP) + return 0; + + sockunion_family(&nexthop_addr) = AF_UNSPEC; + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { + api_nh = &api.nexthops[0]; + + nexthop_addr.sa.sa_family = api.prefix.family; + switch (nexthop_addr.sa.sa_family) { + case AF_INET: + nexthop_addr.sin.sin_addr = api_nh->gate.ipv4; + break; + case AF_INET6: + nexthop_addr.sin6.sin6_addr = api_nh->gate.ipv6; + break; + } + + if (api_nh->ifindex != IFINDEX_INTERNAL) + ifp = if_lookup_by_index(api_nh->ifindex, VRF_DEFAULT); + } + + added = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + debugf(NHRP_DEBUG_ROUTE, "if-route-%s: %pFX via %pSU dev %s", + added ? "add" : "del", &api.prefix, &nexthop_addr, + ifp ? ifp->name : "(none)"); + + nhrp_route_update_zebra(&api.prefix, &nexthop_addr, added ? ifp : NULL); + nhrp_shortcut_prefix_change(&api.prefix, !added); + + return 0; +} + +int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, + union sockunion *via, struct interface **ifp) +{ + struct route_node *rn; + struct route_info *ri; + struct prefix lookup; + afi_t afi = family2afi(sockunion_family(addr)); + + sockunion2hostprefix(addr, &lookup); + + rn = route_node_match(zebra_rib[afi], &lookup); + if (!rn) + return 0; + + ri = rn->info; + if (ri->nhrp_ifp) { + debugf(NHRP_DEBUG_ROUTE, "lookup %pFX: nhrp_if=%s", &lookup, + ri->nhrp_ifp->name); + + if (via) + sockunion_family(via) = AF_UNSPEC; + if (ifp) + *ifp = ri->nhrp_ifp; + } else { + debugf(NHRP_DEBUG_ROUTE, "lookup %pFX: zebra route dev %s", + &lookup, ri->ifp ? ri->ifp->name : "(none)"); + + if (via) + *via = ri->via; + if (ifp) + *ifp = ri->ifp; + } + if (p) + *p = rn->p; + route_unlock_node(rn); + return 1; +} + +enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, + union sockunion *addr, struct prefix *p, + struct nhrp_peer **peer) +{ + struct interface *ifp = in_ifp; + struct nhrp_interface *nifp; + struct nhrp_cache *c; + union sockunion via[4]; + uint32_t network_id = 0; + afi_t afi = family2afi(sockunion_family(addr)); + int i; + + if (ifp) { + nifp = ifp->info; + network_id = nifp->afi[afi].network_id; + + c = nhrp_cache_get(ifp, addr, 0); + if (c && c->cur.type == NHRP_CACHE_LOCAL) { + if (p) + memset(p, 0, sizeof(*p)); + return NHRP_ROUTE_LOCAL; + } + } + + for (i = 0; i < 4; i++) { + if (!nhrp_route_get_nexthop(addr, p, &via[i], &ifp)) + return NHRP_ROUTE_BLACKHOLE; + if (ifp) { + /* Departing from nbma network? */ + nifp = ifp->info; + if (network_id + && network_id != nifp->afi[afi].network_id) + return NHRP_ROUTE_OFF_NBMA; + } + if (sockunion_family(&via[i]) == AF_UNSPEC) + break; + /* Resolve via node, but return the prefix of first match */ + addr = &via[i]; + p = NULL; + } + + if (ifp) { + c = nhrp_cache_get(ifp, addr, 0); + if (c && c->cur.type >= NHRP_CACHE_DYNAMIC) { + if (p) + memset(p, 0, sizeof(*p)); + if (c->cur.type == NHRP_CACHE_LOCAL) + return NHRP_ROUTE_LOCAL; + if (peer) + *peer = nhrp_peer_ref(c->cur.peer); + return NHRP_ROUTE_NBMA_NEXTHOP; + } + } + + return NHRP_ROUTE_BLACKHOLE; +} + +static void nhrp_zebra_connected(struct zclient *zclient) +{ + zclient_send_reg_requests(zclient, VRF_DEFAULT); + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, + ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, + ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, true); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, true); +} + +static zclient_handler *const nhrp_handlers[] = { + [ZEBRA_INTERFACE_ADDRESS_ADD] = nhrp_interface_address_add, + [ZEBRA_INTERFACE_ADDRESS_DELETE] = nhrp_interface_address_delete, + [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = nhrp_route_read, + [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = nhrp_route_read, + [ZEBRA_NHRP_NEIGH_ADDED] = nhrp_neighbor_operation, + [ZEBRA_NHRP_NEIGH_REMOVED] = nhrp_neighbor_operation, + [ZEBRA_NHRP_NEIGH_GET] = nhrp_neighbor_operation, + [ZEBRA_GRE_UPDATE] = nhrp_gre_update, +}; + +void nhrp_zebra_init(void) +{ + zebra_rib[AFI_IP] = route_table_init(); + zebra_rib[AFI_IP6] = route_table_init(); + + zclient = zclient_new(master, &zclient_options_default, nhrp_handlers, + array_size(nhrp_handlers)); + zclient->zebra_connected = nhrp_zebra_connected; + zclient_init(zclient, ZEBRA_ROUTE_NHRP, 0, &nhrpd_privs); +} + +static void nhrp_table_node_cleanup(struct route_table *table, + struct route_node *node) +{ + if (!node->info) + return; + + XFREE(MTYPE_NHRP_ROUTE, node->info); +} + +void nhrp_send_zebra_configure_arp(struct interface *ifp, int family) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) { + debugf(NHRP_DEBUG_COMMON, "%s() : zclient not ready", + __func__); + return; + } + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_CONFIGURE_ARP, ifp->vrf->vrf_id); + stream_putc(s, family); + stream_putl(s, ifp->ifindex); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + +void nhrp_send_zebra_gre_source_set(struct interface *ifp, + unsigned int link_idx, + vrf_id_t link_vrf_id) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) { + zlog_err("%s : zclient not ready", __func__); + return; + } + if (link_idx == IFINDEX_INTERNAL || link_vrf_id == VRF_UNKNOWN) { + /* silently ignore */ + return; + } + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_GRE_SOURCE_SET, ifp->vrf->vrf_id); + stream_putl(s, ifp->ifindex); + stream_putl(s, link_idx); + stream_putl(s, link_vrf_id); + stream_putl(s, 0); /* mtu provisioning */ + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + +void nhrp_send_zebra_nbr(union sockunion *in, + union sockunion *out, + struct interface *ifp) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + s = zclient->obuf; + stream_reset(s); + zclient_neigh_ip_encode(s, out ? ZEBRA_NEIGH_IP_ADD : + ZEBRA_NEIGH_IP_DEL, in, out, + ifp, out ? ZEBRA_NEIGH_STATE_REACHABLE + : ZEBRA_NEIGH_STATE_FAILED); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + +int nhrp_send_zebra_gre_request(struct interface *ifp) +{ + return zclient_send_zebra_gre_request(zclient, ifp); +} + +void nhrp_zebra_terminate(void) +{ + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, false); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, false); + zclient_stop(zclient); + zclient_free(zclient); + + zebra_rib[AFI_IP]->cleanup = nhrp_table_node_cleanup; + zebra_rib[AFI_IP6]->cleanup = nhrp_table_node_cleanup; + route_table_finish(zebra_rib[AFI_IP]); + route_table_finish(zebra_rib[AFI_IP6]); +} + +int nhrp_gre_update(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct nhrp_gre_info gre_info, *val; + struct interface *ifp; + + /* result */ + s = zclient->ibuf; + if (vrf_id != VRF_DEFAULT) + return 0; + + /* read GRE information */ + STREAM_GETL(s, gre_info.ifindex); + STREAM_GETL(s, gre_info.ikey); + STREAM_GETL(s, gre_info.okey); + STREAM_GETL(s, gre_info.ifindex_link); + STREAM_GETL(s, gre_info.vrfid_link); + STREAM_GETL(s, gre_info.vtep_ip.s_addr); + STREAM_GETL(s, gre_info.vtep_ip_remote.s_addr); + if (gre_info.ifindex == IFINDEX_INTERNAL) + val = NULL; + else + val = hash_lookup(nhrp_gre_list, &gre_info); + if (val) { + if (gre_info.vtep_ip.s_addr != val->vtep_ip.s_addr || + gre_info.vrfid_link != val->vrfid_link || + gre_info.ifindex_link != val->ifindex_link || + gre_info.ikey != val->ikey || + gre_info.okey != val->okey) { + /* update */ + memcpy(val, &gre_info, sizeof(struct nhrp_gre_info)); + } + } else { + val = nhrp_gre_info_alloc(&gre_info); + } + ifp = if_lookup_by_index(gre_info.ifindex, vrf_id); + debugf(NHRP_DEBUG_EVENT, "%s: gre interface %d vr %d obtained from system", + ifp ? ifp->name : "<none>", gre_info.ifindex, vrf_id); + if (ifp) + nhrp_interface_update_nbma(ifp, val); + return 0; + +stream_failure: + zlog_err("%s(): error reading response ..", __func__); + return -1; +} |