summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_aspath.c1
-rw-r--r--bgpd/bgp_attr.c1
-rw-r--r--bgpd/bgp_bfd.c1
-rw-r--r--bgpd/bgp_encap_tlv.c1
-rw-r--r--bgpd/bgp_fsm.c1
-rw-r--r--bgpd/bgp_main.c11
-rw-r--r--bgpd/bgp_mpath.c6
-rw-r--r--bgpd/bgp_route.c112
-rw-r--r--bgpd/bgp_routemap.c1
-rw-r--r--bgpd/bgp_table.c1
-rw-r--r--bgpd/bgp_vty.c57
-rw-r--r--bgpd/bgp_vty.h4
-rw-r--r--bgpd/bgp_zebra.c4
-rw-r--r--bgpd/bgpd.c6
-rw-r--r--bgpd/bgpd.h2
-rw-r--r--cumulus/etc/frr/daemons3
-rw-r--r--cumulus/etc/frr/debian.conf1
-rw-r--r--doc/cli.md356
-rw-r--r--ldpd/address.c175
-rw-r--r--ldpd/control.c14
-rw-r--r--ldpd/init.c283
-rw-r--r--ldpd/l2vpn.c111
-rw-r--r--ldpd/labelmapping.c319
-rw-r--r--ldpd/lde.c207
-rw-r--r--ldpd/lde.h28
-rw-r--r--ldpd/lde_lib.c138
-rw-r--r--ldpd/ldp.h41
-rw-r--r--ldpd/ldp_debug.h8
-rw-r--r--ldpd/ldp_zebra.c2
-rw-r--r--ldpd/ldpd.h16
-rw-r--r--ldpd/ldpe.c50
-rw-r--r--ldpd/ldpe.h13
-rw-r--r--ldpd/log.c37
-rw-r--r--ldpd/neighbor.c1
-rw-r--r--ldpd/notification.c134
-rw-r--r--ldpd/packet.c19
-rw-r--r--ldpd/pfkey.c8
-rw-r--r--lib/command.c3
-rw-r--r--lib/plist.c1
-rw-r--r--lib/plist.h5
-rwxr-xr-xtools/frr2
-rwxr-xr-xtools/frr-reload.py2
-rw-r--r--zebra/main.c11
-rw-r--r--zebra/rt_netlink.c12
-rw-r--r--zebra/zebra_fpm_netlink.c8
-rw-r--r--zebra/zebra_fpm_protobuf.c4
-rw-r--r--zebra/zebra_mpls_openbsd.c2
-rw-r--r--zebra/zserv.c28
-rw-r--r--zebra/zserv.h1
49 files changed, 1786 insertions, 466 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 2e78c9a3bf..006631c1f0 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -24,7 +24,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "hash.h"
#include "memory.h"
#include "vector.h"
-#include "vty.h"
#include "log.h"
#include "stream.h"
#include "command.h"
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 153187fd43..1fccd25c8a 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -24,7 +24,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "prefix.h"
#include "memory.h"
#include "vector.h"
-#include "vty.h"
#include "stream.h"
#include "log.h"
#include "hash.h"
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index b8158dc31e..0a06a57fe5 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -31,7 +31,6 @@
#include "buffer.h"
#include "stream.h"
#include "zclient.h"
-#include "vty.h"
#include "bfd.h"
#include "lib/json.h"
#include "filter.h"
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
index 8c5ab8d6f6..7acd23b9ae 100644
--- a/bgpd/bgp_encap_tlv.c
+++ b/bgpd/bgp_encap_tlv.c
@@ -22,7 +22,6 @@
#include "command.h"
#include "memory.h"
#include "prefix.h"
-#include "vty.h"
#include "filter.h"
#include "bgpd.h"
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index a71364381e..7dc7f053d6 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -23,7 +23,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "linklist.h"
#include "prefix.h"
-#include "vty.h"
#include "sockunion.h"
#include "thread.h"
#include "log.h"
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 9607d381d9..2598d66656 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -21,7 +21,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include <zebra.h>
#include "vector.h"
-#include "vty.h"
#include "command.h"
#include "getopt.h"
#include "thread.h"
@@ -75,6 +74,7 @@ static const struct option longopts[] =
{ "vty_socket", required_argument, NULL, OPTION_VTYSOCK },
{ "retain", no_argument, NULL, 'r'},
{ "no_kernel", no_argument, NULL, 'n'},
+ { "ecmp", required_argument, NULL, 'e'},
{ "user", required_argument, NULL, 'u'},
{ "group", required_argument, NULL, 'g'},
{ "skip_runas", no_argument, NULL, 'S'},
@@ -176,6 +176,7 @@ redistribution between different routing protocols.\n\n\
--vty_socket Override vty socket path\n\
-r, --retain When program terminates, retain added route by bgpd.\n\
-n, --no_kernel Do not install route to kernel.\n\
+-e, --ecmp Specify ECMP to use.\n\
-u, --user User to run as\n\
-g, --group Group to run as\n\
-S, --skip_runas Skip user and group run as\n\
@@ -468,6 +469,14 @@ main (int argc, char **argv)
case 'A':
vty_addr = optarg;
break;
+ case 'e':
+ multipath_num = atoi (optarg);
+ if (multipath_num > MULTIPATH_NUM || multipath_num <= 0)
+ {
+ zlog_err ("Multipath Number specified must be less than %d and greater than 0", MULTIPATH_NUM);
+ return 1;
+ }
+ break;
case 'P':
/* Deal with atoi() returning 0 on failure, and bgpd not
listening on bgp port... */
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index f564ff1691..a95c4a008e 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -84,11 +84,11 @@ bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi,
switch (peertype)
{
case BGP_PEER_IBGP:
- bgp->maxpaths[afi][safi].maxpaths_ibgp = MULTIPATH_NUM;
+ bgp->maxpaths[afi][safi].maxpaths_ibgp = multipath_num;
bgp->maxpaths[afi][safi].ibgp_flags = 0;
break;
case BGP_PEER_EBGP:
- bgp->maxpaths[afi][safi].maxpaths_ebgp = MULTIPATH_NUM;
+ bgp->maxpaths[afi][safi].maxpaths_ebgp = multipath_num;
break;
default:
return -1;
@@ -436,7 +436,7 @@ bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best,
char path_buf[PATH_ADDPATH_STR_BUFFER];
mpath_changed = 0;
- maxpaths = MULTIPATH_NUM;
+ maxpaths = multipath_num;
mpath_count = 0;
cur_mpath = NULL;
old_mpath_count = 0;
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 55823a272f..ced587e738 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -5267,7 +5267,7 @@ bgp_aggregate_increment (struct bgp *bgp, struct prefix *p,
struct bgp_table *table;
/* MPLS-VPN aggregation is not yet supported. */
- if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi = SAFI_EVPN))
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN))
return;
table = bgp->aggregate[afi][safi];
@@ -5304,7 +5304,7 @@ bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p,
struct bgp_table *table;
/* MPLS-VPN aggregation is not yet supported. */
- if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi = SAFI_EVPN))
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN))
return;
table = bgp->aggregate[afi][safi];
@@ -6097,7 +6097,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
* neccessarily the same as the prefix address family.
* Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
*/
- if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN) || (safi = SAFI_EVPN))
+ if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
{
if (attr->extra)
{
@@ -6129,7 +6129,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
{
json_nexthop_global = json_object_new_object();
- if ((safi == SAFI_MPLS_VPN) || (safi = SAFI_EVPN))
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->extra->mp_nexthop_global_in));
else
json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->nexthop));
@@ -6139,7 +6139,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
}
else
{
- if ((safi == SAFI_MPLS_VPN) || (safi = SAFI_EVPN))
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
vty_out (vty, "%-16s",
inet_ntoa (attr->extra->mp_nexthop_global_in));
else
@@ -8358,7 +8358,6 @@ DEFUN (show_ip_bgp,
"Display route and more specific routes\n"
JSON_STR)
{
- vrf_id_t vrf = VRF_DEFAULT;
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
int exact_match = 0;
@@ -8366,23 +8365,13 @@ DEFUN (show_ip_bgp,
struct bgp *bgp = NULL;
int idx = 0;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
int uj = use_json (argc, argv);
if (uj) argc--;
- bgp = bgp_lookup_by_vrf_id (vrf);
- if (bgp == NULL)
- {
- if (vrf == VRF_DEFAULT)
- vty_out (vty, "Can't find BGP instance (default)%s", VTY_NEWLINE);
- else
- vty_out (vty, "Can't find BGP instance %d%s", vrf, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
if (argv_find(argv, argc, "cidr-only", &idx))
return bgp_show (vty, bgp, afi, safi, bgp_show_type_cidr_only, NULL, uj);
@@ -8465,7 +8454,6 @@ DEFUN (show_ip_bgp_route,
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
- vrf_id_t vrf = VRF_DEFAULT;;
char *prefix = NULL;
struct bgp *bgp = NULL;
enum bgp_path_type path_type;
@@ -8473,20 +8461,11 @@ DEFUN (show_ip_bgp_route,
int idx = 0;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
- if (vrf != VRF_ALL)
- {
- bgp = bgp_lookup_by_vrf_id (vrf);
- if (bgp == NULL)
- {
- vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
- else
+ if (!bgp)
{
vty_out (vty, "Specified 'all' vrf's but this command currently only works per view/vrf%s", VTY_NEWLINE);
return CMD_WARNING;
@@ -8534,12 +8513,12 @@ DEFUN (show_ip_bgp_regexp,
"Display routes matching the AS path regular expression\n"
"A regular-expression to match the BGP AS paths\n")
{
- vrf_id_t vrf = VRF_DEFAULT;
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
int idx = 0;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
@@ -8564,12 +8543,12 @@ DEFUN (show_ip_bgp_instance_all,
BGP_SAFI_HELP_STR
JSON_STR)
{
- vrf_id_t vrf = VRF_DEFAULT;
afi_t afi = AFI_IP;
safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
int idx = 0;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
@@ -9253,41 +9232,19 @@ DEFUN (show_ip_bgp_instance_neighbor_prefix_counts,
"Display detailed prefix count information\n"
JSON_STR)
{
- vrf_id_t vrf = VRF_DEFAULT;
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
struct peer *peer;
int idx = 0;
struct bgp *bgp = NULL;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
int uj = use_json (argc, argv);
if (uj) argc--;
- if (vrf != VRF_ALL)
- {
- bgp = bgp_lookup_by_vrf_id (vrf);
- if (bgp == NULL)
- {
- if (uj)
- {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(json_no, "warning", "Can't find BGP view");
- vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE);
- json_object_free(json_no);
- }
- else
- vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
- else
- bgp = NULL;
-
argv_find (argv, argc, "neighbors", &idx);
peer = peer_lookup_in_view (vty, bgp, argv[idx+1]->arg, uj);
if (! peer)
@@ -9629,7 +9586,6 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
"Name of the route map\n"
JSON_STR)
{
- vrf_id_t vrf = VRF_DEFAULT;
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
char *rmap_name = NULL;
@@ -9640,29 +9596,13 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
int idx = 0;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
int uj = use_json (argc, argv);
if (uj) argc--;
- bgp = bgp_lookup_by_vrf_id (vrf);
- if (bgp == NULL)
- {
- if (uj)
- {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(json_no, "warning", "Can't find BGP view");
- vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE);
- json_object_free(json_no);
- }
- else
- vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
/* neighbors <A.B.C.D|X:X::X:X|WORD> */
argv_find (argv, argc, "neighbors", &idx);
peerstr = argv[++idx]->arg;
@@ -9809,7 +9749,6 @@ DEFUN (show_ip_bgp_neighbor_routes,
"Display routes learned from neighbor\n"
JSON_STR)
{
- vrf_id_t vrf = VRF_DEFAULT;
char *peerstr = NULL;
struct bgp *bgp = NULL;
afi_t afi = AFI_IP6;
@@ -9819,34 +9758,13 @@ DEFUN (show_ip_bgp_neighbor_routes,
int idx = 0;
- bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf);
+ bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp);
if (!idx)
return CMD_WARNING;
int uj = use_json (argc, argv);
if (uj) argc--;
- if (vrf != VRF_ALL)
- {
- bgp = bgp_lookup_by_vrf_id (vrf);
- if (bgp == NULL)
- {
- if (uj)
- {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(json_no, "warning", "Can't find BGP view");
- vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE);
- json_object_free(json_no);
- }
- else
- vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
- else
- bgp = NULL;
-
/* neighbors <A.B.C.D|X:X::X:X|WORD> */
argv_find (argv, argc, "neighbors", &idx);
peerstr = argv[++idx]->arg;
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index c7bcfe25f9..bafc81eaf8 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -22,7 +22,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "prefix.h"
#include "filter.h"
-#include "vty.h"
#include "routemap.h"
#include "command.h"
#include "linklist.h"
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index 884523919e..06e443b25a 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -23,7 +23,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "prefix.h"
#include "memory.h"
#include "sockunion.h"
-#include "vty.h"
#include "queue.h"
#include "filter.h"
#include "command.h"
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 6ad356a04c..34c7691c26 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -214,10 +214,10 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t
}
/*
- * bgp_vty_find_and_parse_afi_safi_vrf
+ * bgp_vty_find_and_parse_afi_safi_bgp
*
- * For a given 'show ...' command, correctly parse the afi/safi/vrf out from it
- * This function *assumes* that the calling function pre-sets the afi/safi/vrf
+ * For a given 'show ...' command, correctly parse the afi/safi/bgp out from it
+ * This function *assumes* that the calling function pre-sets the afi/safi/bgp
* to appropriate values for the calling function. This is to allow the
* calling function to make decisions appropriate for the show command
* that is being parsed.
@@ -238,7 +238,7 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t
* idx -> The current place in the command, generally should be 0 for this function
* afi -> The parsed afi if it was included in the show command, returned here
* safi -> The parsed safi if it was included in the show command, returned here
- * vrf -> The parsed vrf id if it was included in the show command, returned here
+ * bgp -> Pointer to the bgp data structure we need to fill in.
*
* The function returns the correct location in the parse tree for the
* last token found.
@@ -247,14 +247,14 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t
* it found the last token.
*/
int
-bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, int argc, int *idx,
- afi_t *afi, safi_t *safi, vrf_id_t *vrf)
+bgp_vty_find_and_parse_afi_safi_bgp (struct vty *vty, struct cmd_token **argv, int argc, int *idx,
+ afi_t *afi, safi_t *safi, struct bgp **bgp)
{
char *vrf_name = NULL;
assert (afi);
assert (safi);
- assert (vrf && *vrf != VRF_UNKNOWN);
+ assert (bgp);
if (argv_find (argv, argc, "ip", idx))
*afi = AFI_IP;
@@ -263,26 +263,34 @@ bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, i
{
vrf_name = argv[*idx + 1]->arg;
*idx += 2;
- }
- if (argv_find_and_parse_afi (argv, argc, idx, afi))
- argv_find_and_parse_safi (argv, argc, idx, safi);
-
- if (vrf_name)
- {
- if (strmatch(vrf_name, "all"))
- *vrf = VRF_ALL;
+ if (strmatch (vrf_name, "all"))
+ *bgp = NULL;
else
- *vrf = vrf_name_to_id (vrf_name);
+ {
+ *bgp = bgp_lookup_by_name (vrf_name);
+ if (!*bgp)
+ {
+ vty_out (vty, "View/Vrf specified is unknown: %s%s", vrf_name, VTY_NEWLINE);
+ *idx = 0;
+ return 0;
+ }
+ }
}
-
- if (*vrf == VRF_UNKNOWN)
+ else
{
- vty_out (vty, "View/Vrf specified is unknown: %s", vrf_name);
- *idx = 0;
- return 0;
+ *bgp = bgp_get_default ();
+ if (!*bgp)
+ {
+ vty_out (vty, "Unable to find default BGP instance%s", VTY_NEWLINE);
+ *idx = 0;
+ return 0;
+ }
}
+ if (argv_find_and_parse_afi (argv, argc, idx, afi))
+ argv_find_and_parse_safi (argv, argc, idx, safi);
+
*idx += 1;
return *idx;
}
@@ -1144,6 +1152,13 @@ bgp_maxpaths_config_vty (struct vty *vty, int peer_type, const char *mpaths,
if (set)
{
maxpaths = strtol(mpaths, NULL, 10);
+ if (maxpaths > multipath_num)
+ {
+ vty_out (vty,
+ "%% Maxpaths Specified: %d is > than multipath num specified on bgp command line %d",
+ maxpaths, multipath_num);
+ return CMD_WARNING;
+ }
ret = bgp_maximum_paths_set (bgp, afi, safi, peer_type, maxpaths, options);
}
else
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index 13e67d112e..33d24d530e 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -64,6 +64,6 @@ extern int
argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t *safi);
extern int
-bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, int argc, int *idx,
- afi_t *afi, safi_t *safi, vrf_id_t *vrf);
+bgp_vty_find_and_parse_afi_safi_bgp (struct vty *vty, struct cmd_token **argv, int argc, int *idx,
+ afi_t *afi, safi_t *safi, struct bgp **bgp);
#endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 822f459c28..2a513dda25 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -69,7 +69,7 @@ struct stream *bgp_ifindices_buf = NULL;
Number of supported next-hops are finite, use of arrays should be ok. */
struct attr attr_cp[MULTIPATH_NUM];
struct attr_extra attr_extra_cp[MULTIPATH_NUM];
-int attr_index = 0;
+unsigned int attr_index = 0;
/* Once per address-family initialization of the attribute array */
#define BGP_INFO_ATTR_BUF_INIT()\
@@ -82,7 +82,7 @@ do {\
#define BGP_INFO_ATTR_BUF_COPY(info_src, info_dst)\
do { \
*info_dst = *info_src; \
- assert(attr_index != MULTIPATH_NUM);\
+ assert(attr_index != multipath_num);\
attr_cp[attr_index].extra = &attr_extra_cp[attr_index]; \
bgp_attr_dup (&attr_cp[attr_index], info_src->attr); \
bgp_attr_deep_dup (&attr_cp[attr_index], info_src->attr); \
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 42a0f8d4b8..5592749b28 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -93,6 +93,8 @@ struct bgp_master *bm;
/* BGP community-list. */
struct community_list_handler *bgp_clist;
+unsigned int multipath_num = MULTIPATH_NUM;
+
static void bgp_if_init (struct bgp *bgp);
static void bgp_if_finish (struct bgp *bgp);
@@ -2918,8 +2920,8 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type)
bgp->rib[afi][safi] = bgp_table_init (afi, safi);
/* Enable maximum-paths */
- bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_EBGP, MULTIPATH_NUM, 0);
- bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_IBGP, MULTIPATH_NUM, 0);
+ bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_EBGP, multipath_num, 0);
+ bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_IBGP, multipath_num, 0);
}
bgp->v_update_delay = BGP_UPDATE_DELAY_DEF;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 09c64aa331..eba030e6f9 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "qobj.h"
#include "lib/json.h"
#include "vrf.h"
+#include "vty.h"
/* For union sockunion. */
#include "queue.h"
@@ -1161,6 +1162,7 @@ typedef enum
} bgp_policy_type_e;
extern struct bgp_master *bm;
+extern unsigned int multipath_num;
/* Prototypes. */
extern void bgp_terminate (void);
diff --git a/cumulus/etc/frr/daemons b/cumulus/etc/frr/daemons
index cf03cd9de1..91e2a0f5b6 100644
--- a/cumulus/etc/frr/daemons
+++ b/cumulus/etc/frr/daemons
@@ -28,4 +28,5 @@ ospf6d=no
ripd=no
ripngd=no
isisd=no
-
+pimd=no
+ldpd=no
diff --git a/cumulus/etc/frr/debian.conf b/cumulus/etc/frr/debian.conf
index 8f3e1c1db6..a4945f39ae 100644
--- a/cumulus/etc/frr/debian.conf
+++ b/cumulus/etc/frr/debian.conf
@@ -12,6 +12,7 @@ ripd_options=" --daemon -A 127.0.0.1"
ripngd_options=" --daemon -A ::1"
isisd_options=" --daemon -A 127.0.0.1"
pimd_options=" --daemon -A 127.0.0.1"
+ldpd_options=" --daemon -A 127.0.0.1"
# The list of daemons to watch is automatically generated by the init script.
watchfrr_enable=yes
diff --git a/doc/cli.md b/doc/cli.md
new file mode 100644
index 0000000000..559f75a740
--- /dev/null
+++ b/doc/cli.md
@@ -0,0 +1,356 @@
+FRR Command Line Interface
+==========================
+
+Definition Grammar
+------------------
+
+This is a reference for the syntax used when defining new CLI commands. An
+example definition is:
+
+DEFUN (command_name,
+ command_name_cmd,
+--> "example <command|line [interface]> DEFINITION...",
+ <..doc strings..>)
+
+The arrowed part is the definition string.
+
+Explicit syntax rules in Flex and Bison may be found in lib/command_lex.l and
+lib/command_parse.y, respectively. If you can read BNF and regex those will be
+more useful than this document.
+
+If the parser is throwing syntax or other errors and you can't figure out why,
+it's unlikely to be a bug in the parser. If the error message is not useful,
+please file a bug for a better error message. If all else fails, read the token
+definitions in the lexer source and the Bison BNF in the parser source.
+
+Characters allowed in each token type:
+
+Tokens
+------
+* WORD -- A token that begins with +, -, or a lowercase letter. It is
+ an unchanging part of the command and will only match itself.
+ Example: "show ip bgp", every token is a WORD.
+* IPV4 -- 'A.B.C.D', matches an IPv4 address.
+* IPV6 -- 'X:X::X:X', matches an IPv6 address.
+* IPV4_PREFIX -- 'A.B.C.D/M', matches an IPv4 prefix in CIDR notation.
+* IPV6_PREFIX -- 'X:X::X:X/M', matches an IPv6 prefix in CIDR notation.
+* VARIABLE -- Begins with a capital letter. Matches any input.
+* RANGE -- Numeric range delimited by parentheses, e.g. (-100 - 100) or
+ (10-20). Will only match numbers in the range.
+
+Rules
+-----
+* <angle|brackets> -- Contain sequences of tokens separated by pipes and
+ provide mutual exclusion. Sequences may contain
+ <mutual|exclusion> but not as the first token.
+ Disallowed: "example <<a|b> c|d>"
+ Allowed: "example <a c|b c|d>
+* [square brackets] -- Contains sequences of tokens that are optional (can be
+ omitted).
+* {curly|braces} -- similar to angle brackets, but instead of mutual
+ exclusion, curly braces indicate that one or more of the
+ pipe-separated sequences may be provided in any order.
+* VARIADICS... -- Any token which accepts input (so anything except WORD)
+ and that occurs as the last token of a line may be
+ followed by an ellipsis, which indicates that input
+ matching the token may be repeated an unlimited number
+ of times.
+
+Some general notes:
+
+* Options are allowed at the beginning of the command. The developer is
+ entreated to use these extremely sparingly. They are most useful for
+ implementing the 'no' form of configuration commands. Please think carefully
+ before using them for anything else. There is usually a better solution, even
+ if it is just separating out the command definition into separate ones.
+
+* The developer should judiciously apply separation of concerns when defining
+ CLI. CLI definitions for two unrelated or vaguely related commands or
+ configuration items should be defined in separate commands. Clarity is
+ preferred over LOC (within reason).
+
+Doc Strings
+-----------
+Each token in a command definition should be documented with a brief doc
+string that informs a user of the meaning and/or purpose of the subsequent
+command tree. These strings are provided as the last parameter to DEFUN macros,
+concatenated together and separated by an escaped newline ('\n'). These are
+best explained by example.
+
+DEFUN (config_terminal,
+ config_terminal_cmd,
+ "configure terminal",
+ "Configuration from vty interface\n"
+ "Configuration terminal\n")
+
+The last parameter is split into two lines for readability. Two newline
+delimited doc strings are present, one for each token in the command. The
+second string documents the functionality of the 'terminal' command in the
+'configure' tree.
+
+Note that the first string, for 'configure' does not contain documentation for
+'terminal'. This is because the CLI is best envisioned as a tree, with tokens
+defining branches. An imaginary 'start' token is the root of every command in a
+CLI node. Each subsequent written token descends into a subtree, so the
+documentation for that token ideally summarizes all the functionality contained
+in the subtree.
+
+A consequence of this structure is that the developer must be careful to use
+the same doc strings when defining multiple commands that are part of the same
+tree. Commands which share prefixes must share the same doc strings for those
+prefixes. On startup the parser will generate warnings if it notices
+inconsistent doc strings. Behavior is undefined; the same token may show up
+twice in completions, with different doc strings, or it may show up once with a
+random doc string. Parser warnings should be heeded and fixed to avoid
+confusing users.
+
+The number of doc strings provided must be equal to the amount of tokens
+present in the command definition, read left to right, ignoring any special
+constructs.
+
+In the examples below, each arrowed token needs a doc string.
+
+ "show ip bgp"
+ ^ ^ ^
+
+ "command <foo|bar> [example]"
+ ^ ^ ^ ^
+
+Data Structures
+---------------
+On startup, the CLI parser sequentially parses each command string definition
+and constructs a directed graph with each token forming a node. This graph is
+the basis of the entire CLI system. It is used to match user input in order to
+generate command completions and match commands to functions.
+
+There is one graph per CLI node (not the same as a graph node in the CLI
+graph). The CLI node struct keeps a reference to its graph (see lib/command.h).
+
+While most of the graph maintains the form of a tree, special constructs
+outlined in the Rules section introduce some quirks. <>, [] and {} form
+self-contained 'subgraphs'. Each subgraph is a tree except that all of the
+'leaves' actually share a child node. This helps with minimizing graph size and
+debugging.
+
+As an example, the subgraph generated by <foo|bar> looks like this:
+
+ .
+ .
+ |
+ +----+---+
+ +--- -+ FORK +----+
+ | +--------+ |
+ +--v---+ +--v---+
+ | foo | | bar |
+ +--+---+ +--+---+
+ | +------+ |
+ +------> JOIN <-----+
+ +---+--+
+ |
+ .
+ .
+
+FORK and JOIN nodes are plumbing nodes that don't correspond to user input.
+They're necessary in order to deduplicate these constructs where applicable.
+
+Options follow the same form, except that there is an edge from the FORK node
+to the JOIN node.
+
+Keywords follow the same form, except that there is an edge from JOIN to FORK.
+Because of this the CLI graph cannot be called acyclic. There is special logic
+in the input matching code that keeps a stack of paths already taken through
+the node in order to disallow following the same path more than once.
+
+Variadics are a bit special; they have an edge back to themselves, which allows
+repeating the same input indefinitely.
+
+The leaves of the graph are nodes that have no out edges. These nodes are
+special; their data section does not contain a token, as most nodes do, or
+NULL, as in FORK/JOIN nodes, but instead has a pointer to a cmd_element. All
+paths through the graph that terminate on a leaf are guaranteed to be defined
+by that command. When a user enters a complete command, the command matcher
+tokenizes the input and executes a DFS on the CLI graph. If it is
+simultaneously able to exhaust all input (one input token per graph node), and
+then find exactly one leaf connected to the last node it reaches, then the
+input has matched the corresponding command and the command is executed. If it
+finds more than one node, then the command is ambiguous (more on this in
+deduplication). If it cannot exhaust all input, the command is unknown. If it
+exhausts all input but does not find an edge node, the command is incomplete.
+
+The parser uses an incremental strategy to build the CLI graph for a node. Each
+command is parsed into its own graph, and then this graph is merged into the
+overall graph. During this merge step, the parser makes a best-effort attempt
+to remove duplicate nodes. If it finds a node in the overall graph that is
+equal to a node in the corresponding position in the command graph, it will
+intelligently merge the properties from the node in the command graph into the
+already-existing node. Subgraphs are also checked for isomorphism and merged
+where possible. The definition of whether two nodes are 'equal' is based on the
+equality of some set of token properties; read the parser source for the most
+up-to-date definition of equality.
+
+When the parser is unable to deduplicate some complicated constructs, this
+can result in two identical paths through separate parts of the graph. If
+this occurs and the user enters input that matches these paths, they will
+receive an 'ambiguous command' error and will be unable to execute the command.
+Most of the time the parser can detect and warn about duplicate commands, but
+it will not always be able to do this. Hence care should be taken before
+defining a new command to ensure it is not defined elsewhere.
+
+
+Command handlers
+----------------
+The block that follows a CLI definition is executed when a user enters input
+that matches the definition. Its function signature looks like this:
+
+int (*func) (const struct cmd_element *, struct vty *, int, struct cmd_token *[]);
+
+The first argument is the command definition struct. The last argument is an
+ordered array of tokens that correspond to the path taken through the graph,
+and the argument just prior to that is the length of the array.
+
+The arrangement of the token array has changed from the prior incarnation of
+the CLI system. In the old system, missing arguments were padded with NULLs so
+that the same parts of a command would show up at the same indices regardless
+of what was entered. The new system does not perform such padding and therefore
+it is generally _incorrect_ to assume consistent indices in this array. As a
+simple example:
+
+Command definition:
+ command [foo] <bar|baz>
+
+User enters:
+ command foo bar
+
+Array:
+ [0] -> command
+ [1] -> foo
+ [2] -> bar
+
+User enters:
+ command baz
+
+Array:
+ [0] -> command
+ [1] -> baz
+
+
+
+Command abbreviation & matching priority
+----------------------------------------
+As in the prior implementation, it is possible for users to elide parts of
+tokens when the CLI matcher does not need them to make an unambiguous match.
+This is best explained by example.
+
+Command definitions:
+ command dog cow
+ command dog crow
+
+User input:
+ c d c -> ambiguous command
+ c d co -> match "command dog cow"
+
+In the new implementation, this functionality has improved. Where previously
+the parser would stop at the first ambiguous token, it will now look ahead and
+attempt to disambiguate based on tokens later on in the input string.
+
+Command definitions:
+ show ip bgp A.B.C.D
+ show ipv6 bgp X:X::X:X
+
+User enters:
+ s i b 4.3.2.1 -> match "show ip bgp A.B.C.D"
+ s i b ::e0 -> match "show ipv6 bgp X:X::X:X"
+
+Previously both of these commands would be ambiguous since 'i' does not
+explicitly select either 'ip' or 'ipv6'. However, since the user later provides
+a token that matches only one of the commands (an IPv4 or IPv6 address) the
+parser is able to look ahead and select the appropriate command. This has some
+implications for parsing the argv*[] that is passed to the command handler.
+
+Now consider a command definition such as:
+ command <foo|VAR>
+
+'foo' only matches the string 'foo', but 'VAR' matches any input, including
+'foo'. Who wins? In situations like this the matcher will always choose the
+'better' match, so 'foo' will win.
+
+Consider also:
+ show <ip|ipv6> foo
+
+User input:
+ show ip foo
+
+'ip' partially matches 'ipv6' but exactly matches 'ip', so 'ip' will win.
+
+
+struct cmd_token
+----------------
+
+/* Command token struct. */
+struct cmd_token
+{
+ enum cmd_token_type type; // token type
+ u_char attr; // token attributes
+ bool allowrepeat; // matcher allowed to match token repetitively?
+
+ char *text; // token text
+ char *desc; // token description
+ long long min, max; // for ranges
+ char *arg; // user input that matches this token
+};
+
+This struct is used in the CLI graph to match input against. It is also used to
+pass user input to command handler functions, as it is frequently useful for
+handlers to have access to that information. When a command is matched, the
+sequence of cmd_tokens that form the matching path are duplicated and placed in
+order into argv*[]. Before this happens the ->arg field is set to point at the
+snippet of user input that matched it.
+
+For most nontrivial commands the handler function will need to determine which
+of the possible matching inputs was entered. Previously this was done by
+looking at the first few characters of input. This is now considered an
+anti-pattern and should be avoided. Instead, the ->type or ->text fields for
+this logic. The ->type field can be used when the possible inputs differ in
+type. When the possible types are the same, use the ->text field. This field
+has the full text of the corresponding token in the definition string and using
+it makes for much more readable code. An example is helpful.
+
+Command definition:
+ command <(1-10)|foo|BAR>
+
+In this example, the user may enter any one of:
+ * an integer between 1 and 10
+ * "foo"
+ * anything at all
+
+If the user enters "command f", then:
+
+argv[1]->type == WORD_TKN
+argv[1]->arg == "f"
+argv[1]->text == "foo"
+
+Range tokens have some special treatment; a token with ->type == RANGE_TKN will
+have the ->min and ->max fields set to the bounding values of the range.
+
+
+Permutations
+------------
+Finally, it is sometimes useful to check all the possible combinations of input
+that would match an arbitrary definition string. There is a tool in tools/
+called 'permutations' that reads CLI definition strings on stdin and prints out
+all matching input permutations. It also dumps a text representation of the
+graph, which is more useful for debugging than anything else. It looks like
+this:
+
+$ ./permutations "show [ip] bgp [<view|vrf> WORD]"
+
+show ip bgp view WORD
+show ip bgp vrf WORD
+show ip bgp
+show bgp view WORD
+show bgp vrf WORD
+show bgp
+
+This functionality is also built into VTY/VTYSH; the 'list permutations'
+command will list all possible matching input permutations in the current CLI
+node.
diff --git a/ldpd/address.c b/ldpd/address.c
index 1c4c116f21..ad23ca690b 100644
--- a/ldpd/address.c
+++ b/ldpd/address.c
@@ -26,10 +26,14 @@
static void send_address(struct nbr *, int, struct if_addr_head *,
unsigned int, int);
-static int gen_address_list_tlv(struct ibuf *, uint16_t, int,
- struct if_addr_head *, unsigned int);
+static int gen_address_list_tlv(struct ibuf *, int, struct if_addr_head *,
+ unsigned int);
+static int gen_mac_list_tlv(struct ibuf *, uint8_t *);
static void address_list_add(struct if_addr_head *, struct if_addr *);
static void address_list_clr(struct if_addr_head *);
+static void log_msg_address(int, uint16_t, struct nbr *, int,
+ union ldpd_addr *);
+static void log_msg_mac_withdrawal(int, struct nbr *, uint8_t *);
static void
send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
@@ -83,8 +87,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
size -= LDP_HDR_SIZE;
err |= gen_msg_hdr(buf, msg_type, size);
size -= LDP_MSG_SIZE;
- err |= gen_address_list_tlv(buf, size, af, addr_list,
- tlv_addr_count);
+ err |= gen_address_list_tlv(buf, af, addr_list, tlv_addr_count);
if (err) {
address_list_clr(addr_list);
ibuf_free(buf);
@@ -92,9 +95,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
}
while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
- debug_msg_send("%s: lsr-id %s address %s",
- msg_name(msg_type), inet_ntoa(nbr->id),
- log_addr(af, &if_addr->addr));
+ log_msg_address(1, msg_type, nbr, af, &if_addr->addr);
LIST_REMOVE(if_addr, entry);
free(if_addr);
@@ -137,16 +138,63 @@ send_address_all(struct nbr *nbr, int af)
send_address(nbr, af, &addr_list, addr_count, 0);
}
+void
+send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err;
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE + len_fec_tlv(fec) +
+ TLV_HDR_SIZE;
+ if (mac)
+ size += ETHER_ADDR_LEN;
+
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err = gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ err |= gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size);
+ size -= LDP_MSG_SIZE;
+ err |= gen_address_list_tlv(buf, AF_INET, NULL, 0);
+ err |= gen_fec_tlv(buf, fec);
+ err |= gen_mac_list_tlv(buf, mac);
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ log_msg_mac_withdrawal(1, nbr, mac);
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
int
recv_address(struct nbr *nbr, char *buf, uint16_t len)
{
struct ldp_msg msg;
uint16_t msg_type;
- struct address_list_tlv alt;
enum imsg_type type;
+ struct address_list_tlv alt;
+ uint16_t alt_len;
+ uint16_t alt_family;
struct lde_addr lde_addr;
memcpy(&msg, buf, sizeof(msg));
+ msg_type = ntohs(msg.type);
+ switch (msg_type) {
+ case MSG_TYPE_ADDR:
+ type = IMSG_ADDRESS_ADD;
+ break;
+ case MSG_TYPE_ADDRWITHDRAW:
+ type = IMSG_ADDRESS_DEL;
+ break;
+ default:
+ fatalx("recv_address: unexpected msg type");
+ }
buf += LDP_MSG_SIZE;
len -= LDP_MSG_SIZE;
@@ -155,17 +203,18 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
return (-1);
}
-
memcpy(&alt, buf, sizeof(alt));
- if (ntohs(alt.length) != len - TLV_HDR_SIZE) {
+ alt_len = ntohs(alt.length);
+ alt_family = ntohs(alt.family);
+ if (alt_len > len - TLV_HDR_SIZE) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
return (-1);
}
if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) {
- session_shutdown(nbr, S_UNKNOWN_TLV, msg.id, msg.type);
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
return (-1);
}
- switch (ntohs(alt.family)) {
+ switch (alt_family) {
case AF_IPV4:
if (!nbr->v4_enabled)
/* just ignore the message */
@@ -177,22 +226,18 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
return (0);
break;
default:
- send_notification_nbr(nbr, S_UNSUP_ADDR, msg.id, msg.type);
+ send_notification(nbr->tcp, S_UNSUP_ADDR, msg.id, msg.type);
return (-1);
}
+ alt_len -= sizeof(alt.family);
buf += sizeof(alt);
len -= sizeof(alt);
- msg_type = ntohs(msg.type);
- if (msg_type == MSG_TYPE_ADDR)
- type = IMSG_ADDRESS_ADD;
- else
- type = IMSG_ADDRESS_DEL;
-
- while (len > 0) {
- switch (ntohs(alt.family)) {
+ /* Process all received addresses */
+ while (alt_len > 0) {
+ switch (alt_family) {
case AF_IPV4:
- if (len < sizeof(struct in_addr)) {
+ if (alt_len < sizeof(struct in_addr)) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
msg.type);
return (-1);
@@ -204,9 +249,10 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
buf += sizeof(struct in_addr);
len -= sizeof(struct in_addr);
+ alt_len -= sizeof(struct in_addr);
break;
case AF_IPV6:
- if (len < sizeof(struct in6_addr)) {
+ if (alt_len < sizeof(struct in6_addr)) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
msg.type);
return (-1);
@@ -218,24 +264,57 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
buf += sizeof(struct in6_addr);
len -= sizeof(struct in6_addr);
+ alt_len -= sizeof(struct in6_addr);
break;
default:
fatalx("recv_address: unknown af");
}
- debug_msg_recv("%s: lsr-id %s address %s", msg_name(msg_type),
- inet_ntoa(nbr->id), log_addr(lde_addr.af, &lde_addr.addr));
+ log_msg_address(0, msg_type, nbr, lde_addr.af, &lde_addr.addr);
ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr,
sizeof(lde_addr));
}
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_type;
+ uint16_t tlv_len;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+
+ switch (tlv_type) {
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
return (0);
}
static int
-gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
- struct if_addr_head *addr_list, unsigned int tlv_addr_count)
+gen_address_list_tlv(struct ibuf *buf, int af, struct if_addr_head *addr_list,
+ unsigned int tlv_addr_count)
{
struct address_list_tlv alt;
uint16_t addr_size;
@@ -243,8 +322,7 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
int err = 0;
memset(&alt, 0, sizeof(alt));
- alt.type = TLV_TYPE_ADDRLIST;
- alt.length = htons(size - TLV_HDR_SIZE);
+ alt.type = htons(TLV_TYPE_ADDRLIST);
switch (af) {
case AF_INET:
@@ -258,8 +336,12 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
default:
fatalx("gen_address_list_tlv: unknown af");
}
+ alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count);
err |= ibuf_add(buf, &alt, sizeof(alt));
+ if (addr_list == NULL)
+ return (err);
+
LIST_FOREACH(if_addr, addr_list, entry) {
err |= ibuf_add(buf, &if_addr->addr, addr_size);
if (--tlv_addr_count == 0)
@@ -269,6 +351,23 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
return (err);
}
+static int
+gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac)
+{
+ struct tlv tlv;
+ int err;
+
+ memset(&tlv, 0, sizeof(tlv));
+ tlv.type = htons(TLV_TYPE_MAC_LIST);
+ if (mac)
+ tlv.length = htons(ETHER_ADDR_LEN);
+ err = ibuf_add(buf, &tlv, sizeof(tlv));
+ if (mac)
+ err |= ibuf_add(buf, mac, ETHER_ADDR_LEN);
+
+ return (err);
+}
+
static void
address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr)
{
@@ -292,3 +391,21 @@ address_list_clr(struct if_addr_head *addr_list)
free(if_addr);
}
}
+
+static void
+log_msg_address(int out, uint16_t msg_type, struct nbr *nbr, int af,
+ union ldpd_addr *addr)
+{
+ debug_msg(out, "%s: lsr-id %s, address %s", msg_name(msg_type),
+ inet_ntoa(nbr->id), log_addr(af, addr));
+}
+
+static void
+log_msg_mac_withdrawal(int out, struct nbr *nbr, uint8_t *mac)
+{
+ char buf[ETHER_ADDR_STRLEN];
+
+ debug_msg(out, "mac withdrawal: lsr-id %s, mac %s", inet_ntoa(nbr->id),
+ (mac) ? prefix_mac2str((struct ethaddr *)mac, buf, sizeof(buf)) :
+ "wildcard");
+}
diff --git a/ldpd/control.c b/ldpd/control.c
index 8a2280be07..0bfe0abc9d 100644
--- a/ldpd/control.c
+++ b/ldpd/control.c
@@ -148,9 +148,10 @@ control_connbyfd(int fd)
{
struct ctl_conn *c;
- for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
- c = TAILQ_NEXT(c, entry))
- ; /* nothing */
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.fd == fd)
+ break;
+ }
return (c);
}
@@ -160,9 +161,10 @@ control_connbypid(pid_t pid)
{
struct ctl_conn *c;
- for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.pid != pid;
- c = TAILQ_NEXT(c, entry))
- ; /* nothing */
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.pid == pid)
+ break;
+ }
return (c);
}
diff --git a/ldpd/init.c b/ldpd/init.c
index ed6b53c02d..bc3a69edc7 100644
--- a/ldpd/init.c
+++ b/ldpd/init.c
@@ -24,6 +24,9 @@
#include "ldp_debug.h"
static int gen_init_prms_tlv(struct ibuf *, struct nbr *);
+static int gen_cap_dynamic_tlv(struct ibuf *);
+static int gen_cap_twcard_tlv(struct ibuf *, int);
+static int gen_cap_unotif_tlv(struct ibuf *, int);
void
send_init(struct nbr *nbr)
@@ -34,15 +37,18 @@ send_init(struct nbr *nbr)
debug_msg_send("initialization: lsr-id %s", inet_ntoa(nbr->id));
- size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE;
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE +
+ CAP_TLV_DYNAMIC_SIZE + CAP_TLV_TWCARD_SIZE + CAP_TLV_UNOTIF_SIZE;
if ((buf = ibuf_open(size)) == NULL)
fatal(__func__);
err |= gen_ldp_hdr(buf, size);
size -= LDP_HDR_SIZE;
err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size);
- size -= LDP_MSG_SIZE;
err |= gen_init_prms_tlv(buf, nbr);
+ err |= gen_cap_dynamic_tlv(buf);
+ err |= gen_cap_twcard_tlv(buf, 1);
+ err |= gen_cap_unotif_tlv(buf, 1);
if (err) {
ibuf_free(buf);
return;
@@ -57,6 +63,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
struct ldp_msg msg;
struct sess_prms_tlv sess;
uint16_t max_pdu_len;
+ int caps_rcvd = 0;
debug_msg_recv("initialization: lsr-id %s", inet_ntoa(nbr->id));
@@ -93,6 +100,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
/* Optional Parameters */
while (len > 0) {
struct tlv tlv;
+ uint16_t tlv_type;
uint16_t tlv_len;
if (len < sizeof(tlv)) {
@@ -101,6 +109,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
}
memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
tlv_len = ntohs(tlv.length);
if (tlv_len + TLV_HDR_SIZE > len) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
@@ -109,17 +118,81 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
buf += TLV_HDR_SIZE;
len -= TLV_HDR_SIZE;
- switch (ntohs(tlv.type)) {
+ /*
+ * RFC 5561 - Section 6:
+ * "The S-bit of a Capability Parameter in an Initialization
+ * message MUST be 1 and SHOULD be ignored on receipt".
+ */
+ switch (tlv_type) {
case TLV_TYPE_ATMSESSIONPAR:
session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
return (-1);
case TLV_TYPE_FRSESSION:
session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
return (-1);
+ case TLV_TYPE_DYNAMIC_CAP:
+ if (tlv_len != CAP_TLV_DYNAMIC_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ if (caps_rcvd & F_CAP_TLV_RCVD_DYNAMIC) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ return (-1);
+ }
+ caps_rcvd |= F_CAP_TLV_RCVD_DYNAMIC;
+
+ nbr->flags |= F_NBR_CAP_DYNAMIC;
+
+ log_debug("%s: lsr-id %s announced the Dynamic "
+ "Capability Announcement capability", __func__,
+ inet_ntoa(nbr->id));
+ break;
+ case TLV_TYPE_TWCARD_CAP:
+ if (tlv_len != CAP_TLV_TWCARD_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ return (-1);
+ }
+ caps_rcvd |= F_CAP_TLV_RCVD_TWCARD;
+
+ nbr->flags |= F_NBR_CAP_TWCARD;
+
+ log_debug("%s: lsr-id %s announced the Typed Wildcard "
+ "FEC capability", __func__, inet_ntoa(nbr->id));
+ break;
+ case TLV_TYPE_UNOTIF_CAP:
+ if (tlv_len != CAP_TLV_UNOTIF_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ return (-1);
+ }
+ caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF;
+
+ nbr->flags |= F_NBR_CAP_UNOTIF;
+
+ log_debug("%s: lsr-id %s announced the Unrecognized "
+ "Notification capability", __func__,
+ inet_ntoa(nbr->id));
+ break;
default:
if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
- send_notification_nbr(nbr, S_UNKNOWN_TLV,
- msg.id, msg.type);
+ send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
/* ignore unknown tlv */
break;
}
@@ -145,6 +218,164 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
return (0);
}
+void
+send_capability(struct nbr *nbr, uint16_t capability, int enable)
+{
+ struct ibuf *buf;
+ uint16_t size;
+ int err = 0;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + CAP_TLV_DYNAMIC_SIZE;
+ if ((buf = ibuf_open(size)) == NULL)
+ fatal(__func__);
+
+ err |= gen_ldp_hdr(buf, size);
+ size -= LDP_HDR_SIZE;
+ err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size);
+
+ switch (capability) {
+ case TLV_TYPE_TWCARD_CAP:
+ err |= gen_cap_twcard_tlv(buf, enable);
+ break;
+ case TLV_TYPE_UNOTIF_CAP:
+ err |= gen_cap_unotif_tlv(buf, enable);
+ break;
+ case TLV_TYPE_DYNAMIC_CAP:
+ /*
+ * RFC 5561 - Section 9:
+ * "An LDP speaker MUST NOT include the Dynamic Capability
+ * Announcement Parameter in Capability messages sent to
+ * its peers".
+ */
+ /* FALLTHROUGH */
+ default:
+ fatalx("send_capability: unsupported capability");
+ }
+
+ if (err) {
+ ibuf_free(buf);
+ return;
+ }
+
+ evbuf_enqueue(&nbr->tcp->wbuf, buf);
+ nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
+int
+recv_capability(struct nbr *nbr, char *buf, uint16_t len)
+{
+ struct ldp_msg msg;
+ int enable = 0;
+ int caps_rcvd = 0;
+
+ log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+ memcpy(&msg, buf, sizeof(msg));
+ buf += LDP_MSG_SIZE;
+ len -= LDP_MSG_SIZE;
+
+ /* Optional Parameters */
+ while (len > 0) {
+ struct tlv tlv;
+ uint16_t tlv_type;
+ uint16_t tlv_len;
+ uint8_t reserved;
+
+ if (len < sizeof(tlv)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+
+ memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
+ tlv_len = ntohs(tlv.length);
+ if (tlv_len + TLV_HDR_SIZE > len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ buf += TLV_HDR_SIZE;
+ len -= TLV_HDR_SIZE;
+
+ switch (tlv_type) {
+ case TLV_TYPE_TWCARD_CAP:
+ if (tlv_len != CAP_TLV_TWCARD_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ return (-1);
+ }
+ caps_rcvd |= F_CAP_TLV_RCVD_TWCARD;
+
+ memcpy(&reserved, buf, sizeof(reserved));
+ enable = reserved & STATE_BIT;
+ if (enable)
+ nbr->flags |= F_NBR_CAP_TWCARD;
+ else
+ nbr->flags &= ~F_NBR_CAP_TWCARD;
+
+ log_debug("%s: lsr-id %s %s the Typed Wildcard FEC "
+ "capability", __func__, inet_ntoa(nbr->id),
+ (enable) ? "announced" : "withdrew");
+ break;
+ case TLV_TYPE_UNOTIF_CAP:
+ if (tlv_len != CAP_TLV_UNOTIF_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) {
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+ msg.type);
+ return (-1);
+ }
+ caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF;
+
+ memcpy(&reserved, buf, sizeof(reserved));
+ enable = reserved & STATE_BIT;
+ if (enable)
+ nbr->flags |= F_NBR_CAP_UNOTIF;
+ else
+ nbr->flags &= ~F_NBR_CAP_UNOTIF;
+
+ log_debug("%s: lsr-id %s %s the Unrecognized "
+ "Notification capability", __func__,
+ inet_ntoa(nbr->id), (enable) ? "announced" :
+ "withdrew");
+ break;
+ case TLV_TYPE_DYNAMIC_CAP:
+ /*
+ * RFC 5561 - Section 9:
+ * "An LDP speaker that receives a Capability message
+ * from a peer that includes the Dynamic Capability
+ * Announcement Parameter SHOULD silently ignore the
+ * parameter and process any other Capability Parameters
+ * in the message".
+ */
+ /* FALLTHROUGH */
+ default:
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += tlv_len;
+ len -= tlv_len;
+ }
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ return (0);
+}
+
static int
gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
{
@@ -163,3 +394,45 @@ gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
return (ibuf_add(buf, &parms, SESS_PRMS_SIZE));
}
+
+static int
+gen_cap_dynamic_tlv(struct ibuf *buf)
+{
+ struct capability_tlv cap;
+
+ memset(&cap, 0, sizeof(cap));
+ cap.type = htons(TLV_TYPE_DYNAMIC_CAP);
+ cap.length = htons(CAP_TLV_DYNAMIC_LEN);
+ /* the S-bit is always 1 for the Dynamic Capability Announcement */
+ cap.reserved = STATE_BIT;
+
+ return (ibuf_add(buf, &cap, CAP_TLV_DYNAMIC_SIZE));
+}
+
+static int
+gen_cap_twcard_tlv(struct ibuf *buf, int enable)
+{
+ struct capability_tlv cap;
+
+ memset(&cap, 0, sizeof(cap));
+ cap.type = htons(TLV_TYPE_TWCARD_CAP);
+ cap.length = htons(CAP_TLV_TWCARD_LEN);
+ if (enable)
+ cap.reserved = STATE_BIT;
+
+ return (ibuf_add(buf, &cap, CAP_TLV_TWCARD_SIZE));
+}
+
+static int
+gen_cap_unotif_tlv(struct ibuf *buf, int enable)
+{
+ struct capability_tlv cap;
+
+ memset(&cap, 0, sizeof(cap));
+ cap.type = htons(TLV_TYPE_UNOTIF_CAP);
+ cap.length = htons(CAP_TLV_UNOTIF_LEN);
+ if (enable)
+ cap.reserved = STATE_BIT;
+
+ return (ibuf_add(buf, &cap, CAP_TLV_UNOTIF_SIZE));
+}
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c
index 792608d425..3f4e21e685 100644
--- a/ldpd/l2vpn.c
+++ b/ldpd/l2vpn.c
@@ -152,6 +152,33 @@ l2vpn_if_find_name(struct l2vpn *l2vpn, const char *ifname)
return (RB_FIND(l2vpn_if_head, &l2vpn->if_tree, &lif));
}
+void
+l2vpn_if_update(struct l2vpn_if *lif)
+{
+ struct l2vpn *l2vpn = lif->l2vpn;
+ struct l2vpn_pw *pw;
+ struct map fec;
+ struct nbr *nbr;
+
+ if ((lif->flags & IFF_UP) && (lif->flags & IFF_RUNNING))
+ return;
+
+ RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
+ nbr = nbr_find_ldpid(pw->lsr_id.s_addr);
+ if (nbr == NULL)
+ continue;
+
+ memset(&fec, 0, sizeof(fec));
+ fec.type = MAP_TYPE_PWID;
+ fec.fec.pwid.type = l2vpn->pw_type;
+ fec.fec.pwid.group_id = 0;
+ fec.flags |= F_MAP_PW_ID;
+ fec.fec.pwid.pwid = pw->pwid;
+
+ send_mac_withdrawal(nbr, &fec, lif->mac);
+ }
+}
+
static __inline int
l2vpn_pw_compare(struct l2vpn_pw *a, struct l2vpn_pw *b)
{
@@ -330,7 +357,7 @@ l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map)
st.status_code = S_WRONG_CBIT;
st.msg_id = map->msg_id;
st.msg_type = htons(MSG_TYPE_LABELMAPPING);
- lde_send_labelwithdraw(ln, fn, NO_LABEL, &st);
+ lde_send_labelwithdraw(ln, fn, NULL, &st);
pw->flags &= ~F_PW_CWORD;
lde_send_labelmapping(ln, fn, 1);
@@ -353,7 +380,7 @@ l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map)
}
void
-l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec)
+l2vpn_send_pw_status(struct lde_nbr *ln, uint32_t status, struct fec *fec)
{
struct notify_msg nm;
@@ -364,8 +391,27 @@ l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec)
lde_fec2map(fec, &nm.fec);
nm.flags |= F_NOTIF_FEC;
- lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0,
- &nm, sizeof(nm));
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm,
+ sizeof(nm));
+}
+
+void
+l2vpn_send_pw_status_wcard(struct lde_nbr *ln, uint32_t status,
+ uint16_t pw_type, uint32_t group_id)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_PW_STATUS;
+ nm.pw_status = status;
+ nm.flags |= F_NOTIF_PW_STATUS;
+ nm.fec.type = MAP_TYPE_PWID;
+ nm.fec.fec.pwid.type = pw_type;
+ nm.fec.fec.pwid.group_id = group_id;
+ nm.flags |= F_NOTIF_FEC;
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm,
+ sizeof(nm));
}
void
@@ -376,9 +422,11 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
struct fec_nh *fnh;
struct l2vpn_pw *pw;
- /* TODO group wildcard */
- if (!(nm->fec.flags & F_MAP_PW_ID))
+ if (nm->fec.type == MAP_TYPE_TYPED_WCARD ||
+ !(nm->fec.flags & F_MAP_PW_ID)) {
+ l2vpn_recv_pw_status_wcard(ln, nm);
return;
+ }
lde_map2fec(&nm->fec, ln->id, &fec);
fn = (struct fec_node *)fec_find(&ft, &fec);
@@ -397,7 +445,6 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
/* remote status didn't change */
if (pw->remote_status == nm->pw_status)
return;
-
pw->remote_status = nm->pw_status;
if (l2vpn_pw_ok(pw, fnh))
@@ -406,6 +453,56 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
lde_send_delete_klabel(fn, fnh);
}
+/* RFC4447 PWid group wildcard */
+void
+l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ struct l2vpn_pw *pw;
+ struct map *wcard = &nm->fec;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+ if (fn->fec.type != FEC_TYPE_PWID)
+ continue;
+
+ pw = (struct l2vpn_pw *) fn->data;
+ if (pw == NULL)
+ continue;
+
+ switch (wcard->type) {
+ case MAP_TYPE_TYPED_WCARD:
+ if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
+ wcard->fec.twcard.u.pw_type != fn->fec.u.pwid.type)
+ continue;
+ break;
+ case MAP_TYPE_PWID:
+ if (wcard->fec.pwid.type != fn->fec.u.pwid.type)
+ continue;
+ if (wcard->fec.pwid.group_id != pw->remote_group)
+ continue;
+ break;
+ }
+
+ fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id,
+ 0, 0);
+ if (fnh == NULL)
+ continue;
+
+ /* remote status didn't change */
+ if (pw->remote_status == nm->pw_status)
+ continue;
+ pw->remote_status = nm->pw_status;
+
+ if (l2vpn_pw_ok(pw, fnh))
+ lde_send_change_klabel(fn, fnh);
+ else
+ lde_send_delete_klabel(fn, fnh);
+ }
+}
+
void
l2vpn_sync_pws(int af, union ldpd_addr *addr)
{
diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c
index 62f2a620d2..e8ce7fbdf5 100644
--- a/ldpd/labelmapping.c
+++ b/ldpd/labelmapping.c
@@ -28,9 +28,8 @@
static void enqueue_pdu(struct nbr *, struct ibuf *, uint16_t);
static int gen_label_tlv(struct ibuf *, uint32_t);
-static int tlv_decode_label(struct nbr *, struct ldp_msg *, char *,
- uint16_t, uint32_t *);
static int gen_reqid_tlv(struct ibuf *, uint32_t);
+static void log_msg_mapping(int, uint16_t, struct nbr *, struct map *);
static void
enqueue_pdu(struct nbr *nbr, struct ibuf *buf, uint16_t size)
@@ -71,25 +70,8 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
}
/* calculate size */
- msg_size = LDP_MSG_SIZE + TLV_HDR_SIZE;
- switch (me->map.type) {
- case MAP_TYPE_WILDCARD:
- msg_size += FEC_ELM_WCARD_LEN;
- break;
- case MAP_TYPE_PREFIX:
- msg_size += FEC_ELM_PREFIX_MIN_LEN +
- PREFIX_SIZE(me->map.fec.prefix.prefixlen);
- break;
- case MAP_TYPE_PWID:
- msg_size += FEC_PWID_ELM_MIN_LEN;
- if (me->map.flags & F_MAP_PW_ID)
- msg_size += PW_STATUS_TLV_LEN;
- if (me->map.flags & F_MAP_PW_IFMTU)
- msg_size += FEC_SUBTLV_IFMTU_SIZE;
- if (me->map.flags & F_MAP_PW_STATUS)
- msg_size += PW_STATUS_TLV_SIZE;
- break;
- }
+ msg_size = LDP_MSG_SIZE;
+ msg_size += len_fec_tlv(&me->map);
if (me->map.label != NO_LABEL)
msg_size += LABEL_TLV_SIZE;
if (me->map.flags & F_MAP_REQ_ID)
@@ -124,9 +106,7 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
return;
}
- debug_msg_send("%s: lsr-id %s fec %s label %s", msg_name(type),
- inet_ntoa(nbr->id), log_map(&me->map),
- log_label(me->map.label));
+ log_msg_mapping(1, type, nbr, &me->map);
TAILQ_REMOVE(mh, me, entry);
free(me);
@@ -146,7 +126,8 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
uint32_t label = NO_LABEL, reqid = 0;
uint32_t pw_status = 0;
uint8_t flags = 0;
- int feclen, lbllen, tlen;
+ int feclen, tlen;
+ uint16_t current_tlv = 1;
struct mapping_entry *me;
struct mapping_head mh;
struct map map;
@@ -163,7 +144,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
memcpy(&ft, buf, sizeof(ft));
if (ntohs(ft.type) != TLV_TYPE_FEC) {
- send_notification_nbr(nbr, S_MISS_MSG, msg.id, msg.type);
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
return (-1);
}
feclen = ntohs(ft.length);
@@ -187,9 +168,9 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
!(map.flags & F_MAP_PW_ID) &&
type != MSG_TYPE_LABELWITHDRAW &&
type != MSG_TYPE_LABELRELEASE) {
- send_notification_nbr(nbr, S_MISS_MSG, msg.id,
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id,
msg.type);
- return (-1);
+ goto err;
}
/*
@@ -210,6 +191,24 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
}
/*
+ * RFC 5561 - Section 4:
+ * "An LDP implementation that supports the Typed Wildcard
+ * FEC Element MUST support its use in Label Request, Label
+ * Withdraw, and Label Release messages".
+ */
+ if (map.type == MAP_TYPE_TYPED_WCARD) {
+ switch (type) {
+ case MSG_TYPE_LABELMAPPING:
+ case MSG_TYPE_LABELABORTREQ:
+ session_shutdown(nbr, S_UNKNOWN_FEC, msg.id,
+ msg.type);
+ goto err;
+ default:
+ break;
+ }
+ }
+
+ /*
* LDP supports the use of multiple FEC Elements per
* FEC for the Label Mapping message only.
*/
@@ -226,19 +225,10 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
feclen -= tlen;
} while (feclen > 0);
- /* Mandatory Label TLV */
- if (type == MSG_TYPE_LABELMAPPING) {
- lbllen = tlv_decode_label(nbr, &msg, buf, len, &label);
- if (lbllen == -1)
- goto err;
-
- buf += lbllen;
- len -= lbllen;
- }
-
/* Optional Parameters */
while (len > 0) {
struct tlv tlv;
+ uint16_t tlv_type;
uint16_t tlv_len;
uint32_t reqbuf, labelbuf, statusbuf;
@@ -248,6 +238,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
}
memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
tlv_len = ntohs(tlv.length);
if (tlv_len + TLV_HDR_SIZE > len) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
@@ -256,7 +247,18 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
buf += TLV_HDR_SIZE;
len -= TLV_HDR_SIZE;
- switch (ntohs(tlv.type)) {
+ /*
+ * For Label Mapping messages the Label TLV is mandatory and
+ * should appear right after the FEC TLV.
+ */
+ if (current_tlv == 1 && type == MSG_TYPE_LABELMAPPING &&
+ !(tlv_type & TLV_TYPE_GENERICLABEL)) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id,
+ msg.type);
+ goto err;
+ }
+
+ switch (tlv_type) {
case TLV_TYPE_LABELREQUEST:
switch (type) {
case MSG_TYPE_LABELMAPPING:
@@ -282,6 +284,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
break;
case TLV_TYPE_GENERICLABEL:
switch (type) {
+ case MSG_TYPE_LABELMAPPING:
case MSG_TYPE_LABELWITHDRAW:
case MSG_TYPE_LABELRELEASE:
if (tlv_len != LABEL_TLV_LEN) {
@@ -292,6 +295,16 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
memcpy(&labelbuf, buf, sizeof(labelbuf));
label = ntohl(labelbuf);
+ /* do not accept invalid labels */
+ if (label > MPLS_LABEL_MAX ||
+ (label <= MPLS_LABEL_RESERVED_MAX &&
+ label != MPLS_LABEL_IPV4NULL &&
+ label != MPLS_LABEL_IPV6NULL &&
+ label != MPLS_LABEL_IMPLNULL)) {
+ session_shutdown(nbr, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ goto err;
+ }
break;
default:
/* ignore */
@@ -301,6 +314,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
case TLV_TYPE_ATMLABEL:
case TLV_TYPE_FRLABEL:
switch (type) {
+ case MSG_TYPE_LABELMAPPING:
case MSG_TYPE_LABELWITHDRAW:
case MSG_TYPE_LABELRELEASE:
/* unsupported */
@@ -341,13 +355,14 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
break;
default:
if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
- send_notification_nbr(nbr, S_UNKNOWN_TLV,
- msg.id, msg.type);
+ send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
/* ignore unknown tlv */
break;
}
buf += tlv_len;
len -= tlv_len;
+ current_tlv++;
}
/* notify lde about the received message. */
@@ -396,9 +411,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
if (me->map.flags & F_MAP_REQ_ID)
me->map.requestid = reqid;
- debug_msg_recv("%s: lsr-id %s fec %s label %s", msg_name(type),
- inet_ntoa(nbr->id), log_map(&me->map),
- log_label(me->map.label));
+ log_msg_mapping(0, type, nbr, &me->map);
switch (type) {
case MSG_TYPE_LABELMAPPING:
@@ -423,14 +436,14 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map,
sizeof(struct map));
-next:
+ next:
TAILQ_REMOVE(&mh, me, entry);
free(me);
}
return (0);
-err:
+ err:
mapping_list_clr(&mh);
return (-1);
@@ -450,53 +463,6 @@ gen_label_tlv(struct ibuf *buf, uint32_t label)
}
static int
-tlv_decode_label(struct nbr *nbr, struct ldp_msg *msg, char *buf,
- uint16_t len, uint32_t *label)
-{
- struct label_tlv lt;
-
- if (len < sizeof(lt)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
- return (-1);
- }
- memcpy(&lt, buf, sizeof(lt));
-
- if (!(ntohs(lt.type) & TLV_TYPE_GENERICLABEL)) {
- send_notification_nbr(nbr, S_MISS_MSG, msg->id, msg->type);
- return (-1);
- }
-
- switch (htons(lt.type)) {
- case TLV_TYPE_GENERICLABEL:
- if (ntohs(lt.length) != sizeof(lt) - TLV_HDR_SIZE) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
- return (-1);
- }
-
- *label = ntohl(lt.label);
- if (*label > MPLS_LABEL_MAX ||
- (*label <= MPLS_LABEL_RESERVED_MAX &&
- *label != MPLS_LABEL_IPV4NULL &&
- *label != MPLS_LABEL_IPV6NULL &&
- *label != MPLS_LABEL_IMPLNULL)) {
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
- msg->type);
- return (-1);
- }
- break;
- case TLV_TYPE_ATMLABEL:
- case TLV_TYPE_FRLABEL:
- default:
- /* unsupported */
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
- return (-1);
- }
-
- return (sizeof(lt));
-}
-
-static int
gen_reqid_tlv(struct ibuf *buf, uint32_t reqid)
{
struct reqid_tlv rt;
@@ -520,12 +486,52 @@ gen_pw_status_tlv(struct ibuf *buf, uint32_t status)
return (ibuf_add(buf, &st, sizeof(st)));
}
+uint16_t
+len_fec_tlv(struct map *map)
+{
+ uint16_t len = TLV_HDR_SIZE;
+
+ switch (map->type) {
+ case MAP_TYPE_WILDCARD:
+ len += FEC_ELM_WCARD_LEN;
+ break;
+ case MAP_TYPE_PREFIX:
+ len += FEC_ELM_PREFIX_MIN_LEN +
+ PREFIX_SIZE(map->fec.prefix.prefixlen);
+ break;
+ case MAP_TYPE_PWID:
+ len += FEC_PWID_ELM_MIN_LEN;
+ if (map->flags & F_MAP_PW_ID)
+ len += PW_STATUS_TLV_LEN;
+ if (map->flags & F_MAP_PW_IFMTU)
+ len += FEC_SUBTLV_IFMTU_SIZE;
+ if (map->flags & F_MAP_PW_STATUS)
+ len += PW_STATUS_TLV_SIZE;
+ break;
+ case MAP_TYPE_TYPED_WCARD:
+ len += FEC_ELM_TWCARD_MIN_LEN;
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ case MAP_TYPE_PWID:
+ len += sizeof(uint16_t);
+ break;
+ default:
+ fatalx("len_fec_tlv: unexpected fec type");
+ }
+ break;
+ default:
+ fatalx("len_fec_tlv: unexpected fec type");
+ }
+
+ return (len);
+}
+
int
gen_fec_tlv(struct ibuf *buf, struct map *map)
{
struct tlv ft;
uint16_t family, len, pw_type, ifmtu;
- uint8_t pw_len = 0;
+ uint8_t pw_len = 0, twcard_len;
uint32_t group_id, pwid;
int err = 0;
@@ -562,7 +568,7 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
break;
case MAP_TYPE_PWID:
if (map->flags & F_MAP_PW_ID)
- pw_len += PW_STATUS_TLV_LEN;
+ pw_len += FEC_PWID_SIZE;
if (map->flags & F_MAP_PW_IFMTU)
pw_len += FEC_SUBTLV_IFMTU_SIZE;
@@ -595,6 +601,50 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t));
}
break;
+ case MAP_TYPE_TYPED_WCARD:
+ len = FEC_ELM_TWCARD_MIN_LEN;
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ case MAP_TYPE_PWID:
+ len += sizeof(uint16_t);
+ break;
+ default:
+ fatalx("gen_fec_tlv: unexpected fec type");
+ }
+ ft.length = htons(len);
+ err |= ibuf_add(buf, &ft, sizeof(ft));
+ err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
+ err |= ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t));
+
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ twcard_len = sizeof(uint16_t);
+ err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
+
+ switch (map->fec.twcard.u.prefix_af) {
+ case AF_INET:
+ family = htons(AF_IPV4);
+ break;
+ case AF_INET6:
+ family = htons(AF_IPV6);
+ break;
+ default:
+ fatalx("gen_fec_tlv: unknown af");
+ break;
+ }
+
+ err |= ibuf_add(buf, &family, sizeof(uint16_t));
+ break;
+ case MAP_TYPE_PWID:
+ twcard_len = sizeof(uint16_t);
+ err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
+ pw_type = htons(map->fec.twcard.u.pw_type);
+ err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
+ break;
+ default:
+ fatalx("gen_fec_tlv: unexpected fec type");
+ }
+ break;
default:
break;
}
@@ -607,7 +657,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
uint16_t len, struct map *map)
{
uint16_t off = 0;
- uint8_t pw_len;
+ uint8_t pw_len, twcard_len;
map->type = *buf;
off += sizeof(uint8_t);
@@ -642,7 +692,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
map->fec.prefix.af = AF_INET6;
break;
default:
- send_notification_nbr(nbr, S_UNSUP_ADDR, msg->id,
+ send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id,
msg->type);
return (-1);
}
@@ -752,10 +802,83 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
}
return (off);
+ case MAP_TYPE_TYPED_WCARD:
+ if (len < FEC_ELM_TWCARD_MIN_LEN) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.twcard.type, buf + off, sizeof(uint8_t));
+ off += sizeof(uint8_t);
+ memcpy(&twcard_len, buf + off, sizeof(uint8_t));
+ off += sizeof(uint8_t);
+ if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ if (twcard_len != sizeof(uint16_t)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.twcard.u.prefix_af, buf + off,
+ sizeof(uint16_t));
+ map->fec.twcard.u.prefix_af =
+ ntohs(map->fec.twcard.u.prefix_af);
+ off += sizeof(uint16_t);
+
+ switch (map->fec.twcard.u.prefix_af) {
+ case AF_IPV4:
+ map->fec.twcard.u.prefix_af = AF_INET;
+ break;
+ case AF_IPV6:
+ map->fec.twcard.u.prefix_af = AF_INET6;
+ break;
+ default:
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
+ msg->type);
+ return (-1);
+ }
+ break;
+ case MAP_TYPE_PWID:
+ if (twcard_len != sizeof(uint16_t)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ memcpy(&map->fec.twcard.u.pw_type, buf + off,
+ sizeof(uint16_t));
+ map->fec.twcard.u.pw_type =
+ ntohs(map->fec.twcard.u.pw_type);
+ /* ignore the reserved bit as per RFC 6667 */
+ map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT;
+ off += sizeof(uint16_t);
+ break;
+ default:
+ send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id,
+ msg->type);
+ return (-1);
+ }
+
+ return (off);
default:
- send_notification_nbr(nbr, S_UNKNOWN_FEC, msg->id, msg->type);
+ send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type);
break;
}
return (-1);
}
+
+static void
+log_msg_mapping(int out, uint16_t msg_type, struct nbr *nbr, struct map *map)
+{
+ debug_msg(out, "%s: lsr-id %s, fec %s, label %s", msg_name(msg_type),
+ inet_ntoa(nbr->id), log_map(map), log_label(map->label));
+}
diff --git a/ldpd/lde.c b/ldpd/lde.c
index 1323ba3d02..08339c720a 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -37,7 +37,7 @@
static void lde_shutdown(void);
static int lde_dispatch_imsg(struct thread *);
static int lde_dispatch_parent(struct thread *);
-static __inline int lde_nbr_compare(struct lde_nbr *,
+static __inline int lde_nbr_compare(struct lde_nbr *,
struct lde_nbr *);
static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *);
static void lde_nbr_del(struct lde_nbr *);
@@ -205,9 +205,9 @@ lde_dispatch_imsg(struct thread *thread)
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
struct lde_nbr *ln;
- struct map map;
- struct lde_addr lde_addr;
- struct notify_msg nm;
+ struct map *map;
+ struct lde_addr *lde_addr;
+ struct notify_msg *nm;
ssize_t n;
int shut = 0;
@@ -240,9 +240,10 @@ lde_dispatch_imsg(struct thread *thread)
case IMSG_LABEL_RELEASE:
case IMSG_LABEL_WITHDRAW:
case IMSG_LABEL_ABORT:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct map))
fatalx("lde_dispatch_imsg: wrong imsg len");
- memcpy(&map, imsg.data, sizeof(map));
+ map = imsg.data;
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
@@ -253,22 +254,16 @@ lde_dispatch_imsg(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_LABEL_MAPPING:
- lde_check_mapping(&map, ln);
+ lde_check_mapping(map, ln);
break;
case IMSG_LABEL_REQUEST:
- lde_check_request(&map, ln);
+ lde_check_request(map, ln);
break;
case IMSG_LABEL_RELEASE:
- if (map.type == MAP_TYPE_WILDCARD)
- lde_check_release_wcard(&map, ln);
- else
- lde_check_release(&map, ln);
+ lde_check_release(map, ln);
break;
case IMSG_LABEL_WITHDRAW:
- if (map.type == MAP_TYPE_WILDCARD)
- lde_check_withdraw_wcard(&map, ln);
- else
- lde_check_withdraw(&map, ln);
+ lde_check_withdraw(map, ln);
break;
case IMSG_LABEL_ABORT:
/* not necessary */
@@ -276,9 +271,10 @@ lde_dispatch_imsg(struct thread *thread)
}
break;
case IMSG_ADDRESS_ADD:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct lde_addr))
fatalx("lde_dispatch_imsg: wrong imsg len");
- memcpy(&lde_addr, imsg.data, sizeof(lde_addr));
+ lde_addr = imsg.data;
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
@@ -286,16 +282,17 @@ lde_dispatch_imsg(struct thread *thread)
__func__);
break;
}
- if (lde_address_add(ln, &lde_addr) < 0) {
+ if (lde_address_add(ln, lde_addr) < 0) {
log_debug("%s: cannot add address %s, it "
"already exists", __func__,
- log_addr(lde_addr.af, &lde_addr.addr));
+ log_addr(lde_addr->af, &lde_addr->addr));
}
break;
case IMSG_ADDRESS_DEL:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct lde_addr))
fatalx("lde_dispatch_imsg: wrong imsg len");
- memcpy(&lde_addr, imsg.data, sizeof(lde_addr));
+ lde_addr = imsg.data;
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
@@ -303,16 +300,17 @@ lde_dispatch_imsg(struct thread *thread)
__func__);
break;
}
- if (lde_address_del(ln, &lde_addr) < 0) {
+ if (lde_address_del(ln, lde_addr) < 0) {
log_debug("%s: cannot delete address %s, it "
"does not exist", __func__,
- log_addr(lde_addr.af, &lde_addr.addr));
+ log_addr(lde_addr->af, &lde_addr->addr));
}
break;
case IMSG_NOTIFICATION:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct notify_msg))
fatalx("lde_dispatch_imsg: wrong imsg len");
- memcpy(&nm, imsg.data, sizeof(nm));
+ nm = imsg.data;
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
@@ -321,10 +319,17 @@ lde_dispatch_imsg(struct thread *thread)
break;
}
- switch (nm.status_code) {
+ switch (nm->status_code) {
case S_PW_STATUS:
- l2vpn_recv_pw_status(ln, &nm);
+ l2vpn_recv_pw_status(ln, nm);
break;
+ case S_ENDOFLIB:
+ /*
+ * Do nothing for now. Should be useful in
+ * the future when we implement LDP-IGP
+ * Synchronization (RFC 5443) and Graceful
+ * Restart (RFC 3478).
+ */
default:
break;
}
@@ -391,7 +396,7 @@ lde_dispatch_parent(struct thread *thread)
struct l2vpn_if *nlif;
struct l2vpn_pw *npw;
struct imsg imsg;
- struct kroute kr;
+ struct kroute *kr;
int fd = THREAD_FD(thread);
struct imsgev *iev = THREAD_ARG(thread);
struct imsgbuf *ibuf = &iev->ibuf;
@@ -415,22 +420,23 @@ lde_dispatch_parent(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_NETWORK_ADD:
case IMSG_NETWORK_UPDATE:
- if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kroute)) {
log_warnx("%s: wrong imsg len", __func__);
break;
}
- memcpy(&kr, imsg.data, sizeof(kr));
+ kr = imsg.data;
- switch (kr.af) {
+ switch (kr->af) {
case AF_INET:
fec.type = FEC_TYPE_IPV4;
- fec.u.ipv4.prefix = kr.prefix.v4;
- fec.u.ipv4.prefixlen = kr.prefixlen;
+ fec.u.ipv4.prefix = kr->prefix.v4;
+ fec.u.ipv4.prefixlen = kr->prefixlen;
break;
case AF_INET6:
fec.type = FEC_TYPE_IPV6;
- fec.u.ipv6.prefix = kr.prefix.v6;
- fec.u.ipv6.prefixlen = kr.prefixlen;
+ fec.u.ipv6.prefix = kr->prefix.v6;
+ fec.u.ipv6.prefixlen = kr->prefixlen;
break;
default:
fatalx("lde_dispatch_parent: unknown af");
@@ -438,9 +444,9 @@ lde_dispatch_parent(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_NETWORK_ADD:
- lde_kernel_insert(&fec, kr.af, &kr.nexthop,
- kr.ifindex, kr.priority,
- kr.flags & F_CONNECTED, NULL);
+ lde_kernel_insert(&fec, kr->af, &kr->nexthop,
+ kr->ifindex, kr->priority,
+ kr->flags & F_CONNECTED, NULL);
break;
case IMSG_NETWORK_UPDATE:
lde_kernel_update(&fec);
@@ -929,8 +935,8 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single)
}
void
-lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label,
- struct status_tlv *st)
+lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, struct status_tlv *st)
{
struct lde_wdraw *lw;
struct map map;
@@ -959,11 +965,8 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label,
break;
}
map.label = fn->local_label;
- } else {
- memset(&map, 0, sizeof(map));
- map.type = MAP_TYPE_WILDCARD;
- map.label = label;
- }
+ } else
+ memcpy(&map, wcard, sizeof(map));
if (st) {
map.st.status_code = st->status_code;
@@ -984,8 +987,13 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label,
lw = lde_wdraw_add(ln, fn);
lw->label = map.label;
} else {
+ struct lde_map *me;
+
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+ if (lde_wildcard_apply(wcard, &fn->fec, me) == 0)
+ continue;
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw,
&fn->fec);
@@ -997,16 +1005,62 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label,
}
void
-lde_send_labelwithdraw_all(struct fec_node *fn, uint32_t label)
+lde_send_labelwithdraw_wcard(struct lde_nbr *ln, uint32_t label)
{
- struct lde_nbr *ln;
+ struct map wcard;
- RB_FOREACH(ln, nbr_tree, &lde_nbrs)
- lde_send_labelwithdraw(ln, fn, label, NULL);
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_WILDCARD;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *ln, uint16_t af,
+ uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PREFIX;
+ wcard.fec.twcard.u.prefix_af = af;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
}
void
-lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label)
+lde_send_labelwithdraw_twcard_pwid(struct lde_nbr *ln, uint16_t pw_type,
+ uint32_t label)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_TYPED_WCARD;
+ wcard.fec.twcard.type = MAP_TYPE_PWID;
+ wcard.fec.twcard.u.pw_type = pw_type;
+ wcard.label = label;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *ln, uint16_t pw_type,
+ uint32_t group_id)
+{
+ struct map wcard;
+
+ memset(&wcard, 0, sizeof(wcard));
+ wcard.type = MAP_TYPE_PWID;
+ wcard.fec.pwid.type = pw_type;
+ wcard.fec.pwid.group_id = group_id;
+ /* we can not append a Label TLV when using PWid group wildcards. */
+ wcard.label = NO_LABEL;
+ lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
+}
+
+void
+lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn,
+ struct map *wcard, uint32_t label)
{
struct map map;
struct l2vpn_pw *pw;
@@ -1032,10 +1086,8 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label)
map.flags |= F_MAP_PW_CWORD;
break;
}
- } else {
- memset(&map, 0, sizeof(map));
- map.type = MAP_TYPE_WILDCARD;
- }
+ } else
+ memcpy(&map, wcard, sizeof(map));
map.label = label;
lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0,
@@ -1044,7 +1096,7 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label)
}
void
-lde_send_notification(uint32_t peerid, uint32_t status_code, uint32_t msg_id,
+lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id,
uint16_t msg_type)
{
struct notify_msg nm;
@@ -1055,7 +1107,39 @@ lde_send_notification(uint32_t peerid, uint32_t status_code, uint32_t msg_id,
nm.msg_id = msg_id;
nm.msg_type = msg_type;
- lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0,
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+lde_send_notification_eol_prefix(struct lde_nbr *ln, int af)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_ENDOFLIB;
+ nm.fec.type = MAP_TYPE_TYPED_WCARD;
+ nm.fec.fec.twcard.type = MAP_TYPE_PREFIX;
+ nm.fec.fec.twcard.u.prefix_af = af;
+ nm.flags |= F_NOTIF_FEC;
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
+ &nm, sizeof(nm));
+}
+
+void
+lde_send_notification_eol_pwid(struct lde_nbr *ln, uint16_t pw_type)
+{
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = S_ENDOFLIB;
+ nm.fec.type = MAP_TYPE_TYPED_WCARD;
+ nm.fec.fec.twcard.type = MAP_TYPE_PWID;
+ nm.fec.fec.twcard.u.pw_type = pw_type;
+ nm.flags |= F_NOTIF_FEC;
+
+ lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
&nm, sizeof(nm));
}
@@ -1076,6 +1160,7 @@ lde_nbr_new(uint32_t peerid, struct lde_nbr *new)
ln->id = new->id;
ln->v4_enabled = new->v4_enabled;
ln->v6_enabled = new->v6_enabled;
+ ln->flags = new->flags;
ln->peerid = peerid;
fec_init(&ln->recv_map);
fec_init(&ln->sent_map);
@@ -1352,13 +1437,11 @@ lde_change_egress_label(int af)
/* explicitly withdraw all null labels */
RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
- lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IMPLNULL, NULL);
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLNULL);
if (ln->v4_enabled)
- lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IPV4NULL,
- NULL);
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV4NULL);
if (ln->v6_enabled)
- lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IPV6NULL,
- NULL);
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV6NULL);
}
/* update label of connected routes */
diff --git a/ldpd/lde.h b/ldpd/lde.h
index e0e9873d5c..b5bcb42c8b 100644
--- a/ldpd/lde.h
+++ b/ldpd/lde.h
@@ -90,6 +90,7 @@ struct lde_nbr {
struct in_addr id;
int v4_enabled; /* announce/process v4 msgs */
int v6_enabled; /* announce/process v6 msgs */
+ int flags; /* capabilities */
struct fec_tree recv_req;
struct fec_tree sent_req;
struct fec_tree recv_map;
@@ -143,11 +144,20 @@ void lde_map2fec(struct map *, struct in_addr, struct fec *);
void lde_send_labelmapping(struct lde_nbr *, struct fec_node *,
int);
void lde_send_labelwithdraw(struct lde_nbr *, struct fec_node *,
- uint32_t, struct status_tlv *);
-void lde_send_labelwithdraw_all(struct fec_node *, uint32_t);
-void lde_send_labelrelease(struct lde_nbr *, struct fec_node *,
+ struct map *, struct status_tlv *);
+void lde_send_labelwithdraw_wcard(struct lde_nbr *, uint32_t);
+void lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *,
+ uint16_t, uint32_t);
+void lde_send_labelwithdraw_twcard_pwid(struct lde_nbr *, uint16_t,
+ uint32_t);
+void lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *, uint16_t,
uint32_t);
-void lde_send_notification(uint32_t, uint32_t, uint32_t, uint16_t);
+void lde_send_labelrelease(struct lde_nbr *, struct fec_node *,
+ struct map *, uint32_t);
+void lde_send_notification(struct lde_nbr *, uint32_t, uint32_t,
+ uint16_t);
+void lde_send_notification_eol_prefix(struct lde_nbr *, int);
+void lde_send_notification_eol_pwid(struct lde_nbr *, uint16_t);
struct lde_nbr *lde_nbr_find_by_lsrid(struct in_addr);
struct lde_nbr *lde_nbr_find_by_addr(int, union ldpd_addr *);
struct lde_map *lde_map_add(struct lde_nbr *, struct fec_node *, int);
@@ -178,10 +188,13 @@ void lde_kernel_remove(struct fec *, int, union ldpd_addr *,
void lde_kernel_update(struct fec *);
void lde_check_mapping(struct map *, struct lde_nbr *);
void lde_check_request(struct map *, struct lde_nbr *);
+void lde_check_request_wcard(struct map *, struct lde_nbr *);
void lde_check_release(struct map *, struct lde_nbr *);
void lde_check_release_wcard(struct map *, struct lde_nbr *);
void lde_check_withdraw(struct map *, struct lde_nbr *);
void lde_check_withdraw_wcard(struct map *, struct lde_nbr *);
+int lde_wildcard_apply(struct map *, struct fec *,
+ struct lde_map *);
int lde_gc_timer(struct thread *);
void lde_gc_start_timer(void);
void lde_gc_stop_timer(void);
@@ -195,6 +208,7 @@ void l2vpn_exit(struct l2vpn *);
struct l2vpn_if *l2vpn_if_new(struct l2vpn *, struct kif *);
struct l2vpn_if *l2vpn_if_find(struct l2vpn *, unsigned int);
struct l2vpn_if *l2vpn_if_find_name(struct l2vpn *, const char *);
+void l2vpn_if_update(struct l2vpn_if *);
struct l2vpn_pw *l2vpn_pw_new(struct l2vpn *, struct kif *);
struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, unsigned int);
struct l2vpn_pw *l2vpn_pw_find_name(struct l2vpn *, const char *);
@@ -204,8 +218,12 @@ void l2vpn_pw_reset(struct l2vpn_pw *);
int l2vpn_pw_ok(struct l2vpn_pw *, struct fec_nh *);
int l2vpn_pw_negotiate(struct lde_nbr *, struct fec_node *,
struct map *);
-void l2vpn_send_pw_status(uint32_t, uint32_t, struct fec *);
+void l2vpn_send_pw_status(struct lde_nbr *, uint32_t, struct fec *);
+void l2vpn_send_pw_status_wcard(struct lde_nbr *, uint32_t,
+ uint16_t, uint32_t);
void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *);
+void l2vpn_recv_pw_status_wcard(struct lde_nbr *,
+ struct notify_msg *);
void l2vpn_sync_pws(int, union ldpd_addr *);
void l2vpn_pw_ctl(pid_t);
void l2vpn_binding_ctl(pid_t);
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index 234d373fbb..4444a1e1ac 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -374,7 +374,8 @@ lde_kernel_update(struct fec *fec)
}
if (LIST_EMPTY(&fn->nexthops)) {
- lde_send_labelwithdraw_all(fn, NO_LABEL);
+ RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+ lde_send_labelwithdraw(ln, fn, NULL, NULL);
fn->local_label = NO_LABEL;
fn->data = NULL;
} else {
@@ -478,7 +479,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln)
/* LMp.10 */
if (me->map.label != map->label && lre == NULL) {
/* LMp.10a */
- lde_send_labelrelease(ln, fn, me->map.label);
+ lde_send_labelrelease(ln, fn, NULL, me->map.label);
/*
* Can not use lde_nbr_find_by_addr() because there's
@@ -554,6 +555,12 @@ lde_check_request(struct map *map, struct lde_nbr *ln)
struct fec_node *fn;
struct fec_nh *fnh;
+ /* wildcard label request */
+ if (map->type == MAP_TYPE_TYPED_WCARD) {
+ lde_check_request_wcard(map, ln);
+ return;
+ }
+
/* LRq.1: skip loop detection (not necessary) */
/* LRq.2: is there a next hop for fec? */
@@ -561,7 +568,7 @@ lde_check_request(struct map *map, struct lde_nbr *ln)
fn = (struct fec_node *)fec_find(&ft, &fec);
if (fn == NULL || LIST_EMPTY(&fn->nexthops)) {
/* LRq.5: send No Route notification */
- lde_send_notification(ln->peerid, S_NO_ROUTE, map->msg_id,
+ lde_send_notification(ln, S_NO_ROUTE, map->msg_id,
htons(MSG_TYPE_LABELREQUEST));
return;
}
@@ -575,8 +582,8 @@ lde_check_request(struct map *map, struct lde_nbr *ln)
continue;
/* LRq.4: send Loop Detected notification */
- lde_send_notification(ln->peerid, S_LOOP_DETECTED,
- map->msg_id, htons(MSG_TYPE_LABELREQUEST));
+ lde_send_notification(ln, S_LOOP_DETECTED, map->msg_id,
+ htons(MSG_TYPE_LABELREQUEST));
return;
default:
break;
@@ -605,6 +612,40 @@ lde_check_request(struct map *map, struct lde_nbr *ln)
}
void
+lde_check_request_wcard(struct map *map, struct lde_nbr *ln)
+{
+ struct fec *f;
+ struct fec_node *fn;
+ struct lde_req *lre;
+
+ RB_FOREACH(f, fec_tree, &ft) {
+ fn = (struct fec_node *)f;
+
+ /* only a typed wildcard is possible here */
+ if (lde_wildcard_apply(map, &fn->fec, NULL) == 0)
+ continue;
+
+ /* LRq.2: is there a next hop for fec? */
+ if (LIST_EMPTY(&fn->nexthops))
+ continue;
+
+ /* LRq.6: first check if we have a pending request running */
+ lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
+ if (lre != NULL)
+ /* LRq.7: duplicate request */
+ continue;
+
+ /* LRq.8: record label request */
+ lre = lde_req_add(ln, &fn->fec, 0);
+ if (lre != NULL)
+ lre->msg_id = ntohl(map->msg_id);
+
+ /* LRq.9: perform LSR label distribution */
+ lde_send_labelmapping(ln, fn, 1);
+ }
+}
+
+void
lde_check_release(struct map *map, struct lde_nbr *ln)
{
struct fec fec;
@@ -612,9 +653,13 @@ lde_check_release(struct map *map, struct lde_nbr *ln)
struct lde_wdraw *lw;
struct lde_map *me;
- /* TODO group wildcard */
- if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))
+ /* wildcard label release */
+ if (map->type == MAP_TYPE_WILDCARD ||
+ map->type == MAP_TYPE_TYPED_WCARD ||
+ (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) {
+ lde_check_release_wcard(map, ln);
return;
+ }
lde_map2fec(map, ln->id, &fec);
fn = (struct fec_node *)fec_find(&ft, &fec);
@@ -624,8 +669,7 @@ lde_check_release(struct map *map, struct lde_nbr *ln)
/* LRl.3: first check if we have a pending withdraw running */
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
- if (lw && (map->label == NO_LABEL ||
- (lw->label != NO_LABEL && map->label == lw->label))) {
+ if (lw && (map->label == NO_LABEL || map->label == lw->label)) {
/* LRl.4: delete record of outstanding label withdraw */
lde_wdraw_del(ln, lw);
}
@@ -651,17 +695,20 @@ lde_check_release_wcard(struct map *map, struct lde_nbr *ln)
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
+
+ /* LRl.1: does FEC match a known FEC? */
+ if (lde_wildcard_apply(map, &fn->fec, me) == 0)
+ continue;
/* LRl.3: first check if we have a pending withdraw running */
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
- if (lw && (map->label == NO_LABEL ||
- (lw->label != NO_LABEL && map->label == lw->label))) {
+ if (lw && (map->label == NO_LABEL || map->label == lw->label)) {
/* LRl.4: delete record of outstanding lbl withdraw */
lde_wdraw_del(ln, lw);
}
/* LRl.6: check sent map list and remove it if available */
- me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
if (me &&
(map->label == NO_LABEL || map->label == me->map.label))
lde_map_del(ln, me, 1);
@@ -682,9 +729,13 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
struct lde_map *me;
struct l2vpn_pw *pw;
- /* TODO group wildcard */
- if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))
+ /* wildcard label withdraw */
+ if (map->type == MAP_TYPE_WILDCARD ||
+ map->type == MAP_TYPE_TYPED_WCARD ||
+ (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) {
+ lde_check_withdraw_wcard(map, ln);
return;
+ }
lde_map2fec(map, ln->id, &fec);
fn = (struct fec_node *)fec_find(&ft, &fec);
@@ -707,12 +758,15 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
default:
break;
}
+ if (map->label != NO_LABEL && map->label != fnh->remote_label)
+ continue;
+
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
}
/* LWd.2: send label release */
- lde_send_labelrelease(ln, fn, map->label);
+ lde_send_labelrelease(ln, fn, NULL, map->label);
/* LWd.3: check previously received label mapping */
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
@@ -730,10 +784,14 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
struct lde_map *me;
/* LWd.2: send label release */
- lde_send_labelrelease(ln, NULL, map->label);
+ lde_send_labelrelease(ln, NULL, map, map->label);
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+
+ if (lde_wildcard_apply(map, &fn->fec, me) == 0)
+ continue;
/* LWd.1: remove label from forwarding/switching use */
LIST_FOREACH(fnh, &fn->nexthops, entry) {
@@ -751,12 +809,15 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
default:
break;
}
+ if (map->label != NO_LABEL && map->label !=
+ fnh->remote_label)
+ continue;
+
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
}
/* LWd.3: check previously received label mapping */
- me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (me && (map->label == NO_LABEL ||
map->label == me->map.label))
/*
@@ -767,6 +828,49 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
}
}
+int
+lde_wildcard_apply(struct map *wcard, struct fec *fec, struct lde_map *me)
+{
+ switch (wcard->type) {
+ case MAP_TYPE_WILDCARD:
+ /* full wildcard */
+ return (1);
+ case MAP_TYPE_TYPED_WCARD:
+ switch (wcard->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ if (wcard->fec.twcard.u.prefix_af == AF_INET &&
+ fec->type != FEC_TYPE_IPV4)
+ return (0);
+ if (wcard->fec.twcard.u.prefix_af == AF_INET6 &&
+ fec->type != FEC_TYPE_IPV6)
+ return (0);
+ return (1);
+ case MAP_TYPE_PWID:
+ if (fec->type != FEC_TYPE_PWID)
+ return (0);
+ if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
+ wcard->fec.twcard.u.pw_type != fec->u.pwid.type)
+ return (0);
+ return (1);
+ default:
+ fatalx("lde_wildcard_apply: unexpected fec type");
+ }
+ break;
+ case MAP_TYPE_PWID:
+ /* RFC4447 pw-id group wildcard */
+ if (fec->type != FEC_TYPE_PWID)
+ return (0);
+ if (fec->u.pwid.type != wcard->fec.pwid.type)
+ return (0);
+ if (me == NULL || (me->map.fec.pwid.group_id !=
+ wcard->fec.pwid.group_id))
+ return (0);
+ return (1);
+ default:
+ fatalx("lde_wildcard_apply: unexpected fec type");
+ }
+}
+
/* gabage collector timer: timer to remove dead entries from the LIB */
/* ARGSUSED */
diff --git a/ldpd/ldp.h b/ldpd/ldp.h
index c421cddc38..c2b64d20c6 100644
--- a/ldpd/ldp.h
+++ b/ldpd/ldp.h
@@ -63,6 +63,7 @@
#define MSG_TYPE_HELLO 0x0100
#define MSG_TYPE_INIT 0x0200
#define MSG_TYPE_KEEPALIVE 0x0201
+#define MSG_TYPE_CAPABILITY 0x0202 /* RFC 5561 */
#define MSG_TYPE_ADDR 0x0300
#define MSG_TYPE_ADDRWITHDRAW 0x0301
#define MSG_TYPE_LABELMAPPING 0x0400
@@ -92,9 +93,17 @@
#define TLV_TYPE_FRSESSION 0x0502
#define TLV_TYPE_LABELREQUEST 0x0600
/* RFC 4447 */
-#define TLV_TYPE_PW_STATUS 0x096A
+#define TLV_TYPE_MAC_LIST 0x8404
+#define TLV_TYPE_PW_STATUS 0x896A
#define TLV_TYPE_PW_IF_PARAM 0x096B
#define TLV_TYPE_PW_GROUP_ID 0x096C
+/* RFC 5561 */
+#define TLV_TYPE_RETURNED_TLVS 0x8304
+#define TLV_TYPE_DYNAMIC_CAP 0x8506
+/* RFC 5918 */
+#define TLV_TYPE_TWCARD_CAP 0x850B
+/* RFC 5919 */
+#define TLV_TYPE_UNOTIF_CAP 0x8603
/* RFC 7552 */
#define TLV_TYPE_DUALSTACK 0x8701
@@ -196,6 +205,10 @@ struct hello_prms_opt16_tlv {
#define S_UNASSIGN_TAI 0x00000029
#define S_MISCONF_ERR 0x0000002A
#define S_WITHDRAW_MTHD 0x0000002B
+/* RFC 5561 */
+#define S_UNSSUPORTDCAP 0x0000002E
+/* RFC 5919 */
+#define S_ENDOFLIB 0x0000002F
/* RFC 7552 */
#define S_TRANS_MISMTCH 0x80000032
#define S_DS_NONCMPLNCE 0x80000033
@@ -227,6 +240,26 @@ struct status_tlv {
#define STATUS_TLV_LEN 10
#define STATUS_FATAL 0x80000000
+struct capability_tlv {
+ uint16_t type;
+ uint16_t length;
+ uint8_t reserved;
+};
+#define STATE_BIT 0x80
+
+#define F_CAP_TLV_RCVD_DYNAMIC 0x01
+#define F_CAP_TLV_RCVD_TWCARD 0x02
+#define F_CAP_TLV_RCVD_UNOTIF 0x04
+
+#define CAP_TLV_DYNAMIC_SIZE 5
+#define CAP_TLV_DYNAMIC_LEN 1
+
+#define CAP_TLV_TWCARD_SIZE 5
+#define CAP_TLV_TWCARD_LEN 1
+
+#define CAP_TLV_UNOTIF_SIZE 5
+#define CAP_TLV_UNOTIF_LEN 1
+
#define AF_IPV4 0x1
#define AF_IPV6 0x2
@@ -242,17 +275,23 @@ struct address_list_tlv {
#define FEC_ELM_WCARD_LEN 1
#define FEC_ELM_PREFIX_MIN_LEN 4
#define FEC_PWID_ELM_MIN_LEN 8
+#define FEC_PWID_SIZE 4
+#define FEC_ELM_TWCARD_MIN_LEN 3
#define MAP_TYPE_WILDCARD 0x01
#define MAP_TYPE_PREFIX 0x02
+#define MAP_TYPE_TYPED_WCARD 0x05
#define MAP_TYPE_PWID 0x80
#define MAP_TYPE_GENPWID 0x81
#define CONTROL_WORD_FLAG 0x8000
#define PW_TYPE_ETHERNET_TAGGED 0x0004
#define PW_TYPE_ETHERNET 0x0005
+#define PW_TYPE_WILDCARD 0x7FFF
#define DEFAULT_PW_TYPE PW_TYPE_ETHERNET
+#define PW_TWCARD_RESERVED_BIT 0x8000
+
/* RFC 4447 Sub-TLV record */
struct subtlv {
uint8_t type;
diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h
index aa0cd47e7b..f944851b6e 100644
--- a/ldpd/ldp_debug.h
+++ b/ldpd/ldp_debug.h
@@ -104,6 +104,14 @@ do { \
log_debug("msg[out]: " emsg, __VA_ARGS__); \
} while (0)
+#define debug_msg(out, emsg, ...) \
+do { \
+ if (out) \
+ debug_msg_send(emsg, __VA_ARGS__); \
+ else \
+ debug_msg_recv(emsg, __VA_ARGS__); \
+} while (0)
+
#define debug_kalive_recv(emsg, ...) \
do { \
if (LDP_DEBUG(msg, MSG_RECV_ALL)) \
diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c
index 12954b91af..c41a0dbd91 100644
--- a/ldpd/ldp_zebra.c
+++ b/ldpd/ldp_zebra.c
@@ -65,6 +65,8 @@ ifp2kif(struct interface *ifp, struct kif *kif)
strlcpy(kif->ifname, ifp->name, sizeof(kif->ifname));
kif->ifindex = ifp->ifindex;
kif->flags = ifp->flags;
+ if (ifp->ll_type == ZEBRA_LLT_ETHER)
+ memcpy(kif->mac, ifp->hw_addr, ETHER_ADDR_LEN);
}
static void
diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h
index ff3af19db4..0a7e1177bc 100644
--- a/ldpd/ldpd.h
+++ b/ldpd/ldpd.h
@@ -27,6 +27,7 @@
#include "imsg.h"
#include "thread.h"
#include "qobj.h"
+#include "prefix.h"
#include "filter.h"
#include "ldp.h"
@@ -220,6 +221,13 @@ struct map {
uint32_t group_id;
uint16_t ifmtu;
} pwid;
+ struct {
+ uint8_t type;
+ union {
+ uint16_t prefix_af;
+ uint16_t pw_type;
+ } u;
+ } twcard;
} fec;
struct {
uint32_t status_code;
@@ -244,10 +252,16 @@ struct notify_msg {
uint16_t msg_type; /* network byte order */
uint32_t pw_status;
struct map fec;
+ struct {
+ uint16_t type;
+ uint16_t length;
+ char *data;
+ } rtlvs;
uint8_t flags;
};
#define F_NOTIF_PW_STATUS 0x01 /* pseudowire status tlv present */
#define F_NOTIF_FEC 0x02 /* fec tlv present */
+#define F_NOTIF_RETURNED_TLVS 0x04 /* returned tlvs present */
struct if_addr {
LIST_ENTRY(if_addr) entry;
@@ -337,6 +351,7 @@ struct l2vpn_if {
char ifname[IF_NAMESIZE];
unsigned int ifindex;
uint16_t flags;
+ uint8_t mac[ETHER_ADDR_LEN];
QOBJ_FIELDS
};
RB_HEAD(l2vpn_if_head, l2vpn_if);
@@ -506,6 +521,7 @@ struct kif {
char ifname[IF_NAMESIZE];
unsigned short ifindex;
int flags;
+ uint8_t mac[ETHER_ADDR_LEN];
int mtu;
};
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index 7dcc8fbe16..3bb84e92a9 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -251,8 +251,8 @@ ldpe_dispatch_main(struct thread *thread)
struct iface *niface;
struct tnbr *ntnbr;
struct nbr_params *nnbrp;
- static struct l2vpn *nl2vpn;
- struct l2vpn_if *nlif;
+ static struct l2vpn *l2vpn, *nl2vpn;
+ struct l2vpn_if *lif = NULL, *nlif;
struct l2vpn_pw *npw;
struct imsg imsg;
int fd = THREAD_FD(thread);
@@ -292,11 +292,22 @@ ldpe_dispatch_main(struct thread *thread)
kif = imsg.data;
iface = if_lookup_name(leconf, kif->ifname);
- if (!iface)
+ if (iface) {
+ if_update_info(iface, kif);
+ if_update(iface, AF_UNSPEC);
break;
+ }
- if_update_info(iface, kif);
- if_update(iface, AF_UNSPEC);
+ RB_FOREACH(l2vpn, l2vpn_head, &leconf->l2vpn_tree) {
+ lif = l2vpn_if_find_name(l2vpn, kif->ifname);
+ if (lif) {
+ lif->flags = kif->flags;
+ memcpy(lif->mac, kif->mac,
+ sizeof(lif->mac));
+ l2vpn_if_update(lif);
+ break;
+ }
+ }
break;
case IMSG_NEWADDR:
if (imsg.hdr.len != IMSG_HEADER_SIZE +
@@ -529,10 +540,10 @@ ldpe_dispatch_lde(struct thread *thread)
struct imsgev *iev = THREAD_ARG(thread);
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
- struct map map;
- struct notify_msg nm;
+ struct map *map;
+ struct notify_msg *nm;
+ struct nbr *nbr;
int n, shut = 0;
- struct nbr *nbr = NULL;
iev->ev_read = NULL;
@@ -552,9 +563,10 @@ ldpe_dispatch_lde(struct thread *thread)
case IMSG_RELEASE_ADD:
case IMSG_REQUEST_ADD:
case IMSG_WITHDRAW_ADD:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct map))
fatalx("invalid size of map request");
- memcpy(&map, imsg.data, sizeof(map));
+ map = imsg.data;
nbr = nbr_find_peerid(imsg.hdr.peerid);
if (nbr == NULL) {
@@ -567,16 +579,16 @@ ldpe_dispatch_lde(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_MAPPING_ADD:
- mapping_list_add(&nbr->mapping_list, &map);
+ mapping_list_add(&nbr->mapping_list, map);
break;
case IMSG_RELEASE_ADD:
- mapping_list_add(&nbr->release_list, &map);
+ mapping_list_add(&nbr->release_list, map);
break;
case IMSG_REQUEST_ADD:
- mapping_list_add(&nbr->request_list, &map);
+ mapping_list_add(&nbr->request_list, map);
break;
case IMSG_WITHDRAW_ADD:
- mapping_list_add(&nbr->withdraw_list, &map);
+ mapping_list_add(&nbr->withdraw_list, map);
break;
}
break;
@@ -613,9 +625,10 @@ ldpe_dispatch_lde(struct thread *thread)
}
break;
case IMSG_NOTIFICATION_SEND:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct notify_msg))
fatalx("invalid size of OE request");
- memcpy(&nm, imsg.data, sizeof(nm));
+ nm = imsg.data;
nbr = nbr_find_peerid(imsg.hdr.peerid);
if (nbr == NULL) {
@@ -626,7 +639,7 @@ ldpe_dispatch_lde(struct thread *thread)
if (nbr->state != NBR_STA_OPER)
break;
- send_notification_full(nbr->tcp, &nm);
+ send_notification_full(nbr->tcp, nm);
break;
case IMSG_CTL_END:
case IMSG_CTL_SHOW_LIB:
@@ -791,8 +804,7 @@ ldpe_iface_af_ctl(struct ctl_conn *c, int af, unsigned int idx)
continue;
ictl = if_to_ctl(ia);
- imsg_compose_event(&c->iev,
- IMSG_CTL_SHOW_INTERFACE,
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_INTERFACE,
0, 0, -1, ictl, sizeof(struct ctl_iface));
}
}
diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h
index 052439df88..22b75eb008 100644
--- a/ldpd/ldpe.h
+++ b/ldpd/ldpe.h
@@ -111,6 +111,9 @@ struct nbr {
int flags;
};
#define F_NBR_GTSM_NEGOTIATED 0x01
+#define F_NBR_CAP_DYNAMIC 0x02
+#define F_NBR_CAP_TWCARD 0x04
+#define F_NBR_CAP_UNOTIF 0x08
RB_HEAD(nbr_id_head, nbr);
RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare)
@@ -159,6 +162,8 @@ void recv_hello(struct in_addr, struct ldp_msg *, int, union ldpd_addr *,
/* init.c */
void send_init(struct nbr *);
int recv_init(struct nbr *, char *, uint16_t);
+void send_capability(struct nbr *, uint16_t, int);
+int recv_capability(struct nbr *, char *, uint16_t);
/* keepalive.c */
void send_keepalive(struct nbr *);
@@ -166,15 +171,16 @@ int recv_keepalive(struct nbr *, char *, uint16_t);
/* notification.c */
void send_notification_full(struct tcp_conn *, struct notify_msg *);
-void send_notification(uint32_t, struct tcp_conn *, uint32_t,
- uint16_t);
-void send_notification_nbr(struct nbr *, uint32_t, uint32_t, uint16_t);
+void send_notification(struct tcp_conn *, uint32_t, uint32_t, uint16_t);
+void send_notification_rtlvs(struct nbr *, uint32_t, uint32_t, uint16_t,
+ uint16_t, uint16_t, char *);
int recv_notification(struct nbr *, char *, uint16_t);
int gen_status_tlv(struct ibuf *, uint32_t, uint32_t, uint16_t);
/* address.c */
void send_address_single(struct nbr *, struct if_addr *, int);
void send_address_all(struct nbr *, int);
+void send_mac_withdrawal(struct nbr *, struct map *, uint8_t *);
int recv_address(struct nbr *, char *, uint16_t);
/* labelmapping.c */
@@ -182,6 +188,7 @@ int recv_address(struct nbr *, char *, uint16_t);
void send_labelmessage(struct nbr *, uint16_t, struct mapping_head *);
int recv_labelmessage(struct nbr *, char *, uint16_t, uint16_t);
int gen_pw_status_tlv(struct ibuf *, uint32_t);
+uint16_t len_fec_tlv(struct map *);
int gen_fec_tlv(struct ibuf *, struct map *);
int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *,
uint16_t, struct map *);
diff --git a/ldpd/log.c b/ldpd/log.c
index 77efdb4714..b30604db0d 100644
--- a/ldpd/log.c
+++ b/ldpd/log.c
@@ -313,7 +313,7 @@ log_hello_src(const struct hello_source *src)
const char *
log_map(const struct map *map)
{
- static char buf[64];
+ static char buf[128];
switch (map->type) {
case MAP_TYPE_WILDCARD:
@@ -327,11 +327,34 @@ log_map(const struct map *map)
return ("???");
break;
case MAP_TYPE_PWID:
- if (snprintf(buf, sizeof(buf), "pwid %u (%s)",
- map->fec.pwid.pwid,
+ if (snprintf(buf, sizeof(buf), "pw-id %u group-id %u (%s)",
+ map->fec.pwid.pwid, map->fec.pwid.group_id,
pw_type_name(map->fec.pwid.type)) == -1)
return ("???");
break;
+ case MAP_TYPE_TYPED_WCARD:
+ if (snprintf(buf, sizeof(buf), "typed wildcard") < 0)
+ return ("???");
+ switch (map->fec.twcard.type) {
+ case MAP_TYPE_PREFIX:
+ if (snprintf(buf + strlen(buf), sizeof(buf) -
+ strlen(buf), " (prefix, address-family %s)",
+ af_name(map->fec.twcard.u.prefix_af)) < 0)
+ return ("???");
+ break;
+ case MAP_TYPE_PWID:
+ if (snprintf(buf + strlen(buf), sizeof(buf) -
+ strlen(buf), " (pwid, type %s)",
+ pw_type_name(map->fec.twcard.u.pw_type)) < 0)
+ return ("???");
+ break;
+ default:
+ if (snprintf(buf + strlen(buf), sizeof(buf) -
+ strlen(buf), " (unknown type)") < 0)
+ return ("???");
+ break;
+ }
+ break;
default:
return ("???");
}
@@ -464,6 +487,8 @@ msg_name(uint16_t msg)
return ("initialization");
case MSG_TYPE_KEEPALIVE:
return ("keepalive");
+ case MSG_TYPE_CAPABILITY:
+ return ("capability");
case MSG_TYPE_ADDR:
return ("address");
case MSG_TYPE_ADDRWITHDRAW:
@@ -557,6 +582,10 @@ status_code_name(uint32_t status)
return ("Generic Misconfiguration Error");
case S_WITHDRAW_MTHD:
return ("Label Withdraw PW Status Method");
+ case S_UNSSUPORTDCAP:
+ return ("Unsupported Capability");
+ case S_ENDOFLIB:
+ return ("End-of-LIB");
case S_TRANS_MISMTCH:
return ("Transport Connection Mismatch");
case S_DS_NONCMPLNCE:
@@ -577,6 +606,8 @@ pw_type_name(uint16_t pw_type)
return ("Eth Tagged");
case PW_TYPE_ETHERNET:
return ("Ethernet");
+ case PW_TYPE_WILDCARD:
+ return ("Wildcard");
default:
snprintf(buf, sizeof(buf), "[%0x]", pw_type);
return (buf);
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
index d24ceb1229..077d472850 100644
--- a/ldpd/neighbor.c
+++ b/ldpd/neighbor.c
@@ -744,6 +744,7 @@ nbr_act_session_operational(struct nbr *nbr)
lde_nbr.id = nbr->id;
lde_nbr.v4_enabled = nbr->v4_enabled;
lde_nbr.v6_enabled = nbr->v6_enabled;
+ lde_nbr.flags = nbr->flags;
return (ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0,
&lde_nbr, sizeof(lde_nbr)));
}
diff --git a/ldpd/notification.c b/ldpd/notification.c
index d306361d5c..393994ed5f 100644
--- a/ldpd/notification.c
+++ b/ldpd/notification.c
@@ -24,6 +24,9 @@
#include "ldpe.h"
#include "ldp_debug.h"
+static int gen_returned_tlvs(struct ibuf *, uint16_t, uint16_t, char *);
+static void log_msg_notification(int, struct nbr *, struct notify_msg *);
+
void
send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
{
@@ -35,16 +38,10 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE;
if (nm->flags & F_NOTIF_PW_STATUS)
size += PW_STATUS_TLV_SIZE;
- if (nm->flags & F_NOTIF_FEC) {
- size += TLV_HDR_SIZE;
- switch (nm->fec.type) {
- case MAP_TYPE_PWID:
- size += FEC_PWID_ELM_MIN_LEN;
- if (nm->fec.flags & F_MAP_PW_ID)
- size += sizeof(uint32_t);
- break;
- }
- }
+ if (nm->flags & F_NOTIF_FEC)
+ size += len_fec_tlv(&nm->fec);
+ if (nm->flags & F_NOTIF_RETURNED_TLVS)
+ size += TLV_HDR_SIZE * 2 + nm->rtlvs.length;
if ((buf = ibuf_open(size)) == NULL)
fatal(__func__);
@@ -58,22 +55,25 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
err |= gen_pw_status_tlv(buf, nm->pw_status);
if (nm->flags & F_NOTIF_FEC)
err |= gen_fec_tlv(buf, &nm->fec);
+ if (nm->flags & F_NOTIF_RETURNED_TLVS)
+ err |= gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length,
+ nm->rtlvs.data);
if (err) {
ibuf_free(buf);
return;
}
- if (tcp->nbr)
- debug_msg_send("notification: lsr-id %s status %s%s",
- inet_ntoa(tcp->nbr->id), status_code_name(nm->status_code),
- (nm->status_code & STATUS_FATAL) ? " (fatal)" : "");
+ if (tcp->nbr) {
+ log_msg_notification(1, tcp->nbr, nm);
+ nbr_fsm(tcp->nbr, NBR_EVT_PDU_SENT);
+ }
evbuf_enqueue(&tcp->wbuf, buf);
}
/* send a notification without optional tlvs */
void
-send_notification(uint32_t status_code, struct tcp_conn *tcp, uint32_t msg_id,
+send_notification(struct tcp_conn *tcp, uint32_t status_code, uint32_t msg_id,
uint16_t msg_type)
{
struct notify_msg nm;
@@ -87,11 +87,24 @@ send_notification(uint32_t status_code, struct tcp_conn *tcp, uint32_t msg_id,
}
void
-send_notification_nbr(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
- uint16_t msg_type)
+send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
+ uint16_t msg_type, uint16_t tlv_type, uint16_t tlv_len, char *tlv_data)
{
- send_notification(status_code, nbr->tcp, msg_id, msg_type);
- nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+ struct notify_msg nm;
+
+ memset(&nm, 0, sizeof(nm));
+ nm.status_code = status_code;
+ nm.msg_id = msg_id;
+ nm.msg_type = msg_type;
+ /* do not append the given TLV if it's too big (shouldn't happen) */
+ if (tlv_len < 1024) {
+ nm.rtlvs.type = tlv_type;
+ nm.rtlvs.length = tlv_len;
+ nm.rtlvs.data = tlv_data;
+ nm.flags |= F_NOTIF_RETURNED_TLVS;
+ }
+
+ send_notification_full(nbr->tcp, &nm);
}
int
@@ -126,6 +139,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
/* Optional Parameters */
while (len > 0) {
struct tlv tlv;
+ uint16_t tlv_type;
uint16_t tlv_len;
if (len < sizeof(tlv)) {
@@ -134,6 +148,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
}
memcpy(&tlv, buf, TLV_HDR_SIZE);
+ tlv_type = ntohs(tlv.type);
tlv_len = ntohs(tlv.length);
if (tlv_len + TLV_HDR_SIZE > len) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
@@ -142,7 +157,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
buf += TLV_HDR_SIZE;
len -= TLV_HDR_SIZE;
- switch (ntohs(tlv.type)) {
+ switch (tlv_type) {
case TLV_TYPE_EXTSTATUS:
case TLV_TYPE_RETURNEDPDU:
case TLV_TYPE_RETURNEDMSG:
@@ -172,8 +187,8 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
break;
default:
if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
- send_notification_nbr(nbr, S_UNKNOWN_TLV,
- msg.id, msg.type);
+ send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+ msg.id, msg.type, tlv_type, tlv_len, buf);
/* ignore unknown tlv */
break;
}
@@ -181,9 +196,11 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
len -= tlv_len;
}
- if (nm.status_code == S_PW_STATUS) {
+ /* sanity checks */
+ switch (nm.status_code) {
+ case S_PW_STATUS:
if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
- send_notification_nbr(nbr, S_MISS_MSG,
+ send_notification(nbr->tcp, S_MISS_MSG,
msg.id, msg.type);
return (-1);
}
@@ -192,15 +209,28 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
case MAP_TYPE_PWID:
break;
default:
- send_notification_nbr(nbr, S_BAD_TLV_VAL,
+ send_notification(nbr->tcp, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ return (-1);
+ }
+ break;
+ case S_ENDOFLIB:
+ if (!(nm.flags & F_NOTIF_FEC)) {
+ send_notification(nbr->tcp, S_MISS_MSG,
msg.id, msg.type);
return (-1);
}
+ if (nm.fec.type != MAP_TYPE_TYPED_WCARD) {
+ send_notification(nbr->tcp, S_BAD_TLV_VAL,
+ msg.id, msg.type);
+ return (-1);
+ }
+ break;
+ default:
+ break;
}
- debug_msg_recv("notification: lsr-id %s: %s%s", inet_ntoa(nbr->id),
- status_code_name(ntohl(st.status_code)),
- (st.status_code & htonl(STATUS_FATAL)) ? " (fatal)" : "");
+ log_msg_notification(0, nbr, &nm);
if (st.status_code & htonl(STATUS_FATAL)) {
if (nbr->state == NBR_STA_OPENSENT)
@@ -210,9 +240,16 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
return (-1);
}
- if (nm.status_code == S_PW_STATUS)
+ /* lde needs to know about a few notification messages */
+ switch (nm.status_code) {
+ case S_PW_STATUS:
+ case S_ENDOFLIB:
ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
&nm, sizeof(nm));
+ break;
+ default:
+ break;
+ }
return (0);
}
@@ -236,3 +273,42 @@ gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
return (ibuf_add(buf, &st, STATUS_SIZE));
}
+
+static int
+gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
+ char *tlv_data)
+{
+ struct tlv rtlvs;
+ struct tlv tlv;
+ int err;
+
+ rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
+ rtlvs.length = htons(length + TLV_HDR_SIZE);
+ tlv.type = htons(type);
+ tlv.length = htons(length);
+
+ err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
+ err |= ibuf_add(buf, &tlv, sizeof(tlv));
+ err |= ibuf_add(buf, tlv_data, length);
+
+ return (err);
+}
+
+void
+log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
+{
+ if (nm->status_code & STATUS_FATAL) {
+ debug_msg(out, "notification: lsr-id %s, status %s "
+ "(fatal error)", inet_ntoa(nbr->id),
+ status_code_name(nm->status_code));
+ return;
+ }
+
+ debug_msg(out, "notification: lsr-id %s, status %s",
+ inet_ntoa(nbr->id), status_code_name(nm->status_code));
+ if (nm->flags & F_NOTIF_FEC)
+ debug_msg(out, "notification: fec %s", log_map(&nm->fec));
+ if (nm->flags & F_NOTIF_PW_STATUS)
+ debug_msg(out, "notification: pw-status %s",
+ (nm->pw_status) ? "not forwarding" : "forwarding");
+}
diff --git a/ldpd/packet.c b/ldpd/packet.c
index b085cac055..a7be0f6b42 100644
--- a/ldpd/packet.c
+++ b/ldpd/packet.c
@@ -519,13 +519,7 @@ session_read(struct thread *thread)
return (0);
}
break;
- case MSG_TYPE_ADDR:
- case MSG_TYPE_ADDRWITHDRAW:
- case MSG_TYPE_LABELMAPPING:
- case MSG_TYPE_LABELREQUEST:
- case MSG_TYPE_LABELWITHDRAW:
- case MSG_TYPE_LABELRELEASE:
- case MSG_TYPE_LABELABORTREQ:
+ default:
if (nbr->state != NBR_STA_OPER) {
session_shutdown(nbr, S_SHUTDOWN,
msg->id, msg->type);
@@ -533,8 +527,6 @@ session_read(struct thread *thread)
return (0);
}
break;
- default:
- break;
}
/* switch LDP packet type */
@@ -548,6 +540,9 @@ session_read(struct thread *thread)
case MSG_TYPE_KEEPALIVE:
ret = recv_keepalive(nbr, pdu, msg_size);
break;
+ case MSG_TYPE_CAPABILITY:
+ ret = recv_capability(nbr, pdu, msg_size);
+ break;
case MSG_TYPE_ADDR:
case MSG_TYPE_ADDRWITHDRAW:
ret = recv_address(nbr, pdu, msg_size);
@@ -564,7 +559,7 @@ session_read(struct thread *thread)
log_debug("%s: unknown LDP message from nbr %s",
__func__, inet_ntoa(nbr->id));
if (!(ntohs(msg->type) & UNKNOWN_FLAG))
- send_notification_nbr(nbr,
+ send_notification(nbr->tcp,
S_UNKNOWN_MSG, msg->id, msg->type);
/* ignore the message */
ret = 0;
@@ -632,7 +627,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id,
case NBR_STA_OPER:
log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
- send_notification_nbr(nbr, status, msg_id, msg_type);
+ send_notification(nbr->tcp, status, msg_id, msg_type);
nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
break;
@@ -788,7 +783,7 @@ pending_conn_timeout(struct thread *thread)
* notification message reliably.
*/
tcp = tcp_new(pconn->fd, NULL);
- send_notification(S_NO_HELLO, tcp, 0, 0);
+ send_notification(tcp, S_NO_HELLO, 0, 0);
msgbuf_write(&tcp->wbuf.wbuf);
pending_conn_del(pconn);
diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c
index 29f763e6a6..88a778cccc 100644
--- a/ldpd/pfkey.c
+++ b/ldpd/pfkey.c
@@ -131,7 +131,7 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
sa.sadb_sa_exttype = SADB_EXT_SA;
sa.sadb_sa_len = sizeof(sa) / 8;
sa.sadb_sa_replay = 0;
- sa.sadb_sa_spi = spi;
+ sa.sadb_sa_spi = htonl(spi);
sa.sadb_sa_state = SADB_SASTATE_MATURE;
break;
}
@@ -280,7 +280,7 @@ pfkey_read(int sd, struct sadb_msg *h)
}
static int
-pfkey_reply(int sd, uint32_t *spip)
+pfkey_reply(int sd, uint32_t *spi)
{
struct sadb_msg hdr, *msg;
struct sadb_ext *ext;
@@ -317,7 +317,7 @@ pfkey_reply(int sd, uint32_t *spip)
}
if (hdr.sadb_msg_type == SADB_GETSPI) {
- if (spip == NULL) {
+ if (spi == NULL) {
explicit_bzero(data, len);
free(data);
return (0);
@@ -331,7 +331,7 @@ pfkey_reply(int sd, uint32_t *spip)
ext->sadb_ext_len * PFKEY2_CHUNK)) {
if (ext->sadb_ext_type == SADB_EXT_SA) {
sa = (struct sadb_sa *) ext;
- *spip = sa->sadb_sa_spi;
+ *spi = ntohl(sa->sadb_sa_spi);
break;
}
}
diff --git a/lib/command.c b/lib/command.c
index 24272ccd1e..50976f2010 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1629,7 +1629,8 @@ DEFUN (show_commandtree,
show_commandtree_cmd,
"show commandtree [permutations]",
SHOW_STR
- "Show command tree\n")
+ "Show command tree\n"
+ "Permutations that we are interested in\n")
{
return cmd_list_cmds (vty, argc == 3);
}
diff --git a/lib/plist.c b/lib/plist.c
index 9a2fc4af09..3ed5c8fc5c 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -27,7 +27,6 @@
#include "plist.h"
#include "sockunion.h"
#include "buffer.h"
-#include "stream.h"
#include "log.h"
#include "routemap.h"
#include "lib/json.h"
diff --git a/lib/plist.h b/lib/plist.h
index 2c6f13a5c4..89d9a874f0 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -23,6 +23,11 @@
#ifndef _QUAGGA_PLIST_H
#define _QUAGGA_PLIST_H
+#include <zebra.h>
+
+#include "stream.h"
+#include "vty.h"
+
enum prefix_list_type
{
PREFIX_DENY,
diff --git a/tools/frr b/tools/frr
index ba0b1a84c7..8524ab4083 100755
--- a/tools/frr
+++ b/tools/frr
@@ -21,7 +21,7 @@ V_PATH=/var/run/frr
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd"
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index bf2b006963..4c26300099 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -350,6 +350,8 @@ end
"ip ",
"ipv6 ",
"log ",
+ "mpls",
+ "no ",
"password ",
"ptm-enable",
"router-id ",
diff --git a/zebra/main.c b/zebra/main.c
index 79c9ea7023..b72ce84cd6 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -96,6 +96,7 @@ struct option longopts[] =
{ "vty_addr", required_argument, NULL, 'A'},
{ "vty_port", required_argument, NULL, 'P'},
{ "vty_socket", required_argument, NULL, OPTION_VTYSOCK },
+ { "ecmp", required_argument, NULL, 'e'},
{ "retain", no_argument, NULL, 'r'},
{ "dryrun", no_argument, NULL, 'C'},
#ifdef HAVE_NETLINK
@@ -135,6 +136,8 @@ char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE;
/* Process ID saved for use by init system */
const char *pid_file = PATH_ZEBRA_PID;
+unsigned int multipath_num = MULTIPATH_NUM;
+
/* Help information display. */
static void
usage (char *progname, int status)
@@ -328,6 +331,14 @@ main (int argc, char **argv)
case 'A':
vty_addr = optarg;
break;
+ case 'e':
+ multipath_num = atoi (optarg);
+ if (multipath_num > MULTIPATH_NUM || multipath_num <= 0)
+ {
+ zlog_err ("Multipath Number specified must be less than %d and greater than 0", MULTIPATH_NUM);
+ return 1;
+ }
+ break;
case 'i':
pid_file = optarg;
break;
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 450e15e0f7..6ac1314496 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -1108,7 +1108,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct prefix *src_p,
struct sockaddr_nl snl;
struct nexthop *nexthop = NULL, *tnexthop;
int recursing;
- int nexthop_num;
+ unsigned int nexthop_num;
int discard;
int family = PREFIX_FAMILY(p);
const char *routedesc;
@@ -1224,7 +1224,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct prefix *src_p,
}
/* Singlepath case. */
- if (nexthop_num == 1 || MULTIPATH_NUM == 1)
+ if (nexthop_num == 1 || multipath_num == 1)
{
nexthop_num = 0;
for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
@@ -1300,7 +1300,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct prefix *src_p,
nexthop_num = 0;
for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
{
- if (nexthop_num >= MULTIPATH_NUM)
+ if (nexthop_num >= multipath_num)
break;
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -1457,7 +1457,7 @@ netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp)
mpls_lse_t lse;
zebra_nhlfe_t *nhlfe;
struct nexthop *nexthop = NULL;
- int nexthop_num;
+ unsigned int nexthop_num;
const char *routedesc;
struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
@@ -1521,7 +1521,7 @@ netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp)
/* Fill nexthops (paths) based on single-path or multipath. The paths
* chosen depend on the operation.
*/
- if (nexthop_num == 1 || MULTIPATH_NUM == 1)
+ if (nexthop_num == 1 || multipath_num == 1)
{
routedesc = "single hop";
_netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc);
@@ -1579,7 +1579,7 @@ netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp)
if (!nexthop)
continue;
- if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM)
+ if (nexthop_num >= multipath_num)
break;
if ((cmd == RTM_NEWROUTE &&
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 9fffc9e611..92ab5df2c3 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -137,7 +137,7 @@ typedef struct netlink_route_info_t_
u_char af;
struct prefix *prefix;
uint32_t *metric;
- int num_nhs;
+ unsigned int num_nhs;
/*
* Nexthop structures
@@ -289,7 +289,7 @@ netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
{
- if (ri->num_nhs >= MULTIPATH_NUM)
+ if (ri->num_nhs >= multipath_num)
break;
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -325,7 +325,7 @@ netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf,
size_t in_buf_len)
{
size_t bytelen;
- int nexthop_num = 0;
+ unsigned int nexthop_num = 0;
size_t buf_offset;
netlink_nh_info_t *nhi;
@@ -447,7 +447,7 @@ static void
zfpm_log_route_info (netlink_route_info_t *ri, const char *label)
{
netlink_nh_info_t *nhi;
- int i;
+ unsigned int i;
zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label,
nl_msg_type_to_str (ri->nlmsg_type),
diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c
index 702c355f1a..fba57c68f5 100644
--- a/zebra/zebra_fpm_protobuf.c
+++ b/zebra/zebra_fpm_protobuf.c
@@ -149,7 +149,7 @@ create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
struct nexthop *nexthop, *tnexthop;
int recursing;
uint num_nhs, u;
- struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)];
+ struct nexthop *nexthops[MULTIPATH_NUM];
msg = QPB_ALLOC(allocator, typeof(*msg));
if (!msg) {
@@ -198,7 +198,7 @@ create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
num_nhs = 0;
for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing))
{
- if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM)
+ if (num_nhs >= multipath_num)
break;
if (num_nhs >= ZEBRA_NUM_OF(nexthops))
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index 1127f568c0..b68b03e0c4 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -138,7 +138,7 @@ kernel_lsp_cmd (int action, zebra_lsp_t *lsp)
if (!nexthop)
continue;
- if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM)
+ if (nexthop_num >= multipath_num)
break;
/* XXX */
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 29515e9180..60364410a8 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -1050,12 +1050,12 @@ zread_interface_delete (struct zserv *client, u_short length, struct zebra_vrf *
void
zserv_nexthop_num_warn (const char *caller, const struct prefix *p, const unsigned int nexthop_num)
{
- if (nexthop_num > MULTIPATH_NUM)
+ if (nexthop_num > multipath_num)
{
char buff[PREFIX2STR_BUFFER];
prefix2str(p, buff, sizeof (buff));
zlog_warn("%s: Prefix %s has %d nexthops, but we can only use the first %d",
- caller, buff, nexthop_num, MULTIPATH_NUM);
+ caller, buff, nexthop_num, multipath_num);
}
}
@@ -1274,7 +1274,7 @@ zread_ipv4_nexthop_lookup_mrib (struct zserv *client, u_short length, struct zeb
static int
zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf)
{
- int i;
+ unsigned int i;
struct stream *s;
struct in6_addr nexthop;
struct rib *rib;
@@ -1318,9 +1318,9 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct
* next-hop-addr/next-hop-ifindices. */
if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
{
- int nh_count = 0;
- int if_count = 0;
- int max_nh_if = 0;
+ unsigned int nh_count = 0;
+ unsigned int if_count = 0;
+ unsigned int max_nh_if = 0;
nexthop_num = stream_getc (s);
zserv_nexthop_num_warn(__func__, (const struct prefix *)&p, nexthop_num);
@@ -1332,12 +1332,12 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct
{
case NEXTHOP_TYPE_IPV6:
stream_get (&nexthop, s, 16);
- if (nh_count < MULTIPATH_NUM) {
+ if (nh_count < multipath_num) {
nexthops[nh_count++] = nexthop;
}
break;
case NEXTHOP_TYPE_IFINDEX:
- if (if_count < MULTIPATH_NUM) {
+ if (if_count < multipath_num) {
ifindices[if_count++] = stream_getl (s);
}
break;
@@ -1401,7 +1401,7 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct
static int
zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf)
{
- int i;
+ unsigned int i;
struct stream *s;
struct in6_addr nexthop;
struct rib *rib;
@@ -1454,9 +1454,9 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf)
* next-hop-addr/next-hop-ifindices. */
if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
{
- int nh_count = 0;
- int if_count = 0;
- int max_nh_if = 0;
+ unsigned int nh_count = 0;
+ unsigned int if_count = 0;
+ unsigned int max_nh_if = 0;
nexthop_num = stream_getc (s);
zserv_nexthop_num_warn(__func__, (const struct prefix *)&p, nexthop_num);
@@ -1468,12 +1468,12 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf)
{
case NEXTHOP_TYPE_IPV6:
stream_get (&nexthop, s, 16);
- if (nh_count < MULTIPATH_NUM) {
+ if (nh_count < multipath_num) {
nexthops[nh_count++] = nexthop;
}
break;
case NEXTHOP_TYPE_IFINDEX:
- if (if_count < MULTIPATH_NUM) {
+ if (if_count < multipath_num) {
ifindices[if_count++] = stream_getl (s);
}
break;
diff --git a/zebra/zserv.h b/zebra/zserv.h
index a5a9979468..21cf1004bf 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -135,6 +135,7 @@ struct zebra_t
struct work_queue *lsp_process_q;
};
extern struct zebra_t zebrad;
+extern unsigned int multipath_num;
/* Prototypes. */
extern void zebra_init (void);