Skip to content

Commit

Permalink
pimd: rewrite MSDP neighbor selection logic
Browse files Browse the repository at this point in the history
The previous logic was backwards.

Signed-off-by: David Lamparter <[email protected]>
  • Loading branch information
eqvinox authored and rzalamena committed Dec 28, 2024
1 parent 30b301a commit d725ae5
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 51 deletions.
171 changes: 160 additions & 11 deletions pimd/pim_msdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -705,27 +705,170 @@ static int pim_msdp_sa_comp(const void *p1, const void *p2)
return pim_sgaddr_cmp(sa1->sg, sa2->sg);
}

DEFINE_MTYPE_STATIC(PIMD, MSDP_RP_CACHE, "MSDP RP cache entry");

struct msdp_rp_cache {
struct msdp_rp_cache_item item;

struct in_addr rp_addr;
struct in_addr nexthop_addr;
uint32_t nexthop_asn;
struct in_addr selected_msdp;

struct pim_instance *pim;
struct timeval lastuse;
struct event *refresh;
};

static int msdp_rp_cache_cmp(const struct msdp_rp_cache *a, const struct msdp_rp_cache *b)
{
return IPV4_ADDR_CMP(&a->rp_addr, &b->rp_addr);
}

static uint32_t msdp_rp_cache_hash(const struct msdp_rp_cache *a)
{
return jhash_1word(a->rp_addr.s_addr, 0x4e622350);
}

DECLARE_HASH(msdp_rp_cache, struct msdp_rp_cache, item, msdp_rp_cache_cmp, msdp_rp_cache_hash);

static void msdp_rp_cache_update(struct msdp_rp_cache *ent)
{
struct pim_nexthop nexthop = {};
uint32_t asn = 0;
struct pim_msdp_peer *mp;
unsigned best_preference = ~0U;
const char *rule = "nothing found";
struct in_addr best_addr = {};
struct listnode *node;

if (!pim_route_lookup(ent->pim, ent->rp_addr, &asn, &nexthop)) {
ent->nexthop_addr.s_addr = INADDR_ANY;
ent->nexthop_asn = 0;
return;
}

if (PIM_DEBUG_ZEBRA && (IPV4_ADDR_CMP(&ent->nexthop_addr, &nexthop.mrib_nexthop_addr) ||
ent->nexthop_asn != asn)) {
zlog_debug("MSDP RP %pI4: nexthop changed to %pI4 [AS%u] (was %pI4 [AS%u])",
&ent->rp_addr, &nexthop.mrib_nexthop_addr, asn, &ent->nexthop_addr,
ent->nexthop_asn);
}

ent->nexthop_addr = nexthop.mrib_nexthop_addr;
ent->nexthop_asn = asn;

for (ALL_LIST_ELEMENTS_RO(ent->pim->msdp.peer_list, node, mp)) {
if (mp->state != PIM_MSDP_ESTABLISHED)
continue;

/* N == R */
if (!IPV4_ADDR_CMP(&mp->peer, &ent->rp_addr)) {
best_preference = 1;
best_addr = mp->peer;
rule = "(i) MSDP peer is RP";
if (IPV4_ADDR_CMP(&mp->peer, &ent->nexthop_addr))
rule = "(i) MSDP peer is RP [NOTE: RPF mismatch]";
/* no point in looking further */
break;
}

/* is nexthop */
if (!IPV4_ADDR_CMP(&mp->peer, &ent->nexthop_addr) && best_preference > 3) {
best_preference = 3;
best_addr = mp->peer;
rule = "(ii/iii) MSDP peer is nexthop to RP";
}

if (best_preference == 3)
continue;

if (ent->nexthop_asn == mp->asn &&
(best_preference > 4 || IPV4_ADDR_CMP(&best_addr, &mp->peer) > 0)) {
best_preference = 4;
best_addr = mp->peer;
rule = "(iv) MSDP peer is in closest AS to RP";
}

if (best_preference == 4)
continue;

/* 5 - static peer - not supported */
}

if (PIM_DEBUG_ZEBRA && IPV4_ADDR_CMP(&ent->selected_msdp, &best_addr))
zlog_debug("MSDP RP %pI4: best MSDP peer changed to %pI4 [%s] (was %pI4)",
&ent->rp_addr, &best_addr, rule, &ent->selected_msdp);

ent->selected_msdp = best_addr;
}

static void msdp_rp_cache_timer(struct event *e)
{
struct msdp_rp_cache *ent = EVENT_ARG(e);
int64_t us_since;

us_since = monotime_since(&ent->lastuse, NULL);
if (us_since > 135 * 1000 * 1000) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("MSDP RP %pI4: expired, dropping", &ent->rp_addr);

msdp_rp_cache_del(ent->pim->msdp.rp_cache, ent);
XFREE(MTYPE_MSDP_RP_CACHE, ent);
return;
}

msdp_rp_cache_update(ent);

event_add_timer_msec(ent->pim->msdp.master, msdp_rp_cache_timer, ent, 60000, &ent->refresh);
}

static struct msdp_rp_cache *msdp_rp_cache_get(struct pim_instance *pim, struct in_addr addr)
{
struct msdp_rp_cache *ent, ref = { .rp_addr = addr };

ent = msdp_rp_cache_find(pim->msdp.rp_cache, &ref);
if (!ent) {
ent = XCALLOC(MTYPE_MSDP_RP_CACHE, sizeof(*ent));
ent->rp_addr = addr;
ent->pim = pim;

msdp_rp_cache_add(pim->msdp.rp_cache, ent);
msdp_rp_cache_update(ent);
event_add_timer_msec(pim->msdp.master, msdp_rp_cache_timer, ent, 59000,
&ent->refresh);
}

monotime(&ent->lastuse);
return ent;
}

static void msdp_rp_cache_clear(struct pim_instance *pim)
{
struct msdp_rp_cache *ent;

while ((ent = msdp_rp_cache_pop(pim->msdp.rp_cache))) {
EVENT_OFF(ent->refresh);
XFREE(MTYPE_MSDP_RP_CACHE, ent);
}

msdp_rp_cache_fini(pim->msdp.rp_cache);
}

/* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */
/* XXX: this can use a bit of refining and extensions */
bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
{
struct pim_nexthop nexthop = {0};
struct msdp_rp_cache *ent;

if (mp->peer.s_addr == rp.s_addr) {
ent = msdp_rp_cache_get(mp->pim, rp);
if (ent->selected_msdp.s_addr == mp->peer.s_addr)
return true;
}

/* check if the MSDP peer is the nexthop for the RP */
if (pim_route_lookup(mp->pim, rp, mp->asn, &nexthop) &&
nexthop.mrib_nexthop_addr.s_addr == mp->peer.s_addr) {
return true;
}

if (pim_msdp_log_sa_events(mp->pim))
zlog_info("MSDP peer %pI4 RPF failure for %pI4", &mp->peer, &rp);
zlog_info("MSDP peer %pI4 is not RPF for %pI4", &mp->peer, &rp);

mp->rpf_lookup_failure_count++;

return false;
}

Expand Down Expand Up @@ -1417,13 +1560,17 @@ void pim_msdp_init(struct pim_instance *pim, struct event_loop *master)
pim->msdp.hold_time = PIM_MSDP_PEER_HOLD_TIME;
pim->msdp.keep_alive = PIM_MSDP_PEER_KA_TIME;
pim->msdp.connection_retry = PIM_MSDP_PEER_CONNECT_RETRY_TIME;

msdp_rp_cache_init(pim->msdp.rp_cache);
}

/* counterpart to MSDP init; XXX: unused currently */
void pim_msdp_exit(struct pim_instance *pim)
{
struct pim_msdp_mg *mg;

msdp_rp_cache_clear(pim);

pim_msdp_sa_adv_timer_setup(pim, false);

/* Stop listener and delete all peer sessions */
Expand Down Expand Up @@ -1542,6 +1689,8 @@ void pim_msdp_shutdown(struct pim_instance *pim, bool state)
/* Disable and remove listener flag. */
UNSET_FLAG(pim->msdp.flags, PIM_MSDPF_ENABLE | PIM_MSDPF_LISTENER);
}

msdp_rp_cache_clear(pim);
} else {
pim->msdp.shutdown = false;

Expand Down
4 changes: 4 additions & 0 deletions pimd/pim_msdp.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ struct pim_msdp_listener {
struct event *thread;
};

PREDECL_HASH(msdp_rp_cache);

struct pim_msdp {
enum pim_msdp_flags flags;
struct event_loop *master;
Expand All @@ -224,6 +226,8 @@ struct pim_msdp {
/** List of mesh groups. */
struct pim_mesh_group_list mglist;

struct msdp_rp_cache_head rp_cache[1];

/** MSDP global hold time period. */
uint32_t hold_time;
/** MSDP global keep alive period. */
Expand Down
44 changes: 5 additions & 39 deletions pimd/pim_rpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2)
return 0;
}

bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t asn, struct pim_nexthop *pn)
bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t *asn, struct pim_nexthop *pn)
{
struct interface *ifp;
struct zroute_info *ri;
Expand Down Expand Up @@ -288,43 +288,10 @@ bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t asn, str
* RFC 4611 Section 2.1. Peering between PIM Border Routers.
*/
if (asn) {
long long first_asn;

/*
* We expect a BGP route so if no AS Path information is
* available it means this is not a BGP route.
*/
if (ri->ri_type != ZEBRA_ROUTE_BGP) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: expected BGP route for %pPA (got %d)", __func__,
&addr, ri->ri_type);
goto free_and_exit;
}

errno = 0;
first_asn = strtoll((char *)ri->ri_opaque, NULL, 10);
/* Check for number conversion failures. */
if (first_asn == LLONG_MIN || first_asn == LLONG_MAX) {
zlog_warn("%s: AS number overflow/underflow %s", __func__, ri->ri_opaque);
goto free_and_exit;
}
if (first_asn == 0 && errno != 0) {
zlog_warn("%s: AS number conversion failed: %s", __func__, strerror(errno));
goto free_and_exit;
}

/* AS did not match. */
if (first_asn != asn) {
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: next hop AS did not match for address %pPA (%u != %lld)",
__func__, &addr, asn, first_asn);
goto free_and_exit;
}

/* Proceed to validate next hop. */
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: next hop AS matched for address %pPA (AS %u)", __func__,
&addr, asn);
if (ri->ri_type == ZEBRA_ROUTE_BGP)
*asn = strtoull((char *)ri->ri_opaque, NULL, 10);
else
*asn = 0;
}

SLIST_FOREACH (rni, &ri->ri_nhlist, rni_entry) {
Expand Down Expand Up @@ -384,7 +351,6 @@ bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t asn, str
break;
}

free_and_exit:
zroute_info_free(&ri);
return found;
}
3 changes: 2 additions & 1 deletion pimd/pim_rpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf);

int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2);
void pim_rpf_set_refresh_time(struct pim_instance *pim);
bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t asn, struct pim_nexthop *pn);
bool pim_route_lookup(struct pim_instance *pim, pim_addr addr, uint32_t *asn,
struct pim_nexthop *pn);
#endif /* PIM_RPF_H */

0 comments on commit d725ae5

Please sign in to comment.