mirror_frr/vtysh/vtysh_config.c
Lou Berger 65efcfce42 bgpd: add L3/L2VPN Virtual Network Control feature
This feature adds an L3 & L2 VPN application that makes use of the VPN
and Encap SAFIs.  This code is currently used to support IETF NVO3 style
operation.  In NVO3 terminology it provides the Network Virtualization
Authority (NVA) and the ability to import/export IP prefixes and MAC
addresses from Network Virtualization Edges (NVEs).  The code supports
per-NVE tables.

The NVE-NVA protocol used to communicate routing and Ethernet / Layer 2
(L2) forwarding information between NVAs and NVEs is referred to as the
Remote Forwarder Protocol (RFP). OpenFlow is an example RFP.  For
general background on NVO3 and RFP concepts see [1].  For information on
Openflow see [2].

RFPs are integrated with BGP via the RF API contained in the new "rfapi"
BGP sub-directory.  Currently, only a simple example RFP is included in
Quagga. Developers may use this example as a starting point to integrate
Quagga with an RFP of their choosing, e.g., OpenFlow.  The RFAPI code
also supports the ability import/export of routing information between
VNC and customer edge routers (CEs) operating within a virtual
network. Import/export may take place between BGP views or to the
default zebera VRF.

BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VPN
information between NVAs. BGP based IP VPN support is defined in
RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs), and RFC4659,
BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN . Use
of both the Encapsulation Subsequent Address Family Identifier (SAFI)
and the Tunnel Encapsulation Attribute, RFC5512, The BGP Encapsulation
Subsequent Address Family Identifier (SAFI) and the BGP Tunnel
Encapsulation Attribute, are supported. MAC address distribution does
not follow any standard BGB encoding, although it was inspired by the
early IETF EVPN concepts.

The feature is conditionally compiled and disabled by default.
Use the --enable-bgp-vnc configure option to enable.

The majority of this code was authored by G. Paul Ziemba
<paulz@labn.net>.

[1] http://tools.ietf.org/html/draft-ietf-nvo3-nve-nva-cp-req
[2] https://www.opennetworking.org/sdn-resources/technical-library

Now includes changes needed to merge with cmaster-next.
2016-10-03 08:17:02 -04:00

474 lines
13 KiB
C

/* Configuration generator.
Copyright (C) 2000 Kunihiro Ishiguro
This file is part of GNU Zebra.
GNU Zebra 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, or (at your option) any
later version.
GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <zebra.h>
#include "command.h"
#include "linklist.h"
#include "memory.h"
#include "vtysh/vtysh.h"
#include "vtysh/vtysh_user.h"
DEFINE_MGROUP(MVTYSH, "vtysh")
DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG, "Vtysh configuration")
DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG_LINE, "Vtysh configuration line")
vector configvec;
extern int vtysh_writeconfig_integrated;
struct config
{
/* Configuration node name. */
char *name;
/* Configuration string line. */
struct list *line;
/* Configuration can be nest. */
struct config *config;
/* Index of this config. */
u_int32_t index;
};
struct list *config_top;
static int
line_cmp (char *c1, char *c2)
{
return strcmp (c1, c2);
}
static void
line_del (char *line)
{
XFREE (MTYPE_VTYSH_CONFIG_LINE, line);
}
static struct config *
config_new (void)
{
struct config *config;
config = XCALLOC (MTYPE_VTYSH_CONFIG, sizeof (struct config));
return config;
}
static int
config_cmp (struct config *c1, struct config *c2)
{
return strcmp (c1->name, c2->name);
}
static void
config_del (struct config* config)
{
list_delete (config->line);
if (config->name)
XFREE (MTYPE_VTYSH_CONFIG_LINE, config->name);
XFREE (MTYPE_VTYSH_CONFIG, config);
}
static struct config *
config_get (int index, const char *line)
{
struct config *config;
struct config *config_loop;
struct list *master;
struct listnode *node, *nnode;
config = config_loop = NULL;
master = vector_lookup_ensure (configvec, index);
if (! master)
{
master = list_new ();
master->del = (void (*) (void *))config_del;
master->cmp = (int (*)(void *, void *)) config_cmp;
vector_set_index (configvec, index, master);
}
for (ALL_LIST_ELEMENTS (master, node, nnode, config_loop))
{
if (strcmp (config_loop->name, line) == 0)
config = config_loop;
}
if (! config)
{
config = config_new ();
config->line = list_new ();
config->line->del = (void (*) (void *))line_del;
config->line->cmp = (int (*)(void *, void *)) line_cmp;
config->name = XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line);
config->index = index;
listnode_add (master, config);
}
return config;
}
void
config_add_line (struct list *config, const char *line)
{
listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
static void
config_add_line_uniq (struct list *config, const char *line)
{
struct listnode *node, *nnode;
char *pnt;
for (ALL_LIST_ELEMENTS (config, node, nnode, pnt))
{
if (strcmp (pnt, line) == 0)
return;
}
listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
static void
vtysh_config_parse_line (const char *line)
{
char c;
static struct config *config = NULL;
if (! line)
return;
c = line[0];
if (c == '\0')
return;
/* printf ("[%s]\n", line); */
switch (c)
{
/* Suppress exclamation points ! and commented lines. The !s are generated
* dynamically in vtysh_config_dump() */
case '!':
case '#':
break;
case ' ':
/* Store line to current configuration. */
if (config)
{
if (strncmp (line, " address-family vpnv4",
strlen (" address-family vpnv4")) == 0)
config = config_get (BGP_VPNV4_NODE, line);
else if (strncmp (line, " address-family vpn6",
strlen (" address-family vpn6")) == 0)
config = config_get (BGP_VPNV6_NODE, line);
else if (strncmp (line, " address-family encapv6",
strlen (" address-family encapv6")) == 0)
config = config_get (BGP_ENCAPV6_NODE, line);
else if (strncmp (line, " address-family encap",
strlen (" address-family encap")) == 0)
config = config_get (BGP_ENCAP_NODE, line);
else if (strncmp (line, " address-family ipv4 multicast",
strlen (" address-family ipv4 multicast")) == 0)
config = config_get (BGP_IPV4M_NODE, line);
else if (strncmp (line, " address-family ipv6",
strlen (" address-family ipv6")) == 0)
config = config_get (BGP_IPV6_NODE, line);
else if (strncmp (line, " vnc defaults",
strlen (" vnc defaults")) == 0)
config = config_get (BGP_VNC_DEFAULTS_NODE, line);
else if (strncmp (line, " vnc nve-group",
strlen (" vnc nve-group")) == 0)
config = config_get (BGP_VNC_NVE_GROUP_NODE, line);
else if (strncmp (line, " vnc l2-group",
strlen (" vnc l2-group")) == 0)
config = config_get (BGP_VNC_L2_GROUP_NODE, line);
else if (config->index == RMAP_NODE ||
config->index == INTERFACE_NODE ||
config->index == NS_NODE ||
config->index == VTY_NODE)
config_add_line_uniq (config->line, line);
else
config_add_line (config->line, line);
}
else
config_add_line (config_top, line);
break;
default:
if (strncmp (line, "interface", strlen ("interface")) == 0)
config = config_get (INTERFACE_NODE, line);
else if (strncmp (line, "logical-router", strlen ("ns")) == 0)
config = config_get (NS_NODE, line);
else if (strncmp (line, "vrf", strlen ("vrf")) == 0)
config = config_get (VRF_NODE, line);
else if (strncmp (line, "router-id", strlen ("router-id")) == 0)
config = config_get (ZEBRA_NODE, line);
else if (strncmp (line, "router rip", strlen ("router rip")) == 0)
config = config_get (RIP_NODE, line);
else if (strncmp (line, "router ripng", strlen ("router ripng")) == 0)
config = config_get (RIPNG_NODE, line);
else if (strncmp (line, "router ospf", strlen ("router ospf")) == 0)
config = config_get (OSPF_NODE, line);
else if (strncmp (line, "router ospf6", strlen ("router ospf6")) == 0)
config = config_get (OSPF6_NODE, line);
else if (strncmp (line, "mpls ldp", strlen ("mpls ldp")) == 0)
config = config_get (LDP_NODE, line);
else if (strncmp (line, "l2vpn", strlen ("l2vpn")) == 0)
config = config_get (LDP_L2VPN_NODE, line);
else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0)
config = config_get (BGP_NODE, line);
else if (strncmp (line, "router isis", strlen ("router isis")) == 0)
config = config_get (ISIS_NODE, line);
else if (strncmp (line, "route-map", strlen ("route-map")) == 0)
config = config_get (RMAP_NODE, line);
else if (strncmp (line, "access-list", strlen ("access-list")) == 0)
config = config_get (ACCESS_NODE, line);
else if (strncmp (line, "ipv6 access-list",
strlen ("ipv6 access-list")) == 0)
config = config_get (ACCESS_IPV6_NODE, line);
else if (strncmp (line, "ip prefix-list",
strlen ("ip prefix-list")) == 0)
config = config_get (PREFIX_NODE, line);
else if (strncmp (line, "ipv6 prefix-list",
strlen ("ipv6 prefix-list")) == 0)
config = config_get (PREFIX_IPV6_NODE, line);
else if (strncmp (line, "ip as-path access-list",
strlen ("ip as-path access-list")) == 0)
config = config_get (AS_LIST_NODE, line);
else if (strncmp (line, "ip community-list", strlen ("ip community-list")) == 0 ||
strncmp (line, "ip extcommunity-list", strlen ("ip extcommunity-list")) == 0)
config = config_get (COMMUNITY_LIST_NODE, line);
else if (strncmp (line, "ip route", strlen ("ip route")) == 0)
config = config_get (IP_NODE, line);
else if (strncmp (line, "ipv6 route", strlen ("ipv6 route")) == 0)
config = config_get (IP_NODE, line);
else if (strncmp (line, "key", strlen ("key")) == 0)
config = config_get (KEYCHAIN_NODE, line);
else if (strncmp (line, "line", strlen ("line")) == 0)
config = config_get (VTY_NODE, line);
else if ( (strncmp (line, "ipv6 forwarding",
strlen ("ipv6 forwarding")) == 0)
|| (strncmp (line, "ip forwarding",
strlen ("ip forwarding")) == 0) )
config = config_get (FORWARDING_NODE, line);
else if (strncmp (line, "service", strlen ("service")) == 0)
config = config_get (SERVICE_NODE, line);
else if (strncmp (line, "debug vrf", strlen ("debug vrf")) == 0)
config = config_get (VRF_DEBUG_NODE, line);
else if (strncmp (line, "debug", strlen ("debug")) == 0)
config = config_get (DEBUG_NODE, line);
else if (strncmp (line, "password", strlen ("password")) == 0
|| strncmp (line, "enable password",
strlen ("enable password")) == 0)
config = config_get (AAA_NODE, line);
else if (strncmp (line, "ip protocol", strlen ("ip protocol")) == 0)
config = config_get (PROTOCOL_NODE, line);
else if (strncmp (line, "ipv6 protocol", strlen ("ipv6 protocol")) == 0)
config = config_get (PROTOCOL_NODE, line);
else if (strncmp (line, "ip nht", strlen ("ip nht")) == 0)
config = config_get (PROTOCOL_NODE, line);
else if (strncmp (line, "ipv6 nht", strlen ("ipv6 nht")) == 0)
config = config_get (PROTOCOL_NODE, line);
else if (strncmp (line, "mpls", strlen ("mpls")) == 0)
config = config_get (MPLS_NODE, line);
else
{
if (strncmp (line, "log", strlen ("log")) == 0
|| strncmp (line, "hostname", strlen ("hostname")) == 0
)
config_add_line_uniq (config_top, line);
else
config_add_line (config_top, line);
config = NULL;
}
break;
}
}
void
vtysh_config_parse (char *line)
{
char *begin;
char *pnt;
begin = pnt = line;
while (*pnt != '\0')
{
if (*pnt == '\n')
{
*pnt++ = '\0';
vtysh_config_parse_line (begin);
begin = pnt;
}
else
{
pnt++;
}
}
}
/* Macro to check delimiter is needed between each configuration line
* or not. */
#define NO_DELIMITER(I) \
((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
|| (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE || \
(I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \
|| (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \
|| (I) == AAA_NODE || (I) == VRF_DEBUG_NODE || (I) == MPLS_NODE)
/* Display configuration to file pointer. */
void
vtysh_config_dump (FILE *fp)
{
struct listnode *node, *nnode;
struct listnode *mnode, *mnnode;
struct config *config;
struct list *master;
char *line;
unsigned int i;
for (ALL_LIST_ELEMENTS (config_top, node, nnode, line))
{
fprintf (fp, "%s\n", line);
fflush (fp);
}
fprintf (fp, "!\n");
fflush (fp);
for (i = 0; i < vector_active (configvec); i++)
if ((master = vector_slot (configvec, i)) != NULL)
{
for (ALL_LIST_ELEMENTS (master, node, nnode, config))
{
/* Don't print empty sections for interface/vrf. Route maps on the
* other hand could have a legitimate empty section at the end.
*/
if ((config->index == INTERFACE_NODE || (config->index == VRF_NODE))
&& list_isempty (config->line))
continue;
fprintf (fp, "%s\n", config->name);
fflush (fp);
for (ALL_LIST_ELEMENTS (config->line, mnode, mnnode, line))
{
fprintf (fp, "%s\n", line);
fflush (fp);
}
if (! NO_DELIMITER (i))
{
fprintf (fp, "!\n");
fflush (fp);
}
}
if (NO_DELIMITER (i))
{
fprintf (fp, "!\n");
fflush (fp);
}
}
for (i = 0; i < vector_active (configvec); i++)
if ((master = vector_slot (configvec, i)) != NULL)
{
list_delete (master);
vector_slot (configvec, i) = NULL;
}
list_delete_all_node (config_top);
}
/* Read up configuration file from file_name. */
static int
vtysh_read_file (FILE *confp)
{
struct vty *vty;
int ret;
vty = vty_new ();
vty->fd = 0; /* stdout */
vty->type = VTY_TERM;
vty->node = CONFIG_NODE;
vtysh_execute_no_pager ("enable");
vtysh_execute_no_pager ("configure terminal");
/* Execute configuration file. */
ret = vtysh_config_from_file (vty, confp);
vtysh_execute_no_pager ("end");
vtysh_execute_no_pager ("disable");
vty_close (vty);
return (ret);
}
/* Read up configuration file from config_default_dir. */
int
vtysh_read_config (const char *config_default_dir)
{
FILE *confp = NULL;
int ret;
host_config_set (config_default_dir);
confp = fopen (config_default_dir, "r");
if (confp == NULL)
{
fprintf (stderr, "%% Can't open configuration file %s due to '%s'.\n",
config_default_dir, safe_strerror (errno));
return (CMD_ERR_NO_FILE);
}
ret = vtysh_read_file (confp);
fclose (confp);
return (ret);
}
/* We don't write vtysh specific into file from vtysh. vtysh.conf should
* be edited by hand. So, we handle only "write terminal" case here and
* integrate vtysh specific conf with conf from daemons.
*/
void
vtysh_config_write ()
{
char line[81];
extern struct host host;
if (host.name)
{
sprintf (line, "hostname %s", host.name);
vtysh_config_parse_line(line);
}
if (!vtysh_writeconfig_integrated)
vtysh_config_parse_line ("no service integrated-vtysh-config");
user_config_write ();
}
void
vtysh_config_init ()
{
config_top = list_new ();
config_top->del = (void (*) (void *))line_del;
configvec = vector_init (1);
}