/*
 *	BIRD -- Router Advertisement Configuration
 *
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

CF_HDR

#include "proto/bfd/bfd.h"

CF_DEFINES

#define BFD_CFG ((struct bfd_config *) this_proto)
#define BFD_IFACE ((struct bfd_iface_config *) this_ipatt)
#define BFD_NEIGHBOR this_bfd_neighbor

static struct bfd_neighbor *this_bfd_neighbor;

extern struct bfd_config *bfd_cf;

CF_DECLS

CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
	INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL)

%type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local
%type <i> bfd_neigh_multihop

CF_GRAMMAR

CF_ADDTO(proto, bfd_proto)

bfd_proto_start: proto_start BFD
{
  this_proto = proto_config_new(&proto_bfd, $1);
  init_list(&BFD_CFG->patt_list);
  init_list(&BFD_CFG->neigh_list);

  if (bfd_cf)
    cf_error("Only one BFD instance allowed");
  bfd_cf = BFD_CFG;
};

bfd_proto_item:
   proto_item
 | INTERFACE bfd_iface
 | MULTIHOP bfd_multihop
 | NEIGHBOR bfd_neighbor
 ;

bfd_proto_opts:
   /* empty */
 | bfd_proto_opts bfd_proto_item ';'
 ;

bfd_proto:
   bfd_proto_start proto_name '{' bfd_proto_opts '}';


bfd_iface_start:
{
  this_ipatt = cfg_allocz(sizeof(struct bfd_iface_config));
  init_list(&this_ipatt->ipn_list);

  BFD_IFACE->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
  BFD_IFACE->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
  BFD_IFACE->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
  BFD_IFACE->multiplier = BFD_DEFAULT_MULTIPLIER;
};

bfd_iface_item:
   INTERVAL expr_us { BFD_IFACE->min_rx_int = BFD_IFACE->min_tx_int = $2; }
 | MIN RX INTERVAL expr_us { BFD_IFACE->min_rx_int = $4; }
 | MIN TX INTERVAL expr_us { BFD_IFACE->min_tx_int = $4; }
 | IDLE TX INTERVAL expr_us { BFD_IFACE->idle_tx_int = $4; }
 | MULTIPLIER expr { BFD_IFACE->multiplier = $2; }
 | PASSIVE bool { BFD_IFACE->passive = $2; }
 ;

bfd_iface_opts:
   /* empty */
 | bfd_iface_opts bfd_iface_item ';'
 ;

bfd_iface_opt_list:
   /* empty */
 | '{' bfd_iface_opts '}'
 ;

bfd_iface: bfd_iface_start iface_patt_list_nopx bfd_iface_opt_list
{ add_tail(&BFD_CFG->patt_list, NODE this_ipatt); };

bfd_multihop: bfd_iface_start bfd_iface_opt_list
{ BFD_CFG->multihop = BFD_IFACE; };


bfd_neigh_iface:
   /* empty */ { $$ = NULL; }
 | '%' SYM { $$ = if_get_by_name($2->name); }
 | DEV text { $$ = if_get_by_name($2); }
 ;

bfd_neigh_local:
   /* empty */ { $$ = IPA_NONE; }
 | LOCAL ipa { $$ = $2; }
 ;

bfd_neigh_multihop:
   /* empty */ { $$ = 0; }
 | MULTIHOP bool { $$ = $2; }
 ;

bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
{
  this_bfd_neighbor = cfg_allocz(sizeof(struct bfd_neighbor));
  add_tail(&BFD_CFG->neigh_list, NODE this_bfd_neighbor);

  BFD_NEIGHBOR->addr = $1;
  BFD_NEIGHBOR->local = $3;
  BFD_NEIGHBOR->iface = $2;
  BFD_NEIGHBOR->multihop = $4;

  if ($4 && $2)
    cf_error("Neighbor cannot set both interface and multihop");

  if ($4 && ipa_zero($3))
    cf_error("Multihop neighbor requires specified local address");
};


CF_CLI_HELP(SHOW BFD, ..., [[Show information about BFD protocol]]);
CF_CLI(SHOW BFD SESSIONS, optsym, [<name>], [[Show information about BFD sessions]])
{ bfd_show_sessions(proto_get_named($4, &proto_bfd)); };

CF_CODE

CF_END