diff options
Diffstat (limited to 'pimd/pim_zlookup.c')
-rw-r--r-- | pimd/pim_zlookup.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c new file mode 100644 index 00000000..98548e79 --- /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; +} |