File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / quagga / pimd / pim_zlookup.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:09:11 2016 UTC (7 years, 7 months ago) by misho
Branches: quagga, MAIN
CVS tags: v1_0_20160315, HEAD
quagga 1.0.20160315

/*
  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;
  uint16_t vrf_id;
  uint16_t command;
  int nbytes;
  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;
  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;
  }
  
  nbytes = stream_read(s, zlookup->sock, length);
  if (nbytes < length) {
    zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d",
	     __FILE__, __PRETTY_FUNCTION__, nbytes, length);
    zclient_lookup_failed(zlookup);
    return -3;
  }

  if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB) {
    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;
  }

  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_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 (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) && PIM_DEBUG_ZEBRA) {
      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;
    }

    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) && PIM_DEBUG_ZEBRA) {
	/* Report non-recursive success after first lookup */
	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;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>