[pim] Initial pim 0.155
diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c
new file mode 100644
index 0000000..98548e7
--- /dev/null
+++ b/pimd/pim_zlookup.c
@@ -0,0 +1,455 @@
+/*
+ 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;
+ }
+
+#ifdef HAVE_TCP_ZEBRA
+ zlog_debug("%s: FIXME blocking connect: zclient_socket()",
+ __PRETTY_FUNCTION__);
+ zlookup->sock = zclient_socket();
+ if (zlookup->sock < 0) {
+ zlog_warn("%s: failure connecting TCP socket %s,%d",
+ __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT);
+ }
+ else if (zclient_debug) {
+ zlog_notice("%s: connected TCP socket %s,%d",
+ __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT);
+ }
+#else
+ zlog_debug("%s: FIXME blocking connect: zclient_socket_un()",
+ __PRETTY_FUNCTION__);
+ zlookup->sock = zclient_socket_un(ZEBRA_SERV_PATH);
+ if (zlookup->sock < 0) {
+ zlog_warn("%s: failure connecting UNIX socket %s",
+ __PRETTY_FUNCTION__, ZEBRA_SERV_PATH);
+ }
+ else if (zclient_debug) {
+ zlog_notice("%s: connected UNIX socket %s",
+ __PRETTY_FUNCTION__, ZEBRA_SERV_PATH);
+ }
+#endif /* HAVE_TCP_ZEBRA */
+
+ 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);
+}
+
+struct zclient *zclient_lookup_new()
+{
+ struct zclient *zlookup;
+
+ zlookup = zclient_new();
+ 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 = 14; /* getc=1 getc=1 getw=2 getipv4=4 getc=1 getl=4 getc=1 */
+ uint16_t length, len;
+ u_char marker;
+ u_char version;
+ uint16_t command;
+ int nbytes;
+ struct in_addr raddr;
+ uint8_t distance;
+ uint32_t metric;
+ int nexthop_num;
+ int i;
+
+ 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;
+ stream_reset(s);
+
+ nbytes = stream_read(s, zlookup->sock, 2);
+ if (nbytes < 2) {
+ zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d",
+ __FILE__, __PRETTY_FUNCTION__, nbytes);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -1;
+ }
+ length = stream_getw(s);
+
+ len = length - 2;
+
+ if (len < MIN_LEN) {
+ zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
+ __FILE__, __PRETTY_FUNCTION__, len, MIN_LEN);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -2;
+ }
+
+ nbytes = stream_read(s, zlookup->sock, len);
+ if (nbytes < (length - 2)) {
+ zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d",
+ __FILE__, __PRETTY_FUNCTION__, nbytes, len);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -3;
+ }
+ marker = stream_getc(s);
+ version = stream_getc(s);
+
+ if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) {
+ zlog_err("%s: socket %d version mismatch, marker %d, version %d",
+ __func__, zlookup->sock, marker, version);
+ return -4;
+ }
+
+ command = stream_getw(s);
+ if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_V2) {
+ zlog_err("%s: socket %d command mismatch: %d",
+ __func__, zlookup->sock, command);
+ return -5;
+ }
+
+ 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;
+ }
+
+ len -= MIN_LEN;
+
+ for (i = 0; i < nexthop_num; ++i) {
+ enum nexthop_types_t nexthop_type;
+
+ if (len < 1) {
+ zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
+ __func__, zlookup->sock, len);
+ return -7;
+ }
+
+ nexthop_type = stream_getc(s);
+ --len;
+
+ switch (nexthop_type) {
+ case ZEBRA_NEXTHOP_IFINDEX:
+ case ZEBRA_NEXTHOP_IFNAME:
+ 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 (len < 4) {
+ zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
+ __func__, zlookup->sock, len);
+ return -8;
+ }
+ nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
+ len -= 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);
+ len -= 4;
+ nexthop_tab[num_ifindex].ifindex = 0;
+ nexthop_tab[num_ifindex].protocol_distance = distance;
+ nexthop_tab[num_ifindex].route_metric = metric;
+ {
+ 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_warn("%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_reconnect(zlookup);
+ return -1;
+ }
+
+ s = zlookup->obuf;
+ stream_reset(s);
+ zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_V2);
+ 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__);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(zlookup);
+ return -2;
+ }
+ if (ret == 0) {
+ zlog_err("%s %s: connection closed on zclient lookup socket",
+ __FILE__, __PRETTY_FUNCTION__);
+ close(zlookup->sock);
+ zlookup->sock = -1;
+ zclient_lookup_reconnect(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;
+
+ 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) {
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, addr_str);
+ return -1;
+ }
+
+ /*
+ 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 */
+ char addr_str[100];
+ pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, first_ifindex, addr_str);
+
+ /* use last address as nexthop address */
+ nexthop_tab[0].nexthop_addr = addr;
+ }
+
+ return num_ifindex;
+ }
+
+ {
+ 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_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ lookup, max_lookup, nexthop_str, addr_str);
+ }
+
+ addr = nexthop_addr; /* use nexthop addr for recursive lookup */
+
+ } /* for (max_lookup) */
+
+ 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;
+}