mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-27 00:31:32 +00:00

pim has two zclient sockets to zebra. One is used exclusively to do mrib lookups. The other is to do the normal day to day communication between pim and zebra. With the change to the zebra api to send up all data to all sockets this caused the mrib lookup socket to accumulate data in between mrib lookups. So if at some point in time we get upcoming data but no mrib lookups modify the code to find the mrib lookup it is looking for. Long term we need to figure something else out but this change will get us moving forward again. Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com> Reviewed-by: Daniel Walton <dwalton@cumulusnetworks.com>
448 lines
12 KiB
C
448 lines
12 KiB
C
/*
|
|
PIM for Quagga
|
|
Copyright (C) 2008 Everton da Silva Marques
|
|
|
|
This program is free software; you can redistribute it 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.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING; if not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
|
MA 02110-1301 USA
|
|
|
|
$QuaggaId: $Format:%an, %ai, %h$ $
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
#include "zebra/rib.h"
|
|
|
|
#include "log.h"
|
|
#include "prefix.h"
|
|
#include "zclient.h"
|
|
#include "stream.h"
|
|
#include "network.h"
|
|
#include "thread.h"
|
|
|
|
#include "pimd.h"
|
|
#include "pim_pim.h"
|
|
#include "pim_str.h"
|
|
#include "pim_zlookup.h"
|
|
|
|
extern int zclient_debug;
|
|
|
|
static void zclient_lookup_sched(struct zclient *zlookup, int delay);
|
|
|
|
/* Connect to zebra for nexthop lookup. */
|
|
static int zclient_lookup_connect(struct thread *t)
|
|
{
|
|
struct zclient *zlookup;
|
|
|
|
zlookup = THREAD_ARG(t);
|
|
zlookup->t_connect = NULL;
|
|
|
|
if (zlookup->sock >= 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (zclient_socket_connect(zlookup) < 0) {
|
|
++zlookup->fail;
|
|
zlog_warn("%s: failure connecting zclient socket: failures=%d",
|
|
__PRETTY_FUNCTION__, zlookup->fail);
|
|
}
|
|
else {
|
|
zlookup->fail = 0; /* reset counter on connection */
|
|
}
|
|
|
|
zassert(!zlookup->t_connect);
|
|
if (zlookup->sock < 0) {
|
|
/* Since last connect failed, retry within 10 secs */
|
|
zclient_lookup_sched(zlookup, 10);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Schedule connection with delay. */
|
|
static void zclient_lookup_sched(struct zclient *zlookup, int delay)
|
|
{
|
|
zassert(!zlookup->t_connect);
|
|
|
|
THREAD_TIMER_ON(master, zlookup->t_connect,
|
|
zclient_lookup_connect,
|
|
zlookup, delay);
|
|
|
|
zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
|
|
__PRETTY_FUNCTION__, delay);
|
|
}
|
|
|
|
/* Schedule connection for now. */
|
|
static void zclient_lookup_sched_now(struct zclient *zlookup)
|
|
{
|
|
zassert(!zlookup->t_connect);
|
|
|
|
zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
|
|
zlookup, 0);
|
|
|
|
zlog_notice("%s: zclient lookup immediate connection scheduled",
|
|
__PRETTY_FUNCTION__);
|
|
}
|
|
|
|
/* Schedule reconnection, if needed. */
|
|
static void zclient_lookup_reconnect(struct zclient *zlookup)
|
|
{
|
|
if (zlookup->t_connect) {
|
|
return;
|
|
}
|
|
|
|
zclient_lookup_sched_now(zlookup);
|
|
}
|
|
|
|
static void zclient_lookup_failed(struct zclient *zlookup)
|
|
{
|
|
if (zlookup->sock >= 0) {
|
|
if (close(zlookup->sock)) {
|
|
zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock,
|
|
errno, safe_strerror(errno));
|
|
}
|
|
zlookup->sock = -1;
|
|
}
|
|
|
|
zclient_lookup_reconnect(zlookup);
|
|
}
|
|
|
|
struct zclient *zclient_lookup_new()
|
|
{
|
|
struct zclient *zlookup;
|
|
|
|
zlookup = zclient_new (master);
|
|
if (!zlookup) {
|
|
zlog_err("%s: zclient_new() failure",
|
|
__PRETTY_FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
zlookup->sock = -1;
|
|
zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
|
zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
|
zlookup->t_connect = 0;
|
|
|
|
zclient_lookup_sched_now(zlookup);
|
|
|
|
zlog_notice("%s: zclient lookup socket initialized",
|
|
__PRETTY_FUNCTION__);
|
|
|
|
return zlookup;
|
|
}
|
|
|
|
static int zclient_read_nexthop(struct zclient *zlookup,
|
|
struct pim_zlookup_nexthop nexthop_tab[],
|
|
const int tab_size,
|
|
struct in_addr addr)
|
|
{
|
|
int num_ifindex = 0;
|
|
struct stream *s;
|
|
const uint16_t MIN_LEN = 10; /* getipv4=4 getc=1 getl=4 getc=1 */
|
|
uint16_t length;
|
|
u_char marker;
|
|
u_char version;
|
|
vrf_id_t vrf_id;
|
|
uint16_t command = 0;
|
|
struct in_addr raddr;
|
|
uint8_t distance;
|
|
uint32_t metric;
|
|
int nexthop_num;
|
|
int i, err;
|
|
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_debug("%s: addr=%s",
|
|
__PRETTY_FUNCTION__,
|
|
addr_str);
|
|
}
|
|
|
|
s = zlookup->ibuf;
|
|
|
|
while (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB)
|
|
{
|
|
stream_reset(s);
|
|
err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
|
|
&vrf_id, &command);
|
|
if (err < 0) {
|
|
zlog_err("%s %s: zclient_read_header() failed",
|
|
__FILE__, __PRETTY_FUNCTION__);
|
|
zclient_lookup_failed(zlookup);
|
|
return -1;
|
|
}
|
|
|
|
if (length < MIN_LEN) {
|
|
zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
|
|
__FILE__, __PRETTY_FUNCTION__, length, MIN_LEN);
|
|
zclient_lookup_failed(zlookup);
|
|
return -2;
|
|
}
|
|
|
|
if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) {
|
|
zlog_err("%s: socket %d version mismatch, marker %d, version %d",
|
|
__func__, zlookup->sock, marker, version);
|
|
zclient_lookup_failed(zlookup);
|
|
return -4;
|
|
}
|
|
}
|
|
|
|
raddr.s_addr = stream_get_ipv4(s);
|
|
|
|
if (raddr.s_addr != addr.s_addr) {
|
|
char addr_str[100];
|
|
char raddr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
|
|
zlog_warn("%s: address mismatch: addr=%s raddr=%s",
|
|
__PRETTY_FUNCTION__,
|
|
addr_str, raddr_str);
|
|
/* warning only */
|
|
}
|
|
|
|
distance = stream_getc(s);
|
|
metric = stream_getl(s);
|
|
nexthop_num = stream_getc(s);
|
|
|
|
if (nexthop_num < 1) {
|
|
zlog_err("%s: socket %d bad nexthop_num=%d",
|
|
__func__, zlookup->sock, nexthop_num);
|
|
return -6;
|
|
}
|
|
|
|
length -= MIN_LEN;
|
|
|
|
for (i = 0; i < nexthop_num; ++i) {
|
|
enum nexthop_types_t nexthop_type;
|
|
|
|
if (length < 1) {
|
|
zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
|
|
__func__, zlookup->sock, length);
|
|
return -7;
|
|
}
|
|
|
|
nexthop_type = stream_getc(s);
|
|
--length;
|
|
|
|
switch (nexthop_type) {
|
|
case ZEBRA_NEXTHOP_IFINDEX:
|
|
case ZEBRA_NEXTHOP_IPV4_IFINDEX:
|
|
if (num_ifindex >= tab_size) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
(num_ifindex + 1), tab_size, addr_str);
|
|
return num_ifindex;
|
|
}
|
|
if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) {
|
|
if (length < 4) {
|
|
zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
|
|
__func__, zlookup->sock, length);
|
|
return -8;
|
|
}
|
|
nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
|
|
length -= 4;
|
|
}
|
|
else {
|
|
nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
|
|
}
|
|
nexthop_tab[num_ifindex].ifindex = stream_getl(s);
|
|
nexthop_tab[num_ifindex].protocol_distance = distance;
|
|
nexthop_tab[num_ifindex].route_metric = metric;
|
|
++num_ifindex;
|
|
break;
|
|
case ZEBRA_NEXTHOP_IPV4:
|
|
if (num_ifindex >= tab_size) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
(num_ifindex + 1), tab_size, addr_str);
|
|
return num_ifindex;
|
|
}
|
|
nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
|
|
length -= 4;
|
|
nexthop_tab[num_ifindex].ifindex = 0;
|
|
nexthop_tab[num_ifindex].protocol_distance = distance;
|
|
nexthop_tab[num_ifindex].route_metric = metric;
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
char nexthop_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
|
|
zlog_debug("%s %s: zebra returned recursive nexthop %s for address %s",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
nexthop_str, addr_str);
|
|
}
|
|
++num_ifindex;
|
|
break;
|
|
default:
|
|
/* do nothing */
|
|
{
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
nexthop_type, addr_str);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return num_ifindex;
|
|
}
|
|
|
|
static int zclient_lookup_nexthop_once(struct zclient *zlookup,
|
|
struct pim_zlookup_nexthop nexthop_tab[],
|
|
const int tab_size,
|
|
struct in_addr addr)
|
|
{
|
|
struct stream *s;
|
|
int ret;
|
|
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_debug("%s: addr=%s",
|
|
__PRETTY_FUNCTION__,
|
|
addr_str);
|
|
}
|
|
|
|
/* Check socket. */
|
|
if (zlookup->sock < 0) {
|
|
zlog_err("%s %s: zclient lookup socket is not connected",
|
|
__FILE__, __PRETTY_FUNCTION__);
|
|
zclient_lookup_failed(zlookup);
|
|
return -1;
|
|
}
|
|
|
|
s = zlookup->obuf;
|
|
stream_reset(s);
|
|
zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, VRF_DEFAULT);
|
|
stream_put_in_addr(s, &addr);
|
|
stream_putw_at(s, 0, stream_get_endp(s));
|
|
|
|
ret = writen(zlookup->sock, s->data, stream_get_endp(s));
|
|
if (ret < 0) {
|
|
zlog_err("%s %s: writen() failure writing to zclient lookup socket",
|
|
__FILE__, __PRETTY_FUNCTION__);
|
|
zclient_lookup_failed(zlookup);
|
|
return -2;
|
|
}
|
|
if (ret == 0) {
|
|
zlog_err("%s %s: connection closed on zclient lookup socket",
|
|
__FILE__, __PRETTY_FUNCTION__);
|
|
zclient_lookup_failed(zlookup);
|
|
return -3;
|
|
}
|
|
|
|
return zclient_read_nexthop(zlookup, nexthop_tab,
|
|
tab_size, addr);
|
|
}
|
|
|
|
int zclient_lookup_nexthop(struct zclient *zlookup,
|
|
struct pim_zlookup_nexthop nexthop_tab[],
|
|
const int tab_size,
|
|
struct in_addr addr,
|
|
int max_lookup)
|
|
{
|
|
int lookup;
|
|
uint32_t route_metric = 0xFFFFFFFF;
|
|
uint8_t protocol_distance = 0xFF;
|
|
|
|
for (lookup = 0; lookup < max_lookup; ++lookup) {
|
|
int num_ifindex;
|
|
int first_ifindex;
|
|
struct in_addr nexthop_addr;
|
|
|
|
num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
|
|
PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
|
|
if (num_ifindex < 1) {
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_debug("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
lookup, max_lookup, addr_str);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (lookup < 1) {
|
|
/* this is the non-recursive lookup - save original metric/distance */
|
|
route_metric = nexthop_tab[0].route_metric;
|
|
protocol_distance = nexthop_tab[0].protocol_distance;
|
|
}
|
|
|
|
/*
|
|
FIXME: Non-recursive nexthop ensured only for first ifindex.
|
|
However, recursive route lookup should really be fixed in zebra daemon.
|
|
See also TODO T24.
|
|
*/
|
|
first_ifindex = nexthop_tab[0].ifindex;
|
|
nexthop_addr = nexthop_tab[0].nexthop_addr;
|
|
if (first_ifindex > 0) {
|
|
/* found: first ifindex is non-recursive nexthop */
|
|
|
|
if (lookup > 0) {
|
|
/* Report non-recursive success after first lookup */
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_debug("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
lookup, max_lookup, first_ifindex, addr_str,
|
|
nexthop_tab[0].protocol_distance,
|
|
nexthop_tab[0].route_metric);
|
|
}
|
|
|
|
/* use last address as nexthop address */
|
|
nexthop_tab[0].nexthop_addr = addr;
|
|
|
|
/* report original route metric/distance */
|
|
nexthop_tab[0].route_metric = route_metric;
|
|
nexthop_tab[0].protocol_distance = protocol_distance;
|
|
}
|
|
|
|
return num_ifindex;
|
|
}
|
|
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
char nexthop_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
|
|
zlog_debug("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
lookup, max_lookup, nexthop_str, addr_str,
|
|
nexthop_tab[0].protocol_distance,
|
|
nexthop_tab[0].route_metric);
|
|
}
|
|
|
|
addr = nexthop_addr; /* use nexthop addr for recursive lookup */
|
|
|
|
} /* for (max_lookup) */
|
|
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
char addr_str[100];
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
lookup, max_lookup, addr_str);
|
|
}
|
|
|
|
return -2;
|
|
}
|