Mailing List Archive

[PATCH v3 4/5] bgpd: vty adaptations regarding rd and rt configuration
A new subnode under router bgp command has been created: vrf <>.
This enhancement permits creating a VRF instance specific to VRF.
Then it permits setting specific configuration to vrf entity.
This is the case for route distinguisher, but also route target.
vrf <NAME>
rd <route distinguisher name>
no rd <RD name>
rt import <RT list>
rt export <RT list>
rt both <RT list>
no rt import <>
no rt export <>
no rt both <>
exit
no vrf <NAME>

Currently, this is necessary to manually set route distinguisher.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
---
bgpd/bgp_route.c | 3 +
bgpd/bgp_vty.c | 252 ++++++++++++++++++++++++++++++++++++++-----------------
bgpd/bgpd.c | 126 ++++++++++++++++++++++------
bgpd/bgpd.h | 14 +++-
4 files changed, 291 insertions(+), 104 deletions(-)

diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index eb0c9e50094f..c4236fab481b 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1528,6 +1528,9 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected,
void bgp_vrf_clean_tables (struct bgp_vrf *vrf)
{
afi_t afi;
+
+ if (vrf->rib == NULL || vrf->route == NULL)
+ return;
for (afi = AFI_IP; afi < AFI_MAX; afi++)
{
struct bgp_info *ri, *ri_next;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 63a7b5bfdcf6..4080438b06a0 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -9893,57 +9893,86 @@ bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi,

DEFUN (bgp_vrf,
bgp_vrf_cmd,
- "vrf rd WORD",
- "BGP VPN VRF\n"
- "Route Distinguisher\n"
- "Route Distinguisher\n"
+ "vrf WORD",
+ "BGP VRF\n"
+ "VRF Name\n"
)
{
struct bgp *bgp = vty->index;
struct bgp_vrf *vrf;
- struct prefix_rd prd;
+ if ( (vrf = bgp_vrf_lookup_per_name (bgp, argv[0], 1)) == NULL)
+ return CMD_ERR_NO_MATCH;
+ vty->index_sub = vrf;
+ vty->node = BGP_VRF_NODE;

- if (! str2prefix_rd (argv[0], &prd))
- {
- vty_out (vty, "%% Invalid RD '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return CMD_SUCCESS;
+}
+DEFUN (no_bgp_vrf,
+ no_bgp_vrf_cmd,
+ "no vrf WORD",
+ NO_STR
+ "BGP VRF\n"
+ "VRF Name\n"
+)
+{
+ struct bgp *bgp = vty->index;
+ struct bgp_vrf *vrf;

- vrf = bgp_vrf_lookup (bgp, &prd);
- if (vrf)
+ vrf = bgp_vrf_lookup_per_name (bgp, argv[0], 0);
+ if (! vrf)
{
- vty_out (vty, "%% VRF with RD '%s' already exists%s", argv[0], VTY_NEWLINE);
+ vty_out (vty, "%% No VRF with name '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
+ bgp_vrf_delete (vrf);
+ return CMD_SUCCESS;
+}

- bgp_vrf_create (bgp, &prd);
+DEFUN (exit_bgp_vrf,
+ exit_bgp_vrf_cmd,
+ "exit-bgp-vrf",
+ "Exit from BGP vrf configuration mode\n")
+{
+ if (vty->node == BGP_VRF_NODE)
+ vty->node = BGP_NODE;
return CMD_SUCCESS;
}

-DEFUN (bgp_vrf_exports,
- bgp_vrf_exports_cmd,
- "vrf rd WORD exports .LINE",
- "BGP VPN VRF\n"
+DEFUN (bgp_vrf_rd,
+ bgp_vrf_rd_cmd,
+ "rd WORD",
"Route Distinguisher\n"
- "Route Distinguisher\n"
- "Export RT values\n"
- "Export RT values\n"
+ "Route Distinguisher Name\n"
)
{
struct bgp *bgp = vty->index;
- struct bgp_vrf *vrf;
+ struct bgp_vrf *vrf = vty->index_sub;
struct prefix_rd prd;
- struct ecommunity *ecom = NULL;
- int fail = 0, i;
- char *rts = NULL, *rts_ptr;

if (! str2prefix_rd (argv[0], &prd))
{
vty_out (vty, "%% Invalid RD '%s'%s", argv[0], VTY_NEWLINE);
- fail++;
+ return CMD_WARNING;
}
+ bgp_vrf_update_rd (bgp, vrf, &prd);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_vrf_rt_export,
+ bgp_vrf_rt_export_cmd,
+ "rt export .LINE",
+ "Route Target\n"
+ "Export RT values\n"
+ "Export RT values\n"
+)
+{
+ struct bgp_vrf *vrf = vty->index_sub;
+ struct ecommunity *ecom = NULL;
+ int fail = 0, i;
+ char *rts = NULL, *rts_ptr;
+
/* forge export list */
- i = 1;
+ i = 0;
rts = XCALLOC(MTYPE_TMP,2048);
rts_ptr = rts;
while(i < argc)
@@ -9963,44 +9992,27 @@ DEFUN (bgp_vrf_exports,
if (fail)
return CMD_WARNING;

- vrf = bgp_vrf_lookup (bgp, &prd);
- if (! vrf)
- {
- ecommunity_free (&ecom);
- vty_out (vty, "%% No VRF with RD '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
ecom = ecommunity_intern (ecom);
bgp_vrf_rt_export_set (vrf, ecom);
ecommunity_unintern (&ecom);
return CMD_SUCCESS;
}

-DEFUN (bgp_vrf_imports,
- bgp_vrf_imports_cmd,
- "vrf rd WORD imports .LINE",
- "BGP VPN VRF\n"
- "Route Distinguisher\n"
- "Route Distinguisher\n"
+DEFUN (bgp_vrf_rt_import,
+ bgp_vrf_rt_import_cmd,
+ "rt import .LINE",
+ "Route Target\n"
"Import RT values\n"
"Import RT values\n"
)
{
- struct bgp *bgp = vty->index;
- struct bgp_vrf *vrf;
- struct prefix_rd prd;
+ struct bgp_vrf *vrf = vty->index_sub;
struct ecommunity *ecom = NULL;
int fail = 0, i;
char *rts = NULL, *rts_ptr;

- if (! str2prefix_rd (argv[0], &prd))
- {
- vty_out (vty, "%% Invalid RD '%s'%s", argv[0], VTY_NEWLINE);
- fail++;
- }
/* forge export list */
- i = 1;
+ i = 0;
rts = XCALLOC(MTYPE_TMP,2048);
rts_ptr = rts;
while(i < argc)
@@ -10020,46 +10032,111 @@ DEFUN (bgp_vrf_imports,
if (fail)
return CMD_WARNING;

- vrf = bgp_vrf_lookup (bgp, &prd);
- if (! vrf)
- {
- ecommunity_free (&ecom);
- vty_out (vty, "%% No VRF with RD '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
ecom = ecommunity_intern (ecom);
bgp_vrf_rt_import_set (vrf, ecom);
ecommunity_unintern (&ecom);
return CMD_SUCCESS;
}

-DEFUN (no_bgp_vrf,
- no_bgp_vrf_cmd,
- "no vrf rd WORD",
- NO_STR
- "BGP VPN VRF\n"
- "Route Distinguisher\n"
- "Route Distinguisher\n"
+DEFUN (bgp_vrf_rt_both,
+ bgp_vrf_rt_both_cmd,
+ "rt both .LINE",
+ "Route Target\n"
+ "Import and Export RT values\n"
+ "Import and Export RT values\n"
)
{
- struct bgp *bgp = vty->index;
- struct bgp_vrf *vrf;
- struct prefix_rd prd;
+ struct bgp_vrf *vrf = vty->index_sub;
+ struct ecommunity *ecom = NULL, *ecom1;
+ int fail = 0, i;
+ char *rts = NULL, *rts_ptr;

- if (! str2prefix_rd (argv[0], &prd))
+ /* forge export list */
+ i = 0;
+ rts = XCALLOC(MTYPE_TMP,2048);
+ rts_ptr = rts;
+ while(i < argc)
{
- vty_out (vty, "%% Invalid RD '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
+ rts_ptr += sprintf(rts_ptr, "rt %s ",argv[i]);
+ i++;
}
-
- vrf = bgp_vrf_lookup (bgp, &prd);
- if (! vrf)
+ /* convert list of ecoms string into ecom struct */
+ ecom = ecommunity_str2com (rts, ECOMMUNITY_ROUTE_TARGET, 1);
+ if (! ecom)
{
- vty_out (vty, "%% No VRF with RD '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
+ vty_out (vty, "%% Invalid RT '%s'%s", argv[1], VTY_NEWLINE);
+ fail++;
}
- bgp_vrf_delete (vrf);
+ if (rts)
+ XFREE (MTYPE_TMP, rts);
+ if (fail)
+ return CMD_WARNING;
+
+ ecom1 = ecommunity_intern (ecom);
+ bgp_vrf_rt_import_set (vrf, ecom1);
+ ecommunity_unintern (&ecom1);
+
+ ecom1 = ecommunity_intern (ecom);
+ bgp_vrf_rt_export_set (vrf, ecom1);
+ ecommunity_unintern (&ecom1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rt_import,
+ no_bgp_vrf_rt_import_cmd,
+ "no rt import",
+ NO_STR
+ "Route Target\n"
+ "Import values\n"
+)
+{
+ struct bgp_vrf *vrf = vty->index_sub;
+
+ bgp_vrf_rt_import_unset (vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rt_export,
+ no_bgp_vrf_rt_export_cmd,
+ "no rt export",
+ NO_STR
+ "Route Target\n"
+ "Export RT values\n"
+)
+{
+ struct bgp_vrf *vrf = vty->index_sub;
+
+ bgp_vrf_rt_export_unset (vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rt_both,
+ no_bgp_vrf_rt_both_cmd,
+ "no rt both",
+ NO_STR
+ "Route Target\n"
+ "Import and Export RT values\n"
+)
+{
+ struct bgp_vrf *vrf = vty->index_sub;
+
+ bgp_vrf_rt_export_unset (vrf);
+ bgp_vrf_rt_import_unset (vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rd,
+ no_bgp_vrf_rd_cmd,
+ "no rd WORD",
+ NO_STR
+ "BGP Route Distinguisher\n"
+ "Route Distinguisher\n"
+)
+{
+ struct bgp_vrf *vrf = vty->index_sub;
+
+ bgp_vrf_delete_rd (vrf);
return CMD_SUCCESS;
}

@@ -10127,6 +10204,14 @@ static struct cmd_node bgp_encapv6_node =
1
};

+/* VRF node. */
+static struct cmd_node bgp_vrf_node =
+{
+ BGP_VRF_NODE,
+ "%s(bgp-vrf)# ",
+ 1
+};
+
static void community_list_vty (void);

void
@@ -10142,6 +10227,7 @@ bgp_vty_init (void)
install_node (&bgp_vpnv6_node, NULL);
install_node (&bgp_encap_node, NULL);
install_node (&bgp_encapv6_node, NULL);
+ install_node (&bgp_vrf_node, NULL);

/* Install default VTY commands to new nodes. */
install_default (BGP_NODE);
@@ -10153,11 +10239,19 @@ bgp_vty_init (void)
install_default (BGP_VPNV6_NODE);
install_default (BGP_ENCAP_NODE);
install_default (BGP_ENCAPV6_NODE);
+ install_default (BGP_VRF_NODE);

install_element (BGP_NODE, &bgp_vrf_cmd);
- install_element (BGP_NODE, &bgp_vrf_exports_cmd);
- install_element (BGP_NODE, &bgp_vrf_imports_cmd);
install_element (BGP_NODE, &no_bgp_vrf_cmd);
+ install_element (BGP_VRF_NODE, &bgp_vrf_rd_cmd);
+ install_element (BGP_VRF_NODE, &no_bgp_vrf_rd_cmd);
+ install_element (BGP_VRF_NODE, &bgp_vrf_rt_export_cmd);
+ install_element (BGP_VRF_NODE, &bgp_vrf_rt_import_cmd);
+ install_element (BGP_VRF_NODE, &bgp_vrf_rt_both_cmd);
+ install_element (BGP_VRF_NODE, &no_bgp_vrf_rt_import_cmd);
+ install_element (BGP_VRF_NODE, &no_bgp_vrf_rt_export_cmd);
+ install_element (BGP_VRF_NODE, &no_bgp_vrf_rt_both_cmd);
+ install_element (BGP_VRF_NODE, &exit_bgp_vrf_cmd);

/* "bgp multiple-instance" commands. */
install_element (CONFIG_NODE, &bgp_multiple_instance_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 5725730d9454..450371efd243 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2091,28 +2091,30 @@ bgp_rt_hash_dealloc (struct bgp_rt_sub *rt_sub)
}

struct bgp_vrf *
-bgp_vrf_lookup (struct bgp *bgp, struct prefix_rd *outbound_rd)
+bgp_vrf_lookup_per_name (struct bgp *bgp, const char *name, int create)
{
+ afi_t afi;
struct listnode *node;
struct bgp_vrf *vrf;
+ unsigned int len;

+ if (!name)
+ return NULL;
+ len = strlen(name);
for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
- if (!memcmp (outbound_rd->val, vrf->outbound_rd.val, 8))
- return vrf;
- return NULL;
-}
-
-struct bgp_vrf *
-bgp_vrf_create (struct bgp *bgp, struct prefix_rd *outbound_rd)
-{
- struct bgp_vrf *vrf;
- afi_t afi;
-
- if ( (vrf = XCALLOC (MTYPE_BGP_VRF, sizeof (struct bgp_vrf))) == NULL)
+ {
+ if (strlen (vrf->name) != len)
+ continue;
+ if (0 == strcmp (vrf->name, name))
+ break;
+ }
+ if (vrf || create == 0)
+ return vrf;
+ if ((vrf = XCALLOC (MTYPE_BGP_VRF, sizeof (struct bgp_vrf))) == NULL)
return NULL;
-
vrf->bgp = bgp;
- vrf->outbound_rd = *outbound_rd;
+ vrf->name = strdup (name);
+ vrf->flag |= BGP_VRF_RD_UNSET;

for (afi = AFI_IP; afi < AFI_MAX; afi++)
{
@@ -2126,6 +2128,49 @@ bgp_vrf_create (struct bgp *bgp, struct prefix_rd *outbound_rd)
return vrf;
}

+struct bgp_vrf *
+bgp_vrf_lookup_per_rn (struct bgp *bgp, int afi, struct bgp_node *vrf_rn)
+{
+ struct listnode *node;
+ struct bgp_vrf *vrf;
+
+ if(bgp_node_table (vrf_rn)->type != BGP_TABLE_VRF)
+ return NULL;
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
+ if(vrf->rib[afi] == bgp_node_table (vrf_rn))
+ {
+ return vrf;
+ }
+ return NULL;
+}
+
+struct bgp_vrf *
+bgp_vrf_lookup (struct bgp *bgp, struct prefix_rd *outbound_rd)
+{
+ struct listnode *node;
+ struct bgp_vrf *vrf;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
+ if (!memcmp (outbound_rd->val, vrf->outbound_rd.val, 8))
+ return vrf;
+ return NULL;
+}
+
+struct bgp_vrf *
+bgp_vrf_update_rd (struct bgp *bgp, struct bgp_vrf *vrf, struct prefix_rd *outbound_rd)
+{
+ if (!vrf)
+ {
+ char vrf_rd_str[RD_ADDRSTRLEN];
+ prefix_rd2str (outbound_rd, vrf_rd_str, sizeof (vrf_rd_str));
+ if ( (vrf = bgp_vrf_lookup_per_name (bgp, vrf_rd_str, 1)) == NULL)
+ return NULL;
+ }
+ vrf->flag &= ~BGP_VRF_RD_UNSET;
+ vrf->outbound_rd = *outbound_rd;
+ return vrf;
+}
+
static struct ecommunity * ecommunity_reintern (struct ecommunity *ecom)
{
assert (ecom->refcnt > 0);
@@ -2134,6 +2179,13 @@ static struct ecommunity * ecommunity_reintern (struct ecommunity *ecom)
}

void
+bgp_vrf_rt_export_unset (struct bgp_vrf *vrf)
+{
+ if (vrf->rt_export)
+ ecommunity_unintern (&vrf->rt_export);
+}
+
+void
bgp_vrf_rt_export_set (struct bgp_vrf *vrf, struct ecommunity *rt_export)
{
if (vrf->rt_export)
@@ -2142,7 +2194,7 @@ bgp_vrf_rt_export_set (struct bgp_vrf *vrf, struct ecommunity *rt_export)
vrf->rt_export = ecommunity_reintern (rt_export);
}

-static void
+void
bgp_vrf_rt_import_unset (struct bgp_vrf *vrf)
{
size_t i;
@@ -2191,21 +2243,41 @@ bgp_vrf_rt_import_set (struct bgp_vrf *vrf, struct ecommunity *rt_import)
bgp_vrf_apply_new_imports (vrf, afi);
}

-static void
-bgp_vrf_delete_int (void *arg)
+/* delete RD <> command as well as RD export/import
+ * and RIB table associated
+ */
+void
+bgp_vrf_delete_rd (struct bgp_vrf *vrf)
{
- struct bgp_vrf *vrf = arg;
char vrf_rd_str[RD_ADDRSTRLEN];

+ if (!vrf)
+ return;
+
prefix_rd2str(&vrf->outbound_rd, vrf_rd_str, sizeof(vrf_rd_str));
- zlog_info ("deleting vrf %s", vrf_rd_str);
+ zlog_info ("deleting rd %s", vrf_rd_str);

bgp_vrf_clean_tables (vrf);

bgp_vrf_rt_import_unset (vrf);
if (vrf->rt_export)
ecommunity_unintern (&vrf->rt_export);
+ return;
+}

+static void
+bgp_vrf_delete_int (void *arg)
+{
+ struct bgp_vrf *vrf = arg;
+ char vrf_rd_str[RD_ADDRSTRLEN];
+
+ prefix_rd2str(&vrf->outbound_rd, vrf_rd_str, sizeof(vrf_rd_str));
+ zlog_info ("deleting vrf %s", vrf_rd_str);
+
+ bgp_vrf_delete_rd (vrf);
+
+ if (vrf->name)
+ free (vrf->name);
XFREE (MTYPE_BGP_VRF, vrf);
}

@@ -5790,14 +5862,19 @@ bgp_config_write (struct vty *vty)
char *str_p, *str2_p;
for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
{
- str_p = prefix_rd2str(&(vrf->outbound_rd), rdstr, RD_ADDRSTRLEN);
- vty_out(vty, " vrf rd %s%s", str_p == NULL?"<err>":str_p, VTY_NEWLINE);
+ vty_out(vty, " vrf %s%s", vrf->name, VTY_NEWLINE);
+ /* an RD has been configured */
+ if (!(vrf->flag & BGP_VRF_RD_UNSET))
+ {
+ str_p = prefix_rd2str(&(vrf->outbound_rd), rdstr, RD_ADDRSTRLEN);
+ vty_out(vty, " rd %s%s", str_p == NULL?"<err>":str_p, VTY_NEWLINE);
+ }
if(vrf->rt_import)
{
str2_p = ecommunity_ecom2str (vrf->rt_import, ECOMMUNITY_FORMAT_ROUTE_MAP);
if(str2_p)
{
- vty_out(vty, " vrf rd %s import %s%s", str_p == NULL?"<err>":str_p, str2_p, VTY_NEWLINE);
+ vty_out(vty, " rt import %s%s", str2_p, VTY_NEWLINE);
XFREE (MTYPE_ECOMMUNITY_STR, str2_p);
}
}
@@ -5806,10 +5883,11 @@ bgp_config_write (struct vty *vty)
str2_p = ecommunity_ecom2str (vrf->rt_export, ECOMMUNITY_FORMAT_ROUTE_MAP);
if(str2_p)
{
- vty_out(vty, " vrf rd %s export %s%s", str_p == NULL?"<err>":str_p, str2_p, VTY_NEWLINE);
+ vty_out(vty, " rt export %s%s", str2_p, VTY_NEWLINE);
XFREE (MTYPE_ECOMMUNITY_STR, str2_p);
}
}
+ vty_out (vty, " exit%s", VTY_NEWLINE);
}
}
/* maximum-paths */
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 15245ceeb8d2..c81466e85205 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -31,6 +31,8 @@ typedef u_int32_t as_t;
typedef u_int16_t as16_t; /* we may still encounter 16 Bit asnums */
typedef u_int16_t bgp_size_t;

+struct bgp_node;
+
/* BGP router distinguisher value. */
#define BGP_RD_SIZE 8

@@ -208,6 +210,7 @@ struct bgp_vrf
{
struct bgp *bgp;

+ char *name;
/* RD used for route advertisements */
struct prefix_rd outbound_rd;

@@ -221,6 +224,10 @@ struct bgp_vrf
/* Static route configuration. */
struct bgp_table *route[AFI_MAX];

+ /* internal flag */
+#define BGP_VRF_RD_UNSET 1
+ uint16_t flag;
+
};

/* BGP peer-group support. */
@@ -1033,11 +1040,16 @@ extern int peer_ttl_security_hops_set (struct peer *, int);
extern int peer_ttl_security_hops_unset (struct peer *);

extern void bgp_scan_finish (void);
-extern struct bgp_vrf *bgp_vrf_create (struct bgp *bgp, struct prefix_rd *outbound_rd);
+extern void bgp_vrf_delete_rd (struct bgp_vrf *vrf);
+extern struct bgp_vrf *bgp_vrf_update_rd (struct bgp *bgp, struct bgp_vrf *vrf, struct prefix_rd *outbound_rd);
extern struct bgp_vrf *bgp_vrf_lookup (struct bgp *bgp, struct prefix_rd *outbound_rd);
+extern struct bgp_vrf *bgp_vrf_lookup_per_name (struct bgp *bgp, const char *name, int create);
+extern struct bgp_vrf *bgp_vrf_lookup_per_rn (struct bgp *bgp, int afi, struct bgp_node *vrf_rn);
extern void bgp_vrf_delete (struct bgp_vrf *vrf);
extern void bgp_vrf_rt_export_set (struct bgp_vrf *vrf, struct ecommunity *rt_export);
extern void bgp_vrf_rt_import_set (struct bgp_vrf *vrf, struct ecommunity *rt_import);
extern void bgp_vrf_clean_tables (struct bgp_vrf *vrf);
+extern void bgp_vrf_rt_import_unset (struct bgp_vrf *vrf);
+extern void bgp_vrf_rt_export_unset (struct bgp_vrf *vrf);

#endif /* _QUAGGA_BGPD_H */
--
2.1.4


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev