bgp_vty.o: bgp_vty_clippy.c
bgp_route.o: bgp_route_clippy.c
+bgp_debug.o: bgp_debug_clippy.c
EXTRA_DIST = BGP4-MIB.txt
/* Check if this is a Gateway MAC-IP advertisement */
attr->default_gw = bgp_attr_default_gw(attr);
+ /* Handle scenario where router flag ecommunity is not
+ * set but default gw ext community is present.
+ * Use default gateway, set and propogate R-bit.
+ */
+ if (attr->default_gw)
+ attr->router_flag = 1;
+
+ /* Check EVPN Neighbor advertisement flags, R-bit */
+ bgp_attr_evpn_na_flag(attr, &attr->router_flag);
+
/* Extract the Rmac, if any */
bgp_attr_rmac(attr, &attr->rmac);
/* Flag for default gateway extended community in EVPN */
uint8_t default_gw;
+ /* NA router flag (R-bit) support in EVPN */
+ uint8_t router_flag;
+
/* route tag */
route_tag_t tag;
return 0;
}
+/*
+ * return true if attr contains router flag extended community
+ */
+void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag)
+{
+ struct ecommunity *ecom;
+ int i;
+ uint8_t val;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return;
+
+ /* If there is a evpn na extendd community set router_flag */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (type == ECOMMUNITY_ENCODE_EVPN &&
+ sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) {
+ val = *pnt++;
+ if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) {
+ *router_flag = 1;
+ break;
+ }
+ }
+ }
+}
+
/* dst prefix must be AF_INET or AF_INET6 prefix, to forge EVPN prefix */
extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag,
struct prefix *dst)
uint8_t *sticky);
extern uint8_t bgp_attr_default_gw(struct attr *attr);
+extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag);
+
#endif /* _QUAGGA_BGP_ATTR_EVPN_H */
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_label.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_flowspec.h"
unsigned long conf_bgp_debug_as4;
const char *bgp_origin_str[] = {"i", "e", "?"};
const char *bgp_origin_long_str[] = {"IGP", "EGP", "incomplete"};
+static int bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc,
+ struct prefix *p);
/* Given a string return a pointer the corresponding peer structure */
static struct peer *bgp_find_peer(struct vty *vty, const char *peer_str)
{
/* Print the desc along with a list of peers/prefixes this debug is
* enabled for */
static void bgp_debug_list_print(struct vty *vty, const char *desc,
- struct list *list)
+ struct list *list, uint8_t evpn_dbg)
{
struct bgp_debug_filter *filter;
struct listnode *node, *nnode;
- char buf[INET6_ADDRSTRLEN];
+ char buf[PREFIX2STR_BUFFER];
vty_out(vty, "%s", desc);
if (filter->host)
vty_out(vty, " %s", filter->host);
- if (filter->p)
- vty_out(vty, " %s/%d",
- inet_ntop(filter->p->family,
- &filter->p->u.prefix, buf,
- INET6_ADDRSTRLEN),
- filter->p->prefixlen);
+ if (filter->p) {
+ if (!evpn_dbg) {
+ vty_out(vty, " %s",
+ prefix2str(filter->p, buf,
+ sizeof(buf)));
+ } else {
+ if (filter->p->family == AF_EVPN)
+ bgp_debug_print_evpn_prefix(vty,
+ "", filter->p);
+ }
+ }
}
}
* enabled for
*/
static int bgp_debug_list_conf_print(struct vty *vty, const char *desc,
- struct list *list)
+ struct list *list, uint8_t evpn_dbg)
{
struct bgp_debug_filter *filter;
struct listnode *node, *nnode;
- char buf[INET6_ADDRSTRLEN];
+ char buf[PREFIX2STR_BUFFER];
int write = 0;
if (list && !list_isempty(list)) {
if (filter->p) {
- vty_out(vty, "%s %s/%d\n", desc,
- inet_ntop(filter->p->family,
- &filter->p->u.prefix, buf,
- INET6_ADDRSTRLEN),
- filter->p->prefixlen);
- write++;
+ if (!evpn_dbg) {
+ vty_out(vty, "%s %s\n", desc,
+ prefix2str(filter->p, buf,
+ sizeof(buf)));
+ write++;
+ } else {
+ if (filter->p->family == AF_EVPN)
+ bgp_debug_print_evpn_prefix(vty,
+ desc, filter->p);
+ write++;
+ }
}
}
}
update_group_walk(bgp, update_group_clear_update_dbg, NULL);
}
+static int bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc,
+ struct prefix *p)
+{
+ char evpn_desc[PREFIX2STR_BUFFER + INET_ADDRSTRLEN];
+ char buf[PREFIX2STR_BUFFER];
+ char buf2[ETHER_ADDR_STRLEN];
+
+ if (p->u.prefix_evpn.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ if (is_evpn_prefix_ipaddr_none((struct prefix_evpn *)p)) {
+ sprintf(evpn_desc, "l2vpn evpn type macip mac %s",
+ prefix_mac2str(
+ &p->u.prefix_evpn.macip_addr.mac,
+ buf2, sizeof(buf2)));
+ } else {
+ uint8_t family = is_evpn_prefix_ipaddr_v4(
+ (struct prefix_evpn *)p) ?
+ AF_INET : AF_INET6;
+ sprintf(evpn_desc, "l2vpn evpn type macip mac %s ip %s",
+ prefix_mac2str(
+ &p->u.prefix_evpn.macip_addr.mac,
+ buf2, sizeof(buf2)),
+ inet_ntop(family,
+ &p->u.prefix_evpn.macip_addr.ip.ip.addr,
+ buf, PREFIX2STR_BUFFER));
+ }
+ } else if (p->u.prefix_evpn.route_type == BGP_EVPN_IMET_ROUTE) {
+ sprintf(evpn_desc, "l2vpn evpn type multicast ip %s",
+ inet_ntoa(p->u.prefix_evpn.imet_addr.ip.ipaddr_v4));
+ } else if (p->u.prefix_evpn.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ uint8_t family = is_evpn_prefix_ipaddr_v4(
+ (struct prefix_evpn *)p) ? AF_INET
+ : AF_INET6;
+ sprintf(evpn_desc, "l2vpn evpn type prefix ip %s/%d",
+ inet_ntop(family,
+ &p->u.prefix_evpn.prefix_addr.ip.ip.addr, buf,
+ PREFIX2STR_BUFFER),
+ p->u.prefix_evpn.prefix_addr.ip_prefix_length);
+ }
+
+ vty_out(vty, "%s %s\n", desc, evpn_desc);
+
+ return 0;
+}
+
+static int bgp_debug_parse_evpn_prefix(struct vty *vty, struct cmd_token **argv,
+ int argc, struct prefix **argv_pp)
+{
+ struct prefix *argv_p;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ int evpn_type;
+ int type_idx = 0;
+ int mac_idx = 0;
+ int ip_idx = 0;
+
+ argv_p = *argv_pp;
+
+ if (argv_find(argv, argc, "type", &type_idx) == 0)
+ return CMD_WARNING;
+
+ if (strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0)
+ evpn_type = BGP_EVPN_MAC_IP_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
+ evpn_type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
+ evpn_type = BGP_EVPN_IP_PREFIX_ROUTE;
+ else
+ evpn_type = 0;
+
+ if (evpn_type == BGP_EVPN_MAC_IP_ROUTE) {
+ memset(&ip, 0, sizeof(struct ipaddr));
+ /* get the ip if specified */
+ if (argv_find(argv, argc, "ip", &ip_idx)) {
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+ }
+ argv_find(argv, argc, "mac", &mac_idx);
+ if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+
+ build_evpn_type2_prefix((struct prefix_evpn *)argv_p,
+ &mac, &ip);
+ } else if (evpn_type == BGP_EVPN_IMET_ROUTE) {
+ memset(&ip, 0, sizeof(struct ipaddr));
+ /* get the ip if specified */
+ if (argv_find(argv, argc, "ip", &ip_idx)) {
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+ }
+ build_evpn_type3_prefix((struct prefix_evpn *)argv_p,
+ ip.ipaddr_v4);
+ } else if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ struct prefix ip_prefix;
+
+ memset(&ip_prefix, 0, sizeof(struct prefix));
+ if (argv_find(argv, argc, "ip", &ip_idx)) {
+ (void)str2prefix(argv[ip_idx + 1]->arg, &ip_prefix);
+ apply_mask(&ip_prefix);
+ }
+ build_type5_prefix_from_ip_prefix(
+ (struct prefix_evpn *)argv_p,
+ &ip_prefix);
+ }
+
+ return CMD_SUCCESS;
+}
/* Debug option setting interface. */
unsigned long bgp_debug_option = 0;
return CMD_SUCCESS;
}
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_debug_clippy.c"
+#endif
+
+DEFPY (debug_bgp_update_prefix_afi_safi,
+ debug_bgp_update_prefix_afi_safi_cmd,
+ "debug bgp updates prefix <l2vpn>$afi <evpn>$safi type <macip mac WORD [ip WORD]|multicast ip WORD |prefix ip WORD>",
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Specify a prefix to debug\n"
+ "l2vpn\n"
+ "evpn\n"
+ "Specify EVPN Route type\n"
+ "MAC-IP (Type-2) route\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n"
+ "Multicast (Type-3) route\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n"
+ "Prefix (Type-5) route\n"
+ "IP\n"
+ "Prefix route\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 4;
+ struct prefix *argv_p;
+ int ret = CMD_SUCCESS;
+ afi_t afiz;
+ safi_t safiz;
+
+ argv_p = prefix_new();
+
+ afiz = bgp_vty_afi_from_str(afi);
+ safiz = safi ? bgp_vty_safi_from_str(safi) : SAFI_UNICAST;
+
+ /* check for evpn route type */
+ if (afiz == AFI_L2VPN && safiz == SAFI_EVPN) {
+ ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
+ if (ret != CMD_SUCCESS)
+ return ret;
+ } else {
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+ }
+
+ if (!bgp_debug_update_prefixes)
+ bgp_debug_update_prefixes = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, argv_p)) {
+ vty_out(vty,
+ "BGP updates debugging is already enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, argv_p);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_ON(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_ON(update, UPDATE_PREFIX);
+ vty_out(vty, "BGP updates debugging is on for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_debug_bgp_update_prefix_afi_safi,
+ no_debug_bgp_update_prefix_afi_safi_cmd,
+ "no debug bgp updates prefix <l2vpn>$afi <evpn>$safi type <macip mac WORD [ip WORD]|multicast ip WORD |prefix ip WORD>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Specify a prefix to debug\n"
+ "l2vpn\n"
+ "evpn\n"
+ "Specify EVPN Route type\n"
+ "MAC-IP (Type-2) route\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n"
+ "Multicast (Type-3) route\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n"
+ "Prefix (Type-5) route\n"
+ "IP\n"
+ "Prefix route\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 5;
+ struct prefix *argv_p;
+ int found_prefix = 0;
+ int ret = CMD_SUCCESS;
+ afi_t afiz;
+ safi_t safiz;
+
+ argv_p = prefix_new();
+
+ afiz = bgp_vty_afi_from_str(afi);
+ safiz = safi ? bgp_vty_safi_from_str(safi) : SAFI_UNICAST;
+
+ /* check for evpn route type */
+ if (afiz == AFI_L2VPN && safiz == SAFI_EVPN) {
+ ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
+ if (ret != CMD_SUCCESS)
+ return ret;
+ } else {
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+ }
+
+ if (bgp_debug_update_prefixes
+ && !list_isempty(bgp_debug_update_prefixes)) {
+ found_prefix = bgp_debug_list_remove_entry(
+ bgp_debug_update_prefixes, NULL, argv_p);
+
+ if (list_isempty(bgp_debug_update_prefixes)) {
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_OFF(update, UPDATE_PREFIX);
+ vty_out(vty,
+ "BGP updates debugging (per prefix) is off\n");
+ }
+ }
+ }
+
+ if (found_prefix)
+ vty_out(vty, "BGP updates debugging is off for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ else
+ vty_out(vty, "BGP updates debugging was not enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+
+ return CMD_SUCCESS;
+}
+
+
DEFUN (debug_bgp_update_prefix,
debug_bgp_update_prefix_cmd,
"debug bgp updates prefix <A.B.C.D/M|X:X::X:X/M>",
if (BGP_DEBUG(bestpath, BESTPATH))
bgp_debug_list_print(vty, " BGP bestpath debugging is on",
- bgp_debug_bestpath_prefixes);
+ bgp_debug_bestpath_prefixes, 0);
if (BGP_DEBUG(keepalive, KEEPALIVE))
bgp_debug_list_print(vty, " BGP keepalives debugging is on",
- bgp_debug_keepalive_peers);
+ bgp_debug_keepalive_peers, 0);
if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS))
bgp_debug_list_print(vty,
" BGP neighbor-events debugging is on",
- bgp_debug_neighbor_events_peers);
+ bgp_debug_neighbor_events_peers, 0);
if (BGP_DEBUG(nht, NHT))
vty_out(vty, " BGP next-hop tracking debugging is on\n");
if (BGP_DEBUG(update, UPDATE_PREFIX))
bgp_debug_list_print(vty, " BGP updates debugging is on",
- bgp_debug_update_prefixes);
+ bgp_debug_update_prefixes, 1);
if (BGP_DEBUG(update, UPDATE_IN))
bgp_debug_list_print(vty,
" BGP updates debugging is on (inbound)",
- bgp_debug_update_in_peers);
+ bgp_debug_update_in_peers, 0);
if (BGP_DEBUG(update, UPDATE_OUT))
bgp_debug_list_print(vty,
" BGP updates debugging is on (outbound)",
- bgp_debug_update_out_peers);
+ bgp_debug_update_out_peers, 0);
if (BGP_DEBUG(zebra, ZEBRA))
bgp_debug_list_print(vty, " BGP zebra debugging is on",
- bgp_debug_zebra_prefixes);
+ bgp_debug_zebra_prefixes, 0);
if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
vty_out(vty, " BGP allow martian next hop debugging is on\n");
if (CONF_BGP_DEBUG(bestpath, BESTPATH)) {
write += bgp_debug_list_conf_print(vty, "debug bgp bestpath",
- bgp_debug_bestpath_prefixes);
+ bgp_debug_bestpath_prefixes,
+ 0);
}
if (CONF_BGP_DEBUG(keepalive, KEEPALIVE)) {
write += bgp_debug_list_conf_print(vty, "debug bgp keepalives",
- bgp_debug_keepalive_peers);
+ bgp_debug_keepalive_peers,
+ 0);
}
if (CONF_BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) {
write += bgp_debug_list_conf_print(
vty, "debug bgp neighbor-events",
- bgp_debug_neighbor_events_peers);
+ bgp_debug_neighbor_events_peers, 0);
}
if (CONF_BGP_DEBUG(nht, NHT)) {
if (CONF_BGP_DEBUG(update, UPDATE_PREFIX)) {
write += bgp_debug_list_conf_print(vty,
"debug bgp updates prefix",
- bgp_debug_update_prefixes);
+ bgp_debug_update_prefixes,
+ 1);
}
if (CONF_BGP_DEBUG(update, UPDATE_IN)) {
write += bgp_debug_list_conf_print(vty, "debug bgp updates in",
- bgp_debug_update_in_peers);
+ bgp_debug_update_in_peers,
+ 0);
}
if (CONF_BGP_DEBUG(update, UPDATE_OUT)) {
write += bgp_debug_list_conf_print(vty, "debug bgp updates out",
- bgp_debug_update_out_peers);
+ bgp_debug_update_out_peers,
+ 0);
}
if (CONF_BGP_DEBUG(zebra, ZEBRA)) {
} else {
write += bgp_debug_list_conf_print(
vty, "debug bgp zebra prefix",
- bgp_debug_zebra_prefixes);
+ bgp_debug_zebra_prefixes, 0);
}
}
install_element(CONFIG_NODE, &debug_bgp_update_prefix_cmd);
install_element(ENABLE_NODE, &no_debug_bgp_update_prefix_cmd);
install_element(CONFIG_NODE, &no_debug_bgp_update_prefix_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_update_prefix_afi_safi_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_prefix_afi_safi_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_prefix_afi_safi_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_prefix_afi_safi_cmd);
/* debug bgp zebra prefix A.B.C.D/M */
install_element(ENABLE_NODE, &debug_bgp_zebra_prefix_cmd);
#define ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT 0x02
#define ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC 0x03
#define ECOMMUNITY_EVPN_SUBTYPE_DEF_GW 0x0d
+#define ECOMMUNITY_EVPN_SUBTYPE_ND 0x08
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01
+#define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01
+#define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02
/* Low-order octet of the Extended Communities type field for OPAQUE types */
#define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c
struct ecommunity ecom_sticky;
struct ecommunity ecom_default_gw;
struct ecommunity ecom_rmac;
+ struct ecommunity ecom_na;
struct ecommunity_val eval;
struct ecommunity_val eval_sticky;
struct ecommunity_val eval_default_gw;
struct ecommunity_val eval_rmac;
+ struct ecommunity_val eval_na;
+
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
struct ecommunity *ecom;
ecommunity_merge(attr->ecommunity, &ecom_default_gw);
}
+ if (attr->router_flag) {
+ memset(&ecom_na, 0, sizeof(ecom_na));
+ encode_na_flag_extcomm(&eval_na, attr->router_flag);
+ ecom_na.size = 1;
+ ecom_na.val = (uint8_t *)eval_na.val;
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ &ecom_na);
+ }
+
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
}
{
struct bgp_info *old_select, *new_select;
struct bgp_info_pair old_and_new;
+ struct prefix_evpn *evp;
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
int ret = 0;
old_select = old_and_new.old;
new_select = old_and_new.new;
+ evp = (struct prefix_evpn *)&rn->p;
/* If the best path hasn't changed - see if there is still something to
* update
* to zebra RIB.
SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
if (old_select->attr->default_gw)
SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ if (is_evpn_prefix_ipaddr_v6(evp) &&
+ old_select->attr->router_flag)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
ret = evpn_zebra_install(
bgp, vpn, (struct prefix_evpn *)&rn->p,
old_select->attr->nexthop, flags);
SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
if (new_select->attr->default_gw)
SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ if (is_evpn_prefix_ipaddr_v6(evp) &&
+ new_select->attr->router_flag)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
ret = evpn_zebra_install(bgp, vpn, (struct prefix_evpn *)&rn->p,
new_select->attr->nexthop, flags);
/* If an old best existed and it was a "local" route, the only
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
+ attr.router_flag = CHECK_FLAG(flags,
+ ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? 1 : 0;
/* PMSI is only needed for type-3 routes */
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE)
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr_sticky, 0, 1, &ri,
0);
- else if (evpn_route_is_def_gw(bgp, rn))
+ else if (evpn_route_is_def_gw(bgp, rn)) {
+ if (is_evpn_prefix_ipaddr_v6(evp))
+ attr_def_gw.router_flag = 1;
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr_def_gw, 0, 1, &ri,
0);
- else
+ } else
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr, 0, 1, &ri, 0);
}
eval->val[7] = seq & 0xff;
}
+static inline void encode_na_flag_extcomm(struct ecommunity_val *eval,
+ uint8_t na_flag)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ND;
+ if (na_flag)
+ eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG;
+}
+
static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp,
struct prefix *ip)
{
* @param afi string, one of
* - "ipv4"
* - "ipv6"
+ * - "l2vpn"
* @return the corresponding afi_t
*/
afi_t bgp_vty_afi_from_str(const char *afi_str)
afi = AFI_IP;
else if (strmatch(afi_str, "ipv6"))
afi = AFI_IP6;
+ else if (strmatch(afi_str, "l2vpn"))
+ afi = AFI_L2VPN;
return afi;
}
safi = SAFI_UNICAST;
else if (strmatch(safi_str, "vpn"))
safi = SAFI_MPLS_VPN;
+ else if (strmatch(safi_str, "evpn"))
+ safi = SAFI_EVPN;
else if (strmatch(safi_str, "labeled-unicast"))
safi = SAFI_LABELED_UNICAST;
else if (strmatch(safi_str, "flowspec"))
developer/workflow.rst \
developer/zebra.rst \
user/babeld.rst \
+ user/ldpd.rst \
user/basic.rst \
user/bgp.rst \
user/bugs.rst \
isis
isisd
lan
+ldpd
le
libc
libcap
zebra
bgp
babeld
+ ldpd
eigrpd
isisd
nhrpd
--- /dev/null
+.. _ldp:
+
+***
+LDP
+***
+
+The *ldpd* daemon is a standardised protocol that permits exchanging MPLS label
+information between MPLS devices. The LDP protocol creates peering between
+devices, so as to exchange that label information. This information is stored in
+MPLS table of *zebra*, and it injects that MPLS information in the underlying
+system (Linux kernel or OpenBSD system for instance).
+*ldpd* provides necessary options to create a Layer 2 VPN across MPLS network.
+For instance, it is possible to interconnect several sites that share the same
+broadcast domain.
+
+FRR implements LDP as described in :rfc:`5036`; other LDP standard are the
+following ones: :rfc:`6720`, :rfc:`6667`, :rfc:`5919`, :rfc:`5561`, :rfc:`7552`,
+:rfc:`4447`.
+Because MPLS is already available, FRR also supports :rfc:`3031`.
+
+Running Ldpd
+============
+
+The *ldpd* daemon can be invoked with any of the common
+options (:ref:`common-invocation-options`).
+
+The *zebra* daemon must be running before *ldpd* is invoked.
+
+Configuration of *ldpd* is done in its configuration file
+:file:`ldpd.conf`.
+
+
+.. _understanding-ldp:
+
+Understanding LDP principles
+============================
+
+Let's first introduce some definitions that permit understand better the LDP
+protocol:
+
+- `LSR` : Labeled Switch Router. Networking devices handling labels used to
+ forward traffic between and through them.
+
+- `LER` : Labeled Edge Router. A Labeled edge router is located at the edge of
+ an MPLS network, generally between an IP network and an MPLS network.
+
+
+``LDP`` aims at sharing label information across devices. It tries to establish
+peering with remote LDP capable devices, first by discovering using UDP port 646
+, then by peering using TCP port 646. Once the TCP session is established, the
+label information is shared, through label advertisements.
+
+There are different methods to send label advertisement modes. The
+implementation actually supports the following : Liberal Label Retention +
+Downstream Unsolicited + Independent Control.
+The other advertising modes are depicted below, and compared with the current
+implementation.
+
+- Liberal label retention versus conservative mode
+ In liberal mode, every label sent by every LSR is stored in the MPLS table.
+ In conservative mode, only the label that was sent by the best next hop
+ (determined by the IGP metric) for that particular FEC is stored in the MPLS
+ table.
+
+- Independent LSP Control versus ordered LSP Control
+ MPLS has two ways of binding labels to FEC’s; either through ordered LSP
+ control, or independent LSP control.
+ Ordered LSP control only binds a label to a FEC if it is the egress LSR, or
+ the router received a label binding for a FEC from the next hop router. In
+ this mode, an MPLS router will create a label binding for each FEC and
+ distribute it to its neighbors so long as he has a entry in the RIB for the
+ destination.
+ In the other mode, label bindings are made without any dependencies on another
+ router advertising a label for a particular FEC. Each router makes it own
+ independent decision to create a label for each FEC.
+ By default IOS uses Independent LSP Control, while Juniper implements the
+ Ordered Control. Both modes are interoperable, the difference is that Ordered
+ Control prevent blackholing during the LDP convergence process, at cost of
+ slowing down the convergence itself
+
+- unsolicited downstream versus downstream on demand
+ Downstream on demand label distribution is where an LSR must explicitly
+ request that a label be sent from its downstream router for a particular FEC.
+ Unsolicited label distribution is where a label is sent from the downstream
+ router without the original router requesting it.
+
+.. _configuring-ldpd:
+
+.. _ldp-configuration:
+
+LDP Configuration
+===================
+
+.. index:: [no] mpls ldp
+.. clicmd:: [no] mpls ldp
+
+ Enable or disable LDP daemon
+
+.. index:: [no] router-id A.B.C.D
+.. clicmd:: [no] router-id A.B.C.D
+
+ The following command located under MPLS router node configures the MPLS
+ router-id of the local device.
+
+.. index:: [no] address-family [ipv4 | ipv6]
+.. clicmd:: [no] address-family [ipv4 | ipv6]
+
+ Configure LDP for IPv4 or IPv6 address-family. Located under MPLS route node,
+ this subnode permits configuring the LDP neighbors.
+
+.. index:: [no] interface IFACE
+.. clicmd:: [no] interface IFACE
+
+ Located under MPLS address-family node, use this command to enable or disable
+ LDP discovery per interface. IFACE stands for the interface name where LDP is
+ enabled. By default it is disabled. Once this command executed, the
+ address-family interface node is configured.
+
+.. index:: [no] discovery transport-address A.B.C.D | A:B::C:D
+.. clicmd:: [no] discovery transport-address A.B.C.D | A:B::C:D
+
+ Located under mpls address-family interface node, use this command to set
+ the IPv4 or IPv6 transport-address used by the LDP protocol to talk on this
+ interface.
+
+.. index:: [no] neighbor A.B.C.D password PASSWORD
+.. clicmd:: [no] neighbor A.B.C.D password PASSWORD
+
+ The following command located under MPLS router node configures the router
+ of a LDP device. This device, if found, will have to comply with the
+ configured password. PASSWORD is a clear text password wit its digest sent
+ through the network.
+
+.. index:: [no] neighbor A.B.C.D holdtime HOLDTIME
+.. clicmd:: [no] neighbor A.B.C.D holdtime HOLDTIME
+
+ The following command located under MPLS router node configures the holdtime
+ value in seconds of the LDP neighbor ID. Configuring it triggers a keepalive
+ mechanism. That value can be configured between 15 and 65535 seconds. After
+ this time of non response, the LDP established session will be considered as
+ set to down. By default, no holdtime is configured for the LDP devices.
+
+.. index:: [no] discovery hello holdtime HOLDTIME
+.. clicmd:: [no] discovery hello holdtime HOLDTIME
+
+.. index:: [no] discovery hello interval INTERVAL
+.. clicmd:: [no] discovery hello interval INTERVAL
+
+ INTERVAL value ranges from 1 to 65535 seconds. Default value is 5 seconds.
+ This is the value between each hello timer message sent.
+ HOLDTIME value ranges from 1 to 65535 seconds. Default value is 15 seconds.
+ That value is added as a TLV in the LDP messages.
+
+.. _show-ldp-information:
+
+Show LDP Information
+====================
+
+These commands dump various parts of *ldpd*.
+
+.. index:: show mpls ldp neighbor [A.B.C.D]
+.. clicmd:: show mpls ldp neighbor [A.B.C.D]
+
+ This command dumps the various neighbors discovered. Below example shows that
+ local machine has an operation neighbor with ID set to 1.1.1.1.
+
+ ::
+
+ west-vm# show mpls ldp neighbor
+ AF ID State Remote Address Uptime
+ ipv4 1.1.1.1 OPERATIONAL 1.1.1.1 00:01:37
+ west-vm#
+
+.. index:: show mpls ldp neighbor [A.B.C.D] capabilities
+.. clicmd:: show mpls ldp neighbor [A.B.C.D] capabilities
+
+.. index:: show mpls ldp neighbor [A.B.C.D] detail
+.. clicmd:: show mpls ldp neighbor [A.B.C.D] detail
+
+ Above commands dump other neighbor information.
+
+.. index:: show mpls ldp discovery [detail]
+.. clicmd:: show mpls ldp discovery [detail]
+
+.. index:: show mpls ldp ipv4 discovery [detail]
+.. clicmd:: show mpls ldp ipv4 discovery [detail]
+
+.. index:: show mpls ldp ipv6 discovery [detail]
+.. clicmd:: show mpls ldp ipv6 discovery [detail]
+
+ Above commands dump discovery information.
+
+.. index:: show mpls ldp ipv4 interface
+.. clicmd:: show mpls ldp ipv4 interface
+
+.. index:: show mpls ldp ipv6 interface
+.. clicmd:: show mpls ldp ipv6 interface
+
+ Above command dumps the IPv4 or IPv6 interface per where LDP is enabled.
+ Below output illustrates what is dumped for IPv4.
+
+ ::
+
+ west-vm# show mpls ldp ipv4 interface
+ AF Interface State Uptime Hello Timers ac
+ ipv4 eth1 ACTIVE 00:08:35 5/15 0
+ ipv4 eth3 ACTIVE 00:08:35 5/15 1
+
+
+.. index:: show mpls ldp ipv4|ipv6 binding
+.. clicmd:: show mpls ldp ipv4|ipv6 binding
+
+ Above command dumps the binding obtained through MPLS exchanges with LDP.
+
+ ::
+
+ west-vm# show mpls ldp ipv4 binding
+ AF Destination Nexthop Local Label Remote Label In Use
+ ipv4 1.1.1.1/32 1.1.1.1 16 imp-null yes
+ ipv4 2.2.2.2/32 1.1.1.1 imp-null 16 no
+ ipv4 10.0.2.0/24 1.1.1.1 imp-null imp-null no
+ ipv4 10.115.0.0/24 1.1.1.1 imp-null 17 no
+ ipv4 10.135.0.0/24 1.1.1.1 imp-null imp-null no
+ ipv4 10.200.0.0/24 1.1.1.1 17 imp-null yes
+ west-vm#
+
+LDP debugging commands
+========================
+
+.. index::
+ simple: debug mpls ldp KIND
+ simple: no debug mpls ldp KIND
+
+.. clicmd:: [no] debug mpls ldp KIND
+
+ Enable or disable debugging messages of a given kind. ``KIND`` can
+ be one of:
+
+ - ``discovery``
+ - ``errors``
+ - ``event``
+ - ``labels``
+ - ``messages``
+ - ``zebra``
+
+LDP Example Configuration
+=========================
+
+Below configuration gives a typical MPLS configuration of a device located in a
+MPLS backbone. LDP is enabled on two interfaces and will attempt to peer with
+two neighbors with router-id set to either 1.1.1.1 or 3.3.3.3.
+
+.. code-block:: frr
+
+ mpls ldp
+ router-id 2.2.2.2
+ neighbor 1.1.1.1 password test
+ neighbor 3.3.3.3 password test
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ !
+ interface eth1
+ !
+ interface eth3
+ !
+ exit-address-family
+ !
+
+
+Deploying LDP across a backbone generally is done in a full mesh configuration
+topology. LDP is typically deployed with an IGP like OSPF, that helps discover
+the remote IPs. Below example is an OSPF configuration extract that goes with
+LDP configuration
+
+.. code-block:: frr
+
+ router ospf
+ ospf router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+ !
+
+
+Below output shows the routing entry on the LER side. The OSPF routing entry
+(10.200.0.0) is associated with Label entry (17), and shows that MPLS push action
+that traffic to that destination will be applied.
+
+::
+
+ north-vm# show ip route
+ Codes: K - kernel route, C - connected, S - static, R - RIP,
+ O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
+ T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
+ F - PBR,
+ > - selected route, * - FIB route
+
+ O>* 1.1.1.1/32 [110/120] via 10.115.0.1, eth2, label 16, 00:00:15
+ O>* 2.2.2.2/32 [110/20] via 10.115.0.1, eth2, label implicit-null, 00:00:15
+ O 3.3.3.3/32 [110/10] via 0.0.0.0, loopback1 onlink, 00:01:19
+ C>* 3.3.3.3/32 is directly connected, loopback1, 00:01:29
+ O>* 10.0.2.0/24 [110/11] via 10.115.0.1, eth2, label implicit-null, 00:00:15
+ O 10.100.0.0/24 [110/10] is directly connected, eth1, 00:00:32
+ C>* 10.100.0.0/24 is directly connected, eth1, 00:00:32
+ O 10.115.0.0/24 [110/10] is directly connected, eth2, 00:00:25
+ C>* 10.115.0.0/24 is directly connected, eth2, 00:00:32
+ O>* 10.135.0.0/24 [110/110] via 10.115.0.1, eth2, label implicit-null, 00:00:15
+ O>* 10.200.0.0/24 [110/210] via 10.115.0.1, eth2, label 17, 00:00:15
+ north-vm#
+
install_element(LDP_IPV6_NODE, &ldp_label_remote_accept_cmd);
install_element(LDP_IPV6_NODE, &ldp_ttl_security_disable_cmd);
install_element(LDP_IPV6_NODE, &ldp_interface_cmd);
+ install_element(LDP_IPV6_NODE, &no_ldp_interface_cmd);
install_element(LDP_IPV6_NODE, &ldp_session_holdtime_cmd);
install_element(LDP_IPV6_NODE, &ldp_neighbor_ipv6_targeted_cmd);
install_element(LDP_IPV6_NODE, &ldp_exit_address_family_cmd);
/* Zebra MAC types */
#define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/
#define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/
+#define ZEBRA_MACIP_TYPE_ROUTER_FLAG 0x04 /* Router Flag - proxy NA */
+#define ZEBRA_MACIP_TYPE_OVERRIDE_FLAG 0x08 /* Override Flag */
struct zclient_options {
bool receive_notify;
/* Update interface information. */
set_ifindex(ifp, ifi->ifi_index, zns);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
+ if (!tb[IFLA_MTU]) {
+ zlog_warn(
+ "RTM_NEWLINK for interface %s(%u) without MTU set",
+ name, ifi->ifi_index);
+ return 0;
+ }
ifp->mtu6 = ifp->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]);
ifp->metric = 0;
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
bridge_ifindex, ifi->ifi_flags);
set_ifindex(ifp, ifi->ifi_index, zns);
+ if (!tb[IFLA_MTU]) {
+ zlog_warn(
+ "RTM_NEWLINK for interface %s(%u) without MTU set",
+ name, ifi->ifi_index);
+ return 0;
+ }
ifp->mtu6 = ifp->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]);
ifp->metric = 0;
int local);
extern int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip,
- struct ethaddr *mac);
+ struct ethaddr *mac, uint8_t flags);
extern int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip);
/*
memcpy(&p.u.prefix4, dest, 4);
p.prefixlen = rtm->rtm_dst_len;
- src_p.prefixlen =
- 0; // Forces debug below to not display anything
+ if (rtm->rtm_src_len != 0) {
+ char buf[PREFIX_STRLEN];
+ zlog_warn("unsupported IPv4 sourcedest route (dest %s vrf %u)",
+ prefix2str(&p, buf, sizeof(buf)), vrf_id);
+ return 0;
+ }
+
+ /* Force debug below to not display anything for source */
+ src_p.prefixlen = 0;
} else if (rtm->rtm_family == AF_INET6) {
p.family = AF_INET6;
memcpy(&p.u.prefix6, dest, 16);
src_p.prefixlen = rtm->rtm_src_len;
}
- if (rtm->rtm_src_len != 0) {
- char buf[PREFIX_STRLEN];
- zlog_warn(
- "unsupported IPv[4|6] sourcedest route (dest %s vrf %u)",
- prefix2str(&p, buf, sizeof(buf)), vrf_id);
- return 0;
- }
-
/*
* For ZEBRA_ROUTE_KERNEL types:
*
nh.vrf_id = nh_vrf_id;
rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p,
- NULL, &nh, table, metric, mtu, distance, tag);
+ &src_p, &nh, table, metric, mtu, distance, tag);
} else {
/* This is a multipath route */
if (re->nexthop_num == 0)
XFREE(MTYPE_RE, re);
else
- rib_add_multipath(afi, SAFI_UNICAST, &p, NULL,
- re);
+ rib_add_multipath(afi, SAFI_UNICAST, &p,
+ &src_p, re);
}
} else {
if (!tb[RTA_MULTIPATH]) {
if (gate)
memcpy(&nh.gate, gate, sz);
rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
- &p, NULL, &nh, table, metric, true);
+ &p, &src_p, &nh, table, metric, true);
} else {
/* XXX: need to compare the entire list of nexthops
* here for NLM_F_APPEND stupidity */
rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
- &p, NULL, NULL, table, metric, true);
+ &p, &src_p, NULL, table, metric, true);
}
}
char buf2[INET6_ADDRSTRLEN];
int mac_present = 0;
uint8_t ext_learned;
+ uint8_t router_flag;
ndm = NLMSG_DATA(h);
}
ext_learned = (ndm->ndm_flags & NTF_EXT_LEARNED) ? 1 : 0;
+ router_flag = (ndm->ndm_flags & NTF_ROUTER) ? 1 : 0;
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
if (ndm->ndm_state & NUD_VALID)
return zebra_vxlan_handle_kernel_neigh_update(
ifp, link_if, &ip, &mac, ndm->ndm_state,
- ext_learned);
+ ext_learned, router_flag);
return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip);
}
}
static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
- struct ethaddr *mac, uint32_t flags, int cmd)
+ struct ethaddr *mac, uint8_t flags,
+ uint16_t state, int cmd)
{
struct {
struct nlmsghdr n;
req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH
req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6;
- req.ndm.ndm_state = flags;
+ req.ndm.ndm_state = state;
req.ndm.ndm_ifindex = ifp->ifindex;
req.ndm.ndm_type = RTN_UNICAST;
- req.ndm.ndm_flags = NTF_EXT_LEARNED;
-
+ req.ndm.ndm_flags = flags;
ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6);
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("Tx %s family %s IF %s(%u) Neigh %s MAC %s",
+ zlog_debug("Tx %s family %s IF %s(%u) Neigh %s MAC %s flags 0x%x",
nl_msg_type_to_str(cmd),
nl_family_to_str(req.ndm.ndm_family), ifp->name,
ifp->ifindex, ipaddr2str(ip, buf, sizeof(buf)),
mac ? prefix_mac2str(mac, buf2, sizeof(buf2))
- : "null");
+ : "null", flags);
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
0);
}
int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip,
- struct ethaddr *mac)
+ struct ethaddr *mac, uint8_t flags)
{
- return netlink_neigh_update2(ifp, ip, mac, NUD_NOARP, RTM_NEWNEIGH);
+ return netlink_neigh_update2(ifp, ip, mac, flags,
+ NUD_NOARP, RTM_NEWNEIGH);
}
int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip)
{
- return netlink_neigh_update2(ifp, ip, NULL, 0, RTM_DELNEIGH);
+ return netlink_neigh_update2(ifp, ip, NULL, 0, 0, RTM_DELNEIGH);
}
/*
}
int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip,
- struct ethaddr *mac)
+ struct ethaddr *mac, uint8_t flags)
{
return 0;
}
if (!re)
return 0;
- assert(!src_p || afi == AFI_IP6);
+ assert(!src_p || !src_p->prefixlen || afi == AFI_IP6);
/* Lookup table. */
table = zebra_vrf_table_with_table_id(afi, safi, re->vrf_id, re->table);
char buf2[INET6_ADDRSTRLEN];
rib_dest_t *dest;
- assert(!src_p || afi == AFI_IP6);
+ assert(!src_p || !src_p->prefixlen || afi == AFI_IP6);
/* Lookup table. */
table = zebra_vrf_table_with_table_id(afi, safi, vrf_id, table_id);
#include "jhash.h"
#include "vlan.h"
#include "vxlan.h"
+#ifdef GNU_LINUX
+#include <linux/neighbour.h>
+#endif
#include "zebra/rib.h"
#include "zebra/rt.h"
ipaddr2str(&n->ip, buf, sizeof(buf)), width = strlen(buf);
if (width > wctx->addr_width)
wctx->addr_width = width;
+
}
/*
else
json_object_boolean_true_add(json, "defaultGateway");
}
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) {
+ if (!json)
+ vty_out(vty, " Router");
+ }
if (json == NULL)
vty_out(vty, "\n");
}
return;
}
num_neigh = hashcount(zvni->neigh_table);
- if (json == NULL)
+ if (json == NULL) {
vty_out(vty,
"\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
zvni->vni, num_neigh);
- else {
+ } else {
json_vni = json_object_new_object();
json_object_int_add(json_vni, "numArpNd", num_neigh);
snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
wctx.json = json_vni;
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
- if (json == NULL)
+ if (json == NULL) {
vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
"Type", "MAC", "Remote VTEP");
+ }
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
if (json)
if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW))
SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ /* Set router flag (R-bit) based on local neigh entry add */
+ if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
ZEBRA_MACIP_ADD);
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
struct interface *vlan_if;
+ uint8_t flags;
+ int ret = 0;
if (!(n->flags & ZEBRA_NEIGH_REMOTE))
return 0;
vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
if (!vlan_if)
return -1;
-
- return kernel_add_neigh(vlan_if, &n->ip, &n->emac);
+#ifdef GNU_LINUX
+ flags = NTF_EXT_LEARNED;
+ if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
+ flags |= NTF_ROUTER;
+ ret = kernel_add_neigh(vlan_if, &n->ip, &n->emac, flags);
+#endif
+ return ret;
}
/*
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
+ /* Set Router flag (R-bit) */
+ if (ip->ipa_type == IPADDR_V6)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
memcpy(&n->emac, macaddr, ETH_ALEN);
n->ifindex = ifp->ifindex;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)));
+ ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, n->flags);
static int zvni_local_neigh_update(zebra_vni_t *zvni,
struct interface *ifp,
struct ipaddr *ip,
- struct ethaddr *macaddr)
+ struct ethaddr *macaddr,
+ uint8_t router_flag)
{
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
return 0;
}
+ /*Set router flag (R-bit) */
+ if (router_flag)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
/* Inform BGP. */
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Neigh %s (MAC %s) is now ACTIVE on L2-VNI %u",
+ zlog_debug("Neigh %s (MAC %s) is now ACTIVE on L2-VNI %u with flags 0x%x",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)),
- zvni->vni);
+ zvni->vni, n->flags);
ZEBRA_NEIGH_SET_ACTIVE(n);
- return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, 0);
+ return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, n->flags);
}
static int zvni_remote_neigh_update(zebra_vni_t *zvni,
*/
static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)
{
+ uint8_t flags;
+ int ret = 0;
+
if (!is_l3vni_oper_up(zl3vni))
return -1;
if (!(n->flags & ZEBRA_NEIGH_REMOTE)
|| !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
return 0;
-
- return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac);
+#ifdef GNU_LINUX
+ flags = NTF_EXT_LEARNED;
+ if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
+ flags |= NTF_ROUTER;
+ ret = kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac, flags);
+#endif
+ return ret;
}
/*
memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
+ wctx.addr_width = 15;
wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
wctx.json = json;
+ hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
if (use_json) {
struct ipaddr *ip,
struct ethaddr *macaddr,
uint16_t state,
- uint8_t ext_learned)
+ uint8_t ext_learned,
+ uint8_t router_flag)
{
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
/* Is this about a local neighbor or a remote one? */
if (!ext_learned)
- return zvni_local_neigh_update(zvni, ifp, ip, macaddr);
+ return zvni_local_neigh_update(zvni, ifp, ip, macaddr,
+ router_flag);
return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state);
}
n->r_vtep_ip = vtep_ip;
SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+ /* Set router flag (R-bit) to this Neighbor entry */
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG))
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
/* Install the entry. */
zvni_neigh_install(zvni, n);
}
struct interface *link_if);
extern int zebra_vxlan_handle_kernel_neigh_update(
struct interface *ifp, struct interface *link_if, struct ipaddr *ip,
- struct ethaddr *macaddr, uint16_t state, uint8_t ext_learned);
+ struct ethaddr *macaddr, uint16_t state, uint8_t ext_learned,
+ uint8_t router_flag);
extern int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
struct interface *link_if,
struct ipaddr *ip);
#define ZEBRA_NEIGH_REMOTE 0x02
#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */
#define ZEBRA_NEIGH_DEF_GW 0x08
+#define ZEBRA_NEIGH_ROUTER_FLAG 0x10
enum zebra_neigh_state state;
void zserv_read_file(char *input)
{
int fd;
- struct zserv *client = NULL;
struct thread t;
fd = open(input, O_RDONLY | O_NONBLOCK);