From f81e127ed15b7500249be9b4294ce795ec2b08d6 Mon Sep 17 00:00:00 2001 From: Don Slice Date: Fri, 9 Sep 2016 06:35:47 -0700 Subject: [PATCH 001/136] bgpd: Display interface next-hop for "show ip bgp" with unnumbered Found that the logic had been changed to determine whether the next-hop is a v4 or v6 address. This caused an unnumbered interface to be seen as ipv4 instead of ipv6 so the swp port was not correctly displayed. Changed it back. Manual testing attaced to the ticket and bgp-min will be run before committing. Ticket: CM-12759 Signed-off-by: Don Slice Reviewed-by: CCR-5166 --- bgpd/bgp_route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f767fae1a9..af0159855d 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5922,7 +5922,7 @@ route_vty_out (struct vty *vty, struct prefix *p, vty_out(vty, "?"); } /* IPv4 Next Hop */ - else if (p->family == AF_INET || !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) + else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { if (json_paths) { From e544fa850992c19ec35a6cf7d49530a822b492e2 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 9 Sep 2016 14:42:22 -0400 Subject: [PATCH 002/136] debian: Update release information Signed-off-by: Donald Sharp --- configure.ac | 2 +- debian/changelog | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8fcafbb0f0..3fc22e9505 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.60) -AC_INIT(Quagga, 0.99.24+cl3u3, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.24+cl3u4, [https://bugzilla.quagga.net]) CONFIG_ARGS="$*" AC_SUBST(CONFIG_ARGS) AC_CONFIG_SRCDIR(lib/zebra.h) diff --git a/debian/changelog b/debian/changelog index 3114db3fd1..44974cecd4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +quagga (0.99.24+cl3u4) RELEASED; urgency=medium + + * Closes: CM-12687 - Buffer overflow in zebra RA code + + -- dev-support Wed, 31 Aug 2016 12:36:10 -0400 + quagga (0.99.24+cl3u3) RELEASED; urgency=medium * New Enabled: Merge up-to 0.99.24 code from upstream From ddb13fd374ee736c20198c1075c855efa7af74e9 Mon Sep 17 00:00:00 2001 From: Don Slice Date: Mon, 12 Sep 2016 06:32:11 -0700 Subject: [PATCH 003/136] lib: apply mask to prefix in prefix-list A crash occurred if a prefix was defined in a prefix-list that contained bits in the prefix but a /0 mask. Resolving that crash and improving usability by applying the mask to the supplied prefix and notifying the user if the prefix was modified. Ticket: CM-12744 Signed-off-by: Don Slice Reviewed_By: Testing Done: Manual testing attached to the ticket, bgp-min, bgp-smoke ospf-min, and ospf-smoke all completed before commit --- lib/plist.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/plist.c b/lib/plist.c index a1289801c4..87d46a1054 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -894,7 +894,7 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, struct prefix_list *plist; struct prefix_list_entry *pentry; struct prefix_list_entry *dup; - struct prefix p; + struct prefix p, p_tmp; int any = 0; int seqnum = -1; int lenum = 0; @@ -940,6 +940,11 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE); return CMD_WARNING; } + + /* make a copy to verify prefix matches mask length */ + prefix_copy (&p_tmp, &p); + apply_mask_ipv4 ((struct prefix_ipv4 *) &p_tmp); + break; case AFI_IP6: if (strncmp ("any", prefix, strlen (prefix)) == 0) @@ -957,9 +962,26 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE); return CMD_WARNING; } + + /* make a copy to verify prefix matches mask length */ + prefix_copy (&p_tmp, &p); + apply_mask_ipv6 ((struct prefix_ipv6 *) &p_tmp); + break; } + /* If prefix has bits not under the mask, adjust it to fit */ + if (!prefix_same (&p_tmp, &p)) + { + char buf[PREFIX2STR_BUFFER]; + char buf_tmp[PREFIX2STR_BUFFER]; + prefix2str(&p, buf, sizeof(buf)); + prefix2str(&p_tmp, buf_tmp, sizeof(buf_tmp)); + zlog_warn ("Prefix-list %s prefix changed from %s to %s to match length", + name, buf, buf_tmp); + p = p_tmp; + } + /* ge and le check. */ if (genum && (genum <= p.prefixlen)) return vty_invalid_prefix_range (vty, prefix); @@ -985,14 +1007,6 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, if (dup) { prefix_list_entry_free (pentry); - vty_out (vty, "%% Insertion failed - prefix-list entry exists:%s", - VTY_NEWLINE); - vty_out (vty, " seq %u %s %s", dup->seq, typestr, prefix); - if (! any && genum) - vty_out (vty, " ge %d", genum); - if (! any && lenum) - vty_out (vty, " le %d", lenum); - vty_out (vty, "%s", VTY_NEWLINE); return CMD_SUCCESS; } From 87aea55d3436b8cfaf80334c4a498baaef1f0284 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 9 Sep 2016 12:41:35 -0400 Subject: [PATCH 004/136] ospfd: Fix crash with usage of incorrect command Entering 'show ip ospf interface json' causes ospf to crash. Entering 'show ip ospf interface json' causes ospf to crash if intf has no neighbors on the otherside Modify the code to not crash in these cases. Ticket: CM-12776 Signed-off-by: Donald Sharp Reviewed-by: Daniel Walton --- ospfd/ospf_vty.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 16fab68c99..980d59d341 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3877,7 +3877,13 @@ show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf, struct interface { struct timeval result; unsigned long time_store = 0; - result = tv_sub (oi->t_hello->u.sands, recent_relative_time()); + if (oi->t_hello) + result = tv_sub (oi->t_hello->u.sands, recent_relative_time()); + else + { + result.tv_sec = 0; + result.tv_usec = 0; + } time_store = (1000 * result.tv_sec) + (result.tv_usec / 1000); json_object_int_add(json_interface_sub, "timerHelloInMsecs", time_store); } @@ -3939,20 +3945,29 @@ show_ip_ospf_interface_common (struct vty *vty, struct ospf *ospf, int argc, if (ospf_oi_count(ifp)) { show_ip_ospf_interface_sub (vty, ospf, ifp, json_interface_sub, use_json); + if (use_json) + json_object_object_add (json, ifp->name, json_interface_sub); } } } else if (argv[iface_argv] && strcmp(argv[iface_argv], "json") == 0) { + if (!use_json) + { + json = json_object_new_object(); + json_interface_sub = json_object_new_object (); + use_json = 1; + } /* Show All Interfaces. */ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { if (ospf_oi_count(ifp)) { show_ip_ospf_interface_sub (vty, ospf, ifp, json_interface_sub, use_json); - json_object_object_add(json, ifp->name, json_interface_sub); - } - } + if (use_json) + json_object_object_add(json, ifp->name, json_interface_sub); + } + } } else { From b6df4090322446a2ee32fc5e2d43d074d34a8f1f Mon Sep 17 00:00:00 2001 From: Don Slice Date: Fri, 16 Sep 2016 09:20:03 -0700 Subject: [PATCH 005/136] bgpd: resolve memory leaks in "show ip bgp neighbor json" Found several leaks in bgp_show_peer and bgp_show_peer_afi where json objects are created and then not attached to the parent, causing them to be leaked. If not attaching them, freeing the created objects. Manual testing performed successfully. Fix tested succesfully by the submitter and bgp-smoke completed with same failures as base. Ticket: CM-12846 Signed-off-by: Don Slice Reviewed-by: CCR-5181 --- bgpd/bgp_vty.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index f16cf53cbe..04bb81548a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -10971,8 +10971,6 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, { json_addr = json_object_new_object(); json_af = json_object_new_object(); - json_prefA = json_object_new_object(); - json_prefB = json_object_new_object(); filter = &p->filter[afi][safi]; if (peer_group_active(p)) @@ -10992,6 +10990,7 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) { json_object_int_add(json_af, "orfType", ORF_TYPE_PREFIX); + json_prefA = json_object_new_object(); bgp_show_peer_afi_orf_cap (vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV, PEER_CAP_ORF_PREFIX_RM_ADV, @@ -11006,6 +11005,7 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) { json_object_int_add(json_af, "orfOldType", ORF_TYPE_PREFIX_OLD); + json_prefB = json_object_new_object(); bgp_show_peer_afi_orf_cap (vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV, PEER_CAP_ORF_PREFIX_RM_ADV, @@ -11021,6 +11021,8 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV) || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) json_object_object_add(json_addr, "afDependentCap", json_af); + else + json_object_free(json_af); sprintf (orf_pfx_name, "%s.%d.%d", p->host, afi, safi); orf_pfx_count = prefix_bgp_show_prefix_list (NULL, afi, orf_pfx_name, use_json); @@ -11743,6 +11745,8 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV) || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_RCV)) json_object_object_add(json_add, print_store, json_sub); + else + json_object_free(json_sub); } json_object_object_add(json_cap, "addPath", json_add); @@ -11767,7 +11771,6 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js json_object *json_nxt = NULL; const char *print_store; - json_nxt = json_object_new_object(); if (CHECK_FLAG (p->cap, PEER_CAP_ENHE_ADV) && CHECK_FLAG (p->cap, PEER_CAP_ENHE_RCV)) json_object_string_add(json_cap, "extendedNexthop", "advertisedAndReceived"); @@ -11778,6 +11781,8 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js if (CHECK_FLAG (p->cap, PEER_CAP_ENHE_RCV)) { + json_nxt = json_object_new_object(); + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) { if (CHECK_FLAG (p->af_cap[AFI_IP][safi], PEER_CAP_ENHE_AF_RCV)) @@ -11875,7 +11880,10 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js } } if (! restart_af_count) - json_object_string_add(json_cap, "addressFamiliesByPeer", "none"); + { + json_object_string_add(json_cap, "addressFamiliesByPeer", "none"); + json_object_free(json_restart); + } else json_object_object_add(json_cap, "addressFamiliesByPeer", json_restart); } From 847e983fb53a66681d5da1f31df3d2af557eed2b Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Sep 2016 09:06:24 -0400 Subject: [PATCH 006/136] bgp: Fix incorrect cli The optional parameter was entered as {JSON} when it should have been {json} Signed-off-by: Donald Sharp --- bgpd/bgp_route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 67ea246c1e..5b0cf8e225 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -8036,7 +8036,7 @@ DEFUN (show_bgp_ipv4_prefix, DEFUN (show_bgp_ipv6_route, show_bgp_ipv6_route_cmd, - "show bgp ipv6 X:X::X:X {JSON}", + "show bgp ipv6 X:X::X:X {json}", SHOW_STR BGP_STR "Address family\n" From 50f342078f56cbb3fffb039fdf6ff90d689fe442 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 15 Apr 2016 10:13:31 -0700 Subject: [PATCH 007/136] Quagga: Basic definitions for MPLS Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-4804, ... Reviewed By: CCR-3083 Testing Done: Not relevant --- lib/mpls.h | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 lib/mpls.h diff --git a/lib/mpls.h b/lib/mpls.h new file mode 100644 index 0000000000..8889868970 --- /dev/null +++ b/lib/mpls.h @@ -0,0 +1,172 @@ +/* + * MPLS definitions + * Copyright 2015 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _QUAGGA_MPLS_H +#define _QUAGGA_MPLS_H + +/* Well-known MPLS label values (RFC 3032 etc). */ +#define MPLS_V4_EXP_NULL_LABEL 0 +#define MPLS_RA_LABEL 1 +#define MPLS_V6_EXP_NULL_LABEL 2 +#define MPLS_IMP_NULL_LABEL 3 +#define MPLS_ENTROPY_LABEL_INDICATOR 7 +#define MPLS_GAL_LABEL 13 +#define MPLS_OAM_ALERT_LABEL 14 +#define MPLS_EXTENSION_LABEL 15 + +/* Minimum and maximum label values */ +#define MPLS_MIN_RESERVED_LABEL 0 +#define MPLS_MAX_RESERVED_LABEL 15 +#define MPLS_MIN_UNRESERVED_LABEL 16 +#define MPLS_MAX_UNRESERVED_LABEL 1048575 + +#define IS_MPLS_RESERVED_LABEL(label) \ + (label >= MPLS_MIN_RESERVED_LABEL && label <= MPLS_MAX_RESERVED_LABEL) + +#define IS_MPLS_UNRESERVED_LABEL(label) \ + (label >= MPLS_MIN_UNRESERVED_LABEL && label <= MPLS_MAX_UNRESERVED_LABEL) + +/* Definitions for a MPLS label stack entry (RFC 3032). This encodes the + * label, EXP, BOS and TTL fields. + */ +typedef unsigned int mpls_lse_t; + +#define MPLS_LS_LABEL_MASK 0xFFFFF000 +#define MPLS_LS_LABEL_SHIFT 12 +#define MPLS_LS_EXP_MASK 0x00000E00 +#define MPLS_LS_EXP_SHIFT 9 +#define MPLS_LS_S_MASK 0x00000100 +#define MPLS_LS_S_SHIFT 8 +#define MPLS_LS_TTL_MASK 0x000000FF +#define MPLS_LS_TTL_SHIFT 0 + +#define MPLS_LABEL_VALUE(lse) \ + ((lse & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT) +#define MPLS_LABEL_EXP(lse) \ + ((lse & MPLS_LS_EXP_MASK) >> MPLS_LS_EXP_SHIFT) +#define MPLS_LABEL_BOS(lse) \ + ((lse & MPLS_LS_S_MASK) >> MPLS_LS_S_SHIFT) +#define MPLS_LABEL_TTL(lse) \ + ((lse & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT) + +#define IS_MPLS_LABEL_BOS(ls) (MPLS_LABEL_BOS(ls) == 1) + +#define MPLS_LABEL_LEN_BITS 20 + +/* MPLS label value as a 32-bit (mostly we only care about the label value). */ +typedef unsigned int mpls_label_t; + +#define MPLS_INVALID_LABEL 0xFFFFFFFF + +/* Functions for basic label operations. */ + +/* Encode a label stack entry from fields; convert to network byte-order as + * the Netlink interface expects MPLS labels to be in this format. + */ +static inline mpls_lse_t +mpls_lse_encode (mpls_label_t label, u_int32_t ttl, + u_int32_t exp, u_int32_t bos) +{ + mpls_lse_t lse; + lse = htonl ((label << MPLS_LS_LABEL_SHIFT) | + (exp << MPLS_LS_EXP_SHIFT) | + (bos ? (1 << MPLS_LS_S_SHIFT) : 0) | + (ttl << MPLS_LS_TTL_SHIFT)); + return lse; +} + +/* Extract the fields from a label stack entry after converting to host-byte + * order. This is expected to be called only for messages received over the + * Netlink interface. + */ +static inline void +mpls_lse_decode (mpls_lse_t lse, mpls_label_t *label, + u_int32_t *ttl, u_int32_t *exp, u_int32_t *bos) +{ + mpls_lse_t local_lse; + + local_lse = ntohl (lse); + *label = MPLS_LABEL_VALUE(local_lse); + *exp = MPLS_LABEL_EXP(local_lse); + *bos = MPLS_LABEL_BOS(local_lse); + *ttl = MPLS_LABEL_TTL(local_lse); +} + + +/* Printable string for labels (with consideration for reserved values). */ +static inline char * +label2str (mpls_label_t label, char *buf, int len) +{ + switch(label) { + case MPLS_V4_EXP_NULL_LABEL: + strncpy(buf, "IPv4 Explicit Null", len); + return(buf); + break; + case MPLS_RA_LABEL: + strncpy(buf, "Router Alert", len); + return(buf); + break; + case MPLS_V6_EXP_NULL_LABEL: + strncpy(buf, "IPv6 Explict Null", len); + return(buf); + break; + case MPLS_IMP_NULL_LABEL: + strncpy(buf, "implicit-null", len); + return(buf); + break; + case MPLS_ENTROPY_LABEL_INDICATOR: + strncpy(buf, "Entropy Label Indicator", len); + return(buf); + break; + case MPLS_GAL_LABEL: + strncpy(buf, "Generic Associated Channel", len); + return(buf); + break; + case MPLS_OAM_ALERT_LABEL: + strncpy(buf, "OAM Alert", len); + return(buf); + break; + case MPLS_EXTENSION_LABEL: + strncpy(buf, "Extension", len); + return(buf); + break; + case 4: + case 5: + case 6: + case 8: + case 9: + case 10: + case 11: + case 12: + strncpy(buf, "Reserved", len); + return(buf); + break; + default: + sprintf(buf, "%u", label); + return(buf); + } + + strncpy(buf, "Error", len); + return(buf); +} + +#endif From 54d48ea1537864dda453e5773f87be442ad2b7f7 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 15 Apr 2016 10:29:51 -0700 Subject: [PATCH 008/136] Quagga: Definitions for static LSPs Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-4804, ... Reviewed By: CCR-3084 Testing Done: Not relevant --- lib/nexthop.h | 9 +++ zebra/Makefile.am | 2 +- zebra/zebra_mpls.h | 155 +++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_vrf.h | 6 ++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 zebra/zebra_mpls.h diff --git a/lib/nexthop.h b/lib/nexthop.h index 39e8b5425f..801904306e 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -25,6 +25,7 @@ #define _LIB_NEXTHOP_H #include "prefix.h" +#include "mpls.h" /* Maximum next hop string length - gateway + ifindex */ #define NEXTHOP_STRLEN (INET6_ADDRSTRLEN + 30) @@ -44,6 +45,14 @@ enum nexthop_types_t NEXTHOP_TYPE_BLACKHOLE, /* Null0 nexthop. */ }; +/* Nexthop label structure. */ +struct nexthop_label +{ + u_int8_t num_labels; + u_int8_t reserved[3]; + mpls_label_t label[0]; /* 1 or more labels. */ +}; + /* Nexthop structure. */ struct nexthop { diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 32c94d3f0e..4b6f9188bf 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -46,7 +46,7 @@ noinst_HEADERS = \ interface.h ipforward.h irdp.h router-id.h kernel_socket.h \ rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h \ zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \ - zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h + zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h new file mode 100644 index 0000000000..b24a58e16a --- /dev/null +++ b/zebra/zebra_mpls.h @@ -0,0 +1,155 @@ +/* + * Zebra MPLS Data structures and definitions + * Copyright (C) 2015 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_MPLS_H +#define _ZEBRA_MPLS_H + +#include "prefix.h" +#include "table.h" +#include "queue.h" +#include "hash.h" +#include "jhash.h" +#include "nexthop.h" +#include "vty.h" +#include "memory.h" +#include "mpls.h" +#include "zebra/zserv.h" + + +/* Definitions and macros. */ + +#define NHLFE_FAMILY(nhlfe) \ + (((nhlfe)->nexthop->type == NEXTHOP_TYPE_IPV6 || \ + (nhlfe)->nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) ? AF_INET6 : AF_INET) + + +/* Typedefs */ + +typedef struct zebra_ile_t_ zebra_ile_t; +typedef struct zebra_snhlfe_t_ zebra_snhlfe_t; +typedef struct zebra_slsp_t_ zebra_slsp_t; +typedef struct zebra_nhlfe_t_ zebra_nhlfe_t; +typedef struct zebra_lsp_t_ zebra_lsp_t; + +/* LSP types. */ +enum lsp_types_t +{ + ZEBRA_LSP_INVALID = 0, /* Invalid. */ + ZEBRA_LSP_STATIC = 1, /* Static LSP. */ +}; + +/* + * (Outgoing) nexthop label forwarding entry configuration + */ +struct zebra_snhlfe_t_ +{ + /* Nexthop information */ + enum nexthop_types_t gtype; + union g_addr gate; + char *ifname; + ifindex_t ifindex; + + /* Out label. */ + mpls_label_t out_label; + + /* Backpointer to base entry. */ + zebra_slsp_t *slsp; + + /* Pointers to more outgoing information for same in-label */ + zebra_snhlfe_t *next; + zebra_snhlfe_t *prev; +}; + +/* + * (Outgoing) nexthop label forwarding entry + */ +struct zebra_nhlfe_t_ +{ + /* Type of entry - static etc. */ + enum lsp_types_t type; + + /* Nexthop information (with outgoing label) */ + struct nexthop *nexthop; + + /* Backpointer to base entry. */ + zebra_lsp_t *lsp; + + /* Runtime info - flags, pointers etc. */ + u_int32_t flags; +#define NHLFE_FLAG_CHANGED (1 << 0) +#define NHLFE_FLAG_SELECTED (1 << 1) +#define NHLFE_FLAG_MULTIPATH (1 << 2) +#define NHLFE_FLAG_DELETED (1 << 3) +#define NHLFE_FLAG_INSTALLED (1 << 4) + + zebra_nhlfe_t *next; + zebra_nhlfe_t *prev; + u_char distance; +}; + +/* + * Incoming label entry + */ +struct zebra_ile_t_ +{ + mpls_label_t in_label; +}; + +/* + * Label swap entry static configuration. + */ +struct zebra_slsp_t_ +{ + /* Incoming label */ + zebra_ile_t ile; + + /* List of outgoing nexthop static configuration */ + zebra_snhlfe_t *snhlfe_list; + +}; + +/* + * Label swap entry (ile -> list of nhlfes) + */ +struct zebra_lsp_t_ +{ + /* Incoming label */ + zebra_ile_t ile; + + /* List of NHLFE, pointer to best and num equal-cost. */ + zebra_nhlfe_t *nhlfe_list; + zebra_nhlfe_t *best_nhlfe; + u_int32_t num_ecmp; + + /* Flags */ + u_int32_t flags; +#define LSP_FLAG_SCHEDULED (1 << 0) +#define LSP_FLAG_INSTALLED (1 << 1) +#define LSP_FLAG_CHANGED (1 << 2) + + /* Address-family of NHLFE - saved here for delete. All NHLFEs */ + /* have to be of the same AF */ + u_char addr_family; +}; + + +#endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 456c6fdad8..1062c90988 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -74,6 +74,12 @@ struct zebra_vrf * Back pointer to the owning namespace. */ struct zebra_ns *zns; + + /* MPLS static LSP config table */ + struct hash *slsp_table; + + /* MPLS label forwarding table */ + struct hash *lsp_table; }; extern struct list *zvrf_list; From 7758e3f31396019344d495946ba2bdd22e2544e9 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 15 Apr 2016 10:51:56 -0700 Subject: [PATCH 009/136] Quagga: Static LSP configuration Add support for statically configuring MPLS transit LSPs. This allows the configuration of ILM to one or more NHLFE, as defined in RFC 3031. The currently supported nexthop types are IPv4 or IPv6. The two label operations supported are swap and PHP; the latter is configured by specifying the out-label as "implicit-null". Note that the operation is against the label, so it should be the same for all NHLFEs. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-4804, ... Reviewed By: CCR-3085 Testing Done: In combination with other patches --- lib/command.h | 2 + vtysh/vtysh_config.c | 4 +- zebra/Makefile.am | 4 +- zebra/debug.c | 36 +++ zebra/debug.h | 4 + zebra/main.c | 3 + zebra/zebra_mpls.c | 540 ++++++++++++++++++++++++++++++++++++++++ zebra/zebra_mpls.h | 56 +++++ zebra/zebra_mpls_null.c | 45 ++++ zebra/zebra_vrf.c | 3 + zebra/zebra_vty.c | 180 ++++++++++++++ 11 files changed, 874 insertions(+), 3 deletions(-) create mode 100644 zebra/zebra_mpls.c create mode 100644 zebra/zebra_mpls_null.c diff --git a/lib/command.h b/lib/command.h index 9ee4f2db3d..808e742059 100644 --- a/lib/command.h +++ b/lib/command.h @@ -114,6 +114,7 @@ enum node_type DUMP_NODE, /* Packet dump node. */ FORWARDING_NODE, /* IP forwarding node. */ PROTOCOL_NODE, /* protocol filtering node */ + MPLS_NODE, /* MPLS config node */ VTY_NODE, /* Vty node. */ LINK_PARAMS_NODE, /* Link-parameters node */ }; @@ -528,6 +529,7 @@ struct cmd_token #define LINK_PARAMS_STR "Configure interface link parameters\n" #define OSPF_RI_STR "OSPF Router Information specific commands\n" #define PCE_STR "PCE Router Information specific commands\n" +#define MPLS_STR "MPLS information\n" #define CONF_BACKUP_EXT ".sav" diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 760003eb3b..802074a533 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -256,6 +256,8 @@ vtysh_config_parse_line (const char *line) config = config_get (PROTOCOL_NODE, line); else if (strncmp (line, "ipv6 nht", strlen ("ipv6 nht")) == 0) config = config_get (PROTOCOL_NODE, line); + else if (strncmp (line, "mpls", strlen ("mpls")) == 0) + config = config_get (MPLS_NODE, line); else { if (strncmp (line, "log", strlen ("log")) == 0 @@ -300,7 +302,7 @@ vtysh_config_parse (char *line) || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE || \ (I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \ || (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \ - || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE) + || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE || (I) == MPLS_NODE) /* Display configuration to file pointer. */ void diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 4b6f9188bf..10f7850c96 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -32,13 +32,13 @@ zebra_SOURCES = \ redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ - zebra_ns.c zebra_vrf.c zebra_static.c + zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \ zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ - zebra_memory.c + zebra_memory.c zebra_mpls_null.c noinst_HEADERS = \ zebra_memory.h \ diff --git a/zebra/debug.c b/zebra/debug.c index cdf233879a..92354070ef 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -31,6 +31,7 @@ unsigned long zebra_debug_kernel; unsigned long zebra_debug_rib; unsigned long zebra_debug_fpm; unsigned long zebra_debug_nht; +unsigned long zebra_debug_mpls; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, @@ -82,6 +83,8 @@ DEFUN (show_debugging_zebra, vty_out (vty, " Zebra FPM debugging is on%s", VTY_NEWLINE); if (IS_ZEBRA_DEBUG_NHT) vty_out (vty, " Zebra next-hop tracking debugging is on%s", VTY_NEWLINE); + if (IS_ZEBRA_DEBUG_MPLS) + vty_out (vty, " Zebra MPLS debugging is on%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -108,6 +111,17 @@ DEFUN (debug_zebra_nht, return CMD_WARNING; } +DEFUN (debug_zebra_mpls, + debug_zebra_mpls_cmd, + "debug zebra mpls", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra MPLS LSPs\n") +{ + zebra_debug_mpls = ZEBRA_DEBUG_MPLS; + return CMD_WARNING; +} + DEFUN (debug_zebra_packet, debug_zebra_packet_cmd, "debug zebra packet", @@ -245,6 +259,18 @@ DEFUN (no_debug_zebra_nht, return CMD_SUCCESS; } +DEFUN (no_debug_zebra_mpls, + no_debug_zebra_mpls_cmd, + "no debug zebra mpls", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra MPLS LSPs\n") +{ + zebra_debug_mpls = 0; + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_packet, no_debug_zebra_packet_cmd, "no debug zebra packet", @@ -401,6 +427,11 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug zebra fpm%s", VTY_NEWLINE); write++; } + if (IS_ZEBRA_DEBUG_MPLS) + { + vty_out (vty, "debug zebra mpls%s", VTY_NEWLINE); + write++; + } return write; } @@ -412,6 +443,7 @@ zebra_debug_init (void) zebra_debug_kernel = 0; zebra_debug_rib = 0; zebra_debug_fpm = 0; + zebra_debug_mpls = 0; install_node (&debug_node, config_write_debug); @@ -420,6 +452,7 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &show_debugging_zebra_cmd); install_element (ENABLE_NODE, &debug_zebra_events_cmd); install_element (ENABLE_NODE, &debug_zebra_nht_cmd); + install_element (ENABLE_NODE, &debug_zebra_mpls_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_direct_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_detail_cmd); @@ -430,6 +463,7 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &debug_zebra_fpm_cmd); install_element (ENABLE_NODE, &no_debug_zebra_events_cmd); install_element (ENABLE_NODE, &no_debug_zebra_nht_cmd); + install_element (ENABLE_NODE, &no_debug_zebra_mpls_cmd); install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd); install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &no_debug_zebra_kernel_msgdump_cmd); @@ -439,6 +473,7 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_events_cmd); install_element (CONFIG_NODE, &debug_zebra_nht_cmd); + install_element (CONFIG_NODE, &debug_zebra_mpls_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_direct_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_detail_cmd); @@ -449,6 +484,7 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_fpm_cmd); install_element (CONFIG_NODE, &no_debug_zebra_events_cmd); install_element (CONFIG_NODE, &no_debug_zebra_nht_cmd); + install_element (CONFIG_NODE, &no_debug_zebra_mpls_cmd); install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd); install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &no_debug_zebra_kernel_msgdump_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index 4416068bf2..f8ebf3d616 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -41,6 +41,8 @@ #define ZEBRA_DEBUG_FPM 0x01 #define ZEBRA_DEBUG_NHT 0x01 +#define ZEBRA_DEBUG_MPLS 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -61,6 +63,7 @@ #define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM) #define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT) +#define IS_ZEBRA_DEBUG_MPLS (zebra_debug_mpls & ZEBRA_DEBUG_MPLS) extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; @@ -68,6 +71,7 @@ extern unsigned long zebra_debug_kernel; extern unsigned long zebra_debug_rib; extern unsigned long zebra_debug_fpm; extern unsigned long zebra_debug_nht; +extern unsigned long zebra_debug_mpls; extern void zebra_debug_init (void); diff --git a/zebra/main.c b/zebra/main.c index e67568140a..e06a17ca42 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -46,6 +46,7 @@ #include "zebra/zebra_ptm.h" #include "zebra/zebra_ns.h" #include "zebra/redistribute.h" +#include "zebra/zebra_mpls.h" #define ZEBRA_PTM_SUPPORT @@ -362,6 +363,8 @@ main (int argc, char **argv) zebra_ptm_init(); #endif + zebra_mpls_init (); + /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c new file mode 100644 index 0000000000..db1f318595 --- /dev/null +++ b/zebra/zebra_mpls.c @@ -0,0 +1,540 @@ +/* Zebra MPLS code + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "str.h" +#include "command.h" +#include "if.h" +#include "log.h" +#include "sockunion.h" +#include "linklist.h" +#include "thread.h" +#include "workqueue.h" +#include "prefix.h" +#include "routemap.h" +#include "stream.h" +#include "nexthop.h" + +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/debug.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_mpls.h" + +DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") +DEFINE_MTYPE_STATIC(ZEBRA, SLSP, "MPLS static LSP config") +DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE, "MPLS static nexthop object") +DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE_IFNAME, "MPLS static nexthop ifname") + +/* Default rtm_table for all clients */ +extern struct zebra_t zebrad; + +/* static function declarations */ +static unsigned int +label_hash (void *p); +static int +label_cmp (const void *p1, const void *p2); +static void +lsp_config_write (struct hash_backet *backet, void *ctxt); +static void * +slsp_alloc (void *p); +static int +snhlfe_match (zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); +static zebra_snhlfe_t * +snhlfe_find (zebra_slsp_t *slsp, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); +static zebra_snhlfe_t * +snhlfe_add (zebra_slsp_t *slsp, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex, + mpls_label_t out_label); +static int +snhlfe_del (zebra_snhlfe_t *snhlfe); +static int +snhlfe_del_all (zebra_slsp_t *slsp); +static char * +snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size); + + + + +/* Static functions */ + +/* + * Hash function for label. + */ +static unsigned int +label_hash (void *p) +{ + const zebra_ile_t *ile = p; + + return (jhash_1word(ile->in_label, 0)); +} + +/* + * Compare 2 LSP hash entries based on in-label. + */ +static int +label_cmp (const void *p1, const void *p2) +{ + const zebra_ile_t *ile1 = p1; + const zebra_ile_t *ile2 = p2; + + return (ile1->in_label == ile2->in_label); +} + +/* + * Write out static LSP configuration. + */ +static void +lsp_config_write (struct hash_backet *backet, void *ctxt) +{ + zebra_slsp_t *slsp; + zebra_snhlfe_t *snhlfe; + struct vty *vty = (struct vty *) ctxt; + char buf[INET6_ADDRSTRLEN]; + + slsp = (zebra_slsp_t *) backet->data; + if (!slsp) + return; + + for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) + { + char lstr[30]; + snhlfe2str (snhlfe, buf, BUFSIZ); + vty_out (vty, "mpls lsp %u %s %s%s", + slsp->ile.in_label, buf, + label2str(snhlfe->out_label, lstr, 30), VTY_NEWLINE); + } +} + +/* + * Callback to allocate static LSP. + */ +static void * +slsp_alloc (void *p) +{ + const zebra_ile_t *ile = p; + zebra_slsp_t *slsp; + + slsp = XCALLOC (MTYPE_SLSP, sizeof(zebra_slsp_t)); + slsp->ile = *ile; + return ((void *)slsp); +} + +/* + * Check if static NHLFE matches with search info passed. + */ +static int +snhlfe_match (zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + u_char cmp = -1; + + if (snhlfe->gtype != gtype) + return -1; + + switch (snhlfe->gtype) + { + case NEXTHOP_TYPE_IPV4: + cmp = memcmp(&(snhlfe->gate.ipv4), &(gate->ipv4), + sizeof(struct in_addr)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + cmp = memcmp(&(snhlfe->gate.ipv6), &(gate->ipv6), + sizeof(struct in6_addr)); + if (!cmp && snhlfe->gtype == NEXTHOP_TYPE_IPV6_IFINDEX) + cmp = !(snhlfe->ifindex == ifindex); + break; + default: + break; + } + + return cmp; +} + +/* + * Locate static NHLFE that matches with passed info. + */ +static zebra_snhlfe_t * +snhlfe_find (zebra_slsp_t *slsp, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + zebra_snhlfe_t *snhlfe; + + if (!slsp) + return NULL; + + for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) + { + if (!snhlfe_match (snhlfe, gtype, gate, ifname, ifindex)) + break; + } + + return snhlfe; +} + + +/* + * Add static NHLFE. Base LSP config entry must have been created + * and duplicate check done. + */ +static zebra_snhlfe_t * +snhlfe_add (zebra_slsp_t *slsp, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex, + mpls_label_t out_label) +{ + zebra_snhlfe_t *snhlfe; + + if (!slsp) + return NULL; + + snhlfe = XCALLOC(MTYPE_SNHLFE, sizeof(zebra_snhlfe_t)); + snhlfe->slsp = slsp; + snhlfe->out_label = out_label; + snhlfe->gtype = gtype; + switch (gtype) + { + case NEXTHOP_TYPE_IPV4: + snhlfe->gate.ipv4 = gate->ipv4; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + snhlfe->gate.ipv6 = gate->ipv6; + if (ifindex) + snhlfe->ifindex = ifindex; + break; + default: + XFREE (MTYPE_SNHLFE, snhlfe); + return NULL; + } + + if (slsp->snhlfe_list) + slsp->snhlfe_list->prev = snhlfe; + snhlfe->next = slsp->snhlfe_list; + slsp->snhlfe_list = snhlfe; + + return snhlfe; +} + +/* + * Delete static NHLFE. Entry must be present on list. + */ +static int +snhlfe_del (zebra_snhlfe_t *snhlfe) +{ + zebra_slsp_t *slsp; + + if (!snhlfe) + return -1; + + slsp = snhlfe->slsp; + if (!slsp) + return -1; + + if (snhlfe->next) + snhlfe->next->prev = snhlfe->prev; + if (snhlfe->prev) + snhlfe->prev->next = snhlfe->next; + else + slsp->snhlfe_list = snhlfe->next; + + snhlfe->prev = snhlfe->next = NULL; + if (snhlfe->ifname) + XFREE (MTYPE_SNHLFE_IFNAME, snhlfe->ifname); + XFREE (MTYPE_SNHLFE, snhlfe); + + return 0; +} + +/* + * Delete all static NHLFE entries for this LSP (in label). + */ +static int +snhlfe_del_all (zebra_slsp_t *slsp) +{ + zebra_snhlfe_t *snhlfe, *snhlfe_next; + + if (!slsp) + return -1; + + for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe_next) + { + snhlfe_next = snhlfe->next; + snhlfe_del (snhlfe); + } + + return 0; +} + +/* + * Create printable string for NHLFE configuration. + */ +static char * +snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size) +{ + buf[0] = '\0'; + switch (snhlfe->gtype) + { + case NEXTHOP_TYPE_IPV4: + inet_ntop (AF_INET, &snhlfe->gate.ipv4, buf, size); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + inet_ntop (AF_INET6, &snhlfe->gate.ipv6, buf, size); + if (snhlfe->ifindex) + strcat (buf, ifindex2ifname (snhlfe->ifindex)); + break; + default: + break; + } + + return buf; +} + + + +/* Public functions */ + +/* + * Check that the label values used in LSP creation are consistent. The + * main criteria is that if there is ECMP, the label operation must still + * be consistent - i.e., all paths either do a swap or do PHP. This is due + * to current HW restrictions. + */ +int +zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct hash *slsp_table; + zebra_ile_t tmp_ile; + zebra_slsp_t *slsp; + zebra_snhlfe_t *snhlfe; + + /* Lookup table. */ + slsp_table = zvrf->slsp_table; + if (!slsp_table) + return 0; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + slsp = hash_lookup (slsp_table, &tmp_ile); + if (!slsp) + return 1; + + snhlfe = snhlfe_find (slsp, gtype, gate, ifname, ifindex); + if (snhlfe) + { + if (snhlfe->out_label == out_label) + return 1; + + /* If not only NHLFE, cannot allow label change. */ + if (snhlfe != slsp->snhlfe_list || + snhlfe->next) + return 0; + } + else + { + /* If other NHLFEs exist, label operation must match. */ + if (slsp->snhlfe_list) + { + int cur_op, new_op; + + cur_op = (slsp->snhlfe_list->out_label == MPLS_IMP_NULL_LABEL); + new_op = (out_label == MPLS_IMP_NULL_LABEL); + if (cur_op != new_op) + return 0; + } + } + + /* Label values are good. */ + return 1; +} + + +/* + * Add static LSP entry. This may be the first entry for this incoming label + * or an additional nexthop; an existing entry may also have outgoing label + * changed. + * Note: The label operation (swap or PHP) is common for the LSP entry (all + * NHLFEs). + */ +int +zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct hash *slsp_table; + zebra_ile_t tmp_ile; + zebra_slsp_t *slsp; + zebra_snhlfe_t *snhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + slsp_table = zvrf->slsp_table; + if (!slsp_table) + return -1; + + /* If entry is present, exit. */ + tmp_ile.in_label = in_label; + slsp = hash_get (slsp_table, &tmp_ile, slsp_alloc); + if (!slsp) + return -1; + snhlfe = snhlfe_find (slsp, gtype, gate, ifname, ifindex); + if (snhlfe) + { + if (snhlfe->out_label == out_label) + /* No change */ + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + snhlfe2str (snhlfe, buf, BUFSIZ); + zlog_debug ("Upd static LSP in-label %u nexthop %s " + "out-label %u (old %u)", + in_label, buf, out_label, snhlfe->out_label); + } + snhlfe->out_label = out_label; + } + else + { + /* Add static LSP entry to this nexthop */ + snhlfe = snhlfe_add (slsp, gtype, gate, ifname, ifindex, out_label); + if (!snhlfe) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + { + snhlfe2str (snhlfe, buf, BUFSIZ); + zlog_debug ("Add static LSP in-label %u nexthop %s out-label %u", + in_label, buf, out_label); + } + } + + return 0; +} + +/* + * Delete static LSP entry. This may be the delete of one particular + * NHLFE for this incoming label or the delete of the entire entry (i.e., + * all NHLFEs). + * NOTE: Delete of the only NHLFE will also end up deleting the entire + * LSP configuration. + */ +int +zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + struct hash *slsp_table; + zebra_ile_t tmp_ile; + zebra_slsp_t *slsp; + zebra_snhlfe_t *snhlfe; + + /* Lookup table. */ + slsp_table = zvrf->slsp_table; + if (!slsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + slsp = hash_lookup (slsp_table, &tmp_ile); + if (!slsp) + return 0; + + /* Is it delete of entire LSP or a specific NHLFE? */ + if (gtype == NEXTHOP_TYPE_BLACKHOLE) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Del static LSP in-label %u", in_label); + + /* Delete all static NHLFEs */ + snhlfe_del_all (slsp); + } + else + { + /* Find specific NHLFE, exit if not found. */ + snhlfe = snhlfe_find (slsp, gtype, gate, ifname, ifindex); + if (!snhlfe) + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + char buf[BUFSIZ]; + snhlfe2str (snhlfe, buf, BUFSIZ); + zlog_debug ("Del static LSP in-label %u nexthop %s", + in_label, buf); + } + + /* Delete static LSP NHLFE */ + snhlfe_del (snhlfe); + } + + /* Remove entire static LSP entry if no NHLFE - valid in either case above. */ + if (!slsp->snhlfe_list) + { + slsp = hash_release(slsp_table, &tmp_ile); + if (slsp) + XFREE(MTYPE_SLSP, slsp); + } + + return 0; +} + +/* + * Display MPLS LSP configuration of all static LSPs (VTY command handler). + */ +int +zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + hash_iterate(zvrf->slsp_table, lsp_config_write, vty); + return (zvrf->slsp_table->count ? 1 : 0); +} + +/* + * Allocate MPLS tables for this VRF and do other initialization. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_mpls_init_tables (struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + zvrf->slsp_table = hash_create(label_hash, label_cmp); +} + +/* + * Global MPLS initialization. + */ +void +zebra_mpls_init (void) +{ + /* Filler for subsequent use. */ +} diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index b24a58e16a..b62230ead6 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -152,4 +152,60 @@ struct zebra_lsp_t_ }; +/* Function declarations. */ + +/* + * Check that the label values used in LSP creation are consistent. The + * main criteria is that if there is ECMP, the label operation must still + * be consistent - i.e., all paths either do a swap or do PHP. This is due + * to current HW restrictions. + */ +int +zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); + +/* + * Add static LSP entry. This may be the first entry for this incoming label + * or an additional nexthop; an existing entry may also have outgoing label + * changed. + * Note: The label operation (swap or PHP) is common for the LSP entry (all + * NHLFEs). + */ +int +zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); + +/* + * Delete static LSP entry. This may be the delete of one particular + * NHLFE for this incoming label or the delete of the entire entry (i.e., + * all NHLFEs). + * NOTE: Delete of the only NHLFE will also end up deleting the entire + * LSP configuration. + */ +int +zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex); + +/* + * Display MPLS LSP configuration of all static LSPs (VTY command handler). + */ +int +zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf); + +/* + * Allocate MPLS tables for this VRF. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_mpls_init_tables (struct zebra_vrf *zvrf); + +/* + * Global MPLS initialization. + */ +void +zebra_mpls_init (void); + #endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c new file mode 100644 index 0000000000..a8c9aa3322 --- /dev/null +++ b/zebra/zebra_mpls_null.c @@ -0,0 +1,45 @@ +#include +#include "nexthop.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_mpls.h" + +int +zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + return 1; +} + +int +zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + return 0; +} + +int +zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + return 0; +} + +int +zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + return 0; +} + +void +zebra_mpls_init_tables (struct zebra_vrf *zvrf) +{ +} + +void +zebra_mpls_init (void) +{ +} diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 890d749ca4..ab825281e4 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -32,6 +32,7 @@ #include "zebra/router-id.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_static.h" +#include "zebra/zebra_mpls.h" extern struct zebra_t zebrad; struct list *zvrf_list; @@ -332,6 +333,8 @@ zebra_vrf_alloc (vrf_id_t vrf_id, const char *name) zvrf->name[strlen(name)] = '\0'; } + zebra_mpls_init_tables (zvrf); + return zvrf; } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index a7ee63d87f..4e0df372bb 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -30,11 +30,13 @@ #include "rib.h" #include "nexthop.h" #include "vrf.h" +#include "mpls.h" #include "lib/json.h" #include "routemap.h" #include "zebra/zserv.h" #include "zebra/zebra_vrf.h" +#include "zebra/zebra_mpls.h" #include "zebra/zebra_rnh.h" #include "zebra/redistribute.h" #include "zebra/zebra_routemap.h" @@ -1910,6 +1912,160 @@ DEFUN (no_ip_route_mask_flags_tag_distance2_vrf, return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); } +static int +zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, + const char *gate_str, const char *outlabel_str, + const char *flag_str) +{ + struct zebra_vrf *zvrf; + int ret; + enum nexthop_types_t gtype; + union g_addr gate; + mpls_label_t label; + mpls_label_t in_label, out_label; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + { + vty_out (vty, "%% Default VRF does not exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!inlabel_str) + { + vty_out (vty, "%% No Label Information%s", VTY_NEWLINE); + return CMD_WARNING; + } + + out_label = MPLS_IMP_NULL_LABEL; /* as initialization */ + label = atoi(inlabel_str); + if (!IS_MPLS_UNRESERVED_LABEL(label)) + { + vty_out (vty, "%% Invalid label%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (add_cmd) + { + if (!gate_str) + { + vty_out (vty, "%% No Nexthop Information%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!outlabel_str) + { + vty_out (vty, "%% No Outgoing label Information%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + in_label = label; + gtype = NEXTHOP_TYPE_BLACKHOLE; /* as initialization */ + + if (gate_str) + { + /* Gateway is a IPv4 or IPv6 nexthop. */ + ret = inet_pton (AF_INET6, gate_str, &gate.ipv6); + if (ret) + gtype = NEXTHOP_TYPE_IPV6; + else + { + ret = inet_pton (AF_INET, gate_str, &gate.ipv4); + if (ret) + gtype = NEXTHOP_TYPE_IPV4; + else + { + vty_out (vty, "%% Invalid nexthop%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + } + + if (outlabel_str) + { + if (outlabel_str[0] == 'i') + out_label = MPLS_IMP_NULL_LABEL; + else + out_label = atoi(outlabel_str); + } + + if (add_cmd) + { + /* Check that label value is consistent. */ + if (!zebra_mpls_lsp_label_consistent (zvrf, in_label, out_label, gtype, + &gate, NULL, 0)) + { + vty_out (vty, "%% Label value not consistent%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = zebra_mpls_static_lsp_add (zvrf, in_label, out_label, gtype, + &gate, NULL, 0); + } + else + ret = zebra_mpls_static_lsp_del (zvrf, in_label, gtype, &gate, NULL, 0); + + if (ret) + { + vty_out (vty, "%% LSP cannot be %s%s", + add_cmd ? "added" : "deleted", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (mpls_transit_lsp, + mpls_transit_lsp_cmd, + "mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n" + "IPv4 gateway address\n" + "IPv6 gateway address\n" + "Outgoing MPLS label\n" + "Use Implicit-Null label\n") +{ + return zebra_mpls_transit_lsp (vty, 1, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (no_mpls_transit_lsp, + no_mpls_transit_lsp_cmd, + "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X)", + NO_STR + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n" + "IPv4 gateway address\n" + "IPv6 gateway address\n") +{ + return zebra_mpls_transit_lsp (vty, 0, argv[0], argv[1], NULL, NULL); +} + +ALIAS (no_mpls_transit_lsp, + no_mpls_transit_lsp_out_label_cmd, + "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", + NO_STR + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n" + "IPv4 gateway address\n" + "IPv6 gateway address\n" + "Outgoing MPLS label\n" + "Use Implicit-Null label\n") + +DEFUN (no_mpls_transit_lsp_all, + no_mpls_transit_lsp_all_cmd, + "no mpls lsp <16-1048575>", + NO_STR + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n") +{ + return zebra_mpls_transit_lsp (vty, 0, argv[0], NULL, NULL, NULL); +} + /* New RIB. Detailed information for IPv4 route. */ static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) @@ -5738,6 +5894,21 @@ zebra_ip_config (struct vty *vty) return write; } +/* MPLS LSP configuration write function. */ +static int +zebra_mpls_config (struct vty *vty) +{ + int write = 0; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + write += zebra_mpls_write_lsp_config(vty, zvrf); + return write; +} + DEFUN (ip_zebra_import_table_distance, ip_zebra_import_table_distance_cmd, "ip import-table <1-252> distance <1-255>", @@ -5905,12 +6076,16 @@ config_write_protocol (struct vty *vty) static struct cmd_node ip_node = { IP_NODE, "", 1 }; static struct cmd_node protocol_node = { PROTOCOL_NODE, "", 1 }; +/* MPLS node for MPLS LSP. */ +static struct cmd_node mpls_node = { MPLS_NODE, "", 1 }; + /* Route VTY. */ void zebra_vty_init (void) { install_node (&ip_node, zebra_ip_config); install_node (&protocol_node, config_write_protocol); + install_node (&mpls_node, zebra_mpls_config); install_element (CONFIG_NODE, &allow_external_route_update_cmd); install_element (CONFIG_NODE, &no_allow_external_route_update_cmd); @@ -6238,4 +6413,9 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd); install_element (ENABLE_NODE, &show_ipv6_mroute_vrf_all_cmd); #endif /* HAVE_IPV6 */ + + install_element (CONFIG_NODE, &mpls_transit_lsp_cmd); + install_element (CONFIG_NODE, &no_mpls_transit_lsp_cmd); + install_element (CONFIG_NODE, &no_mpls_transit_lsp_out_label_cmd); + install_element (CONFIG_NODE, &no_mpls_transit_lsp_all_cmd); } From 24a7c906dc8a108732188240b117565acbb8fc92 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 15 Apr 2016 13:32:23 -0700 Subject: [PATCH 010/136] Quagga: Fix alignment in netlink messages in some cases The alignment of nlmsg_len is calculated wrong leading to wrong rta_len calculations for nested TLVs when the data length of the last TLV added to the nested TLV is not aligned to RTA_ALIGNTO already. Use same fix that was implemented in iproute2 by Thomas Graf circa 2005. A reference to the fix is at http://oss.sgi.com/archives/netdev/2005-03/msg03103.html. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-6491 Reviewed By: CCR-3087 Testing Done: MPLS testing with other patches in SE-1 Note: Prior to MPLS, we didn't face this problem as we haven't really had any nested TLVs; even if RTA_MULTIPATH were to be considered a nested TLV, it didn't have any non-aligned fields. --- zebra/rt_netlink.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a6984f7f2f..41ecb1d0c4 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1647,14 +1647,14 @@ addattr_l (struct nlmsghdr *n, unsigned int maxlen, int type, void *data, int al len = RTA_LENGTH (alen); - if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) + if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) return -1; rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; memcpy (RTA_DATA (rta), data, alen); - n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); return 0; } @@ -1667,14 +1667,14 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) len = RTA_LENGTH (alen); - if ((int)RTA_ALIGN (rta->rta_len) + len > maxlen) + if (RTA_ALIGN (rta->rta_len) + RTA_ALIGN (len) > maxlen) return -1; subrta = (struct rtattr *) (((char *) rta) + RTA_ALIGN (rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; memcpy (RTA_DATA (subrta), data, alen); - rta->rta_len = NLMSG_ALIGN (rta->rta_len) + len; + rta->rta_len = NLMSG_ALIGN (rta->rta_len) + RTA_ALIGN (len); return 0; } @@ -1684,21 +1684,7 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) int addattr32 (struct nlmsghdr *n, unsigned int maxlen, int type, int data) { - int len; - struct rtattr *rta; - - len = RTA_LENGTH (4); - - if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) - return -1; - - rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len)); - rta->rta_type = type; - rta->rta_len = len; - memcpy (RTA_DATA (rta), &data, 4); - n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; - - return 0; + return addattr_l(n, maxlen, type, &data, sizeof(u_int32_t)); } static int From 40c7bdb0c9ba746d1f1bdfe1cb4d03aa5f407661 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 15 Apr 2016 19:19:37 -0700 Subject: [PATCH 011/136] Quagga: Install label forwarding entries for statically configured LSPs Install the statically configured LSPs into the FIB (kernel). This is done using the new attributes and definitions for MPLS in the kernel - RTA_VIA, RTA_NEWDST and AF_MPLS. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-4804 Reviewed By: CCR-3088 Testing Done: Manual in SE-1 --- lib/nexthop.c | 28 +- lib/nexthop.h | 6 + zebra/connected.c | 55 +++ zebra/kernel_null.c | 7 + zebra/rib.h | 3 + zebra/rt.h | 6 + zebra/rt_netlink.c | 483 +++++++++++++++++++-- zebra/zebra_mpls.c | 916 +++++++++++++++++++++++++++++++++++++++- zebra/zebra_mpls.h | 46 ++ zebra/zebra_mpls_null.c | 10 + zebra/zebra_rib.c | 17 + zebra/zserv.h | 3 + 12 files changed, 1553 insertions(+), 27 deletions(-) diff --git a/lib/nexthop.c b/lib/nexthop.c index 427f77f87a..465cc94851 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -32,8 +32,10 @@ #include "thread.h" #include "prefix.h" #include "nexthop.h" +#include "mpls.h" -DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") +DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") +DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") /* check if nexthops are same, non-recursive */ int @@ -138,6 +140,7 @@ copy_nexthops (struct nexthop **tnh, struct nexthop *nh) void nexthop_free (struct nexthop *nexthop) { + nexthop_del_labels (nexthop); if (nexthop->resolved) nexthops_free(nexthop->resolved); XFREE (MTYPE_NEXTHOP, nexthop); @@ -156,6 +159,29 @@ nexthops_free (struct nexthop *nexthop) } } +/* Update nexthop with label information. */ +void +nexthop_add_labels (struct nexthop *nexthop, u_int8_t num_labels, + mpls_label_t *label) +{ + struct nexthop_label *nh_label; + int i; + + nh_label = XCALLOC (MTYPE_NH_LABEL, sizeof (struct nexthop_label)); + nh_label->num_labels = num_labels; + for (i = 0; i < num_labels; i++) + nh_label->label[i] = *(label + i); + nexthop->nh_label = nh_label; +} + +/* Free label information of nexthop, if present. */ +void +nexthop_del_labels (struct nexthop *nexthop) +{ + if (nexthop->nh_label) + XFREE (MTYPE_NH_LABEL, nexthop->nh_label); +} + const char * nexthop2str (struct nexthop *nexthop, char *str, int size) { diff --git a/lib/nexthop.h b/lib/nexthop.h index 801904306e..c06dfe0e25 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -84,6 +84,9 @@ struct nexthop * obtained by recursive resolution will be added to `resolved'. * Only one level of recursive resolution is currently supported. */ struct nexthop *resolved; + + /* Label(s) associated with this nexthop. */ + struct nexthop_label *nh_label; }; extern int zebra_rnh_ip_default_route; @@ -106,6 +109,9 @@ void copy_nexthops (struct nexthop **tnh, struct nexthop *nh); void nexthop_free (struct nexthop *nexthop); void nexthops_free (struct nexthop *nexthop); +void nexthop_add_labels (struct nexthop *, u_int8_t, mpls_label_t *); +void nexthop_del_labels (struct nexthop *); + extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern int nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2); diff --git a/zebra/connected.c b/zebra/connected.c index 290973a5cb..9737f8deba 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -38,6 +38,7 @@ #include "zebra/interface.h" #include "zebra/connected.h" #include "zebra/rtadv.h" +#include "zebra/zebra_mpls.h" /* communicate the withdrawal of a connected address */ static void @@ -213,6 +214,15 @@ connected_up_ipv4 (struct interface *ifp, struct connected *ifc) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling RIB processing", ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } /* Add connected IPv4 route to the interface. */ @@ -333,6 +343,15 @@ connected_down_ipv4 (struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } /* Delete connected IPv4 route to the interface. */ @@ -359,6 +378,15 @@ connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } void @@ -389,6 +417,15 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } /* Add connected IPv6 route to the interface. */ @@ -479,6 +516,15 @@ connected_down_ipv6 (struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } void @@ -504,6 +550,15 @@ connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } int diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index 17b3c7bc8d..0802570e60 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -30,6 +30,7 @@ #include "zebra/connected.h" #include "zebra/rt_netlink.h" #include "zebra/rib.h" +#include "zebra/zebra_mpls.h" int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } int kernel_update_ipv4 (struct prefix *a, struct rib *b) { return 0; } @@ -65,3 +66,9 @@ int kernel_neigh_update (int a, int b, uint32_t c, char *d, int e) void kernel_init (struct zebra_ns *zns) { return; } void kernel_terminate (struct zebra_ns *zns) { return; } void route_read (struct zebra_ns *zns) { return; } + +int kernel_add_lsp (zebra_lsp_t *l) { return 0; } + +int kernel_del_lsp (zebra_lsp_t *l) { return 0; } + +int kernel_upd_lsp (zebra_lsp_t *l) { return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 9867323e6e..0f7f70ada7 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -30,6 +30,7 @@ #include "nexthop.h" #include "vrf.h" #include "if.h" +#include "mpls.h" #define DISTANCE_INFINITY 255 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */ @@ -373,6 +374,8 @@ extern struct route_table *rib_table_ipv6; extern int rib_gc_dest (struct route_node *rn); extern struct route_table *rib_tables_iter_next (rib_tables_iter_t *iter); +extern u_char route_distance(int type); + /* * Inline functions. */ diff --git a/zebra/rt.h b/zebra/rt.h index 46e71fa46e..1137f880f0 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -26,6 +26,8 @@ #include "prefix.h" #include "if.h" #include "zebra/rib.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_mpls.h" extern int kernel_add_ipv4 (struct prefix *, struct rib *); extern int kernel_update_ipv4 (struct prefix *, struct rib *); @@ -39,4 +41,8 @@ extern int kernel_add_ipv6 (struct prefix *, struct rib *); extern int kernel_update_ipv6 (struct prefix *, struct rib *); extern int kernel_delete_ipv6 (struct prefix *, struct rib *); +extern int kernel_add_lsp (zebra_lsp_t *); +extern int kernel_upd_lsp (zebra_lsp_t *); +extern int kernel_del_lsp (zebra_lsp_t *); + #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 41ecb1d0c4..8d19cbb5dc 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -40,6 +40,7 @@ #include "privs.h" #include "nexthop.h" #include "vrf.h" +#include "mpls.h" #include "zebra/zserv.h" #include "zebra/zebra_ns.h" @@ -50,6 +51,7 @@ #include "zebra/debug.h" #include "zebra/rtadv.h" #include "zebra/zebra_ptm.h" +#include "zebra/zebra_mpls.h" #include "rt_netlink.h" @@ -69,6 +71,27 @@ static const struct message nlmsg_str[] = { {0, NULL} }; +/* TODO - Temporary definitions, need to refine. */ +#ifndef AF_MPLS +#define AF_MPLS 28 +#endif + +#ifndef RTA_VIA +#define RTA_VIA 16 +#endif + +#ifndef RTA_NEWDST +#define RTA_NEWDST 19 +#endif +/* End of temporary definitions */ + +struct gw_family_t +{ + u_int16_t filler; + u_int16_t family; + union g_addr gate; +}; + extern struct zebra_privs_t zserv_privs; extern u_int32_t nl_rcvbufsize; @@ -439,10 +462,10 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *, /* OK we got netlink message. */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u", + zlog_debug ("netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u", nl->name, lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, - h->nlmsg_seq, h->nlmsg_pid); + h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid); /* skip unsolicited messages originating from command socket * linux sets the originators port-id for {NEW|DEL}ADDR messages, @@ -961,6 +984,11 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h, if (rtm->rtm_src_len != 0) return 0; + /* We don't care about change notifications for the MPLS table. */ + /* TODO: Revisit this. */ + if (rtm->rtm_family == AF_MPLS) + return 0; + /* Table corresponding to route. */ if (tb[RTA_TABLE]) table = *(int *) RTA_DATA (tb[RTA_TABLE]); @@ -1161,6 +1189,11 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; } + /* We don't care about change notifications for the MPLS table. */ + /* TODO: Revisit this. */ + if (rtm->rtm_family == AF_MPLS) + return 0; + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg)); if (len < 0) return -1; @@ -1723,10 +1756,10 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns) n->nlmsg_flags |= NLM_F_ACK; if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_talk: %s type %s(%u), seq=%u flags 0x%x", + zlog_debug ("netlink_talk: %s type %s(%u), len=%d seq=%u flags 0x%x", nl->name, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, - n->nlmsg_seq, n->nlmsg_flags); + n->nlmsg_len, n->nlmsg_seq, n->nlmsg_flags); /* Send message to netlink interface. */ if (zserv_privs.change (ZPRIVS_RAISE)) @@ -1757,6 +1790,60 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns) return netlink_parse_info (netlink_talk_filter, nl, zns, 0); } +static void +_netlink_route_nl_add_gateway_info (u_char route_family, u_char gw_family, + struct nlmsghdr *nlmsg, + size_t req_size, int bytelen, + struct nexthop *nexthop) +{ + if (route_family == AF_MPLS) + { + struct gw_family_t gw_fam; + + gw_fam.family = gw_family; + if (gw_family == AF_INET) + memcpy (&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen); + else + memcpy (&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen); + addattr_l (nlmsg, req_size, RTA_VIA, &gw_fam.family, bytelen+2); + } + else + { + if (gw_family == AF_INET) + addattr_l (nlmsg, req_size, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); + else + addattr_l (nlmsg, req_size, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); + } +} + +static void +_netlink_route_rta_add_gateway_info (u_char route_family, u_char gw_family, + struct rtattr *rta, struct rtnexthop *rtnh, + size_t req_size, int bytelen, + struct nexthop *nexthop) +{ + if (route_family == AF_MPLS) + { + struct gw_family_t gw_fam; + + gw_fam.family = gw_family; + if (gw_family == AF_INET) + memcpy (&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen); + else + memcpy (&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen); + rta_addattr_l (rta, req_size, RTA_VIA, &gw_fam.family, bytelen+2); + rtnh->rtnh_len += RTA_LENGTH (bytelen + 2); + } + else + { + if (gw_family == AF_INET) + rta_addattr_l (rta, req_size, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); + else + rta_addattr_l (rta, req_size, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + } +} + /* This function takes a nexthop as argument and adds * the appropriate netlink attributes to an existing * netlink message. @@ -1778,6 +1865,8 @@ _netlink_route_build_singlepath( size_t req_size, int cmd) { + mpls_lse_t out_lse; + char label_buf[100]; if (rtmsg->rtm_family == AF_INET && (nexthop->type == NEXTHOP_TYPE_IPV6 @@ -1805,14 +1894,28 @@ _netlink_route_build_singlepath( return; } + label_buf[0] = '\0'; + if (rtmsg->rtm_family == AF_MPLS) + { + assert (nexthop->nh_label); + + /* Fill out label, if present. */ + if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + { + out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1); + addattr_l (nlmsg, req_size, RTA_NEWDST, &out_lse, sizeof(mpls_lse_t)); + sprintf (label_buf, "label %d", nexthop->nh_label->label[0]); + } + } + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtmsg->rtm_flags |= RTNH_F_ONLINK; if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - addattr_l (nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); + _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET, nlmsg, + req_size, bytelen, nexthop); if (cmd == RTM_NEWROUTE) { @@ -1826,16 +1929,16 @@ _netlink_route_build_singlepath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); + label_buf, nexthop->ifindex); } if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - addattr_l (nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); + _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET6, nlmsg, + req_size, bytelen, nexthop); if (cmd == RTM_NEWROUTE) { @@ -1849,10 +1952,10 @@ _netlink_route_build_singlepath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); + label_buf, nexthop->ifindex); } if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) @@ -1920,6 +2023,9 @@ _netlink_route_build_multipath( struct rtmsg *rtmsg, union g_addr **src) { + mpls_lse_t out_lse; + char label_buf[100]; + rtnh->rtnh_len = sizeof (*rtnh); rtnh->rtnh_flags = 0; rtnh->rtnh_hops = 0; @@ -1952,6 +2058,21 @@ _netlink_route_build_multipath( return; } + label_buf[0] = '\0'; + if (rtmsg->rtm_family == AF_MPLS) + { + assert (nexthop->nh_label); + + /* Fill out label, if present. */ + if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + { + out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1); + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_NEWDST, + &out_lse, sizeof(mpls_lse_t)); + rtnh->rtnh_len += RTA_LENGTH (sizeof(mpls_lse_t)); + sprintf (label_buf, "label %d", nexthop->nh_label->label[0]); + } + } if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtnh->rtnh_flags |= RTNH_F_ONLINK; @@ -1959,10 +2080,8 @@ _netlink_route_build_multipath( if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; - + _netlink_route_rta_add_gateway_info (rtmsg->rtm_family, AF_INET, rta, + rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop); if (nexthop->rmap_src.ipv4.s_addr) *src = &nexthop->rmap_src; else if (nexthop->src.ipv4.s_addr) @@ -1970,17 +2089,16 @@ _netlink_route_build_multipath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); + label_buf, nexthop->ifindex); } if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + _netlink_route_rta_add_gateway_info (rtmsg->rtm_family, AF_INET6, rta, + rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop); if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) *src = &nexthop->rmap_src; @@ -1989,10 +2107,10 @@ _netlink_route_build_multipath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); + label_buf, nexthop->ifindex); } /* ifindex */ if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX @@ -2023,6 +2141,44 @@ _netlink_route_build_multipath( } } +static inline void +_netlink_mpls_build_singlepath( + const char *routedesc, + zebra_nhlfe_t *nhlfe, + struct nlmsghdr *nlmsg, + struct rtmsg *rtmsg, + size_t req_size, + int cmd) +{ + int bytelen; + u_char family; + + family = NHLFE_FAMILY (nhlfe); + bytelen = (family == AF_INET ? 4 : 16); + _netlink_route_build_singlepath(routedesc, bytelen, nhlfe->nexthop, + nlmsg, rtmsg, req_size, cmd); +} + + +static inline void +_netlink_mpls_build_multipath( + const char *routedesc, + zebra_nhlfe_t *nhlfe, + struct rtattr *rta, + struct rtnexthop *rtnh, + struct rtmsg *rtmsg, + union g_addr **src) +{ + int bytelen; + u_char family; + + family = NHLFE_FAMILY (nhlfe); + bytelen = (family == AF_INET ? 4 : 16); + _netlink_route_build_multipath(routedesc, bytelen, nhlfe->nexthop, + rta, rtnh, rtmsg, src); +} + + /* Log debug information for netlink_route_multipath * if debug logging is enabled. * @@ -2048,11 +2204,22 @@ _netlink_route_debug( zlog_debug ("netlink_route_multipath() (%s): %s %s vrf %u type %s", routedesc, lookup (nlmsg_str, cmd), - prefix2str (p, buf, sizeof(buf)), - zvrf->vrf_id, nexthop_type_to_str (nexthop->type)); + prefix2str (p, buf, sizeof(buf)), zvrf->vrf_id, + (nexthop) ? nexthop_type_to_str (nexthop->type) : "UNK"); } } +static void +_netlink_mpls_debug( + int cmd, + u_int32_t label, + const char *routedesc) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("netlink_mpls_multipath() (%s): %s %u/20", + routedesc, lookup (nlmsg_str, cmd), label); +} + static int netlink_neigh_update (int cmd, int ifindex, uint32_t addr, char *lla, int llalen) { @@ -2493,6 +2660,272 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen) lla, llalen); } +/* + * MPLS label forwarding table change via netlink interface. + */ +static int +netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp) +{ + mpls_lse_t lse; + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop = NULL; + int nexthop_num; + const char *routedesc; + struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT); + + struct + { + struct nlmsghdr n; + struct rtmsg r; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE); + + + /* + * Count # nexthops so we can decide whether to use singlepath + * or multipath case. + */ + nexthop_num = 0; + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + if (cmd == RTM_NEWROUTE) + { + /* Count all selected NHLFEs */ + if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + nexthop_num++; + } + else /* DEL */ + { + /* Count all installed NHLFEs */ + if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + nexthop_num++; + } + } + + if (nexthop_num == 0) // unexpected + return 0; + + req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); + req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.r.rtm_family = AF_MPLS; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_dst_len = MPLS_LABEL_LEN_BITS; + req.r.rtm_protocol = RTPROT_ZEBRA; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + + if (cmd == RTM_NEWROUTE) + /* We do a replace to handle update. */ + req.n.nlmsg_flags |= NLM_F_REPLACE; + + /* Fill destination */ + lse = mpls_lse_encode (lsp->ile.in_label, 0, 0, 1); + addattr_l (&req.n, sizeof req, RTA_DST, &lse, sizeof(mpls_lse_t)); + + /* Fill nexthops (paths) based on single-path or multipath. The paths + * chosen depend on the operation. + */ + if (nexthop_num == 1 || MULTIPATH_NUM == 1) + { + routedesc = "single hop"; + _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc); + + nexthop_num = 0; + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if ((cmd == RTM_NEWROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) || + (cmd == RTM_DELROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))) + { + /* Add the gateway */ + _netlink_mpls_build_singlepath(routedesc, nhlfe, + &req.n, &req.r, sizeof req, cmd); + if (cmd == RTM_NEWROUTE) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + nexthop_num++; + break; + } + } + } + else /* Multipath case */ + { + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *) buf; + struct rtnexthop *rtnh; + union g_addr *src1 = NULL; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH (0); + rtnh = RTA_DATA (rta); + + routedesc = "multihop"; + _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc); + + nexthop_num = 0; + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM) + break; + + if ((cmd == RTM_NEWROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) || + (cmd == RTM_DELROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))) + { + nexthop_num++; + + /* Build the multipath */ + _netlink_mpls_build_multipath(routedesc, nhlfe, rta, + rtnh, &req.r, &src1); + rtnh = RTNH_NEXT (rtnh); + + if (cmd == RTM_NEWROUTE) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + + } + } + + /* Add the multipath */ + if (rta->rta_len > RTA_LENGTH (0)) + addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta), + RTA_PAYLOAD (rta)); + } + + /* Talk to netlink socket. */ + return netlink_talk (&req.n, &zns->netlink_cmd, zns); +} + +/* + * Handle failure in LSP install, clear flags for NHLFE. + */ +static inline void +clear_nhlfe_installed (zebra_lsp_t *lsp) +{ + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop; + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } +} + +/* + * Install Label Forwarding entry into the kernel. + */ +int +kernel_add_lsp (zebra_lsp_t *lsp) +{ + int ret; + + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); + if (!ret) + SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + else + clear_nhlfe_installed (lsp); + + return ret; +} + +/* + * Update Label Forwarding entry in the kernel. This means that the Label + * forwarding entry is already installed and needs an update - either a new + * path is to be added, an installed path has changed (e.g., outgoing label) + * or an installed path (but not all paths) has to be removed. + * TODO: Performs a DEL followed by ADD now, need to change to REPLACE. Note + * that REPLACE was originally implemented for IPv4 nexthops but removed as + * it was not functioning when moving from swap to PHP as that was signaled + * through the metric field (before kernel-MPLS). This shouldn't be an issue + * any longer, so REPLACE can be reintroduced. + */ +int +kernel_upd_lsp (zebra_lsp_t *lsp) +{ + int ret; + + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + + /* First issue a DEL and clear the installed flag. */ + netlink_mpls_multipath (RTM_DELROUTE, lsp); + UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + + /* Then issue an ADD. */ + ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); + if (!ret) + SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + else + clear_nhlfe_installed (lsp); + + return ret; +} + +/* + * Delete Label Forwarding entry from the kernel. + */ +int +kernel_del_lsp (zebra_lsp_t *lsp) +{ + if (!lsp) // unexpected + return -1; + + if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) + { + netlink_mpls_multipath (RTM_DELROUTE, lsp); + UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + } + + return 0; +} + extern struct thread_master *master; /* Kernel route reflection. */ diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index db1f318595..892aec965a 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -48,6 +48,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") DEFINE_MTYPE_STATIC(ZEBRA, SLSP, "MPLS static LSP config") +DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object") DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE, "MPLS static nexthop object") DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE_IFNAME, "MPLS static nexthop ifname") @@ -59,6 +60,53 @@ static unsigned int label_hash (void *p); static int label_cmp (const void *p1, const void *p2); +static int +nhlfe_nexthop_active_ipv4 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop); +static int +nhlfe_nexthop_active_ipv6 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop); +static int +nhlfe_nexthop_active (zebra_nhlfe_t *nhlfe); +static void +lsp_select_best_nhlfe (zebra_lsp_t *lsp); +static void +lsp_uninstall_from_kernel (struct hash_backet *backet, void *ctxt); +static void +lsp_schedule (struct hash_backet *backet, void *ctxt); +static wq_item_status +lsp_process (struct work_queue *wq, void *data); +static void +lsp_processq_del (struct work_queue *wq, void *data); +static void +lsp_processq_complete (struct work_queue *wq); +static int +lsp_processq_add (zebra_lsp_t *lsp); +static void * +lsp_alloc (void *p); +static char * +nhlfe2str (zebra_nhlfe_t *nhlfe, char *buf, int size); +static int +nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); +static zebra_nhlfe_t * +nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex); +static zebra_nhlfe_t * +nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex, mpls_label_t out_label); +static int +nhlfe_del (zebra_nhlfe_t *snhlfe); +static int +static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); +static int +static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex); +static int +static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label); static void lsp_config_write (struct hash_backet *backet, void *ctxt); static void * @@ -79,6 +127,8 @@ static int snhlfe_del_all (zebra_slsp_t *slsp); static char * snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size); +static void +mpls_processq_init (struct zebra_t *zebra); @@ -108,6 +158,810 @@ label_cmp (const void *p1, const void *p2) return (ile1->in_label == ile2->in_label); } +/* + * Check if an IPv4 nexthop for a NHLFE is active. Update nexthop based on + * the passed flag. + * NOTE: Looking only for connected routes right now. + */ +static int +nhlfe_nexthop_active_ipv4 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop) +{ + struct route_table *table; + struct prefix_ipv4 p; + struct route_node *rn; + struct rib *match; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); + if (!table) + return 0; + + /* Lookup nexthop in IPv4 routing table. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = nexthop->gate.ipv4; + + rn = route_node_match (table, (struct prefix *) &p); + if (!rn) + return 0; + + route_unlock_node (rn); + + /* Locate a valid connected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if ((match->type == ZEBRA_ROUTE_CONNECT) && + !CHECK_FLAG (match->status, RIB_ENTRY_REMOVED) && + CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + if (!match || !match->nexthop) + return 0; + + nexthop->ifindex = match->nexthop->ifindex; + return 1; +} + + +/* + * Check if an IPv6 nexthop for a NHLFE is active. Update nexthop based on + * the passed flag. + * NOTE: Looking only for connected routes right now. + */ +static int +nhlfe_nexthop_active_ipv6 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop) +{ + struct route_table *table; + struct prefix_ipv6 p; + struct route_node *rn; + struct rib *match; + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, VRF_DEFAULT); + if (!table) + return 0; + + /* Lookup nexthop in IPv6 routing table. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.prefix = nexthop->gate.ipv6; + + rn = route_node_match (table, (struct prefix *) &p); + if (!rn) + return 0; + + route_unlock_node (rn); + + /* Locate a valid connected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if ((match->type == ZEBRA_ROUTE_CONNECT) && + !CHECK_FLAG (match->status, RIB_ENTRY_REMOVED) && + CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + if (!match || !match->nexthop) + return 0; + + nexthop->ifindex = match->nexthop->ifindex; + return 1; +} + + +/* + * Check the nexthop reachability for a NHLFE and return if valid (reachable) + * or not. + * NOTE: Each NHLFE points to only 1 nexthop. + */ +static int +nhlfe_nexthop_active (zebra_nhlfe_t *nhlfe) +{ + struct nexthop *nexthop; + struct interface *ifp; + + nexthop = nhlfe->nexthop; + if (!nexthop) // unexpected + return 0; + + /* Check on nexthop based on type. */ + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + if (nhlfe_nexthop_active_ipv4 (nhlfe, nexthop)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + + case NEXTHOP_TYPE_IPV6: + if (nhlfe_nexthop_active_ipv6 (nhlfe, nexthop)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6)) + { + ifp = if_lookup_by_index (nexthop->ifindex); + if (ifp && if_is_operative(ifp)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + else + { + if (nhlfe_nexthop_active_ipv6 (nhlfe, nexthop)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + + default: + break; + } + + return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); +} + +/* + * Walk through NHLFEs for a LSP forwarding entry, verify nexthop + * reachability and select the best. Multipath entries are also + * marked. This is invoked when an LSP scheduled for processing (due + * to some change) is examined. + */ +static void +lsp_select_best_nhlfe (zebra_lsp_t *lsp) +{ + zebra_nhlfe_t *nhlfe; + zebra_nhlfe_t *best; + struct nexthop *nexthop; + int changed = 0; + + if (!lsp) + return; + + best = NULL; + lsp->num_ecmp = 0; + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + + /* + * First compute the best path, after checking nexthop status. We are only + * concerned with non-deleted NHLFEs. + */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + /* Clear selection flags. */ + UNSET_FLAG (nhlfe->flags, + (NHLFE_FLAG_SELECTED | NHLFE_FLAG_MULTIPATH)); + + if (!CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED) && + nhlfe_nexthop_active (nhlfe)) + { + if (!best || (nhlfe->distance < best->distance)) + best = nhlfe; + } + } + + lsp->best_nhlfe = best; + if (!lsp->best_nhlfe) + return; + + /* Mark best NHLFE as selected. */ + SET_FLAG (lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); + + /* + * If best path exists, see if there is ECMP. While doing this, note if a + * new (uninstalled) NHLFE has been selected, an installed entry that is + * still selected has a change or an installed entry is to be removed. + */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + int nh_chg, nh_sel, nh_inst; + + nexthop = nhlfe->nexthop; + if (!nexthop) // unexpected + continue; + + if (!CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) && + (nhlfe->distance == lsp->best_nhlfe->distance)) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_MULTIPATH); + lsp->num_ecmp++; + } + + if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED) && + !changed) + { + nh_chg = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + nh_sel = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED); + nh_inst = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + + if ((nh_sel && !nh_inst) || + (nh_sel && nh_inst && nh_chg) || + (nh_inst && !nh_sel)) + changed = 1; + } + + /* We have finished examining, clear changed flag. */ + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + } + + if (changed) + SET_FLAG (lsp->flags, LSP_FLAG_CHANGED); +} + +/* + * Delete LSP forwarding entry from kernel, if installed. Called upon + * process exit. + */ +static void +lsp_uninstall_from_kernel (struct hash_backet *backet, void *ctxt) +{ + zebra_lsp_t *lsp; + + lsp = (zebra_lsp_t *) backet->data; + if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) + kernel_del_lsp (lsp); +} + +/* + * Schedule LSP forwarding entry for processing. Called upon changes + * that may impact LSPs such as nexthop / connected route changes. + */ +static void +lsp_schedule (struct hash_backet *backet, void *ctxt) +{ + zebra_lsp_t *lsp; + + lsp = (zebra_lsp_t *) backet->data; + lsp_processq_add (lsp); +} + +/* + * Process a LSP entry that is in the queue. Recalculate best NHLFE and + * any multipaths and update or delete from the kernel, as needed. + */ +static wq_item_status +lsp_process (struct work_queue *wq, void *data) +{ + zebra_lsp_t *lsp; + zebra_nhlfe_t *oldbest, *newbest; + char buf[BUFSIZ], buf2[BUFSIZ]; + + lsp = (zebra_lsp_t *)data; + if (!lsp) // unexpected + return WQ_SUCCESS; + + oldbest = lsp->best_nhlfe; + + /* Select best NHLFE(s) */ + lsp_select_best_nhlfe (lsp); + + newbest = lsp->best_nhlfe; + + if (IS_ZEBRA_DEBUG_MPLS) + { + if (oldbest) + nhlfe2str (oldbest, buf, BUFSIZ); + if (newbest) + nhlfe2str (newbest, buf2, BUFSIZ); + zlog_debug ("Process LSP in-label %u oldbest %s newbest %s " + "flags 0x%x ecmp# %d", + lsp->ile.in_label, oldbest ? buf : "NULL", + newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp); + } + + if (!CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) + { + /* Not already installed */ + if (newbest) + kernel_add_lsp (lsp); + } + else + { + /* Installed, may need an update and/or delete. */ + if (!newbest) + kernel_del_lsp (lsp); + else if (CHECK_FLAG (lsp->flags, LSP_FLAG_CHANGED)) + kernel_upd_lsp (lsp); + } + + return WQ_SUCCESS; +} + + +/* + * Callback upon processing completion of a LSP forwarding entry. + */ +static void +lsp_processq_del (struct work_queue *wq, void *data) +{ + struct zebra_vrf *zvrf; + zebra_lsp_t *lsp; + struct hash *lsp_table; + zebra_nhlfe_t *nhlfe, *nhlfe_next; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + assert (zvrf); + + lsp_table = zvrf->lsp_table; + if (!lsp_table) // unexpected + return; + + lsp = (zebra_lsp_t *)data; + if (!lsp) // unexpected + return; + + /* Clear flag, remove any NHLFEs marked for deletion. If no NHLFEs exist, + * delete LSP entry also. + */ + UNSET_FLAG (lsp->flags, LSP_FLAG_SCHEDULED); + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) + { + nhlfe_next = nhlfe->next; + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + nhlfe_del (nhlfe); + } + + if (!lsp->nhlfe_list) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } +} + +/* + * Callback upon finishing the processing of all scheduled + * LSP forwarding entries. + */ +static void +lsp_processq_complete (struct work_queue *wq) +{ + /* Nothing to do for now. */ +} + +/* + * Add LSP forwarding entry to queue for subsequent processing. + */ +static int +lsp_processq_add (zebra_lsp_t *lsp) +{ + /* If already scheduled, exit. */ + if (CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + return 0; + + work_queue_add (zebrad.lsp_process_q, lsp); + SET_FLAG (lsp->flags, LSP_FLAG_SCHEDULED); + return 0; +} + +/* + * Callback to allocate LSP forwarding table entry. + */ +static void * +lsp_alloc (void *p) +{ + const zebra_ile_t *ile = p; + zebra_lsp_t *lsp; + + lsp = XCALLOC (MTYPE_LSP, sizeof(zebra_lsp_t)); + lsp->ile = *ile; + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Alloc LSP in-label %u", lsp->ile.in_label); + + return ((void *)lsp); +} + +/* + * Create printable string for NHLFE entry. + */ +static char * +nhlfe2str (zebra_nhlfe_t *nhlfe, char *buf, int size) +{ + struct nexthop *nexthop; + + buf[0] = '\0'; + nexthop = nhlfe->nexthop; + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + inet_ntop (AF_INET, &nexthop->gate.ipv4, buf, size); + break; + case NEXTHOP_TYPE_IPV6: + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, size); + break; + default: + break; + } + + return buf; +} + +/* + * Check if NHLFE matches with search info passed. + */ +static int +nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct nexthop *nhop; + u_char cmp = -1; + + nhop = nhlfe->nexthop; + if (!nhop) + return -1; + + if (nhop->type != gtype) + return -1; + + switch (nhop->type) + { + case NEXTHOP_TYPE_IPV4: + cmp = memcmp(&(nhop->gate.ipv4), &(gate->ipv4), + sizeof(struct in_addr)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + cmp = memcmp(&(nhop->gate.ipv6), &(gate->ipv6), + sizeof(struct in6_addr)); + if (!cmp && nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + cmp = !(nhop->ifindex == ifindex); + break; + default: + break; + } + + return cmp; +} + + +/* + * Locate NHLFE that matches with passed info. + */ +static zebra_nhlfe_t * +nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + zebra_nhlfe_t *nhlfe; + + if (!lsp) + return NULL; + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + if (nhlfe->type != lsp_type) + continue; + if (!nhlfe_nhop_match (nhlfe, gtype, gate, ifname, ifindex)) + break; + } + + return nhlfe; +} + +/* + * Add NHLFE. Base entry must have been created and duplicate + * check done. + */ +static zebra_nhlfe_t * +nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex, mpls_label_t out_label) +{ + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop; + + if (!lsp) + return NULL; + + nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t)); + if (!nhlfe) + return NULL; + + nhlfe->lsp = lsp; + nhlfe->type = lsp_type; + nhlfe->distance = lsp_distance (lsp_type); + + nexthop = nexthop_new(); + if (!nexthop) + { + XFREE (MTYPE_NHLFE, nhlfe); + return NULL; + } + nexthop_add_labels (nexthop, 1, &out_label); + + nexthop->type = gtype; + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + nexthop->gate.ipv4 = gate->ipv4; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + nexthop->gate.ipv6 = gate->ipv6; + if (ifindex) + nexthop->ifindex = ifindex; + break; + default: + nexthop_free(nexthop); + XFREE (MTYPE_NHLFE, nhlfe); + return NULL; + break; + } + + nhlfe->nexthop = nexthop; + if (lsp->nhlfe_list) + lsp->nhlfe_list->prev = nhlfe; + nhlfe->next = lsp->nhlfe_list; + lsp->nhlfe_list = nhlfe; + + return nhlfe; +} + +/* + * Delete NHLFE. Entry must be present on list. + */ +static int +nhlfe_del (zebra_nhlfe_t *nhlfe) +{ + zebra_lsp_t *lsp; + + if (!nhlfe) + return -1; + + lsp = nhlfe->lsp; + if (!lsp) + return -1; + + /* Free nexthop. */ + if (nhlfe->nexthop) + nexthop_free(nhlfe->nexthop); + + /* Unlink from LSP */ + if (nhlfe->next) + nhlfe->next->prev = nhlfe->prev; + if (nhlfe->prev) + nhlfe->prev->next = nhlfe->next; + else + lsp->nhlfe_list = nhlfe->next; + + XFREE (MTYPE_NHLFE, nhlfe); + + return 0; +} + + +/* + * Install/update a static NHLFE for an LSP in the forwarding table. This may + * be a new LSP entry or a new NHLFE for an existing in-label or an update of + * the out-label for an existing NHLFE (update case). + */ +static int +static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex); + if (nhlfe) + { + struct nexthop *nh = nhlfe->nexthop; + + assert (nh); + assert (nh->nh_label); + + /* Clear deleted flag (in case it was set) */ + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (nh->nh_label->label[0] == out_label) + /* No change */ + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("LSP in-label %u type %d nexthop %s " + "out-label changed to %u (old %u)", + in_label, ZEBRA_LSP_STATIC, buf, + out_label, nh->nh_label->label[0]); + } + + /* Update out label, trigger processing. */ + nh->nh_label->label[0] = out_label; + } + else + { + /* Add LSP entry to this nexthop */ + nhlfe = nhlfe_add (lsp, ZEBRA_LSP_STATIC, gtype, gate, + ifname, ifindex, out_label); + if (!nhlfe) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Add LSP in-label %u type %d nexthop %s " + "out-label %u", + in_label, ZEBRA_LSP_STATIC, buf, + out_label); + } + + lsp->addr_family = NHLFE_FAMILY (nhlfe); + } + + /* Mark NHLFE, queue LSP for processing. */ + SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + if (lsp_processq_add (lsp)) + return -1; + + return 0; +} + +/* + * Uninstall a particular static NHLFE in the forwarding table. If this is + * the only NHLFE, the entire LSP forwarding entry has to be deleted. + */ +static int +static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp) + return 0; + nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex); + if (!nhlfe) + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", + in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags); + } + + /* Mark NHLFE for delete or directly delete, as appropriate. */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (lsp_processq_add (lsp)) + return -1; + } + else + { + nhlfe_del (nhlfe); + + /* Free LSP entry if no other NHLFEs and not scheduled. */ + if (!lsp->nhlfe_list && + !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } + } + return 0; +} + +/* + * Uninstall all static NHLFEs for a particular LSP forwarding entry. + * If no other NHLFEs exist, the entry would be deleted. + */ +static int +static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe, *nhlfe_next; + int schedule_lsp = 0; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp || !lsp->nhlfe_list) + return 0; + + /* Mark NHLFEs for delete or directly delete, as appropriate. */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) + { + nhlfe_next = nhlfe->next; + + /* Skip non-static NHLFEs */ + if (nhlfe->type != ZEBRA_LSP_STATIC) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", + in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags); + } + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + schedule_lsp = 1; + } + else + { + nhlfe_del (nhlfe); + } + } + + /* Queue LSP for processing, if needed, else delete. */ + if (schedule_lsp) + { + if (lsp_processq_add (lsp)) + return -1; + } + else if (!lsp->nhlfe_list && + !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } + + return 0; +} + /* * Write out static LSP configuration. */ @@ -318,6 +1172,27 @@ snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size) return buf; } +/* + * Initialize work queue for processing changed LSPs. + */ +static void +mpls_processq_init (struct zebra_t *zebra) +{ + zebra->lsp_process_q = work_queue_new (zebra->master, "LSP processing"); + if (!zebra->lsp_process_q) + { + zlog_err ("%s: could not initialise work queue!", __func__); + return; + } + + zebra->lsp_process_q->spec.workfunc = &lsp_process; + zebra->lsp_process_q->spec.del_item_data = &lsp_processq_del; + zebra->lsp_process_q->spec.errorfunc = NULL; + zebra->lsp_process_q->spec.completion_func = &lsp_processq_complete; + zebra->lsp_process_q->spec.max_retries = 0; + zebra->lsp_process_q->spec.hold = 10; +} + /* Public functions */ @@ -438,6 +1313,11 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, } } + /* (Re)Install LSP in the main table. */ + if (static_lsp_install (zvrf, in_label, out_label, gtype, + gate, ifname, ifindex)) + return -1; + return 0; } @@ -475,6 +1355,9 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("Del static LSP in-label %u", in_label); + /* Uninstall entire LSP from the main table. */ + static_lsp_uninstall_all (zvrf, in_label); + /* Delete all static NHLFEs */ snhlfe_del_all (slsp); } @@ -493,6 +1376,10 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, in_label, buf); } + /* Uninstall LSP from the main table. */ + static_lsp_uninstall (zvrf, in_label, gtype, + gate, ifname, ifindex); + /* Delete static LSP NHLFE */ snhlfe_del (snhlfe); } @@ -508,6 +1395,19 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, return 0; } +/* + * Schedule all MPLS label forwarding entries for processing. + * Called upon changes that may affect one or more of them such as + * interface or nexthop state changes. + */ +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + hash_iterate(zvrf->lsp_table, lsp_schedule, NULL); +} + /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). */ @@ -518,6 +1418,19 @@ zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) return (zvrf->slsp_table->count ? 1 : 0); } +/* + * Called upon process exiting, need to delete LSP forwarding + * entries from the kernel. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); +} + /* * Allocate MPLS tables for this VRF and do other initialization. * NOTE: Currently supported only for default VRF. @@ -528,6 +1441,7 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) if (!zvrf) return; zvrf->slsp_table = hash_create(label_hash, label_cmp); + zvrf->lsp_table = hash_create(label_hash, label_cmp); } /* @@ -536,5 +1450,5 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) void zebra_mpls_init (void) { - /* Filler for subsequent use. */ + mpls_processq_init (&zebrad); } diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index b62230ead6..5fb481781a 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -189,12 +189,28 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, enum nexthop_types_t gtype, union g_addr *gate, char *ifname, ifindex_t ifindex); +/* + * Schedule all MPLS label forwarding entries for processing. + * Called upon changes that may affect one or more of them such as + * interface or nexthop state changes. + */ +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf); + /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). */ int zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf); +/* + * Called upon process exiting, need to delete LSP forwarding + * entries from the kernel. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf); + /* * Allocate MPLS tables for this VRF. * NOTE: Currently supported only for default VRF. @@ -208,4 +224,34 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf); void zebra_mpls_init (void); +/* Inline functions. */ + +/* + * Distance (priority) definition for LSP NHLFE. + */ +static inline u_char +lsp_distance (enum lsp_types_t type) +{ + if (type == ZEBRA_LSP_STATIC) + return (route_distance (ZEBRA_ROUTE_STATIC)); + + return 150; +} + +/* + * Map RIB type to LSP type. Used when labeled-routes from BGP + * are converted into LSPs. + */ +static inline enum lsp_types_t +lsp_type_from_rib_type (int rib_type) +{ + switch (rib_type) + { + case ZEBRA_ROUTE_STATIC: + return ZEBRA_LSP_STATIC; + default: + return ZEBRA_LSP_INVALID; + } +} + #endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index a8c9aa3322..15169f0f25 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -28,12 +28,22 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, return 0; } +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) +{ +} + int zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) { return 0; } +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf) +{ +} + void zebra_mpls_init_tables (struct zebra_vrf *zvrf) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e238f8e8eb..910610fc60 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -37,6 +37,7 @@ #include "routemap.h" #include "nexthop.h" #include "vrf.h" +#include "mpls.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -117,6 +118,19 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int prior #define rnode_info(node, ...) \ _rnode_zlog(__func__, vrf_id, node, LOG_INFO, __VA_ARGS__) +u_char +route_distance (int type) +{ + u_char distance; + + if ((unsigned)type >= array_size(route_info)) + distance = 150; + else + distance = route_info[type].distance; + + return distance; +} + int is_zebra_valid_kernel_table(u_int32_t table_id) { @@ -3123,6 +3137,9 @@ rib_close (void) if (zvrf->other_table[AFI_IP6][table_id]) rib_close_table (zvrf->other_table[AFI_IP6][table_id]); } + + zebra_mpls_close_tables(zvrf); + } /* Routing information base initialize. */ diff --git a/zebra/zserv.h b/zebra/zserv.h index 3667f5b2b6..ce243dd6ac 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -130,6 +130,9 @@ struct zebra_t /* rib work queue */ struct work_queue *ribq; struct meta_queue *mq; + + /* LSP work queue */ + struct work_queue *lsp_process_q; }; extern struct zebra_t zebrad; From 3ab18ff268c297e7279d71f8f82fc8ce7712c23e Mon Sep 17 00:00:00 2001 From: vivek Date: Sat, 16 Apr 2016 11:23:04 -0700 Subject: [PATCH 012/136] Quagga: Display MPLS label forwarding table Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-4084, ... Reviewed By: CCR-3089 Testing Done: Manual --- zebra/zebra_mpls.c | 112 ++++++++++++++++++++++++++++++++++++++++ zebra/zebra_mpls.h | 26 ++++++++++ zebra/zebra_mpls_null.c | 10 ++++ zebra/zebra_vty.c | 36 +++++++++++++ 4 files changed, 184 insertions(+) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 892aec965a..688585d6da 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -108,6 +108,12 @@ static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, static int static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label); static void +nhlfe_print (zebra_nhlfe_t *nhlfe, struct vty *vty); +static void +lsp_print (zebra_lsp_t *lsp, void *ctxt); +static void +lsp_print_hash (struct hash_backet *backet, void *ctxt); +static void lsp_config_write (struct hash_backet *backet, void *ctxt); static void * slsp_alloc (void *p); @@ -962,6 +968,78 @@ static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) return 0; } +/* + * Print the NHLFE for a LSP forwarding entry. + */ +static void +nhlfe_print (zebra_nhlfe_t *nhlfe, struct vty *vty) +{ + struct nexthop *nexthop; + char buf[BUFSIZ]; + + nexthop = nhlfe->nexthop; + if (!nexthop || !nexthop->nh_label) // unexpected + return; + + vty_out(vty, " type: %s remote label: %s distance: %d%s", + nhlfe_type2str(nhlfe->type), + label2str(nexthop->nh_label->label[0], buf, BUFSIZ), + nhlfe->distance, VTY_NEWLINE); + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out (vty, " via %s", + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + if (nexthop->ifindex) + vty_out (vty, " dev %s", ifindex2ifname (nexthop->ifindex)); + break; + default: + break; + } + vty_out(vty, "%s", CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) ? + " (installed)" : ""); + vty_out(vty, "%s", VTY_NEWLINE); +} + +/* + * Print an LSP forwarding entry. + */ +static void +lsp_print (zebra_lsp_t *lsp, void *ctxt) +{ + zebra_nhlfe_t *nhlfe; + struct vty *vty; + + vty = (struct vty *) ctxt; + + vty_out(vty, "Local label: %u%s%s", + lsp->ile.in_label, + CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)" : "", + VTY_NEWLINE); + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + nhlfe_print (nhlfe, vty); +} + +/* + * Print an LSP forwarding hash entry. + */ +static void +lsp_print_hash (struct hash_backet *backet, void *ctxt) +{ + zebra_lsp_t *lsp; + + lsp = (zebra_lsp_t *) backet->data; + if (!lsp) + return; + + lsp_print (lsp, ctxt); +} + /* * Write out static LSP configuration. */ @@ -1408,6 +1486,40 @@ zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) hash_iterate(zvrf->lsp_table, lsp_schedule, NULL); } +/* + * Display MPLS label forwarding table for a specific LSP + * (VTY command handler). + */ +void +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label) +{ + struct hash *lsp_table; + zebra_lsp_t *lsp; + zebra_ile_t tmp_ile; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return; + + /* If entry is not present, exit. */ + tmp_ile.in_label = label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp) + return; + + lsp_print (lsp, (void *)vty); +} + +/* + * Display MPLS label forwarding table (VTY command handler). + */ +void +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf) +{ + hash_iterate(zvrf->lsp_table, lsp_print_hash, vty); +} + /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). */ diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 5fb481781a..7d1f494142 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -197,6 +197,19 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, void zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf); +/* + * Display MPLS label forwarding table for a specific LSP + * (VTY command handler). + */ +void +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label); + +/* + * Display MPLS label forwarding table (VTY command handler). + */ +void +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf); + /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). */ @@ -254,4 +267,17 @@ lsp_type_from_rib_type (int rib_type) } } +/* NHLFE type as printable string. */ +static inline const char * +nhlfe_type2str(enum lsp_types_t lsp_type) +{ + switch (lsp_type) + { + case ZEBRA_LSP_STATIC: + return "Static"; + default: + return "Unknown"; + } +} + #endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 15169f0f25..03b1f5694b 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -33,6 +33,16 @@ zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) { } +void +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label) +{ +} + +void +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf) +{ +} + int zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 4e0df372bb..e5f7c181f9 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -5909,6 +5909,37 @@ zebra_mpls_config (struct vty *vty) return write; } +DEFUN (show_mpls_table, + show_mpls_table_cmd, + "show mpls table", + SHOW_STR + MPLS_STR + "MPLS table\n") +{ + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + zebra_mpls_print_lsp_table(vty, zvrf); + return CMD_SUCCESS; +} + +DEFUN (show_mpls_table_lsp, + show_mpls_table_lsp_cmd, + "show mpls table <16-1048575>", + SHOW_STR + MPLS_STR + "MPLS table\n" + "LSP to display information about\n") +{ + u_int32_t label; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + label = atoi(argv[0]); + zebra_mpls_print_lsp (vty, zvrf, label); + return CMD_SUCCESS; +} + DEFUN (ip_zebra_import_table_distance, ip_zebra_import_table_distance_cmd, "ip import-table <1-252> distance <1-255>", @@ -6418,4 +6449,9 @@ zebra_vty_init (void) install_element (CONFIG_NODE, &no_mpls_transit_lsp_cmd); install_element (CONFIG_NODE, &no_mpls_transit_lsp_out_label_cmd); install_element (CONFIG_NODE, &no_mpls_transit_lsp_all_cmd); + + install_element (VIEW_NODE, &show_mpls_table_cmd); + install_element (ENABLE_NODE, &show_mpls_table_cmd); + install_element (VIEW_NODE, &show_mpls_table_lsp_cmd); + install_element (ENABLE_NODE, &show_mpls_table_lsp_cmd); } From ea6bebb83c855430adc9c64c399ec9c840ebb4fd Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 18 Apr 2016 18:02:26 +0000 Subject: [PATCH 013/136] Quagga: Fix memcmp usage for MPLS Signed-off-by: Daniel Walton Ticket: CM-6611 Reviewed By: Trivial Testing Done: Manual in SE-1 --- zebra/zebra_mpls.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 688585d6da..096f576a4c 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -604,20 +604,20 @@ nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, union g_addr *gate, char *ifname, ifindex_t ifindex) { struct nexthop *nhop; - u_char cmp = -1; + int cmp = 1; nhop = nhlfe->nexthop; if (!nhop) - return -1; + return 1; if (nhop->type != gtype) - return -1; + return 1; switch (nhop->type) { case NEXTHOP_TYPE_IPV4: cmp = memcmp(&(nhop->gate.ipv4), &(gate->ipv4), - sizeof(struct in_addr)); + sizeof(struct in_addr)); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -1086,10 +1086,10 @@ static int snhlfe_match (zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, union g_addr *gate, char *ifname, ifindex_t ifindex) { - u_char cmp = -1; + int cmp = 1; if (snhlfe->gtype != gtype) - return -1; + return 1; switch (snhlfe->gtype) { From 939fba2741e0645266039e8b5b450fa81cc3b7e7 Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 18 Apr 2016 18:24:10 +0000 Subject: [PATCH 014/136] Quagga: Fix MPLS LSP scheduling to follow nexthop route update Fix LSP scheduling to occur only after routes are processed because the LSP resolution depends on the nexthop route being selected. This is similar to how NHT processing is scheduled. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Reviewed-by: Daniel Walton Ticket: CM-6743 Reviewed By: CCR-3233 Testing Done: Verified the failed test multiple times. --- zebra/connected.c | 25 +++++++++++++------------ zebra/zebra_mpls.c | 1 + zebra/zebra_mpls.h | 28 ++++++++++++++++++++++++++++ zebra/zebra_rib.c | 10 ++++++++++ zebra/zebra_vrf.h | 4 ++++ 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index 9737f8deba..55c3792d4f 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -39,6 +39,7 @@ #include "zebra/connected.h" #include "zebra/rtadv.h" #include "zebra/zebra_mpls.h" +#include "zebra/debug.h" /* communicate the withdrawal of a connected address */ static void @@ -218,10 +219,10 @@ connected_up_ipv4 (struct interface *ifp, struct connected *ifc) /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) + if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", ifp->vrf_id, ifp->name); - zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + mpls_mark_lsps_for_processing (vrf_info_lookup(ifp->vrf_id)); } } @@ -347,10 +348,10 @@ connected_down_ipv4 (struct interface *ifp, struct connected *ifc) /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) + if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", ifp->vrf_id, ifp->name); - zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + mpls_mark_lsps_for_processing (vrf_info_lookup(ifp->vrf_id)); } } @@ -382,10 +383,10 @@ connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) + if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", ifp->vrf_id, ifp->name); - zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + mpls_mark_lsps_for_processing (vrf_info_lookup(ifp->vrf_id)); } } @@ -421,10 +422,10 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc) /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) + if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", ifp->vrf_id, ifp->name); - zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + mpls_mark_lsps_for_processing (vrf_info_lookup(ifp->vrf_id)); } } @@ -520,10 +521,10 @@ connected_down_ipv6 (struct interface *ifp, struct connected *ifc) /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) + if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", ifp->vrf_id, ifp->name); - zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + mpls_mark_lsps_for_processing (vrf_info_lookup(ifp->vrf_id)); } } @@ -554,10 +555,10 @@ connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) + if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", ifp->vrf_id, ifp->name); - zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + mpls_mark_lsps_for_processing (vrf_info_lookup(ifp->vrf_id)); } } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 096f576a4c..edfc08019b 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1554,6 +1554,7 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) return; zvrf->slsp_table = hash_create(label_hash, label_cmp); zvrf->lsp_table = hash_create(label_hash, label_cmp); + zvrf->mpls_flags = 0; } /* diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 7d1f494142..d25869664c 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -33,6 +33,7 @@ #include "memory.h" #include "mpls.h" #include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" /* Definitions and macros. */ @@ -280,4 +281,31 @@ nhlfe_type2str(enum lsp_types_t lsp_type) } } +static inline void +mpls_mark_lsps_for_processing(struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + + zvrf->mpls_flags |= MPLS_FLAG_SCHEDULE_LSPS; +} + +static inline void +mpls_unmark_lsps_for_processing(struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + + zvrf->mpls_flags &= ~MPLS_FLAG_SCHEDULE_LSPS; +} + +static inline int +mpls_should_lsps_be_processed(struct zebra_vrf *zvrf) +{ + if (!zvrf) + return 0; + + return ((zvrf->mpls_flags & MPLS_FLAG_SCHEDULE_LSPS) ? 1 : 0); +} + #endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 910610fc60..601721c6d4 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2033,6 +2033,16 @@ meta_queue_process_complete (struct work_queue *dummy) zebra_evaluate_rnh(zvrf->vrf_id, AF_INET6, 0, RNH_IMPORT_CHECK_TYPE, NULL); } } + + /* Schedule LSPs for processing, if needed. */ + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (mpls_should_lsps_be_processed(zvrf)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("%u: Scheduling all LSPs upon RIB completion", zvrf->vrf_id); + zebra_mpls_lsp_schedule (zvrf); + mpls_unmark_lsps_for_processing(zvrf); + } } /* Dispatch the meta queue by picking, processing and unlocking the next RN from diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 1062c90988..0baddc1b6a 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -80,6 +80,10 @@ struct zebra_vrf /* MPLS label forwarding table */ struct hash *lsp_table; + + /* MPLS processing flags */ + u_int16_t mpls_flags; +#define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) }; extern struct list *zvrf_list; From b78b820d46d6a5d9335585ccaa9aa956ec34af2b Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 18 Apr 2016 19:28:58 +0000 Subject: [PATCH 015/136] MPLS: Display enhancements and JSON support Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp Reviewed-by: Vivek Venkatraman Ticket: CM-6712, CM-6713 Reviewed By: CCR-3206, CCR-3209 Testing Done: Manual --- zebra/zebra_mpls.c | 214 +++++++++++++++++++++++++++++++++------- zebra/zebra_mpls.h | 6 +- zebra/zebra_mpls_null.c | 6 +- zebra/zebra_vty.c | 16 +-- 4 files changed, 196 insertions(+), 46 deletions(-) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index edfc08019b..c337a8312a 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -36,6 +36,7 @@ #include "routemap.h" #include "stream.h" #include "nexthop.h" +#include "lib/json.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -111,10 +112,6 @@ static void nhlfe_print (zebra_nhlfe_t *nhlfe, struct vty *vty); static void lsp_print (zebra_lsp_t *lsp, void *ctxt); -static void -lsp_print_hash (struct hash_backet *backet, void *ctxt); -static void -lsp_config_write (struct hash_backet *backet, void *ctxt); static void * slsp_alloc (void *p); static int @@ -968,6 +965,41 @@ static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) return 0; } +static json_object * +nhlfe_json (zebra_nhlfe_t *nhlfe) +{ + char buf[BUFSIZ]; + json_object *json_nhlfe = NULL; + struct nexthop *nexthop = nhlfe->nexthop; + + json_nhlfe = json_object_new_object(); + json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); + json_object_int_add(json_nhlfe, "outLabel", nexthop->nh_label->label[0]); + json_object_int_add(json_nhlfe, "distance", nhlfe->distance); + + if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED)) + json_object_boolean_true_add(json_nhlfe, "installed"); + + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + json_object_string_add(json_nhlfe, "nexthop", + inet_ntoa (nexthop->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_add(json_nhlfe, "nexthop", + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + + if (nexthop->ifindex) + json_object_string_add(json_nhlfe, "interface", ifindex2ifname (nexthop->ifindex)); + break; + default: + break; + } + return json_nhlfe; +} + /* * Print the NHLFE for a LSP forwarding entry. */ @@ -993,7 +1025,7 @@ nhlfe_print (zebra_nhlfe_t *nhlfe, struct vty *vty) case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: vty_out (vty, " via %s", - inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); if (nexthop->ifindex) vty_out (vty, " dev %s", ifindex2ifname (nexthop->ifindex)); break; @@ -1026,43 +1058,58 @@ lsp_print (zebra_lsp_t *lsp, void *ctxt) } /* - * Print an LSP forwarding hash entry. + * JSON objects for an LSP forwarding entry. */ -static void -lsp_print_hash (struct hash_backet *backet, void *ctxt) +static json_object * +lsp_json (zebra_lsp_t *lsp) { - zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe = NULL; + json_object *json = json_object_new_object(); + json_object *json_nhlfe_list = json_object_new_array(); - lsp = (zebra_lsp_t *) backet->data; - if (!lsp) - return; + json_object_int_add(json, "inLabel", lsp->ile.in_label); - lsp_print (lsp, ctxt); + if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) + json_object_boolean_true_add(json, "installed"); + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); + + json_object_object_add(json, "nexthops", json_nhlfe_list); + return json; +} + + +/* Return a sorted linked list of the hash contents */ +static struct list * +hash_get_sorted_list (struct hash *hash, void *cmp) +{ + unsigned int i; + struct hash_backet *hb; + struct list *sorted_list = list_new(); + + sorted_list->cmp = (int (*)(void *, void *)) cmp; + + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hb->next) + listnode_add_sort(sorted_list, hb->data); + + return sorted_list; } /* - * Write out static LSP configuration. + * Compare two LSPs based on their label values. */ -static void -lsp_config_write (struct hash_backet *backet, void *ctxt) +static int +lsp_cmp (zebra_lsp_t *lsp1, zebra_lsp_t *lsp2) { - zebra_slsp_t *slsp; - zebra_snhlfe_t *snhlfe; - struct vty *vty = (struct vty *) ctxt; - char buf[INET6_ADDRSTRLEN]; + if (lsp1->ile.in_label < lsp2->ile.in_label) + return -1; - slsp = (zebra_slsp_t *) backet->data; - if (!slsp) - return; + if (lsp1->ile.in_label > lsp2->ile.in_label) + return 1; - for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) - { - char lstr[30]; - snhlfe2str (snhlfe, buf, BUFSIZ); - vty_out (vty, "mpls lsp %u %s %s%s", - slsp->ile.in_label, buf, - label2str(snhlfe->out_label, lstr, 30), VTY_NEWLINE); - } + return 0; } /* @@ -1079,6 +1126,21 @@ slsp_alloc (void *p) return ((void *)slsp); } +/* + * Compare two static LSPs based on their label values. + */ +static int +slsp_cmp (zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) +{ + if (slsp1->ile.in_label < slsp2->ile.in_label) + return -1; + + if (slsp1->ile.in_label > slsp2->ile.in_label) + return 1; + + return 0; +} + /* * Check if static NHLFE matches with search info passed. */ @@ -1491,11 +1553,13 @@ zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) * (VTY command handler). */ void -zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label) +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label, + u_char use_json) { struct hash *lsp_table; zebra_lsp_t *lsp; zebra_ile_t tmp_ile; + json_object *json = NULL; /* Lookup table. */ lsp_table = zvrf->lsp_table; @@ -1508,16 +1572,76 @@ zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t labe if (!lsp) return; - lsp_print (lsp, (void *)vty); + if (use_json) + { + json = lsp_json(lsp); + vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + json_object_free(json); + } + else + lsp_print (lsp, (void *)vty); } /* * Display MPLS label forwarding table (VTY command handler). */ void -zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf) +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { - hash_iterate(zvrf->lsp_table, lsp_print_hash, vty); + char buf[BUFSIZ]; + json_object *json = NULL; + zebra_lsp_t *lsp = NULL; + zebra_nhlfe_t *nhlfe = NULL; + struct nexthop *nexthop = NULL; + struct listnode *node = NULL; + struct list *lsp_list = hash_get_sorted_list(zvrf->lsp_table, lsp_cmp); + + if (use_json) + { + json = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) + json_object_object_add(json, label2str(lsp->ile.in_label, buf, BUFSIZ), + lsp_json(lsp)); + + vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + json_object_free(json); + } + else + { + vty_out (vty, " Inbound Outbound%s", VTY_NEWLINE); + vty_out (vty, " Label Type Nexthop Label%s", VTY_NEWLINE); + vty_out (vty, "-------- ------- --------------- --------%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) + { + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + vty_out (vty, "%8d %7s ", lsp->ile.in_label, nhlfe_type2str(nhlfe->type)); + nexthop = nhlfe->nexthop; + + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + vty_out (vty, "%15s", inet_ntoa (nexthop->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out (vty, "%15s", inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + break; + default: + break; + } + + vty_out (vty, " %8d%s", nexthop->nh_label->label[0], VTY_NEWLINE); + } + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + + list_delete_all_node(lsp_list); } /* @@ -1526,7 +1650,25 @@ zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf) int zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) { - hash_iterate(zvrf->slsp_table, lsp_config_write, vty); + zebra_slsp_t *slsp; + zebra_snhlfe_t *snhlfe; + struct listnode *node; + char buf[INET6_ADDRSTRLEN]; + struct list *slsp_list = hash_get_sorted_list(zvrf->slsp_table, slsp_cmp); + + for (ALL_LIST_ELEMENTS_RO(slsp_list, node, slsp)) + { + for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) + { + char lstr[30]; + snhlfe2str (snhlfe, buf, BUFSIZ); + vty_out (vty, "mpls lsp %u %s %s%s", + slsp->ile.in_label, buf, + label2str(snhlfe->out_label, lstr, 30), VTY_NEWLINE); + } + } + + list_delete_all_node(slsp_list); return (zvrf->slsp_table->count ? 1 : 0); } diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index d25869664c..2707379194 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -203,13 +203,15 @@ zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf); * (VTY command handler). */ void -zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label); +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label, + u_char use_json); /* * Display MPLS label forwarding table (VTY command handler). */ void -zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf); +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json); /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 03b1f5694b..dc9993e7e7 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -34,12 +34,14 @@ zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) } void -zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label) +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label, + u_char use_json) { } void -zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf) +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index e5f7c181f9..95fbfeffce 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -5911,32 +5911,36 @@ zebra_mpls_config (struct vty *vty) DEFUN (show_mpls_table, show_mpls_table_cmd, - "show mpls table", + "show mpls table {json}", SHOW_STR MPLS_STR - "MPLS table\n") + "MPLS table\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; + u_char use_json = (argv[0] != NULL); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_mpls_print_lsp_table(vty, zvrf); + zebra_mpls_print_lsp_table(vty, zvrf, use_json); return CMD_SUCCESS; } DEFUN (show_mpls_table_lsp, show_mpls_table_lsp_cmd, - "show mpls table <16-1048575>", + "show mpls table <16-1048575> {json}", SHOW_STR MPLS_STR "MPLS table\n" - "LSP to display information about\n") + "LSP to display information about\n" + "JavaScript Object Notation\n") { u_int32_t label; struct zebra_vrf *zvrf; + u_char use_json = (argv[1] != NULL); zvrf = vrf_info_lookup(VRF_DEFAULT); label = atoi(argv[0]); - zebra_mpls_print_lsp (vty, zvrf, label); + zebra_mpls_print_lsp (vty, zvrf, label, use_json); return CMD_SUCCESS; } From a22f3f5dadce22784157cdef9b150114b894fd70 Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 18 Apr 2016 22:54:11 +0000 Subject: [PATCH 016/136] MPLS: Configure static routes with labels in Quagga Introduce ability to configure static routes with labels. Only supported for IPv4. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-6040 Reviewed By: CCR-3090 Testing Done: Testing in SE-1 --- zebra/zebra_mpls.c | 57 ++++++ zebra/zebra_mpls.h | 16 ++ zebra/zebra_mpls_null.c | 14 ++ zebra/zebra_static.c | 23 ++- zebra/zebra_static.h | 20 +- zebra/zebra_vty.c | 407 ++++++++++++++++++++++++++-------------- 6 files changed, 383 insertions(+), 154 deletions(-) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index c337a8312a..3e84114772 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1337,6 +1337,63 @@ mpls_processq_init (struct zebra_t *zebra) /* Public functions */ +/* + * String to label conversion, labels separated by '/'. + */ +int +mpls_str2label (const char *label_str, u_int8_t *num_labels, + mpls_label_t *labels) +{ + char *endp; + int i; + + *num_labels = 0; + for (i = 0; i < MPLS_MAX_LABELS; i++) + { + u_int32_t label; + + label = strtoul(label_str, &endp, 0); + + /* validity checks */ + if (endp == label_str) + return -1; + + if (!IS_MPLS_UNRESERVED_LABEL(label)) + return -1; + + labels[i] = label; + if (*endp == '\0') + { + *num_labels = i + 1; + return 0; + } + + /* Check separator. */ + if (*endp != '/') + return -1; + + label_str = endp + 1; + } + + /* Too many labels. */ + return -1; +} + +/* + * Label to string conversion, labels in string separated by '/'. + */ +char * +mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, + char *buf, int len) +{ + buf[0] = '\0'; + if (num_labels == 1) + snprintf (buf, len, "%u", labels[0]); + else if (num_labels == 2) + snprintf (buf, len, "%u/%u", labels[0], labels[1]); + return buf; +} + /* * Check that the label values used in LSP creation are consistent. The * main criteria is that if there is ECMP, the label operation must still diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 2707379194..11949c9313 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -38,6 +38,8 @@ /* Definitions and macros. */ +#define MPLS_MAX_LABELS 2 /* Maximum # labels that can be pushed. */ + #define NHLFE_FAMILY(nhlfe) \ (((nhlfe)->nexthop->type == NEXTHOP_TYPE_IPV6 || \ (nhlfe)->nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) ? AF_INET6 : AF_INET) @@ -155,6 +157,20 @@ struct zebra_lsp_t_ /* Function declarations. */ +/* + * String to label conversion, labels separated by '/'. + */ +int +mpls_str2label (const char *label_str, u_int8_t *num_labels, + mpls_label_t *labels); + +/* + * Label to string conversion, labels in string separated by '/'. + */ +char * +mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, + char *buf, int len); + /* * Check that the label values used in LSP creation are consistent. The * main criteria is that if there is ECMP, the label operation must still diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index dc9993e7e7..2953138dbc 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -4,6 +4,20 @@ #include "zebra/zserv.h" #include "zebra/zebra_mpls.h" +int +mpls_str2label (const char *label_str, u_int8_t *num_labels, + mpls_label_t *labels) +{ + return 0; +} + +char * +mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, + char *buf, int len) +{ + return NULL; +} + int zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, mpls_label_t out_label, enum nexthop_types_t gtype, diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 18efe26d9f..4acb77497d 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -180,6 +180,7 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro rib_addnode (rn, rib, 1); } } + static int static_nexthop_same (struct nexthop *nexthop, struct static_route *si) { @@ -314,7 +315,8 @@ int static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, union g_addr *gate, ifindex_t ifindex, const char *ifname, u_char flags, u_short tag, - u_char distance, struct zebra_vrf *zvrf) + u_char distance, struct zebra_vrf *zvrf, + struct static_nh_label *snh_label) { struct route_node *rn; struct static_route *si; @@ -349,7 +351,8 @@ static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, (afi == AFI_IP6 && IPV6_ADDR_SAME (gate, &si->addr.ipv6)))) && (! ifindex || ifindex == si->ifindex)) { - if ((distance == si->distance) && (tag == si->tag)) + if ((distance == si->distance) && (tag == si->tag) && + !memcmp (&si->snh_label, snh_label, sizeof (struct static_nh_label))) { route_unlock_node (rn); return 0; @@ -359,10 +362,10 @@ static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, } } - /* Distance or tag changed. */ + /* Distance or tag or label changed, delete existing first. */ if (update) - static_delete_route (afi, safi, type, p, gate, - ifindex, update->tag, update->distance, zvrf); + static_delete_route (afi, safi, type, p, gate, ifindex, update->tag, + update->distance, zvrf, &update->snh_label); /* Make new static route structure. */ si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route)); @@ -391,6 +394,9 @@ static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, break; } + /* Save labels, if any. */ + memcpy (&si->snh_label, snh_label, sizeof (struct static_nh_label)); + /* Add new static route information to the tree with sort by distance value and gateway address. */ for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next) @@ -427,7 +433,8 @@ static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, int static_delete_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, union g_addr *gate, ifindex_t ifindex, - u_short tag, u_char distance, struct zebra_vrf *zvrf) + u_short tag, u_char distance, struct zebra_vrf *zvrf, + struct static_nh_label *snh_label) { struct route_node *rn; struct static_route *si; @@ -450,7 +457,9 @@ static_delete_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, (afi == AFI_IP && IPV4_ADDR_SAME (gate, &si->addr.ipv4)) || (afi == AFI_IP6 && IPV6_ADDR_SAME (gate, &si->addr.ipv6)))) && (! ifindex || ifindex == si->ifindex) - && (! tag || (tag == si->tag))) + && (! tag || (tag == si->tag)) + && (! snh_label->num_labels || + !memcmp (&si->snh_label, snh_label, sizeof (struct static_nh_label)))) break; /* Can't find static route. */ diff --git a/zebra/zebra_static.h b/zebra/zebra_static.h index 0f00609b55..9178a6dd45 100644 --- a/zebra/zebra_static.h +++ b/zebra/zebra_static.h @@ -23,6 +23,14 @@ #ifndef __ZEBRA_STATIC_H__ #define __ZEBRA_STATIC_H__ +/* Static route label information */ +struct static_nh_label +{ + u_int8_t num_labels; + u_int8_t reserved[3]; + mpls_label_t label[2]; +}; + /* Static route information. */ struct static_route { @@ -66,6 +74,9 @@ struct static_route see ZEBRA_FLAG_REJECT ZEBRA_FLAG_BLACKHOLE */ + + /* Label information */ + struct static_nh_label snh_label; }; extern void @@ -77,13 +88,14 @@ extern int static_add_route (afi_t, safi_t safi, u_char type, struct prefix *p, union g_addr *gate, ifindex_t ifindex, const char *ifname, u_char flags, u_short tag, - u_char distance, struct zebra_vrf *zvrf); + u_char distance, struct zebra_vrf *zvrf, + struct static_nh_label *snh_label); extern int static_delete_route (afi_t, safi_t safi, u_char type, struct prefix *p, - union g_addr *gate, ifindex_t ifindex, - u_short tag, u_char distance, - struct zebra_vrf *zvrf); + union g_addr *gate, ifindex_t ifindex, u_short tag, + u_char distance, struct zebra_vrf *zvrf, + struct static_nh_label *snh_label); #endif diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 95fbfeffce..fa2a270889 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -58,7 +58,7 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, const char *dest_str, const char *mask_str, const char *gate_str, const char *flag_str, const char *tag_str, const char *distance_str, - const char *vrf_id_str) + const char *vrf_id_str, const char *label_str) { int ret; u_char distance; @@ -71,7 +71,9 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, unsigned int ifindex = 0; const char *ifname = NULL; u_char type = STATIC_IPV4_BLACKHOLE; + struct static_nh_label snh_label; + memset (&snh_label, 0, sizeof (struct static_nh_label)); ret = str2prefix (dest_str, &p); if (ret <= 0) { @@ -113,6 +115,17 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, return CMD_WARNING; } + /* Labels */ + if (label_str) + { + if (mpls_str2label (label_str, &snh_label.num_labels, + snh_label.label)) + { + vty_out (vty, "%% Malformed label(s)%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + /* Null0 static route. */ if ((gate_str != NULL) && (strncasecmp (gate_str, "Null0", strlen (gate_str)) == 0)) { @@ -122,9 +135,11 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, return CMD_WARNING; } if (add_cmd) - static_add_route (AFI_IP, safi, type, &p, NULL, ifindex, ifname, ZEBRA_FLAG_BLACKHOLE, tag, distance, zvrf); + static_add_route (AFI_IP, safi, type, &p, NULL, ifindex, ifname, + ZEBRA_FLAG_BLACKHOLE, tag, distance, zvrf, &snh_label); else - static_delete_route (AFI_IP, safi, type, &p, NULL, ifindex, tag, distance, zvrf); + static_delete_route (AFI_IP, safi, type, &p, NULL, ifindex, tag, + distance, zvrf, &snh_label); return CMD_SUCCESS; } @@ -148,9 +163,11 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, if (gate_str == NULL) { if (add_cmd) - static_add_route (AFI_IP, safi, type, &p, NULL, ifindex, ifname, flag, tag, distance, zvrf); + static_add_route (AFI_IP, safi, type, &p, NULL, ifindex, ifname, flag, + tag, distance, zvrf, &snh_label); else - static_delete_route (AFI_IP, safi, type, &p, NULL, ifindex, tag, distance, zvrf); + static_delete_route (AFI_IP, safi, type, &p, NULL, ifindex, tag, distance, + zvrf, &snh_label); return CMD_SUCCESS; } @@ -175,9 +192,13 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, type = STATIC_IPV4_GATEWAY; if (add_cmd) - static_add_route (AFI_IP, safi, type, &p, ifindex ? NULL : (union g_addr *)&gate, ifindex, ifname, flag, tag, distance, zvrf); + static_add_route (AFI_IP, safi, type, &p, + ifindex ? NULL : (union g_addr *)&gate, ifindex, ifname, + flag, tag, distance, zvrf, &snh_label); else - static_delete_route (AFI_IP, safi, type, &p, ifindex ? NULL : (union g_addr *)&gate, ifindex, tag, distance, zvrf); + static_delete_route (AFI_IP, safi, type, &p, + ifindex ? NULL : (union g_addr *)&gate, ifindex, tag, + distance, zvrf, &snh_label); return CMD_SUCCESS; } @@ -193,7 +214,8 @@ DEFUN (ip_mroute_dist, "Nexthop interface name\n" "Distance\n") { - return zebra_static_ipv4 (vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, NULL, argc > 2 ? argv[2] : NULL, NULL); + return zebra_static_ipv4 (vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], + NULL, NULL, argc > 2 ? argv[2] : NULL, NULL, NULL); } ALIAS (ip_mroute_dist, @@ -215,7 +237,8 @@ DEFUN (no_ip_mroute_dist, "Nexthop interface name\n" "Distance\n") { - return zebra_static_ipv4 (vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, NULL, argc > 2 ? argv[2] : NULL, NULL); + return zebra_static_ipv4 (vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], + NULL, NULL, argc > 2 ? argv[2] : NULL, NULL, NULL); } ALIAS (no_ip_mroute_dist, @@ -328,21 +351,23 @@ DEFUN (show_ip_rpf_addr, /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n") + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, - NULL, NULL); + NULL, NULL, argv[2]); } DEFUN (ip_route_tag, ip_route_tag_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -350,30 +375,34 @@ DEFUN (ip_route_tag, "IP gateway interface name\n" "Null interface\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], - NULL, NULL); + NULL, NULL, argv[3]); } DEFUN (ip_route_flags, ip_route_flags_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole)", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], NULL, - NULL, NULL); + NULL, NULL, argv[3]); } DEFUN (ip_route_flags_tag, ip_route_flags_tag_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -382,11 +411,13 @@ DEFUN (ip_route_flags_tag, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], argv[3], - NULL, NULL); + NULL, NULL, argv[4]); } DEFUN (ip_route_flags2, @@ -399,7 +430,7 @@ DEFUN (ip_route_flags2, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], NULL, - NULL, NULL); + NULL, NULL, NULL); } DEFUN (ip_route_flags2_tag, @@ -415,28 +446,30 @@ DEFUN (ip_route_flags2_tag, { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], argv[2], - NULL, NULL); + NULL, NULL, NULL); } /* Mask as A.B.C.D format. */ DEFUN (ip_route_mask, ip_route_mask_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n") + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, - NULL, NULL); + NULL, NULL, argv[3]); } DEFUN (ip_route_mask_tag, ip_route_mask_tag_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -445,11 +478,13 @@ DEFUN (ip_route_mask_tag, "IP gateway interface name\n" "Null interface\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], - NULL, NULL); + NULL, NULL, argv[4]); } DEFUN (ip_route_mask_flags, @@ -465,7 +500,7 @@ DEFUN (ip_route_mask_flags, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], NULL, - NULL, NULL); + NULL, NULL, NULL); } DEFUN (ip_route_mask_flags_tag, @@ -484,7 +519,7 @@ DEFUN (ip_route_mask_flags_tag, { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], argv[4], - NULL, NULL); + NULL, NULL, NULL); } DEFUN (ip_route_mask_flags2, @@ -498,7 +533,7 @@ DEFUN (ip_route_mask_flags2, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], NULL, - NULL, NULL); + NULL, NULL, NULL); } DEFUN (ip_route_mask_flags2_tag, @@ -514,28 +549,30 @@ DEFUN (ip_route_mask_flags2_tag, "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], argv[3], - NULL, NULL); + NULL, NULL, NULL); } /* Distance option value. */ DEFUN (ip_route_distance, ip_route_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, - argv[2], NULL); + argv[2], NULL, argv[3]); } DEFUN (ip_route_tag_distance, ip_route_tag_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -544,11 +581,13 @@ DEFUN (ip_route_tag_distance, "Null interface\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], - argv[3], NULL); + argv[3], NULL, argv[4]); } DEFUN (ip_route_flags_distance, @@ -564,7 +603,7 @@ DEFUN (ip_route_flags_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], NULL, - argv[3], NULL); + argv[3], NULL, NULL); } DEFUN (ip_route_flags_tag_distance, @@ -582,7 +621,7 @@ DEFUN (ip_route_flags_tag_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], argv[3], - argv[4], NULL); + argv[4], NULL, NULL); } DEFUN (ip_route_flags_distance2, @@ -596,7 +635,7 @@ DEFUN (ip_route_flags_distance2, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], NULL, - argv[2], NULL); + argv[2], NULL, NULL); } DEFUN (ip_route_flags_tag_distance2, @@ -612,12 +651,12 @@ DEFUN (ip_route_flags_tag_distance2, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], argv[2], - argv[3], NULL); + argv[3], NULL, NULL); } DEFUN (ip_route_mask_distance, ip_route_mask_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -625,15 +664,17 @@ DEFUN (ip_route_mask_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, - argv[3], NULL); + argv[3], NULL, argv[4]); } DEFUN (ip_route_mask_tag_distance, ip_route_mask_tag_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -643,10 +684,12 @@ DEFUN (ip_route_mask_tag_distance, "Null interface\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], - argv[4], NULL); + argv[4], NULL, argv[5]); } DEFUN (ip_route_mask_flags_tag_distance, @@ -665,7 +708,7 @@ DEFUN (ip_route_mask_flags_tag_distance, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], argv[4], - argv[5], NULL); + argv[5], NULL, NULL); } @@ -683,7 +726,7 @@ DEFUN (ip_route_mask_flags_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], NULL, - argv[4], NULL); + argv[4], NULL, NULL); } DEFUN (ip_route_mask_flags_distance2, @@ -698,7 +741,7 @@ DEFUN (ip_route_mask_flags_distance2, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], NULL, - argv[3], NULL); + argv[3], NULL, NULL); } DEFUN (ip_route_mask_flags_tag_distance2, @@ -715,27 +758,29 @@ DEFUN (ip_route_mask_flags_tag_distance2, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], argv[3], - argv[4], NULL); + argv[4], NULL, NULL); } DEFUN (no_ip_route, no_ip_route_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) {label WORD}", NO_STR IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n") + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, - NULL, NULL); + NULL, NULL, argv[2]); } DEFUN (no_ip_route_tag, no_ip_route_tag_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -744,10 +789,12 @@ DEFUN (no_ip_route_tag, "IP gateway interface name\n" "Null interface\n" "Tag of this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], - NULL, NULL); + NULL, NULL, argv[3]); } ALIAS (no_ip_route, @@ -787,7 +834,7 @@ DEFUN (no_ip_route_flags2, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, NULL, NULL, - NULL, NULL); + NULL, NULL, NULL); } DEFUN (no_ip_route_flags2_tag, @@ -803,12 +850,12 @@ DEFUN (no_ip_route_flags2_tag, "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, NULL, argv[1], - NULL, NULL); + NULL, NULL, NULL); } DEFUN (no_ip_route_mask, no_ip_route_mask_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -816,15 +863,17 @@ DEFUN (no_ip_route_mask, "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n") + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, - NULL, NULL); + NULL, NULL, argv[3]); } DEFUN (no_ip_route_mask_tag, no_ip_route_mask_tag_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -834,10 +883,12 @@ DEFUN (no_ip_route_mask_tag, "IP gateway interface name\n" "Null interface\n" "Tag of this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], - NULL, NULL); + NULL, NULL, argv[4]); } ALIAS (no_ip_route_mask, @@ -880,7 +931,7 @@ DEFUN (no_ip_route_mask_flags2, "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, NULL, NULL, - NULL, NULL); + NULL, NULL, NULL); } DEFUN (no_ip_route_mask_flags2_tag, @@ -897,12 +948,12 @@ DEFUN (no_ip_route_mask_flags2_tag, "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, NULL, argv[2], - NULL, NULL); + NULL, NULL, NULL); } DEFUN (no_ip_route_distance, no_ip_route_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -910,15 +961,17 @@ DEFUN (no_ip_route_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, - argv[2], NULL); + argv[2], NULL, argv[3]); } DEFUN (no_ip_route_tag_distance, no_ip_route_tag_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -928,10 +981,12 @@ DEFUN (no_ip_route_tag_distance, "Null interface\n" "Tag of this route\n" "Tag value\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], - argv[3], NULL); + argv[3], NULL, argv[4]); } DEFUN (no_ip_route_flags_distance, @@ -948,7 +1003,7 @@ DEFUN (no_ip_route_flags_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], NULL, - argv[3], NULL); + argv[3], NULL, NULL); } DEFUN (no_ip_route_flags_tag_distance, @@ -967,7 +1022,7 @@ DEFUN (no_ip_route_flags_tag_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], argv[3], - argv[4], NULL); + argv[4], NULL, NULL); } DEFUN (no_ip_route_flags_distance2, @@ -982,12 +1037,12 @@ DEFUN (no_ip_route_flags_distance2, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], NULL, - argv[2], NULL); + argv[2], NULL, NULL); } DEFUN (no_ip_route_flags_tag_distance2, no_ip_route_flags_tag_distance2_cmd, - "no ip route A.B.C.D/M (reject|blackhole) tag <1-65535> <1-255>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -996,15 +1051,17 @@ DEFUN (no_ip_route_flags_tag_distance2, "Silently discard pkts when matched\n" "Tag of this route\n" "Tag value\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], argv[2], - argv[3], NULL); + argv[3], NULL, argv[4]); } DEFUN (no_ip_route_mask_distance, no_ip_route_mask_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -1013,15 +1070,17 @@ DEFUN (no_ip_route_mask_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, - argv[3], NULL); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], + argv[4], NULL, argv[5]); } DEFUN (no_ip_route_mask_tag_distance, no_ip_route_mask_tag_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -1032,10 +1091,12 @@ DEFUN (no_ip_route_mask_tag_distance, "Null interface\n" "Tag of this route\n" "Tag value\n" - "Distance value for this route\n") + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], - argv[4], NULL); + argv[4], NULL, argv[5]); } DEFUN (no_ip_route_mask_flags_distance, @@ -1053,7 +1114,7 @@ DEFUN (no_ip_route_mask_flags_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], NULL, - argv[4], NULL); + argv[4], NULL, NULL); } DEFUN (no_ip_route_mask_flags_tag_distance, @@ -1073,7 +1134,7 @@ DEFUN (no_ip_route_mask_flags_tag_distance, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], argv[4], - argv[5], NULL); + argv[5], NULL, NULL); } DEFUN (no_ip_route_mask_flags_distance2, @@ -1089,7 +1150,7 @@ DEFUN (no_ip_route_mask_flags_distance2, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], NULL, - argv[3], NULL); + argv[3], NULL, NULL); } DEFUN (no_ip_route_mask_flags_tag_distance2, @@ -1107,7 +1168,7 @@ DEFUN (no_ip_route_mask_flags_tag_distance2, "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], argv[3], - argv[4], NULL); + argv[4], NULL, NULL); } /* Static route configuration. */ @@ -1122,7 +1183,8 @@ DEFUN (ip_route_vrf, "Null interface\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, NULL, argv[2]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, + NULL, NULL, argv[2], NULL); } DEFUN (ip_route_tag_vrf, @@ -1138,7 +1200,8 @@ DEFUN (ip_route_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, + argv[2], NULL, argv[3], NULL); } DEFUN (ip_route_flags_vrf, @@ -1153,7 +1216,8 @@ DEFUN (ip_route_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], NULL, NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], NULL, NULL, argv[3], NULL); } DEFUN (ip_route_flags_tag_vrf, @@ -1171,7 +1235,8 @@ DEFUN (ip_route_flags_tag_vrf, VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], argv[3], NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], NULL, argv[4], NULL); } DEFUN (ip_route_flags2_vrf, @@ -1184,7 +1249,8 @@ DEFUN (ip_route_flags2_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], NULL, NULL, argv[2]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], + NULL, NULL, argv[2], NULL); } DEFUN (ip_route_flags2_tag_vrf, @@ -1200,7 +1266,8 @@ DEFUN (ip_route_flags2_tag_vrf, VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], argv[2], NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], + argv[2], NULL, argv[3], NULL); } /* Mask as A.B.C.D format. */ @@ -1216,7 +1283,8 @@ DEFUN (ip_route_mask_vrf, "Null interface\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, NULL, NULL, argv[3], NULL); } DEFUN (ip_route_mask_tag_vrf, @@ -1234,7 +1302,8 @@ DEFUN (ip_route_mask_tag_vrf, VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, argv[3], NULL, argv[4], NULL); } DEFUN (ip_route_mask_flags_vrf, @@ -1250,7 +1319,8 @@ DEFUN (ip_route_mask_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], NULL, NULL, argv[4], NULL); } DEFUN (ip_route_mask_flags_tag_vrf, @@ -1269,7 +1339,8 @@ DEFUN (ip_route_mask_flags_tag_vrf, VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL, argv[5], NULL); } DEFUN (ip_route_mask_flags2_vrf, @@ -1283,7 +1354,8 @@ DEFUN (ip_route_mask_flags2_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], NULL, NULL, argv[3], NULL); } DEFUN (ip_route_mask_flags2_tag_vrf, @@ -1299,7 +1371,8 @@ DEFUN (ip_route_mask_flags2_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], NULL, argv[4], NULL); } /* Distance option value. */ @@ -1315,7 +1388,8 @@ DEFUN (ip_route_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, argv[2], argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, + NULL, argv[2], argv[3], NULL); } DEFUN (ip_route_tag_distance_vrf, @@ -1333,7 +1407,8 @@ DEFUN (ip_route_tag_distance_vrf, VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, + argv[2], argv[3], argv[4], NULL); } DEFUN (ip_route_flags_distance_vrf, @@ -1349,7 +1424,8 @@ DEFUN (ip_route_flags_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], NULL, argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], NULL, argv[3], argv[4], NULL); } DEFUN (ip_route_flags_tag_distance_vrf, @@ -1367,7 +1443,8 @@ DEFUN (ip_route_flags_tag_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], argv[3], argv[4],argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4],argv[5], NULL); } DEFUN (ip_route_flags_distance2_vrf, @@ -1381,7 +1458,8 @@ DEFUN (ip_route_flags_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], NULL, argv[2], argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], + NULL, argv[2], argv[3], NULL); } DEFUN (ip_route_flags_tag_distance2_vrf, @@ -1397,7 +1475,8 @@ DEFUN (ip_route_flags_tag_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], argv[2], argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, argv[1], + argv[2], argv[3], argv[4], NULL); } DEFUN (ip_route_mask_distance_vrf, @@ -1413,7 +1492,8 @@ DEFUN (ip_route_mask_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, NULL, argv[3], argv[4], NULL); } DEFUN (ip_route_mask_tag_distance_vrf, @@ -1431,7 +1511,8 @@ DEFUN (ip_route_mask_tag_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, argv[3], argv[4], argv[5], NULL); } DEFUN (ip_route_mask_flags_tag_distance_vrf, @@ -1450,7 +1531,8 @@ DEFUN (ip_route_mask_flags_tag_distance_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], argv[6], NULL); } @@ -1468,7 +1550,8 @@ DEFUN (ip_route_mask_flags_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4], argv[5], NULL); } DEFUN (ip_route_mask_flags_distance2_vrf, @@ -1483,7 +1566,8 @@ DEFUN (ip_route_mask_flags_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], NULL, argv[3], argv[4], NULL); } DEFUN (ip_route_mask_flags_tag_distance2_vrf, @@ -1500,7 +1584,8 @@ DEFUN (ip_route_mask_flags_tag_distance2_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], argv[5], NULL); } DEFUN (no_ip_route_vrf, @@ -1515,7 +1600,8 @@ DEFUN (no_ip_route_vrf, "Null interface\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, NULL, argv[2]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, + NULL, NULL, argv[2], NULL); } DEFUN (no_ip_route_flags_vrf, @@ -1531,7 +1617,8 @@ DEFUN (no_ip_route_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], NULL, NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], NULL, NULL, argv[3], NULL); } DEFUN (no_ip_route_tag_vrf, @@ -1548,7 +1635,8 @@ DEFUN (no_ip_route_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, + argv[2], NULL, argv[3], NULL); } DEFUN (no_ip_route_flags_tag_vrf, @@ -1566,7 +1654,8 @@ DEFUN (no_ip_route_flags_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], argv[3], NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], argv[3], NULL, argv[4], NULL); } DEFUN (no_ip_route_flags2_vrf, @@ -1580,7 +1669,8 @@ DEFUN (no_ip_route_flags2_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], NULL, NULL, argv[2]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], + NULL, NULL, argv[2], NULL); } DEFUN (no_ip_route_flags2_tag_vrf, @@ -1596,7 +1686,8 @@ DEFUN (no_ip_route_flags2_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], argv[2], NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], + argv[2], NULL, argv[3], NULL); } DEFUN (no_ip_route_mask_vrf, @@ -1612,7 +1703,8 @@ DEFUN (no_ip_route_mask_vrf, "Null interface\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + NULL, NULL, NULL, argv[3], NULL); } DEFUN (no_ip_route_mask_flags_vrf, @@ -1629,7 +1721,8 @@ DEFUN (no_ip_route_mask_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], NULL, NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + argv[3], NULL, NULL, argv[4], NULL); } DEFUN (no_ip_route_mask_tag_vrf, @@ -1647,7 +1740,8 @@ DEFUN (no_ip_route_mask_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + NULL, argv[3], NULL, argv[4], NULL); } DEFUN (no_ip_route_mask_flags_tag_vrf, @@ -1666,7 +1760,8 @@ DEFUN (no_ip_route_mask_flags_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL, argv[5], NULL); } DEFUN (no_ip_route_mask_flags2_vrf, @@ -1681,7 +1776,8 @@ DEFUN (no_ip_route_mask_flags2_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], NULL, NULL, argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], NULL, NULL, argv[3], NULL); } DEFUN (no_ip_route_mask_flags2_tag_vrf, @@ -1698,7 +1794,8 @@ DEFUN (no_ip_route_mask_flags2_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], argv[3], NULL, argv[4], NULL); } @@ -1715,7 +1812,8 @@ DEFUN (no_ip_route_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, argv[2], argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, + NULL, argv[2], argv[3], NULL); } DEFUN (no_ip_route_tag_distance_vrf, @@ -1733,7 +1831,8 @@ DEFUN (no_ip_route_tag_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, + argv[2], argv[3], argv[4], NULL); } DEFUN (no_ip_route_flags_distance_vrf, @@ -1750,7 +1849,8 @@ DEFUN (no_ip_route_flags_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], NULL, argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], NULL, argv[3], argv[4], NULL); } DEFUN (no_ip_route_flags_tag_distance_vrf, @@ -1769,7 +1869,8 @@ DEFUN (no_ip_route_flags_tag_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], argv[3], argv[4],argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4],argv[5], NULL); } DEFUN (no_ip_route_flags_distance2_vrf, @@ -1784,7 +1885,8 @@ DEFUN (no_ip_route_flags_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], NULL, argv[2], argv[3]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], + NULL, argv[2], argv[3], NULL); } DEFUN (no_ip_route_flags_tag_distance2_vrf, @@ -1801,7 +1903,8 @@ DEFUN (no_ip_route_flags_tag_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], argv[2] , argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], + argv[2] , argv[3], argv[4], NULL); } DEFUN (no_ip_route_mask_distance_vrf, @@ -1818,7 +1921,8 @@ DEFUN (no_ip_route_mask_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + NULL, NULL, argv[3], argv[4], NULL); } DEFUN (no_ip_route_mask_tag_distance_vrf, @@ -1837,7 +1941,8 @@ DEFUN (no_ip_route_mask_tag_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + NULL, argv[3], argv[4], argv[5], NULL); } DEFUN (no_ip_route_mask_flags_distance_vrf, @@ -1855,7 +1960,8 @@ DEFUN (no_ip_route_mask_flags_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4], argv[5], NULL); } DEFUN (no_ip_route_mask_flags_tag_distance_vrf, @@ -1875,7 +1981,8 @@ DEFUN (no_ip_route_mask_flags_tag_distance_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], argv[6], NULL); } DEFUN (no_ip_route_mask_flags_distance2_vrf, @@ -1891,7 +1998,8 @@ DEFUN (no_ip_route_mask_flags_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], argv[4]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], NULL, argv[3], argv[4], NULL); } DEFUN (no_ip_route_mask_flags_tag_distance2_vrf, @@ -1909,7 +2017,8 @@ DEFUN (no_ip_route_mask_flags_tag_distance2_vrf, "Distance value for this route\n" VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], argv[5], NULL); } static int @@ -3729,7 +3838,7 @@ static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) struct static_route *si; struct route_table *stable; struct zebra_vrf *zvrf; - char buf[PREFIX_STRLEN]; + char buf[BUFSIZ]; int write =0; struct listnode *node; @@ -3775,6 +3884,12 @@ static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) if (si->vrf_id != VRF_DEFAULT) vty_out (vty, " vrf %s", zvrf ? zvrf->name : ""); + /* Label information */ + if (si->snh_label.num_labels) + vty_out (vty, " label %s", + mpls_label2str (si->snh_label.num_labels, + si->snh_label.label, buf, sizeof buf)); + vty_out (vty, "%s", VTY_NEWLINE); write = 1; @@ -3802,6 +3917,7 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, unsigned int ifindex = 0; struct interface *ifp = NULL; struct zebra_vrf *zvrf; + struct static_nh_label snh_label; ret = str2prefix (dest_str, &p); if (ret <= 0) @@ -3840,6 +3956,9 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, if (tag_str) tag = atoi(tag_str); + /* Labels -- not supported for IPv6 for now. */ + memset (&snh_label, 0, sizeof (struct static_nh_label)); + /* When gateway is valid IPv6 addrees, then gate is treated as nexthop address other case gate is treated as interface name. */ ret = inet_pton (AF_INET6, gate_str, &gate_addr); @@ -3895,9 +4014,11 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, } if (add_cmd) - static_add_route (AFI_IP6, SAFI_UNICAST, type, &p, (union g_addr *)gate, ifindex, ifname, flag, tag, distance, zvrf); + static_add_route (AFI_IP6, SAFI_UNICAST, type, &p, (union g_addr *)gate, + ifindex, ifname, flag, tag, distance, zvrf, &snh_label); else - static_delete_route (AFI_IP6, SAFI_UNICAST, type, &p, (union g_addr *)gate, ifindex, tag, distance, zvrf); + static_delete_route (AFI_IP6, SAFI_UNICAST, type, &p, (union g_addr *)gate, + ifindex, tag, distance, zvrf, &snh_label); return CMD_SUCCESS; } From c0f4be83a11c22ff7e9920fc44ebc015d5b1f776 Mon Sep 17 00:00:00 2001 From: vivek Date: Tue, 19 Apr 2016 23:08:10 +0000 Subject: [PATCH 017/136] MPLS: Install labeled static routes This patch installs labeled static routes in the FIB. The routes are installed using the RTA_ENCAP (and RTA_ENCAP_TYPE) nested attributes. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-6040 Reviewed By: CCR-3091 Testing Done: Tested in SE-1, brief manual testing now --- lib/nexthop.c | 3 + zebra/rt_netlink.c | 166 ++++++++++++++++++++++++++++++++++++++----- zebra/zebra_rib.c | 3 + zebra/zebra_static.c | 86 ++++++++++++++++------ zebra/zebra_vty.c | 17 +++++ 5 files changed, 235 insertions(+), 40 deletions(-) diff --git a/lib/nexthop.c b/lib/nexthop.c index 465cc94851..8e775b68be 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -129,6 +129,9 @@ copy_nexthops (struct nexthop **tnh, struct nexthop *nh) nexthop->ifindex = nh->ifindex; memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + if (nh->nh_label) + nexthop_add_labels (nexthop, nh->nh_label->num_labels, + &nh->nh_label->label[0]); nexthop_add(tnh, nexthop); if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 8d19cbb5dc..3b564f2b15 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -77,12 +77,28 @@ static const struct message nlmsg_str[] = { #endif #ifndef RTA_VIA -#define RTA_VIA 16 +#define RTA_VIA 18 #endif #ifndef RTA_NEWDST #define RTA_NEWDST 19 #endif + +#ifndef RTA_ENCAP_TYPE +#define RTA_ENCAP_TYPE 21 +#endif + +#ifndef RTA_ENCAP +#define RTA_ENCAP 22 +#endif + +#ifndef LWTUNNEL_ENCAP_MPLS +#define LWTUNNEL_ENCAP_MPLS 1 +#endif + +#ifndef MPLS_IPTUNNEL_DST +#define MPLS_IPTUNNEL_DST 1 +#endif /* End of temporary definitions */ struct gw_family_t @@ -1720,6 +1736,39 @@ addattr32 (struct nlmsghdr *n, unsigned int maxlen, int type, int data) return addattr_l(n, maxlen, type, &data, sizeof(u_int32_t)); } +/* Some more utility functions from iproute2 */ +static struct rtattr * +addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +static int +addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = NLMSG_TAIL(n) - nest; + return n->nlmsg_len; +} + +static struct rtattr * +rta_nest(struct rtattr *rta, int maxlen, int type) +{ + struct rtattr *nest = RTA_TAIL(rta); + + rta_addattr_l(rta, maxlen, type, NULL, 0); + return nest; +} + +static int +rta_nest_end(struct rtattr *rta, struct rtattr *nest) +{ + nest->rta_len = RTA_TAIL(rta) - nest; + return rta->rta_len; +} + static int netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id) @@ -1865,7 +1914,8 @@ _netlink_route_build_singlepath( size_t req_size, int cmd) { - mpls_lse_t out_lse; + struct nexthop_label *nh_label; + mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[100]; if (rtmsg->rtm_family == AF_INET && @@ -1895,16 +1945,55 @@ _netlink_route_build_singlepath( } label_buf[0] = '\0'; + /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP + * (in the case of LER) + */ + nh_label = nexthop->nh_label; if (rtmsg->rtm_family == AF_MPLS) { - assert (nexthop->nh_label); + assert (nh_label); + assert (nh_label->num_labels == 1); + } - /* Fill out label, if present. */ - if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + if (nh_label && nh_label->num_labels) + { + int i, num_labels = 0; + u_int32_t bos; + char label_buf1[20]; + + for (i = 0; i < nh_label->num_labels; i++) { - out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1); - addattr_l (nlmsg, req_size, RTA_NEWDST, &out_lse, sizeof(mpls_lse_t)); - sprintf (label_buf, "label %d", nexthop->nh_label->label[0]); + if (nh_label->label[i] != MPLS_IMP_NULL_LABEL) + { + bos = ((i == (nh_label->num_labels - 1)) ? 1 : 0); + out_lse[i] = mpls_lse_encode (nh_label->label[i], 0, 0, bos); + if (!num_labels) + sprintf (label_buf, "label %d", nh_label->label[i]); + else + { + sprintf (label_buf1, "/%d", nh_label->label[i]); + strcat (label_buf, label_buf1); + } + num_labels++; + } + } + if (num_labels) + { + if (rtmsg->rtm_family == AF_MPLS) + addattr_l (nlmsg, req_size, RTA_NEWDST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + else + { + struct rtattr *nest; + u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + + addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, + &encap, sizeof (u_int16_t)); + nest = addattr_nest(nlmsg, req_size, RTA_ENCAP); + addattr_l (nlmsg, req_size, MPLS_IPTUNNEL_DST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + addattr_nest_end(nlmsg, nest); + } } } @@ -2023,7 +2112,8 @@ _netlink_route_build_multipath( struct rtmsg *rtmsg, union g_addr **src) { - mpls_lse_t out_lse; + struct nexthop_label *nh_label; + mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[100]; rtnh->rtnh_len = sizeof (*rtnh); @@ -2059,18 +2149,60 @@ _netlink_route_build_multipath( } label_buf[0] = '\0'; + /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP + * (in the case of LER) + */ + nh_label = nexthop->nh_label; if (rtmsg->rtm_family == AF_MPLS) { - assert (nexthop->nh_label); + assert (nh_label); + assert (nh_label->num_labels == 1); + } - /* Fill out label, if present. */ - if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + if (nh_label && nh_label->num_labels) + { + int i, num_labels = 0; + u_int32_t bos; + char label_buf1[20]; + + for (i = 0; i < nh_label->num_labels; i++) { - out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1); - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_NEWDST, - &out_lse, sizeof(mpls_lse_t)); - rtnh->rtnh_len += RTA_LENGTH (sizeof(mpls_lse_t)); - sprintf (label_buf, "label %d", nexthop->nh_label->label[0]); + if (nh_label->label[i] != MPLS_IMP_NULL_LABEL) + { + bos = ((i == (nh_label->num_labels - 1)) ? 1 : 0); + out_lse[i] = mpls_lse_encode (nh_label->label[i], 0, 0, bos); + if (!num_labels) + sprintf (label_buf, "label %d", nh_label->label[i]); + else + { + sprintf (label_buf1, "/%d", nh_label->label[i]); + strcat (label_buf, label_buf1); + } + num_labels++; + } + } + if (num_labels) + { + if (rtmsg->rtm_family == AF_MPLS) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_NEWDST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + rtnh->rtnh_len += RTA_LENGTH (num_labels * sizeof(mpls_lse_t)); + } + else + { + struct rtattr *nest; + u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + int len = rta->rta_len; + + rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE, + &encap, sizeof (u_int16_t)); + nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP); + rta_addattr_l (rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + rta_nest_end(rta, nest); + rtnh->rtnh_len += rta->rta_len - len; + } } } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 601721c6d4..f79dfafa5e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -204,6 +204,9 @@ rib_copy_nexthops (struct rib *rib, struct nexthop *nh) nexthop->ifindex = nh->ifindex; memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + if (nh->nh_label) + nexthop_add_labels (nexthop, nh->nh_label->num_labels, + &nh->nh_label->label[0]); rib_nexthop_add(rib, nexthop); if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) copy_nexthops(&nexthop->resolved, nh->resolved); diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 4acb77497d..fddc9d54c8 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -42,6 +42,7 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro struct route_node *rn; struct route_table *table; struct prefix nh_p; + struct nexthop *nexthop = NULL; /* Lookup table. */ table = zebra_vrf_table (afi, safi, si->vrf_id); @@ -71,29 +72,34 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro switch (si->type) { case STATIC_IPV4_GATEWAY: - rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + nexthop = rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); nh_p.family = AF_INET; nh_p.prefixlen = IPV4_MAX_BITLEN; nh_p.u.prefix4 = si->addr.ipv4; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IFINDEX: - rib_nexthop_ifindex_add (rib, si->ifindex); + nexthop = rib_nexthop_ifindex_add (rib, si->ifindex); break; case STATIC_IPV4_BLACKHOLE: - rib_nexthop_blackhole_add (rib); + nexthop = rib_nexthop_blackhole_add (rib); break; case STATIC_IPV6_GATEWAY: - rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + nexthop = rib_nexthop_ipv6_add (rib, &si->addr.ipv6); nh_p.family = AF_INET6; nh_p.prefixlen = IPV6_MAX_BITLEN; nh_p.u.prefix6 = si->addr.ipv6; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IPV6_GATEWAY_IFINDEX: - rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, si->ifindex); + nexthop = rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, + si->ifindex); break; } + /* Update label(s), if present. */ + if (si->snh_label.num_labels) + nexthop_add_labels (nexthop, si->snh_label.num_labels, + &si->snh_label.label[0]); if (IS_ZEBRA_DEBUG_RIB) { @@ -130,29 +136,34 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro switch (si->type) { case STATIC_IPV4_GATEWAY: - rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + nexthop = rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); nh_p.family = AF_INET; nh_p.prefixlen = IPV4_MAX_BITLEN; nh_p.u.prefix4 = si->addr.ipv4; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IFINDEX: - rib_nexthop_ifindex_add (rib, si->ifindex); + nexthop = rib_nexthop_ifindex_add (rib, si->ifindex); break; case STATIC_IPV4_BLACKHOLE: - rib_nexthop_blackhole_add (rib); + nexthop = rib_nexthop_blackhole_add (rib); break; case STATIC_IPV6_GATEWAY: - rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + nexthop = rib_nexthop_ipv6_add (rib, &si->addr.ipv6); nh_p.family = AF_INET6; nh_p.prefixlen = IPV6_MAX_BITLEN; nh_p.u.prefix6 = si->addr.ipv6; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IPV6_GATEWAY_IFINDEX: - rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, si->ifindex); + nexthop = rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, + si->ifindex); break; } + /* Update label(s), if present. */ + if (si->snh_label.num_labels) + nexthop_add_labels (nexthop, si->snh_label.num_labels, + &si->snh_label.label[0]); /* Save the flags of this static routes (reject, blackhole) */ rib->flags = si->flags; @@ -181,30 +192,59 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro } } +static int +static_nexthop_label_same (struct nexthop *nexthop, + struct static_nh_label *snh_label) +{ + int i; + + if ((snh_label->num_labels == 0 && nexthop->nh_label) || + (snh_label->num_labels != 0 && !nexthop->nh_label)) + return 0; + + if (snh_label->num_labels != 0) + if (snh_label->num_labels != nexthop->nh_label->num_labels) + return 0; + + for (i = 0; i < snh_label->num_labels; i++) + if (snh_label->label[i] != nexthop->nh_label->label[i]) + return 0; + + return 1; +} + static int static_nexthop_same (struct nexthop *nexthop, struct static_route *si) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 - && si->type == STATIC_IPV4_GATEWAY - && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->addr.ipv4)) - return 1; - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - && si->type == STATIC_IFINDEX - && nexthop->ifindex == si->ifindex) - return 1; + int gw_match = 0; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE && si->type == STATIC_IPV4_BLACKHOLE) return 1; - if (nexthop->type == NEXTHOP_TYPE_IPV6 + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + && si->type == STATIC_IPV4_GATEWAY + && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->addr.ipv4)) + gw_match = 1; + else if (nexthop->type == NEXTHOP_TYPE_IFINDEX + && si->type == STATIC_IFINDEX + && nexthop->ifindex == si->ifindex) + gw_match = 1; + else if (nexthop->type == NEXTHOP_TYPE_IPV6 && si->type == STATIC_IPV6_GATEWAY && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6)) - return 1; - if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + gw_match = 1; + else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX && si->type == STATIC_IPV6_GATEWAY_IFINDEX && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6) && nexthop->ifindex == si->ifindex) - return 1; - return 0; + gw_match = 1; + + if (!gw_match) + return 0; + + /* Check match on label(s), if any */ + return static_nexthop_label_same (nexthop, &si->snh_label); } /* Uninstall static route from RIB. */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index fa2a270889..1dfa40145b 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2318,6 +2318,15 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) default: break; } + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) + { + vty_out (vty, " label %s", + mpls_label2str (nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, BUFSIZ)); + } + vty_out (vty, "%s", VTY_NEWLINE); } vty_out (vty, "%s", VTY_NEWLINE); @@ -2558,6 +2567,14 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, break; } + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) + { + vty_out (vty, " label %s", + mpls_label2str (nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, BUFSIZ)); + } + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) vty_out (vty, ", bh"); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT)) From 27559ebdebb8b6d1e24f7cd2b8541d5cce8e2bb2 Mon Sep 17 00:00:00 2001 From: vivek Date: Fri, 22 Apr 2016 00:54:30 +0000 Subject: [PATCH 018/136] MPLS: Fix pointer manipulation when forming netlink nested attributes Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Fixes: f128f2f1f8a2fa975063dd321719607693991eb0 Ticket: CM-10506 Reviewed By: CCR-4536 Testing Done: Manual verification --- zebra/rt_netlink.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 3b564f2b15..07d2923028 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -101,6 +101,16 @@ static const struct message nlmsg_str[] = { #endif /* End of temporary definitions */ +#ifndef NLMSG_TAIL +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((u_char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#endif + +#ifndef RTA_TAIL +#define RTA_TAIL(rta) \ + ((struct rtattr *) (((u_char *) (rta)) + RTA_ALIGN((rta)->rta_len))) +#endif + struct gw_family_t { u_int16_t filler; @@ -1749,7 +1759,7 @@ addattr_nest(struct nlmsghdr *n, int maxlen, int type) static int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { - nest->rta_len = NLMSG_TAIL(n) - nest; + nest->rta_len = (u_char *)NLMSG_TAIL(n) - (u_char *)nest; return n->nlmsg_len; } @@ -1765,7 +1775,7 @@ rta_nest(struct rtattr *rta, int maxlen, int type) static int rta_nest_end(struct rtattr *rta, struct rtattr *nest) { - nest->rta_len = RTA_TAIL(rta) - nest; + nest->rta_len = (u_char *)RTA_TAIL(rta) - (u_char *)nest; return rta->rta_len; } From 8429abe0c2f8ee2ef6ed3904c393a4182c4589fc Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 1 Mar 2016 15:27:36 -0300 Subject: [PATCH 019/136] ldpd: copy original sources from OpenBSD (14/09/2016) Signed-off-by: Renato Westphal --- ldpd/accept.c | 135 +++++ ldpd/address.c | 297 ++++++++++ ldpd/adjacency.c | 346 +++++++++++ ldpd/control.c | 308 ++++++++++ ldpd/control.h | 38 ++ ldpd/hello.c | 574 +++++++++++++++++++ ldpd/init.c | 166 ++++++ ldpd/interface.c | 531 +++++++++++++++++ ldpd/keepalive.c | 58 ++ ldpd/l2vpn.c | 510 +++++++++++++++++ ldpd/labelmapping.c | 764 +++++++++++++++++++++++++ ldpd/lde.c | 1335 +++++++++++++++++++++++++++++++++++++++++++ ldpd/lde.h | 202 +++++++ ldpd/lde_lib.c | 784 +++++++++++++++++++++++++ ldpd/ldp.h | 304 ++++++++++ ldpd/ldpd.c | 1227 +++++++++++++++++++++++++++++++++++++++ ldpd/ldpd.h | 606 ++++++++++++++++++++ ldpd/ldpe.c | 808 ++++++++++++++++++++++++++ ldpd/ldpe.h | 282 +++++++++ ldpd/log.c | 600 +++++++++++++++++++ ldpd/log.h | 63 ++ ldpd/neighbor.c | 827 +++++++++++++++++++++++++++ ldpd/notification.c | 239 ++++++++ ldpd/packet.c | 788 +++++++++++++++++++++++++ ldpd/pfkey.c | 466 +++++++++++++++ ldpd/socket.c | 391 +++++++++++++ ldpd/util.c | 356 ++++++++++++ lib/imsg-buffer.c | 309 ++++++++++ lib/imsg.c | 302 ++++++++++ lib/imsg.h | 112 ++++ lib/openbsd-queue.h | 533 +++++++++++++++++ lib/openbsd-tree.h | 748 ++++++++++++++++++++++++ 32 files changed, 15009 insertions(+) create mode 100644 ldpd/accept.c create mode 100644 ldpd/address.c create mode 100644 ldpd/adjacency.c create mode 100644 ldpd/control.c create mode 100644 ldpd/control.h create mode 100644 ldpd/hello.c create mode 100644 ldpd/init.c create mode 100644 ldpd/interface.c create mode 100644 ldpd/keepalive.c create mode 100644 ldpd/l2vpn.c create mode 100644 ldpd/labelmapping.c create mode 100644 ldpd/lde.c create mode 100644 ldpd/lde.h create mode 100644 ldpd/lde_lib.c create mode 100644 ldpd/ldp.h create mode 100644 ldpd/ldpd.c create mode 100644 ldpd/ldpd.h create mode 100644 ldpd/ldpe.c create mode 100644 ldpd/ldpe.h create mode 100644 ldpd/log.c create mode 100644 ldpd/log.h create mode 100644 ldpd/neighbor.c create mode 100644 ldpd/notification.c create mode 100644 ldpd/packet.c create mode 100644 ldpd/pfkey.c create mode 100644 ldpd/socket.c create mode 100644 ldpd/util.c create mode 100644 lib/imsg-buffer.c create mode 100644 lib/imsg.c create mode 100644 lib/imsg.h create mode 100644 lib/openbsd-queue.h create mode 100644 lib/openbsd-tree.h diff --git a/ldpd/accept.c b/ldpd/accept.c new file mode 100644 index 0000000000..bc13ad49e7 --- /dev/null +++ b/ldpd/accept.c @@ -0,0 +1,135 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +struct accept_ev { + LIST_ENTRY(accept_ev) entry; + struct event ev; + void (*accept_cb)(int, short, void *); + void *arg; + int fd; +}; + +struct { + LIST_HEAD(, accept_ev) queue; + struct event evt; +} accept_queue; + +static void accept_arm(void); +static void accept_unarm(void); +static void accept_cb(int, short, void *); +static void accept_timeout(int, short, void *); + +void +accept_init(void) +{ + LIST_INIT(&accept_queue.queue); + evtimer_set(&accept_queue.evt, accept_timeout, NULL); +} + +int +accept_add(int fd, void (*cb)(int, short, void *), void *arg) +{ + struct accept_ev *av; + + if ((av = calloc(1, sizeof(*av))) == NULL) + return (-1); + av->fd = fd; + av->accept_cb = cb; + av->arg = arg; + LIST_INSERT_HEAD(&accept_queue.queue, av, entry); + + event_set(&av->ev, av->fd, EV_READ, accept_cb, av); + event_add(&av->ev, NULL); + + log_debug("%s: accepting on fd %d", __func__, fd); + + return (0); +} + +void +accept_del(int fd) +{ + struct accept_ev *av; + + LIST_FOREACH(av, &accept_queue.queue, entry) + if (av->fd == fd) { + log_debug("%s: %d removed from queue", __func__, fd); + event_del(&av->ev); + LIST_REMOVE(av, entry); + free(av); + return; + } +} + +void +accept_pause(void) +{ + struct timeval evtpause = { 1, 0 }; + + log_debug(__func__); + accept_unarm(); + evtimer_add(&accept_queue.evt, &evtpause); +} + +void +accept_unpause(void) +{ + if (evtimer_pending(&accept_queue.evt, NULL)) { + log_debug(__func__); + evtimer_del(&accept_queue.evt); + accept_arm(); + } +} + +static void +accept_arm(void) +{ + struct accept_ev *av; + LIST_FOREACH(av, &accept_queue.queue, entry) + event_add(&av->ev, NULL); +} + +static void +accept_unarm(void) +{ + struct accept_ev *av; + LIST_FOREACH(av, &accept_queue.queue, entry) + event_del(&av->ev); +} + +static void +accept_cb(int fd, short event, void *arg) +{ + struct accept_ev *av = arg; + event_add(&av->ev, NULL); + av->accept_cb(fd, event, av->arg); +} + +static void +accept_timeout(int fd, short event, void *bula) +{ + log_debug(__func__); + accept_arm(); +} diff --git a/ldpd/address.c b/ldpd/address.c new file mode 100644 index 0000000000..5e95fcc271 --- /dev/null +++ b/ldpd/address.c @@ -0,0 +1,297 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +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 void address_list_add(struct if_addr_head *, struct if_addr *); +static void address_list_clr(struct if_addr_head *); + +static void +send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, + unsigned int addr_count, int withdraw) +{ + struct ibuf *buf; + uint16_t msg_type; + uint8_t addr_size; + struct if_addr *if_addr; + uint16_t size; + unsigned int tlv_addr_count = 0; + int err = 0; + + /* nothing to send */ + if (LIST_EMPTY(addr_list)) + return; + + if (!withdraw) + msg_type = MSG_TYPE_ADDR; + else + msg_type = MSG_TYPE_ADDRWITHDRAW; + + switch (af) { + case AF_INET: + addr_size = sizeof(struct in_addr); + break; + case AF_INET6: + addr_size = sizeof(struct in6_addr); + break; + default: + fatalx("send_address: unknown af"); + } + + while ((if_addr = LIST_FIRST(addr_list)) != NULL) { + /* + * Send as many addresses as possible - respect the session's + * negotiated maximum pdu length. + */ + size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE; + if (size + addr_count * addr_size <= nbr->max_pdu_len) + tlv_addr_count = addr_count; + else + tlv_addr_count = (nbr->max_pdu_len - size) / addr_size; + size += tlv_addr_count * addr_size; + addr_count -= tlv_addr_count; + + 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, size); + size -= LDP_MSG_SIZE; + err |= gen_address_list_tlv(buf, size, af, addr_list, + tlv_addr_count); + if (err) { + address_list_clr(addr_list); + ibuf_free(buf); + return; + } + + while ((if_addr = LIST_FIRST(addr_list)) != NULL) { + log_debug("msg-out: %s: lsr-id %s, address %s", + msg_name(msg_type), inet_ntoa(nbr->id), + log_addr(af, &if_addr->addr)); + + LIST_REMOVE(if_addr, entry); + free(if_addr); + if (--tlv_addr_count == 0) + break; + } + + evbuf_enqueue(&nbr->tcp->wbuf, buf); + } + + nbr_fsm(nbr, NBR_EVT_PDU_SENT); +} + +void +send_address_single(struct nbr *nbr, struct if_addr *if_addr, int withdraw) +{ + struct if_addr_head addr_list; + + LIST_INIT(&addr_list); + address_list_add(&addr_list, if_addr); + send_address(nbr, if_addr->af, &addr_list, 1, withdraw); +} + +void +send_address_all(struct nbr *nbr, int af) +{ + struct if_addr_head addr_list; + struct if_addr *if_addr; + unsigned int addr_count = 0; + + LIST_INIT(&addr_list); + LIST_FOREACH(if_addr, &global.addr_list, entry) { + if (if_addr->af != af) + continue; + + address_list_add(&addr_list, if_addr); + addr_count++; + } + + send_address(nbr, af, &addr_list, addr_count, 0); +} + +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 lde_addr lde_addr; + + memcpy(&msg, buf, sizeof(msg)); + buf += LDP_MSG_SIZE; + len -= LDP_MSG_SIZE; + + /* Address List TLV */ + if (len < ADDR_LIST_SIZE) { + 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) { + 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); + return (-1); + } + switch (ntohs(alt.family)) { + case AF_IPV4: + if (!nbr->v4_enabled) + /* just ignore the message */ + return (0); + break; + case AF_IPV6: + if (!nbr->v6_enabled) + /* just ignore the message */ + return (0); + break; + default: + send_notification_nbr(nbr, S_UNSUP_ADDR, msg.id, msg.type); + return (-1); + } + 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)) { + case AF_IPV4: + if (len < sizeof(struct in_addr)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + return (-1); + } + + memset(&lde_addr, 0, sizeof(lde_addr)); + lde_addr.af = AF_INET; + memcpy(&lde_addr.addr, buf, sizeof(struct in_addr)); + + buf += sizeof(struct in_addr); + len -= sizeof(struct in_addr); + break; + case AF_IPV6: + if (len < sizeof(struct in6_addr)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + return (-1); + } + + memset(&lde_addr, 0, sizeof(lde_addr)); + lde_addr.af = AF_INET6; + memcpy(&lde_addr.addr, buf, sizeof(struct in6_addr)); + + buf += sizeof(struct in6_addr); + len -= sizeof(struct in6_addr); + break; + default: + fatalx("recv_address: unknown af"); + } + + log_debug("msg-in: %s: lsr-id %s, address %s", + msg_name(msg_type), inet_ntoa(nbr->id), + log_addr(lde_addr.af, &lde_addr.addr)); + + ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr, + sizeof(lde_addr)); + } + + 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) +{ + struct address_list_tlv alt; + uint16_t addr_size; + struct if_addr *if_addr; + int err = 0; + + memset(&alt, 0, sizeof(alt)); + alt.type = TLV_TYPE_ADDRLIST; + alt.length = htons(size - TLV_HDR_SIZE); + + switch (af) { + case AF_INET: + alt.family = htons(AF_IPV4); + addr_size = sizeof(struct in_addr); + break; + case AF_INET6: + alt.family = htons(AF_IPV6); + addr_size = sizeof(struct in6_addr); + break; + default: + fatalx("gen_address_list_tlv: unknown af"); + } + + err |= ibuf_add(buf, &alt, sizeof(alt)); + LIST_FOREACH(if_addr, addr_list, entry) { + err |= ibuf_add(buf, &if_addr->addr, addr_size); + if (--tlv_addr_count == 0) + break; + } + + return (err); +} + +static void +address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr) +{ + struct if_addr *new; + + new = malloc(sizeof(*new)); + if (new == NULL) + fatal(__func__); + *new = *if_addr; + + LIST_INSERT_HEAD(addr_list, new, entry); +} + +static void +address_list_clr(struct if_addr_head *addr_list) +{ + struct if_addr *if_addr; + + while ((if_addr = LIST_FIRST(addr_list)) != NULL) { + LIST_REMOVE(if_addr, entry); + free(if_addr); + } +} diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c new file mode 100644 index 0000000000..266717729f --- /dev/null +++ b/ldpd/adjacency.c @@ -0,0 +1,346 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2015 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static void adj_del_single(struct adj *); +static void adj_itimer(int, short, void *); +static void tnbr_del(struct tnbr *); +static void tnbr_hello_timer(int, short, void *); +static void tnbr_start_hello_timer(struct tnbr *); +static void tnbr_stop_hello_timer(struct tnbr *); + +struct adj * +adj_new(struct in_addr lsr_id, struct hello_source *source, + union ldpd_addr *addr) +{ + struct adj *adj; + + log_debug("%s: lsr-id %s, %s", __func__, inet_ntoa(lsr_id), + log_hello_src(source)); + + if ((adj = calloc(1, sizeof(*adj))) == NULL) + fatal(__func__); + + adj->lsr_id = lsr_id; + adj->nbr = NULL; + adj->source = *source; + adj->trans_addr = *addr; + + evtimer_set(&adj->inactivity_timer, adj_itimer, adj); + + LIST_INSERT_HEAD(&global.adj_list, adj, global_entry); + + switch (source->type) { + case HELLO_LINK: + LIST_INSERT_HEAD(&source->link.ia->adj_list, adj, ia_entry); + break; + case HELLO_TARGETED: + source->target->adj = adj; + break; + } + + return (adj); +} + +static void +adj_del_single(struct adj *adj) +{ + log_debug("%s: lsr-id %s, %s (%s)", __func__, inet_ntoa(adj->lsr_id), + log_hello_src(&adj->source), af_name(adj_get_af(adj))); + + adj_stop_itimer(adj); + + LIST_REMOVE(adj, global_entry); + if (adj->nbr) + LIST_REMOVE(adj, nbr_entry); + switch (adj->source.type) { + case HELLO_LINK: + LIST_REMOVE(adj, ia_entry); + break; + case HELLO_TARGETED: + adj->source.target->adj = NULL; + break; + } + + free(adj); +} + +void +adj_del(struct adj *adj, uint32_t notif_status) +{ + struct nbr *nbr = adj->nbr; + struct adj *atmp; + + adj_del_single(adj); + + /* + * If the neighbor still exists but none of its remaining + * adjacencies (if any) are from the preferred address-family, + * then delete it. + */ + if (nbr && nbr_adj_count(nbr, nbr->af) == 0) { + LIST_FOREACH_SAFE(adj, &nbr->adj_list, nbr_entry, atmp) + adj_del_single(adj); + session_shutdown(nbr, notif_status, 0, 0); + nbr_del(nbr); + } +} + +struct adj * +adj_find(struct hello_source *source) +{ + struct adj *adj; + + LIST_FOREACH(adj, &global.adj_list, global_entry) { + if (adj->source.type != source->type) + continue; + + switch (source->type) { + case HELLO_LINK: + if (ldp_addrcmp(source->link.ia->af, + &adj->source.link.src_addr, + &source->link.src_addr) == 0) + return (adj); + break; + case HELLO_TARGETED: + if (adj->source.target == source->target) + return (adj); + break; + } + } + + return (NULL); +} + +int +adj_get_af(struct adj *adj) +{ + switch (adj->source.type) { + case HELLO_LINK: + return (adj->source.link.ia->af); + case HELLO_TARGETED: + return (adj->source.target->af); + default: + fatalx("adj_get_af: unknown hello type"); + } +} + +/* adjacency timers */ + +/* ARGSUSED */ +static void +adj_itimer(int fd, short event, void *arg) +{ + struct adj *adj = arg; + + log_debug("%s: lsr-id %s", __func__, inet_ntoa(adj->lsr_id)); + + if (adj->source.type == HELLO_TARGETED) { + if (!(adj->source.target->flags & F_TNBR_CONFIGURED) && + adj->source.target->pw_count == 0) { + /* remove dynamic targeted neighbor */ + tnbr_del(adj->source.target); + return; + } + adj->source.target->adj = NULL; + } + + adj_del(adj, S_HOLDTIME_EXP); +} + +void +adj_start_itimer(struct adj *adj) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = adj->holdtime; + if (evtimer_add(&adj->inactivity_timer, &tv) == -1) + fatal(__func__); +} + +void +adj_stop_itimer(struct adj *adj) +{ + if (evtimer_pending(&adj->inactivity_timer, NULL) && + evtimer_del(&adj->inactivity_timer) == -1) + fatal(__func__); +} + +/* targeted neighbors */ + +struct tnbr * +tnbr_new(struct ldpd_conf *xconf, int af, union ldpd_addr *addr) +{ + struct tnbr *tnbr; + + if ((tnbr = calloc(1, sizeof(*tnbr))) == NULL) + fatal(__func__); + + tnbr->af = af; + tnbr->addr = *addr; + tnbr->state = TNBR_STA_DOWN; + tnbr->hello_holdtime = (ldp_af_conf_get(xconf, af))->thello_holdtime; + tnbr->hello_interval = (ldp_af_conf_get(xconf, af))->thello_interval; + + return (tnbr); +} + +static void +tnbr_del(struct tnbr *tnbr) +{ + tnbr_stop_hello_timer(tnbr); + if (tnbr->adj) + adj_del(tnbr->adj, S_SHUTDOWN); + LIST_REMOVE(tnbr, entry); + free(tnbr); +} + +struct tnbr * +tnbr_find(struct ldpd_conf *xconf, int af, union ldpd_addr *addr) +{ + struct tnbr *tnbr; + + LIST_FOREACH(tnbr, &xconf->tnbr_list, entry) + if (af == tnbr->af && + ldp_addrcmp(af, addr, &tnbr->addr) == 0) + return (tnbr); + + return (NULL); +} + +struct tnbr * +tnbr_check(struct tnbr *tnbr) +{ + if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) && + tnbr->pw_count == 0) { + tnbr_del(tnbr); + return (NULL); + } + + return (tnbr); +} + +void +tnbr_update(struct tnbr *tnbr) +{ + int socket_ok, rtr_id_ok; + + if ((ldp_af_global_get(&global, tnbr->af))->ldp_edisc_socket != -1) + socket_ok = 1; + else + socket_ok = 0; + + if (leconf->rtr_id.s_addr != INADDR_ANY) + rtr_id_ok = 1; + else + rtr_id_ok = 0; + + if (tnbr->state == TNBR_STA_DOWN) { + if (!socket_ok || !rtr_id_ok) + return; + + tnbr->state = TNBR_STA_ACTIVE; + send_hello(HELLO_TARGETED, NULL, tnbr); + + evtimer_set(&tnbr->hello_timer, tnbr_hello_timer, tnbr); + tnbr_start_hello_timer(tnbr); + } else if (tnbr->state == TNBR_STA_ACTIVE) { + if (socket_ok && rtr_id_ok) + return; + + tnbr->state = TNBR_STA_DOWN; + tnbr_stop_hello_timer(tnbr); + } +} + +void +tnbr_update_all(int af) +{ + struct tnbr *tnbr; + + /* update targeted neighbors */ + LIST_FOREACH(tnbr, &leconf->tnbr_list, entry) + if (tnbr->af == af || af == AF_UNSPEC) + tnbr_update(tnbr); +} + +/* target neighbors timers */ + +/* ARGSUSED */ +static void +tnbr_hello_timer(int fd, short event, void *arg) +{ + struct tnbr *tnbr = arg; + + send_hello(HELLO_TARGETED, NULL, tnbr); + tnbr_start_hello_timer(tnbr); +} + +static void +tnbr_start_hello_timer(struct tnbr *tnbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = tnbr->hello_interval; + if (evtimer_add(&tnbr->hello_timer, &tv) == -1) + fatal(__func__); +} + +static void +tnbr_stop_hello_timer(struct tnbr *tnbr) +{ + if (evtimer_pending(&tnbr->hello_timer, NULL) && + evtimer_del(&tnbr->hello_timer) == -1) + fatal(__func__); +} + +struct ctl_adj * +adj_to_ctl(struct adj *adj) +{ + static struct ctl_adj actl; + + actl.af = adj_get_af(adj); + actl.id = adj->lsr_id; + actl.type = adj->source.type; + switch (adj->source.type) { + case HELLO_LINK: + memcpy(actl.ifname, adj->source.link.ia->iface->name, + sizeof(actl.ifname)); + break; + case HELLO_TARGETED: + actl.src_addr = adj->source.target->addr; + break; + } + actl.holdtime = adj->holdtime; + actl.trans_addr = adj->trans_addr; + + return (&actl); +} diff --git a/ldpd/control.c b/ldpd/control.c new file mode 100644 index 0000000000..42322eb08c --- /dev/null +++ b/ldpd/control.c @@ -0,0 +1,308 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" +#include "control.h" + +#define CONTROL_BACKLOG 5 + +static void control_accept(int, short, void *); +static struct ctl_conn *control_connbyfd(int); +static struct ctl_conn *control_connbypid(pid_t); +static void control_close(int); +static void control_dispatch_imsg(int, short, void *); + +struct ctl_conns ctl_conns; + +static int control_fd; + +int +control_init(void) +{ + struct sockaddr_un sun; + int fd; + mode_t old_umask; + + if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + 0)) == -1) { + log_warn("%s: socket", __func__); + return (-1); + } + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, LDPD_SOCKET, sizeof(sun.sun_path)); + + if (unlink(LDPD_SOCKET) == -1) + if (errno != ENOENT) { + log_warn("%s: unlink %s", __func__, LDPD_SOCKET); + close(fd); + return (-1); + } + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + log_warn("%s: bind: %s", __func__, LDPD_SOCKET); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(LDPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("%s: chmod", __func__); + close(fd); + (void)unlink(LDPD_SOCKET); + return (-1); + } + + control_fd = fd; + + return (0); +} + +int +control_listen(void) +{ + if (listen(control_fd, CONTROL_BACKLOG) == -1) { + log_warn("%s: listen", __func__); + return (-1); + } + + return (accept_add(control_fd, control_accept, NULL)); +} + +void +control_cleanup(void) +{ + accept_del(control_fd); + close(control_fd); + unlink(LDPD_SOCKET); +} + +/* ARGSUSED */ +static void +control_accept(int listenfd, short event, void *bula) +{ + int connfd; + socklen_t len; + struct sockaddr_un sun; + struct ctl_conn *c; + + len = sizeof(sun); + if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len, + SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) { + /* + * Pause accept if we are out of file descriptors, or + * libevent will haunt us here too. + */ + if (errno == ENFILE || errno == EMFILE) + accept_pause(); + else if (errno != EWOULDBLOCK && errno != EINTR && + errno != ECONNABORTED) + log_warn("%s: accept4", __func__); + return; + } + + if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { + log_warn(__func__); + close(connfd); + return; + } + + imsg_init(&c->iev.ibuf, connfd); + c->iev.handler = control_dispatch_imsg; + c->iev.events = EV_READ; + event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, + c->iev.handler, &c->iev); + event_add(&c->iev.ev, NULL); + + TAILQ_INSERT_TAIL(&ctl_conns, c, entry); +} + +static struct ctl_conn * +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 */ + + return (c); +} + +static struct ctl_conn * +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 */ + + return (c); +} + +static void +control_close(int fd) +{ + struct ctl_conn *c; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warnx("%s: fd %d: not found", __func__, fd); + return; + } + + msgbuf_clear(&c->iev.ibuf.w); + TAILQ_REMOVE(&ctl_conns, c, entry); + + event_del(&c->iev.ev); + close(c->iev.ibuf.fd); + accept_unpause(); + free(c); +} + +/* ARGSUSED */ +static void +control_dispatch_imsg(int fd, short event, void *bula) +{ + struct ctl_conn *c; + struct imsg imsg; + ssize_t n; + unsigned int ifidx; + int verbose; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warnx("%s: fd %d: not found", __func__, fd); + return; + } + + if (event & EV_READ) { + if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || + n == 0) { + control_close(fd); + return; + } + } + if (event & EV_WRITE) { + if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) { + control_close(fd); + return; + } + } + + for (;;) { + if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { + control_close(fd); + return; + } + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_FIB_COUPLE: + case IMSG_CTL_FIB_DECOUPLE: + case IMSG_CTL_RELOAD: + c->iev.ibuf.pid = imsg.hdr.pid; + ldpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_IFINFO: + c->iev.ibuf.pid = imsg.hdr.pid; + ldpe_imsg_compose_parent(imsg.hdr.type, + imsg.hdr.pid, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_INTERFACE: + if (imsg.hdr.len == IMSG_HEADER_SIZE + + sizeof(ifidx)) { + memcpy(&ifidx, imsg.data, sizeof(ifidx)); + ldpe_iface_ctl(c, ifidx); + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, + 0, -1, NULL, 0); + } + break; + case IMSG_CTL_SHOW_DISCOVERY: + ldpe_adj_ctl(c); + break; + case IMSG_CTL_SHOW_LIB: + case IMSG_CTL_SHOW_L2VPN_PW: + case IMSG_CTL_SHOW_L2VPN_BINDING: + c->iev.ibuf.pid = imsg.hdr.pid; + ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid, + imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_NBR: + ldpe_nbr_ctl(c); + break; + case IMSG_CTL_CLEAR_NBR: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ctl_nbr)) + break; + + nbr_clear_ctl(imsg.data); + break; + case IMSG_CTL_LOG_VERBOSE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(verbose)) + break; + + /* forward to other processes */ + ldpe_imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, + imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid, + imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + + memcpy(&verbose, imsg.data, sizeof(verbose)); + log_verbose(verbose); + break; + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + + imsg_event_add(&c->iev); +} + +int +control_imsg_relay(struct imsg *imsg) +{ + struct ctl_conn *c; + + if ((c = control_connbypid(imsg->hdr.pid)) == NULL) + return (0); + + return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, + -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); +} diff --git a/ldpd/control.h b/ldpd/control.h new file mode 100644 index 0000000000..fd6e47071d --- /dev/null +++ b/ldpd/control.h @@ -0,0 +1,38 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + +#include +#include + +struct ctl_conn { + TAILQ_ENTRY(ctl_conn) entry; + struct imsgev iev; +}; +TAILQ_HEAD(ctl_conns, ctl_conn); + +extern struct ctl_conns ctl_conns; + +int control_init(void); +int control_listen(void); +void control_cleanup(void); +int control_imsg_relay(struct imsg *); + +#endif /* _CONTROL_H_ */ diff --git a/ldpd/hello.c b/ldpd/hello.c new file mode 100644 index 0000000000..d0daed347d --- /dev/null +++ b/ldpd/hello.c @@ -0,0 +1,574 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static int gen_hello_prms_tlv(struct ibuf *buf, uint16_t, uint16_t); +static int gen_opt4_hello_prms_tlv(struct ibuf *, uint16_t, uint32_t); +static int gen_opt16_hello_prms_tlv(struct ibuf *, uint16_t, uint8_t *); +static int gen_ds_hello_prms_tlv(struct ibuf *, uint32_t); +static int tlv_decode_hello_prms(char *, uint16_t, uint16_t *, uint16_t *); +static int tlv_decode_opt_hello_prms(char *, uint16_t, int *, int, + union ldpd_addr *, uint32_t *, uint16_t *); + +int +send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) +{ + int af; + union ldpd_addr dst; + uint16_t size, holdtime = 0, flags = 0; + int fd = 0; + struct ibuf *buf; + int err = 0; + + switch (type) { + case HELLO_LINK: + af = ia->af; + holdtime = ia->hello_holdtime; + flags = 0; + fd = (ldp_af_global_get(&global, af))->ldp_disc_socket; + + /* multicast destination address */ + switch (af) { + case AF_INET: + if (!(leconf->ipv4.flags & F_LDPD_AF_NO_GTSM)) + flags |= F_HELLO_GTSM; + dst.v4 = global.mcast_addr_v4; + break; + case AF_INET6: + dst.v6 = global.mcast_addr_v6; + break; + default: + fatalx("send_hello: unknown af"); + } + break; + case HELLO_TARGETED: + af = tnbr->af; + holdtime = tnbr->hello_holdtime; + flags = F_HELLO_TARGETED; + if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count) + flags |= F_HELLO_REQ_TARG; + fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket; + + /* unicast destination address */ + dst = tnbr->addr; + break; + default: + fatalx("send_hello: unknown hello type"); + } + + /* calculate message size */ + size = LDP_HDR_SIZE + LDP_MSG_SIZE + sizeof(struct hello_prms_tlv); + switch (af) { + case AF_INET: + size += sizeof(struct hello_prms_opt4_tlv); + break; + case AF_INET6: + size += sizeof(struct hello_prms_opt16_tlv); + break; + default: + fatalx("send_hello: unknown af"); + } + size += sizeof(struct hello_prms_opt4_tlv); + if (ldp_is_dual_stack(leconf)) + size += sizeof(struct hello_prms_opt4_tlv); + + /* generate message */ + 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_HELLO, size); + err |= gen_hello_prms_tlv(buf, holdtime, flags); + + /* + * RFC 7552 - Section 6.1: + * "An LSR MUST include only the transport address whose address + * family is the same as that of the IP packet carrying the Hello + * message". + */ + switch (af) { + case AF_INET: + err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR, + leconf->ipv4.trans_addr.v4.s_addr); + break; + case AF_INET6: + err |= gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR, + leconf->ipv6.trans_addr.v6.s6_addr); + break; + default: + fatalx("send_hello: unknown af"); + } + + err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_CONFIG, + htonl(global.conf_seqnum)); + + /* + * RFC 7552 - Section 6.1.1: + * "A Dual-stack LSR (i.e., an LSR supporting Dual-stack LDP for a peer) + * MUST include the Dual-Stack capability TLV in all of its LDP Hellos". + */ + if (ldp_is_dual_stack(leconf)) + err |= gen_ds_hello_prms_tlv(buf, leconf->trans_pref); + + if (err) { + ibuf_free(buf); + return (-1); + } + + send_packet(fd, af, &dst, ia, buf->buf, buf->wpos); + ibuf_free(buf); + + return (0); +} + +void +recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, + union ldpd_addr *src, struct iface *iface, int multicast, char *buf, + uint16_t len) +{ + struct adj *adj = NULL; + struct nbr *nbr, *nbrt; + uint16_t holdtime, flags; + int tlvs_rcvd; + int ds_tlv; + union ldpd_addr trans_addr; + uint32_t scope_id = 0; + uint32_t conf_seqnum; + uint16_t trans_pref; + int r; + struct hello_source source; + struct iface_af *ia = NULL; + struct tnbr *tnbr = NULL; + + r = tlv_decode_hello_prms(buf, len, &holdtime, &flags); + if (r == -1) { + log_debug("%s: lsr-id %s: failed to decode params", __func__, + inet_ntoa(lsr_id)); + return; + } + /* safety checks */ + if (holdtime != 0 && holdtime < MIN_HOLDTIME) { + log_debug("%s: lsr-id %s: invalid hello holdtime (%u)", + __func__, inet_ntoa(lsr_id), holdtime); + return; + } + if (multicast && (flags & F_HELLO_TARGETED)) { + log_debug("%s: lsr-id %s: multicast targeted hello", __func__, + inet_ntoa(lsr_id)); + return; + } + if (!multicast && !((flags & F_HELLO_TARGETED))) { + log_debug("%s: lsr-id %s: unicast link hello", __func__, + inet_ntoa(lsr_id)); + return; + } + buf += r; + len -= r; + + r = tlv_decode_opt_hello_prms(buf, len, &tlvs_rcvd, af, &trans_addr, + &conf_seqnum, &trans_pref); + if (r == -1) { + log_debug("%s: lsr-id %s: failed to decode optional params", + __func__, inet_ntoa(lsr_id)); + return; + } + if (r != len) { + log_debug("%s: lsr-id %s: unexpected data in message", + __func__, inet_ntoa(lsr_id)); + return; + } + + /* implicit transport address */ + if (!(tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)) + trans_addr = *src; + if (bad_addr(af, &trans_addr)) { + log_debug("%s: lsr-id %s: invalid transport address %s", + __func__, inet_ntoa(lsr_id), log_addr(af, &trans_addr)); + return; + } + if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&trans_addr.v6)) { + /* + * RFC 7552 - Section 6.1: + * "An LSR MUST use a global unicast IPv6 address in an IPv6 + * Transport Address optional object of outgoing targeted + * Hellos and check for the same in incoming targeted Hellos + * (i.e., MUST discard the targeted Hello if it failed the + * check)". + */ + if (flags & F_HELLO_TARGETED) { + log_debug("%s: lsr-id %s: invalid targeted hello " + "transport address %s", __func__, inet_ntoa(lsr_id), + log_addr(af, &trans_addr)); + return; + } + scope_id = iface->ifindex; + } + + memset(&source, 0, sizeof(source)); + if (flags & F_HELLO_TARGETED) { + /* + * RFC 7552 - Section 5.2: + * "The link-local IPv6 addresses MUST NOT be used as the + * targeted LDP Hello packet's source or destination addresses". + */ + if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&src->v6)) { + log_debug("%s: lsr-id %s: targeted hello with " + "link-local source address", __func__, + inet_ntoa(lsr_id)); + return; + } + + tnbr = tnbr_find(leconf, af, src); + + /* remove the dynamic tnbr if the 'R' bit was cleared */ + if (tnbr && (tnbr->flags & F_TNBR_DYNAMIC) && + !((flags & F_HELLO_REQ_TARG))) { + tnbr->flags &= ~F_TNBR_DYNAMIC; + tnbr = tnbr_check(tnbr); + } + + if (!tnbr) { + if (!((flags & F_HELLO_REQ_TARG) && + ((ldp_af_conf_get(leconf, af))->flags & + F_LDPD_AF_THELLO_ACCEPT))) + return; + + tnbr = tnbr_new(leconf, af, src); + tnbr->flags |= F_TNBR_DYNAMIC; + tnbr_update(tnbr); + LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry); + } + + source.type = HELLO_TARGETED; + source.target = tnbr; + } else { + ia = iface_af_get(iface, af); + source.type = HELLO_LINK; + source.link.ia = ia; + source.link.src_addr = *src; + } + + adj = adj_find(&source); + nbr = nbr_find_ldpid(lsr_id.s_addr); + + /* check dual-stack tlv */ + ds_tlv = (tlvs_rcvd & F_HELLO_TLV_RCVD_DS) ? 1 : 0; + if (ds_tlv && trans_pref != leconf->trans_pref) { + /* + * RFC 7552 - Section 6.1.1: + * "If the Dual-Stack capability TLV is present and the remote + * preference does not match the local preference (or does not + * get recognized), then the LSR MUST discard the Hello message + * and log an error. + * If an LDP session was already in place, then the LSR MUST + * send a fatal Notification message with status code of + * 'Transport Connection Mismatch' and reset the session". + */ + log_debug("%s: lsr-id %s: remote transport preference does not " + "match the local preference", __func__, inet_ntoa(lsr_id)); + if (nbr) + session_shutdown(nbr, S_TRANS_MISMTCH, msg->id, + msg->type); + if (adj) + adj_del(adj, S_SHUTDOWN); + return; + } + + /* + * Check for noncompliant dual-stack neighbor according to + * RFC 7552 section 6.1.1. + */ + if (nbr && !ds_tlv) { + switch (af) { + case AF_INET: + if (nbr_adj_count(nbr, AF_INET6) > 0) { + session_shutdown(nbr, S_DS_NONCMPLNCE, + msg->id, msg->type); + return; + } + break; + case AF_INET6: + if (nbr_adj_count(nbr, AF_INET) > 0) { + session_shutdown(nbr, S_DS_NONCMPLNCE, + msg->id, msg->type); + return; + } + break; + default: + fatalx("recv_hello: unknown af"); + } + } + + /* + * Protections against misconfigured networks and buggy implementations. + */ + if (nbr && nbr->af == af && + (ldp_addrcmp(af, &nbr->raddr, &trans_addr) || + nbr->raddr_scope != scope_id)) { + log_warnx("%s: lsr-id %s: hello packet advertising a different " + "transport address", __func__, inet_ntoa(lsr_id)); + if (adj) + adj_del(adj, S_SHUTDOWN); + return; + } + if (nbr == NULL) { + nbrt = nbr_find_addr(af, &trans_addr); + if (nbrt) { + log_debug("%s: transport address %s is already being " + "used by lsr-id %s", __func__, log_addr(af, + &trans_addr), inet_ntoa(nbrt->id)); + if (adj) + adj_del(adj, S_SHUTDOWN); + return; + } + } + + if (adj == NULL) { + adj = adj_new(lsr_id, &source, &trans_addr); + if (nbr) { + adj->nbr = nbr; + LIST_INSERT_HEAD(&nbr->adj_list, adj, nbr_entry); + } + } + + /* + * If the hello adjacency's address-family doesn't match the local + * preference, then an adjacency is still created but we don't attempt + * to start an LDP session. + */ + if (nbr == NULL && (!ds_tlv || + ((trans_pref == DUAL_STACK_LDPOV4 && af == AF_INET) || + (trans_pref == DUAL_STACK_LDPOV6 && af == AF_INET6)))) + nbr = nbr_new(lsr_id, af, ds_tlv, &trans_addr, scope_id); + + /* dynamic LDPv4 GTSM negotiation as per RFC 6720 */ + if (nbr) { + if (flags & F_HELLO_GTSM) + nbr->flags |= F_NBR_GTSM_NEGOTIATED; + else + nbr->flags &= ~F_NBR_GTSM_NEGOTIATED; + } + + /* update neighbor's configuration sequence number */ + if (nbr && (tlvs_rcvd & F_HELLO_TLV_RCVD_CONF)) { + if (conf_seqnum > nbr->conf_seqnum && + nbr_pending_idtimer(nbr)) + nbr_stop_idtimer(nbr); + nbr->conf_seqnum = conf_seqnum; + } + + /* always update the holdtime to properly handle runtime changes */ + switch (source.type) { + case HELLO_LINK: + if (holdtime == 0) + holdtime = LINK_DFLT_HOLDTIME; + + adj->holdtime = min(ia->hello_holdtime, holdtime); + break; + case HELLO_TARGETED: + if (holdtime == 0) + holdtime = TARGETED_DFLT_HOLDTIME; + + adj->holdtime = min(tnbr->hello_holdtime, holdtime); + } + if (adj->holdtime != INFINITE_HOLDTIME) + adj_start_itimer(adj); + else + adj_stop_itimer(adj); + + if (nbr && nbr->state == NBR_STA_PRESENT && !nbr_pending_idtimer(nbr) && + nbr_session_active_role(nbr) && !nbr_pending_connect(nbr)) + nbr_establish_connection(nbr); +} + +static int +gen_hello_prms_tlv(struct ibuf *buf, uint16_t holdtime, uint16_t flags) +{ + struct hello_prms_tlv parms; + + memset(&parms, 0, sizeof(parms)); + parms.type = htons(TLV_TYPE_COMMONHELLO); + parms.length = htons(sizeof(parms.holdtime) + sizeof(parms.flags)); + parms.holdtime = htons(holdtime); + parms.flags = htons(flags); + + return (ibuf_add(buf, &parms, sizeof(parms))); +} + +static int +gen_opt4_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint32_t value) +{ + struct hello_prms_opt4_tlv parms; + + memset(&parms, 0, sizeof(parms)); + parms.type = htons(type); + parms.length = htons(sizeof(parms.value)); + parms.value = value; + + return (ibuf_add(buf, &parms, sizeof(parms))); +} + +static int +gen_opt16_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint8_t *value) +{ + struct hello_prms_opt16_tlv parms; + + memset(&parms, 0, sizeof(parms)); + parms.type = htons(type); + parms.length = htons(sizeof(parms.value)); + memcpy(&parms.value, value, sizeof(parms.value)); + + return (ibuf_add(buf, &parms, sizeof(parms))); +} + +static int +gen_ds_hello_prms_tlv(struct ibuf *buf, uint32_t value) +{ + if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) + value = htonl(value); + else + value = htonl(value << 28); + + return (gen_opt4_hello_prms_tlv(buf, TLV_TYPE_DUALSTACK, value)); +} + +static int +tlv_decode_hello_prms(char *buf, uint16_t len, uint16_t *holdtime, + uint16_t *flags) +{ + struct hello_prms_tlv tlv; + + if (len < sizeof(tlv)) + return (-1); + memcpy(&tlv, buf, sizeof(tlv)); + + if (tlv.type != htons(TLV_TYPE_COMMONHELLO)) + return (-1); + if (ntohs(tlv.length) != sizeof(tlv) - TLV_HDR_SIZE) + return (-1); + + *holdtime = ntohs(tlv.holdtime); + *flags = ntohs(tlv.flags); + + return (sizeof(tlv)); +} + +static int +tlv_decode_opt_hello_prms(char *buf, uint16_t len, int *tlvs_rcvd, int af, + union ldpd_addr *addr, uint32_t *conf_number, uint16_t *trans_pref) +{ + struct tlv tlv; + uint16_t tlv_len; + int total = 0; + + *tlvs_rcvd = 0; + memset(addr, 0, sizeof(*addr)); + *conf_number = 0; + *trans_pref = 0; + + /* + * RFC 7552 - Section 6.1: + * "An LSR SHOULD accept the Hello message that contains both IPv4 and + * IPv6 Transport Address optional objects but MUST use only the + * transport address whose address family is the same as that of the + * IP packet carrying the Hello message. An LSR SHOULD accept only + * the first Transport Address optional object for a given address + * family in the received Hello message and ignore the rest if the + * LSR receives more than one Transport Address optional object for a + * given address family". + */ + while (len >= sizeof(tlv)) { + memcpy(&tlv, buf, TLV_HDR_SIZE); + tlv_len = ntohs(tlv.length); + if (tlv_len + TLV_HDR_SIZE > len) + return (-1); + buf += TLV_HDR_SIZE; + len -= TLV_HDR_SIZE; + total += TLV_HDR_SIZE; + + switch (ntohs(tlv.type)) { + case TLV_TYPE_IPV4TRANSADDR: + if (tlv_len != sizeof(addr->v4)) + return (-1); + if (af != AF_INET) + return (-1); + if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR) + break; + memcpy(&addr->v4, buf, sizeof(addr->v4)); + *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR; + break; + case TLV_TYPE_IPV6TRANSADDR: + if (tlv_len != sizeof(addr->v6)) + return (-1); + if (af != AF_INET6) + return (-1); + if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR) + break; + memcpy(&addr->v6, buf, sizeof(addr->v6)); + *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR; + break; + case TLV_TYPE_CONFIG: + if (tlv_len != sizeof(uint32_t)) + return (-1); + memcpy(conf_number, buf, sizeof(uint32_t)); + *tlvs_rcvd |= F_HELLO_TLV_RCVD_CONF; + break; + case TLV_TYPE_DUALSTACK: + if (tlv_len != sizeof(uint32_t)) + return (-1); + /* + * RFC 7552 - Section 6.1: + * "A Single-stack LSR does not need to use the + * Dual-Stack capability in Hello messages and SHOULD + * ignore this capability if received". + */ + if (!ldp_is_dual_stack(leconf)) + break; + /* Shame on you, Cisco! */ + if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) { + memcpy(trans_pref, buf + sizeof(uint16_t), + sizeof(uint16_t)); + *trans_pref = ntohs(*trans_pref); + } else { + memcpy(trans_pref, buf , sizeof(uint16_t)); + *trans_pref = ntohs(*trans_pref) >> 12; + } + *tlvs_rcvd |= F_HELLO_TLV_RCVD_DS; + break; + default: + /* if unknown flag set, ignore TLV */ + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + return (-1); + break; + } + buf += tlv_len; + len -= tlv_len; + total += tlv_len; + } + + return (total); +} diff --git a/ldpd/init.c b/ldpd/init.c new file mode 100644 index 0000000000..eb22bf52ac --- /dev/null +++ b/ldpd/init.c @@ -0,0 +1,166 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static int gen_init_prms_tlv(struct ibuf *, struct nbr *); + +void +send_init(struct nbr *nbr) +{ + 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 + SESS_PRMS_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); + if (err) { + ibuf_free(buf); + return; + } + + evbuf_enqueue(&nbr->tcp->wbuf, buf); +} + +int +recv_init(struct nbr *nbr, char *buf, uint16_t len) +{ + struct ldp_msg msg; + struct sess_prms_tlv sess; + uint16_t max_pdu_len; + + log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + + memcpy(&msg, buf, sizeof(msg)); + buf += LDP_MSG_SIZE; + len -= LDP_MSG_SIZE; + + if (len < SESS_PRMS_SIZE) { + session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type); + return (-1); + } + memcpy(&sess, buf, sizeof(sess)); + if (ntohs(sess.length) != SESS_PRMS_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + return (-1); + } + if (ntohs(sess.proto_version) != LDP_VERSION) { + session_shutdown(nbr, S_BAD_PROTO_VER, msg.id, msg.type); + return (-1); + } + if (ntohs(sess.keepalive_time) < MIN_KEEPALIVE) { + session_shutdown(nbr, S_KEEPALIVE_BAD, msg.id, msg.type); + return (-1); + } + if (sess.lsr_id != leconf->rtr_id.s_addr || + ntohs(sess.lspace_id) != 0) { + session_shutdown(nbr, S_NO_HELLO, msg.id, msg.type); + return (-1); + } + + buf += SESS_PRMS_SIZE; + len -= SESS_PRMS_SIZE; + + /* Optional Parameters */ + while (len > 0) { + struct tlv tlv; + 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_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 (ntohs(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); + default: + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + send_notification_nbr(nbr, S_UNKNOWN_TLV, + msg.id, msg.type); + /* ignore unknown tlv */ + break; + } + buf += tlv_len; + len -= tlv_len; + } + + nbr->keepalive = min(nbr_get_keepalive(nbr->af, nbr->id), + ntohs(sess.keepalive_time)); + + max_pdu_len = ntohs(sess.max_pdu_len); + /* + * RFC 5036 - Section 3.5.3: + * "A value of 255 or less specifies the default maximum length of + * 4096 octets". + */ + if (max_pdu_len <= 255) + max_pdu_len = LDP_MAX_LEN; + nbr->max_pdu_len = min(max_pdu_len, LDP_MAX_LEN); + + nbr_fsm(nbr, NBR_EVT_INIT_RCVD); + + return (0); +} + +static int +gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr) +{ + struct sess_prms_tlv parms; + + memset(&parms, 0, sizeof(parms)); + parms.type = htons(TLV_TYPE_COMMONSESSION); + parms.length = htons(SESS_PRMS_LEN); + parms.proto_version = htons(LDP_VERSION); + parms.keepalive_time = htons(nbr_get_keepalive(nbr->af, nbr->id)); + parms.reserved = 0; + parms.pvlim = 0; + parms.max_pdu_len = 0; + parms.lsr_id = nbr->id.s_addr; + parms.lspace_id = 0; + + return (ibuf_add(buf, &parms, SESS_PRMS_SIZE)); +} diff --git a/ldpd/interface.c b/ldpd/interface.c new file mode 100644 index 0000000000..ff4c8f169c --- /dev/null +++ b/ldpd/interface.c @@ -0,0 +1,531 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static struct if_addr *if_addr_new(struct kaddr *); +static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *); +static int if_start(struct iface *, int); +static int if_reset(struct iface *, int); +static void if_update_af(struct iface_af *, int); +static void if_hello_timer(int, short, void *); +static void if_start_hello_timer(struct iface_af *); +static void if_stop_hello_timer(struct iface_af *); +static int if_join_ipv4_group(struct iface *, struct in_addr *); +static int if_leave_ipv4_group(struct iface *, struct in_addr *); +static int if_join_ipv6_group(struct iface *, struct in6_addr *); +static int if_leave_ipv6_group(struct iface *, struct in6_addr *); + +struct iface * +if_new(struct kif *kif) +{ + struct iface *iface; + + if ((iface = calloc(1, sizeof(*iface))) == NULL) + fatal("if_new: calloc"); + + strlcpy(iface->name, kif->ifname, sizeof(iface->name)); + + /* get type */ + if (kif->flags & IFF_POINTOPOINT) + iface->type = IF_TYPE_POINTOPOINT; + if (kif->flags & IFF_BROADCAST && + kif->flags & IFF_MULTICAST) + iface->type = IF_TYPE_BROADCAST; + + /* get index and flags */ + LIST_INIT(&iface->addr_list); + iface->ifindex = kif->ifindex; + iface->flags = kif->flags; + iface->linkstate = kif->link_state; + iface->if_type = kif->if_type; + + /* ipv4 */ + iface->ipv4.af = AF_INET; + iface->ipv4.iface = iface; + iface->ipv4.enabled = 0; + iface->ipv4.state = IF_STA_DOWN; + LIST_INIT(&iface->ipv4.adj_list); + + /* ipv6 */ + iface->ipv6.af = AF_INET6; + iface->ipv6.iface = iface; + iface->ipv6.enabled = 0; + iface->ipv6.state = IF_STA_DOWN; + LIST_INIT(&iface->ipv6.adj_list); + + return (iface); +} + +struct iface * +if_lookup(struct ldpd_conf *xconf, unsigned short ifindex) +{ + struct iface *iface; + + LIST_FOREACH(iface, &xconf->iface_list, entry) + if (iface->ifindex == ifindex) + return (iface); + + return (NULL); +} + +void +if_exit(struct iface *iface) +{ + struct if_addr *if_addr; + + log_debug("%s: interface %s", __func__, iface->name); + + if (iface->ipv4.state == IF_STA_ACTIVE) + if_reset(iface, AF_INET); + if (iface->ipv6.state == IF_STA_ACTIVE) + if_reset(iface, AF_INET6); + + while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) { + LIST_REMOVE(if_addr, entry); + free(if_addr); + } +} + +struct iface_af * +iface_af_get(struct iface *iface, int af) +{ + switch (af) { + case AF_INET: + return (&iface->ipv4); + case AF_INET6: + return (&iface->ipv6); + default: + fatalx("iface_af_get: unknown af"); + } +} + +static struct if_addr * +if_addr_new(struct kaddr *ka) +{ + struct if_addr *if_addr; + + if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL) + fatal(__func__); + + if_addr->af = ka->af; + if_addr->addr = ka->addr; + if_addr->prefixlen = ka->prefixlen; + if_addr->dstbrd = ka->dstbrd; + + return (if_addr); +} + +static struct if_addr * +if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka) +{ + struct if_addr *if_addr; + int af = ka->af; + + LIST_FOREACH(if_addr, addr_list, entry) + if (!ldp_addrcmp(af, &if_addr->addr, &ka->addr) && + if_addr->prefixlen == ka->prefixlen && + !ldp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd)) + return (if_addr); + + return (NULL); +} + +void +if_addr_add(struct kaddr *ka) +{ + struct iface *iface; + struct if_addr *if_addr; + struct nbr *nbr; + + if (if_addr_lookup(&global.addr_list, ka) == NULL) { + if_addr = if_addr_new(ka); + + LIST_INSERT_HEAD(&global.addr_list, if_addr, entry); + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->state != NBR_STA_OPER) + continue; + if (if_addr->af == AF_INET && !nbr->v4_enabled) + continue; + if (if_addr->af == AF_INET6 && !nbr->v6_enabled) + continue; + + send_address_single(nbr, if_addr, 0); + } + } + + iface = if_lookup(leconf, ka->ifindex); + if (iface) { + if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6)) + iface->linklocal = ka->addr.v6; + + if (if_addr_lookup(&iface->addr_list, ka) == NULL) { + if_addr = if_addr_new(ka); + LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry); + if_update(iface, if_addr->af); + } + } +} + +void +if_addr_del(struct kaddr *ka) +{ + struct iface *iface; + struct if_addr *if_addr; + struct nbr *nbr; + + iface = if_lookup(leconf, ka->ifindex); + if (iface) { + if (ka->af == AF_INET6 && + IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6)) + memset(&iface->linklocal, 0, sizeof(iface->linklocal)); + + if_addr = if_addr_lookup(&iface->addr_list, ka); + if (if_addr) { + LIST_REMOVE(if_addr, entry); + if_update(iface, if_addr->af); + free(if_addr); + } + } + + if_addr = if_addr_lookup(&global.addr_list, ka); + if (if_addr) { + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->state != NBR_STA_OPER) + continue; + if (if_addr->af == AF_INET && !nbr->v4_enabled) + continue; + if (if_addr->af == AF_INET6 && !nbr->v6_enabled) + continue; + send_address_single(nbr, if_addr, 1); + } + LIST_REMOVE(if_addr, entry); + free(if_addr); + } +} + +static int +if_start(struct iface *iface, int af) +{ + struct iface_af *ia; + struct timeval now; + + log_debug("%s: %s address-family %s", __func__, iface->name, + af_name(af)); + + ia = iface_af_get(iface, af); + + gettimeofday(&now, NULL); + ia->uptime = now.tv_sec; + + switch (af) { + case AF_INET: + if (if_join_ipv4_group(iface, &global.mcast_addr_v4)) + return (-1); + break; + case AF_INET6: + if (if_join_ipv6_group(iface, &global.mcast_addr_v6)) + return (-1); + break; + default: + fatalx("if_start: unknown af"); + } + + send_hello(HELLO_LINK, ia, NULL); + + evtimer_set(&ia->hello_timer, if_hello_timer, ia); + if_start_hello_timer(ia); + return (0); +} + +static int +if_reset(struct iface *iface, int af) +{ + struct iface_af *ia; + struct adj *adj; + + log_debug("%s: %s address-family %s", __func__, iface->name, + af_name(af)); + + ia = iface_af_get(iface, af); + if_stop_hello_timer(ia); + + while ((adj = LIST_FIRST(&ia->adj_list)) != NULL) + adj_del(adj, S_SHUTDOWN); + + /* try to cleanup */ + switch (af) { + case AF_INET: + if (global.ipv4.ldp_disc_socket != -1) + if_leave_ipv4_group(iface, &global.mcast_addr_v4); + break; + case AF_INET6: + if (global.ipv6.ldp_disc_socket != -1) + if_leave_ipv6_group(iface, &global.mcast_addr_v6); + break; + default: + fatalx("if_start: unknown af"); + } + + return (0); +} + +static void +if_update_af(struct iface_af *ia, int link_ok) +{ + int addr_ok = 0, socket_ok, rtr_id_ok; + struct if_addr *if_addr; + + switch (ia->af) { + case AF_INET: + /* + * NOTE: for LDPv4, each interface should have at least one + * valid IP address otherwise they can not be enabled. + */ + LIST_FOREACH(if_addr, &ia->iface->addr_list, entry) { + if (if_addr->af == AF_INET) { + addr_ok = 1; + break; + } + } + break; + case AF_INET6: + /* for IPv6 the link-local address is enough. */ + if (IN6_IS_ADDR_LINKLOCAL(&ia->iface->linklocal)) + addr_ok = 1; + break; + default: + fatalx("if_update_af: unknown af"); + } + + if ((ldp_af_global_get(&global, ia->af))->ldp_disc_socket != -1) + socket_ok = 1; + else + socket_ok = 0; + + if (leconf->rtr_id.s_addr != INADDR_ANY) + rtr_id_ok = 1; + else + rtr_id_ok = 0; + + if (ia->state == IF_STA_DOWN) { + if (!ia->enabled || !link_ok || !addr_ok || !socket_ok || + !rtr_id_ok) + return; + + ia->state = IF_STA_ACTIVE; + if_start(ia->iface, ia->af); + } else if (ia->state == IF_STA_ACTIVE) { + if (ia->enabled && link_ok && addr_ok && socket_ok && rtr_id_ok) + return; + + ia->state = IF_STA_DOWN; + if_reset(ia->iface, ia->af); + } +} + +void +if_update(struct iface *iface, int af) +{ + int link_ok; + + link_ok = (iface->flags & IFF_UP) && + LINK_STATE_IS_UP(iface->linkstate); + + if (af == AF_INET || af == AF_UNSPEC) + if_update_af(&iface->ipv4, link_ok); + if (af == AF_INET6 || af == AF_UNSPEC) + if_update_af(&iface->ipv6, link_ok); +} + +void +if_update_all(int af) +{ + struct iface *iface; + + LIST_FOREACH(iface, &leconf->iface_list, entry) + if_update(iface, af); +} + +/* timers */ +/* ARGSUSED */ +static void +if_hello_timer(int fd, short event, void *arg) +{ + struct iface_af *ia = arg; + + send_hello(HELLO_LINK, ia, NULL); + if_start_hello_timer(ia); +} + +static void +if_start_hello_timer(struct iface_af *ia) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = ia->hello_interval; + if (evtimer_add(&ia->hello_timer, &tv) == -1) + fatal(__func__); +} + +static void +if_stop_hello_timer(struct iface_af *ia) +{ + if (evtimer_pending(&ia->hello_timer, NULL) && + evtimer_del(&ia->hello_timer) == -1) + fatal(__func__); +} + +struct ctl_iface * +if_to_ctl(struct iface_af *ia) +{ + static struct ctl_iface ictl; + struct timeval now; + struct adj *adj; + + ictl.af = ia->af; + memcpy(ictl.name, ia->iface->name, sizeof(ictl.name)); + ictl.ifindex = ia->iface->ifindex; + ictl.state = ia->state; + ictl.flags = ia->iface->flags; + ictl.linkstate = ia->iface->linkstate; + ictl.type = ia->iface->type; + ictl.if_type = ia->iface->if_type; + ictl.hello_holdtime = ia->hello_holdtime; + ictl.hello_interval = ia->hello_interval; + + gettimeofday(&now, NULL); + if (ia->state != IF_STA_DOWN && + ia->uptime != 0) { + ictl.uptime = now.tv_sec - ia->uptime; + } else + ictl.uptime = 0; + + ictl.adj_cnt = 0; + LIST_FOREACH(adj, &ia->adj_list, ia_entry) + ictl.adj_cnt++; + + return (&ictl); +} + +/* multicast membership sockopts */ +in_addr_t +if_get_ipv4_addr(struct iface *iface) +{ + struct if_addr *if_addr; + + LIST_FOREACH(if_addr, &iface->addr_list, entry) + if (if_addr->af == AF_INET) + return (if_addr->addr.v4.s_addr); + + return (INADDR_ANY); +} + +static int +if_join_ipv4_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + + log_debug("%s: interface %s addr %s", __func__, iface->name, + inet_ntoa(*addr)); + + mreq.imr_multiaddr = *addr; + mreq.imr_interface.s_addr = if_get_ipv4_addr(iface); + + if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, + IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { + log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s", + __func__, iface->name, inet_ntoa(*addr)); + return (-1); + } + return (0); +} + +static int +if_leave_ipv4_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + + log_debug("%s: interface %s addr %s", __func__, iface->name, + inet_ntoa(*addr)); + + mreq.imr_multiaddr = *addr; + mreq.imr_interface.s_addr = if_get_ipv4_addr(iface); + + if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, + IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { + log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s " + "address %s", __func__, iface->name, inet_ntoa(*addr)); + return (-1); + } + + return (0); +} + +static int +if_join_ipv6_group(struct iface *iface, struct in6_addr *addr) +{ + struct ipv6_mreq mreq; + + log_debug("%s: interface %s addr %s", __func__, iface->name, + log_in6addr(addr)); + + mreq.ipv6mr_multiaddr = *addr; + mreq.ipv6mr_interface = iface->ifindex; + + if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6, + IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { + log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s", + __func__, iface->name, log_in6addr(addr)); + return (-1); + } + + return (0); +} + +static int +if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr) +{ + struct ipv6_mreq mreq; + + log_debug("%s: interface %s addr %s", __func__, iface->name, + log_in6addr(addr)); + + mreq.ipv6mr_multiaddr = *addr; + mreq.ipv6mr_interface = iface->ifindex; + + if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6, + IPV6_LEAVE_GROUP, (void *)&mreq, sizeof(mreq)) < 0) { + log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s", + __func__, iface->name, log_in6addr(addr)); + return (-1); + } + + return (0); +} diff --git a/ldpd/keepalive.c b/ldpd/keepalive.c new file mode 100644 index 0000000000..4cd49d485b --- /dev/null +++ b/ldpd/keepalive.c @@ -0,0 +1,58 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +void +send_keepalive(struct nbr *nbr) +{ + struct ibuf *buf; + uint16_t size; + + size = LDP_HDR_SIZE + LDP_MSG_SIZE; + if ((buf = ibuf_open(size)) == NULL) + fatal(__func__); + + gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + gen_msg_hdr(buf, MSG_TYPE_KEEPALIVE, size); + + evbuf_enqueue(&nbr->tcp->wbuf, buf); +} + +int +recv_keepalive(struct nbr *nbr, char *buf, uint16_t len) +{ + struct ldp_msg msg; + + memcpy(&msg, buf, sizeof(msg)); + if (len != LDP_MSG_SIZE) { + session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type); + return (-1); + } + + if (nbr->state != NBR_STA_OPER) + nbr_fsm(nbr, NBR_EVT_KEEPALIVE_RCVD); + + return (0); +} diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c new file mode 100644 index 0000000000..22c98745e2 --- /dev/null +++ b/ldpd/l2vpn.c @@ -0,0 +1,510 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +static void l2vpn_pw_fec(struct l2vpn_pw *, struct fec *); + +struct l2vpn * +l2vpn_new(const char *name) +{ + struct l2vpn *l2vpn; + + if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL) + fatal("l2vpn_new: calloc"); + + strlcpy(l2vpn->name, name, sizeof(l2vpn->name)); + + /* set default values */ + l2vpn->mtu = DEFAULT_L2VPN_MTU; + l2vpn->pw_type = DEFAULT_PW_TYPE; + + LIST_INIT(&l2vpn->if_list); + LIST_INIT(&l2vpn->pw_list); + + return (l2vpn); +} + +struct l2vpn * +l2vpn_find(struct ldpd_conf *xconf, const char *name) +{ + struct l2vpn *l2vpn; + + LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) + if (strcmp(l2vpn->name, name) == 0) + return (l2vpn); + + return (NULL); +} + +void +l2vpn_del(struct l2vpn *l2vpn) +{ + struct l2vpn_if *lif; + struct l2vpn_pw *pw; + + while ((lif = LIST_FIRST(&l2vpn->if_list)) != NULL) { + LIST_REMOVE(lif, entry); + free(lif); + } + while ((pw = LIST_FIRST(&l2vpn->pw_list)) != NULL) { + LIST_REMOVE(pw, entry); + free(pw); + } + + free(l2vpn); +} + +void +l2vpn_init(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + l2vpn_pw_init(pw); +} + +void +l2vpn_exit(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + l2vpn_pw_exit(pw); +} + +struct l2vpn_if * +l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif) +{ + struct l2vpn_if *lif; + + if ((lif = calloc(1, sizeof(*lif))) == NULL) + fatal("l2vpn_if_new: calloc"); + + lif->l2vpn = l2vpn; + strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname)); + lif->ifindex = kif->ifindex; + lif->flags = kif->flags; + lif->link_state = kif->link_state; + + return (lif); +} + +struct l2vpn_if * +l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex) +{ + struct l2vpn_if *lif; + + LIST_FOREACH(lif, &l2vpn->if_list, entry) + if (lif->ifindex == ifindex) + return (lif); + + return (NULL); +} + +struct l2vpn_pw * +l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif) +{ + struct l2vpn_pw *pw; + + if ((pw = calloc(1, sizeof(*pw))) == NULL) + fatal("l2vpn_pw_new: calloc"); + + pw->l2vpn = l2vpn; + strlcpy(pw->ifname, kif->ifname, sizeof(pw->ifname)); + pw->ifindex = kif->ifindex; + + return (pw); +} + +struct l2vpn_pw * +l2vpn_pw_find(struct l2vpn *l2vpn, unsigned int ifindex) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + if (pw->ifindex == ifindex) + return (pw); + + return (NULL); +} + +void +l2vpn_pw_init(struct l2vpn_pw *pw) +{ + struct fec fec; + + l2vpn_pw_reset(pw); + + l2vpn_pw_fec(pw, &fec); + lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, + 0, (void *)pw); +} + +void +l2vpn_pw_exit(struct l2vpn_pw *pw) +{ + struct fec fec; + + l2vpn_pw_fec(pw, &fec); + lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0); +} + +static void +l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec) +{ + memset(fec, 0, sizeof(*fec)); + fec->type = FEC_TYPE_PWID; + fec->u.pwid.type = pw->l2vpn->pw_type; + fec->u.pwid.pwid = pw->pwid; + fec->u.pwid.lsr_id = pw->lsr_id; +} + +void +l2vpn_pw_reset(struct l2vpn_pw *pw) +{ + pw->remote_group = 0; + pw->remote_mtu = 0; + pw->remote_status = 0; + + if (pw->flags & F_PW_CWORD_CONF) + pw->flags |= F_PW_CWORD; + else + pw->flags &= ~F_PW_CWORD; + + if (pw->flags & F_PW_STATUSTLV_CONF) + pw->flags |= F_PW_STATUSTLV; + else + pw->flags &= ~F_PW_STATUSTLV; +} + +int +l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) +{ + struct fec fec; + struct fec_node *fn; + + /* check for a remote label */ + if (fnh->remote_label == NO_LABEL) + return (0); + + /* MTUs must match */ + if (pw->l2vpn->mtu != pw->remote_mtu) + return (0); + + /* check pw status if applicable */ + if ((pw->flags & F_PW_STATUSTLV) && + pw->remote_status != PW_FORWARDING) + return (0); + + /* check for a working lsp to the nexthop */ + memset(&fec, 0, sizeof(fec)); + switch (pw->af) { + case AF_INET: + fec.type = FEC_TYPE_IPV4; + fec.u.ipv4.prefix = pw->addr.v4; + fec.u.ipv4.prefixlen = 32; + break; + case AF_INET6: + fec.type = FEC_TYPE_IPV6; + fec.u.ipv6.prefix = pw->addr.v6; + fec.u.ipv6.prefixlen = 128; + break; + default: + fatalx("l2vpn_pw_ok: unknown af"); + } + + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL || fn->local_label == NO_LABEL) + return (0); + /* + * Need to ensure that there's a label binding for all nexthops. + * Otherwise, ECMP for this route could render the pseudowire unusable. + */ + LIST_FOREACH(fnh, &fn->nexthops, entry) + if (fnh->remote_label == NO_LABEL) + return (0); + + return (1); +} + +int +l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map) +{ + struct l2vpn_pw *pw; + struct status_tlv st; + + /* NOTE: thanks martini & friends for all this mess */ + + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL) + /* + * pseudowire not configured, return and record + * the mapping later + */ + return (0); + + /* RFC4447 - Section 6.2: control word negotiation */ + if (fec_find(&ln->sent_map, &fn->fec)) { + if ((map->flags & F_MAP_PW_CWORD) && + !(pw->flags & F_PW_CWORD_CONF)) { + /* ignore the received label mapping */ + return (1); + } else if (!(map->flags & F_MAP_PW_CWORD) && + (pw->flags & F_PW_CWORD_CONF)) { + /* append a "Wrong C-bit" status code */ + 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); + + pw->flags &= ~F_PW_CWORD; + lde_send_labelmapping(ln, fn, 1); + } + } else if (map->flags & F_MAP_PW_CWORD) { + if (pw->flags & F_PW_CWORD_CONF) + pw->flags |= F_PW_CWORD; + else + /* act as if no label mapping had been received */ + return (1); + } else + pw->flags &= ~F_PW_CWORD; + + /* RFC4447 - Section 5.4.3: pseudowire status negotiation */ + if (fec_find(&ln->recv_map, &fn->fec) == NULL && + !(map->flags & F_MAP_PW_STATUS)) + pw->flags &= ~F_PW_STATUSTLV; + + return (0); +} + +void +l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec) +{ + 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; + lde_fec2map(fec, &nm.fec); + nm.flags |= F_NOTIF_FEC; + + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0, + &nm, sizeof(nm)); +} + +void +l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) +{ + struct fec fec; + struct fec_node *fn; + struct fec_nh *fnh; + struct l2vpn_pw *pw; + + /* TODO group wildcard */ + if (!(nm->fec.flags & F_MAP_PW_ID)) + return; + + lde_map2fec(&nm->fec, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + /* unknown fec */ + return; + + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL) + return; + + fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0); + if (fnh == NULL) + return; + + /* 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)) + lde_send_change_klabel(fn, fnh); + else + lde_send_delete_klabel(fn, fnh); +} + +void +l2vpn_sync_pws(int af, union ldpd_addr *addr) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + struct fec fec; + struct fec_node *fn; + struct fec_nh *fnh; + + LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) { + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + if (af != pw->af || ldp_addrcmp(af, &pw->addr, addr)) + continue; + + l2vpn_pw_fec(pw, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + continue; + fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *) + &pw->lsr_id, 0); + if (fnh == NULL) + continue; + + if (l2vpn_pw_ok(pw, fnh)) + lde_send_change_klabel(fn, fnh); + else + lde_send_delete_klabel(fn, fnh); + } + } +} + +void +l2vpn_pw_ctl(pid_t pid) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + static struct ctl_pw pwctl; + + LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + memset(&pwctl, 0, sizeof(pwctl)); + strlcpy(pwctl.ifname, pw->ifname, + sizeof(pwctl.ifname)); + pwctl.pwid = pw->pwid; + pwctl.lsr_id = pw->lsr_id; + pwctl.status = pw->flags & F_PW_STATUS_UP; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0, + pid, &pwctl, sizeof(pwctl)); + } +} + +void +l2vpn_binding_ctl(pid_t pid) +{ + struct fec *f; + struct fec_node *fn; + struct lde_map *me; + struct l2vpn_pw *pw; + static struct ctl_pw pwctl; + + RB_FOREACH(f, fec_tree, &ft) { + if (f->type != FEC_TYPE_PWID) + continue; + + fn = (struct fec_node *)f; + if (fn->local_label == NO_LABEL && + LIST_EMPTY(&fn->downstream)) + continue; + + memset(&pwctl, 0, sizeof(pwctl)); + pwctl.type = f->u.pwid.type; + pwctl.pwid = f->u.pwid.pwid; + pwctl.lsr_id = f->u.pwid.lsr_id; + + pw = (struct l2vpn_pw *) fn->data; + if (pw) { + pwctl.local_label = fn->local_label; + pwctl.local_gid = 0; + pwctl.local_ifmtu = pw->l2vpn->mtu; + } else + pwctl.local_label = NO_LABEL; + + LIST_FOREACH(me, &fn->downstream, entry) + if (f->u.pwid.lsr_id.s_addr == me->nexthop->id.s_addr) + break; + + if (me) { + pwctl.remote_label = me->map.label; + pwctl.remote_gid = me->map.fec.pwid.group_id; + if (me->map.flags & F_MAP_PW_IFMTU) + pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, + 0, pid, &pwctl, sizeof(pwctl)); + } else if (pw) { + pwctl.remote_label = NO_LABEL; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, + 0, pid, &pwctl, sizeof(pwctl)); + } + } +} + +/* ldpe */ + +void +ldpe_l2vpn_init(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + ldpe_l2vpn_pw_init(pw); +} + +void +ldpe_l2vpn_exit(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + ldpe_l2vpn_pw_exit(pw); +} + +void +ldpe_l2vpn_pw_init(struct l2vpn_pw *pw) +{ + struct tnbr *tnbr; + + tnbr = tnbr_find(leconf, pw->af, &pw->addr); + if (tnbr == NULL) { + tnbr = tnbr_new(leconf, pw->af, &pw->addr); + tnbr_update(tnbr); + LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry); + } + + tnbr->pw_count++; +} + +void +ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw) +{ + struct tnbr *tnbr; + + tnbr = tnbr_find(leconf, pw->af, &pw->addr); + if (tnbr) { + tnbr->pw_count--; + tnbr_check(tnbr); + } +} diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c new file mode 100644 index 0000000000..88e64071bb --- /dev/null +++ b/ldpd/labelmapping.c @@ -0,0 +1,764 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2014, 2015 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +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 +enqueue_pdu(struct nbr *nbr, struct ibuf *buf, uint16_t size) +{ + struct ldp_hdr *ldp_hdr; + + ldp_hdr = ibuf_seek(buf, 0, sizeof(struct ldp_hdr)); + ldp_hdr->length = htons(size); + evbuf_enqueue(&nbr->tcp->wbuf, buf); +} + +/* Generic function that handles all Label Message types */ +void +send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) +{ + struct ibuf *buf = NULL; + struct mapping_entry *me; + uint16_t msg_size, size = 0; + int first = 1; + int err = 0; + + /* nothing to send */ + if (TAILQ_EMPTY(mh)) + return; + + while ((me = TAILQ_FIRST(mh)) != NULL) { + /* generate pdu */ + if (first) { + if ((buf = ibuf_open(nbr->max_pdu_len + + LDP_HDR_DEAD_LEN)) == NULL) + fatal(__func__); + + /* real size will be set up later */ + err |= gen_ldp_hdr(buf, 0); + + size = LDP_HDR_PDU_LEN; + first = 0; + } + + /* 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; + } + if (me->map.label != NO_LABEL) + msg_size += LABEL_TLV_SIZE; + if (me->map.flags & F_MAP_REQ_ID) + msg_size += REQID_TLV_SIZE; + if (me->map.flags & F_MAP_STATUS) + msg_size += STATUS_SIZE; + + /* maximum pdu length exceeded, we need a new ldp pdu */ + if (size + msg_size > nbr->max_pdu_len) { + enqueue_pdu(nbr, buf, size); + first = 1; + continue; + } + + size += msg_size; + + /* append message and tlvs */ + err |= gen_msg_hdr(buf, type, msg_size); + err |= gen_fec_tlv(buf, &me->map); + if (me->map.label != NO_LABEL) + err |= gen_label_tlv(buf, me->map.label); + if (me->map.flags & F_MAP_REQ_ID) + err |= gen_reqid_tlv(buf, me->map.requestid); + if (me->map.flags & F_MAP_PW_STATUS) + err |= gen_pw_status_tlv(buf, me->map.pw_status); + if (me->map.flags & F_MAP_STATUS) + err |= gen_status_tlv(buf, me->map.st.status_code, + me->map.st.msg_id, me->map.st.msg_type); + if (err) { + ibuf_free(buf); + mapping_list_clr(mh); + return; + } + + log_debug("msg-out: %s: lsr-id %s, fec %s, label %s", + msg_name(type), inet_ntoa(nbr->id), log_map(&me->map), + log_label(me->map.label)); + + TAILQ_REMOVE(mh, me, entry); + free(me); + } + + enqueue_pdu(nbr, buf, size); + + nbr_fsm(nbr, NBR_EVT_PDU_SENT); +} + +/* Generic function that handles all Label Message types */ +int +recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) +{ + struct ldp_msg msg; + struct tlv ft; + uint32_t label = NO_LABEL, reqid = 0; + uint32_t pw_status = 0; + uint8_t flags = 0; + int feclen, lbllen, tlen; + struct mapping_entry *me; + struct mapping_head mh; + struct map map; + + memcpy(&msg, buf, sizeof(msg)); + buf += LDP_MSG_SIZE; + len -= LDP_MSG_SIZE; + + /* FEC TLV */ + if (len < sizeof(ft)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + return (-1); + } + + memcpy(&ft, buf, sizeof(ft)); + if (ntohs(ft.type) != TLV_TYPE_FEC) { + send_notification_nbr(nbr, S_MISS_MSG, msg.id, msg.type); + return (-1); + } + feclen = ntohs(ft.length); + if (feclen > len - TLV_HDR_SIZE) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + return (-1); + } + + buf += TLV_HDR_SIZE; /* just advance to the end of the fec header */ + len -= TLV_HDR_SIZE; + + TAILQ_INIT(&mh); + do { + memset(&map, 0, sizeof(map)); + map.msg_id = msg.id; + + if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, + &map)) == -1) + goto err; + if (map.type == MAP_TYPE_PWID && + !(map.flags & F_MAP_PW_ID) && + type != MSG_TYPE_LABELWITHDRAW && + type != MSG_TYPE_LABELRELEASE) { + send_notification_nbr(nbr, S_MISS_MSG, msg.id, + msg.type); + return (-1); + } + + /* + * The Wildcard FEC Element can be used only in the + * Label Withdraw and Label Release messages. + */ + if (map.type == MAP_TYPE_WILDCARD) { + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELREQUEST: + 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. + */ + if (type != MSG_TYPE_LABELMAPPING && + tlen != feclen) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + } + + mapping_list_add(&mh, &map); + + buf += tlen; + len -= tlen; + 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_len; + uint32_t reqbuf, labelbuf, statusbuf; + + if (len < sizeof(tlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + + memcpy(&tlv, buf, TLV_HDR_SIZE); + tlv_len = ntohs(tlv.length); + if (tlv_len + TLV_HDR_SIZE > len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + buf += TLV_HDR_SIZE; + len -= TLV_HDR_SIZE; + + switch (ntohs(tlv.type)) { + case TLV_TYPE_LABELREQUEST: + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELREQUEST: + if (tlv_len != REQID_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, + msg.id, msg.type); + goto err; + } + + flags |= F_MAP_REQ_ID; + memcpy(&reqbuf, buf, sizeof(reqbuf)); + reqid = ntohl(reqbuf); + break; + default: + /* ignore */ + break; + } + break; + case TLV_TYPE_HOPCOUNT: + case TLV_TYPE_PATHVECTOR: + /* ignore */ + break; + case TLV_TYPE_GENERICLABEL: + switch (type) { + case MSG_TYPE_LABELWITHDRAW: + case MSG_TYPE_LABELRELEASE: + if (tlv_len != LABEL_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, + msg.id, msg.type); + goto err; + } + + memcpy(&labelbuf, buf, sizeof(labelbuf)); + label = ntohl(labelbuf); + break; + default: + /* ignore */ + break; + } + break; + case TLV_TYPE_ATMLABEL: + case TLV_TYPE_FRLABEL: + switch (type) { + case MSG_TYPE_LABELWITHDRAW: + case MSG_TYPE_LABELRELEASE: + /* unsupported */ + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + goto err; + break; + default: + /* ignore */ + break; + } + break; + case TLV_TYPE_STATUS: + if (tlv_len != STATUS_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + goto err; + } + /* ignore */ + break; + case TLV_TYPE_PW_STATUS: + switch (type) { + case MSG_TYPE_LABELMAPPING: + if (tlv_len != PW_STATUS_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, + msg.id, msg.type); + goto err; + } + + flags |= F_MAP_PW_STATUS; + memcpy(&statusbuf, buf, sizeof(statusbuf)); + pw_status = ntohl(statusbuf); + break; + default: + /* ignore */ + break; + } + break; + default: + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + send_notification_nbr(nbr, S_UNKNOWN_TLV, + msg.id, msg.type); + /* ignore unknown tlv */ + break; + } + buf += tlv_len; + len -= tlv_len; + } + + /* notify lde about the received message. */ + while ((me = TAILQ_FIRST(&mh)) != NULL) { + int imsg_type = IMSG_NONE; + + me->map.flags |= flags; + switch (me->map.type) { + case MAP_TYPE_PREFIX: + switch (me->map.fec.prefix.af) { + case AF_INET: + if (label == MPLS_LABEL_IPV6NULL) { + session_shutdown(nbr, S_BAD_TLV_VAL, + msg.id, msg.type); + goto err; + } + if (!nbr->v4_enabled) + goto next; + break; + case AF_INET6: + if (label == MPLS_LABEL_IPV4NULL) { + session_shutdown(nbr, S_BAD_TLV_VAL, + msg.id, msg.type); + goto err; + } + if (!nbr->v6_enabled) + goto next; + break; + default: + fatalx("recv_labelmessage: unknown af"); + } + break; + case MAP_TYPE_PWID: + if (label <= MPLS_LABEL_RESERVED_MAX) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + goto err; + } + if (me->map.flags & F_MAP_PW_STATUS) + me->map.pw_status = pw_status; + break; + default: + break; + } + me->map.label = label; + if (me->map.flags & F_MAP_REQ_ID) + me->map.requestid = reqid; + + log_debug("msg-in: label mapping: lsr-id %s, fec %s, label %s", + inet_ntoa(nbr->id), log_map(&me->map), + log_label(me->map.label)); + + switch (type) { + case MSG_TYPE_LABELMAPPING: + imsg_type = IMSG_LABEL_MAPPING; + break; + case MSG_TYPE_LABELREQUEST: + imsg_type = IMSG_LABEL_REQUEST; + break; + case MSG_TYPE_LABELWITHDRAW: + imsg_type = IMSG_LABEL_WITHDRAW; + break; + case MSG_TYPE_LABELRELEASE: + imsg_type = IMSG_LABEL_RELEASE; + break; + case MSG_TYPE_LABELABORTREQ: + imsg_type = IMSG_LABEL_ABORT; + break; + default: + break; + } + + ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map, + sizeof(struct map)); + +next: + TAILQ_REMOVE(&mh, me, entry); + free(me); + } + + return (0); + +err: + mapping_list_clr(&mh); + + return (-1); +} + +/* Other TLV related functions */ +static int +gen_label_tlv(struct ibuf *buf, uint32_t label) +{ + struct label_tlv lt; + + lt.type = htons(TLV_TYPE_GENERICLABEL); + lt.length = htons(LABEL_TLV_LEN); + lt.label = htonl(label); + + return (ibuf_add(buf, <, sizeof(lt))); +} + +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(<, 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; + + rt.type = htons(TLV_TYPE_LABELREQUEST); + rt.length = htons(REQID_TLV_LEN); + rt.reqid = htonl(reqid); + + return (ibuf_add(buf, &rt, sizeof(rt))); +} + +int +gen_pw_status_tlv(struct ibuf *buf, uint32_t status) +{ + struct pw_status_tlv st; + + st.type = htons(TLV_TYPE_PW_STATUS); + st.length = htons(PW_STATUS_TLV_LEN); + st.value = htonl(status); + + return (ibuf_add(buf, &st, sizeof(st))); +} + +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; + uint32_t group_id, pwid; + int err = 0; + + ft.type = htons(TLV_TYPE_FEC); + + switch (map->type) { + case MAP_TYPE_WILDCARD: + ft.length = htons(sizeof(uint8_t)); + err |= ibuf_add(buf, &ft, sizeof(ft)); + err |= ibuf_add(buf, &map->type, sizeof(map->type)); + break; + case MAP_TYPE_PREFIX: + len = PREFIX_SIZE(map->fec.prefix.prefixlen); + ft.length = htons(sizeof(map->type) + sizeof(family) + + sizeof(map->fec.prefix.prefixlen) + len); + err |= ibuf_add(buf, &ft, sizeof(ft)); + err |= ibuf_add(buf, &map->type, sizeof(map->type)); + switch (map->fec.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(family)); + err |= ibuf_add(buf, &map->fec.prefix.prefixlen, + sizeof(map->fec.prefix.prefixlen)); + if (len) + err |= ibuf_add(buf, &map->fec.prefix.prefix, len); + break; + case MAP_TYPE_PWID: + if (map->flags & F_MAP_PW_ID) + pw_len += PW_STATUS_TLV_LEN; + if (map->flags & F_MAP_PW_IFMTU) + pw_len += FEC_SUBTLV_IFMTU_SIZE; + + len = FEC_PWID_ELM_MIN_LEN + pw_len; + + ft.length = htons(len); + err |= ibuf_add(buf, &ft, sizeof(ft)); + + err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); + pw_type = map->fec.pwid.type; + if (map->flags & F_MAP_PW_CWORD) + pw_type |= CONTROL_WORD_FLAG; + pw_type = htons(pw_type); + err |= ibuf_add(buf, &pw_type, sizeof(uint16_t)); + err |= ibuf_add(buf, &pw_len, sizeof(uint8_t)); + group_id = htonl(map->fec.pwid.group_id); + err |= ibuf_add(buf, &group_id, sizeof(uint32_t)); + if (map->flags & F_MAP_PW_ID) { + pwid = htonl(map->fec.pwid.pwid); + err |= ibuf_add(buf, &pwid, sizeof(uint32_t)); + } + if (map->flags & F_MAP_PW_IFMTU) { + struct subtlv stlv; + + stlv.type = SUBTLV_IFMTU; + stlv.length = FEC_SUBTLV_IFMTU_SIZE; + err |= ibuf_add(buf, &stlv, sizeof(uint16_t)); + + ifmtu = htons(map->fec.pwid.ifmtu); + err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t)); + } + break; + default: + break; + } + + return (err); +} + +int +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; + + map->type = *buf; + off += sizeof(uint8_t); + + switch (map->type) { + case MAP_TYPE_WILDCARD: + if (len == FEC_ELM_WCARD_LEN) + return (off); + else { + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, + msg->type); + return (-1); + } + break; + case MAP_TYPE_PREFIX: + if (len < FEC_ELM_PREFIX_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + /* Address Family */ + memcpy(&map->fec.prefix.af, buf + off, + sizeof(map->fec.prefix.af)); + off += sizeof(map->fec.prefix.af); + map->fec.prefix.af = ntohs(map->fec.prefix.af); + switch (map->fec.prefix.af) { + case AF_IPV4: + map->fec.prefix.af = AF_INET; + break; + case AF_IPV6: + map->fec.prefix.af = AF_INET6; + break; + default: + send_notification_nbr(nbr, S_UNSUP_ADDR, msg->id, + msg->type); + return (-1); + } + + /* Prefix Length */ + map->fec.prefix.prefixlen = buf[off]; + off += sizeof(uint8_t); + if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + /* Prefix */ + memset(&map->fec.prefix.prefix, 0, + sizeof(map->fec.prefix.prefix)); + memcpy(&map->fec.prefix.prefix, buf + off, + PREFIX_SIZE(map->fec.prefix.prefixlen)); + + /* Just in case... */ + ldp_applymask(map->fec.prefix.af, &map->fec.prefix.prefix, + &map->fec.prefix.prefix, map->fec.prefix.prefixlen); + + return (off + PREFIX_SIZE(map->fec.prefix.prefixlen)); + case MAP_TYPE_PWID: + if (len < FEC_PWID_ELM_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + /* PW type */ + memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t)); + map->fec.pwid.type = ntohs(map->fec.pwid.type); + if (map->fec.pwid.type & CONTROL_WORD_FLAG) { + map->flags |= F_MAP_PW_CWORD; + map->fec.pwid.type &= ~CONTROL_WORD_FLAG; + } + off += sizeof(uint16_t); + + /* PW info Length */ + pw_len = buf[off]; + off += sizeof(uint8_t); + + if (len != FEC_PWID_ELM_MIN_LEN + pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + /* Group ID */ + memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t)); + map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id); + off += sizeof(uint32_t); + + /* PW ID */ + if (pw_len == 0) + return (off); + + if (pw_len < sizeof(uint32_t)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t)); + map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid); + map->flags |= F_MAP_PW_ID; + off += sizeof(uint32_t); + pw_len -= sizeof(uint32_t); + + /* Optional Interface Parameter Sub-TLVs */ + while (pw_len > 0) { + struct subtlv stlv; + + if (pw_len < sizeof(stlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + memcpy(&stlv, buf + off, sizeof(stlv)); + if (stlv.length > pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + return (-1); + } + + switch (stlv.type) { + case SUBTLV_IFMTU: + if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) { + session_shutdown(nbr, S_BAD_TLV_LEN, + msg->id, msg->type); + return (-1); + } + memcpy(&map->fec.pwid.ifmtu, buf + off + + SUBTLV_HDR_SIZE, sizeof(uint16_t)); + map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu); + map->flags |= F_MAP_PW_IFMTU; + break; + default: + /* ignore */ + break; + } + off += stlv.length; + pw_len -= stlv.length; + } + + return (off); + default: + send_notification_nbr(nbr, S_UNKNOWN_FEC, msg->id, msg->type); + break; + } + + return (-1); +} diff --git a/ldpd/lde.c b/ldpd/lde.c new file mode 100644 index 0000000000..8859ed25b8 --- /dev/null +++ b/ldpd/lde.c @@ -0,0 +1,1335 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2004, 2005 Claudio Jeker + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldp.h" +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" +#include "lde.h" + +static void lde_sig_handler(int sig, short, void *); +static __dead void lde_shutdown(void); +static int lde_imsg_compose_parent(int, pid_t, void *, uint16_t); +static void lde_dispatch_imsg(int, short, void *); +static void lde_dispatch_parent(int, short, void *); +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 *); +static struct lde_nbr *lde_nbr_find(uint32_t); +static void lde_nbr_clear(void); +static void lde_nbr_addr_update(struct lde_nbr *, + struct lde_addr *, int); +static void lde_map_free(void *); +static int lde_address_add(struct lde_nbr *, struct lde_addr *); +static int lde_address_del(struct lde_nbr *, struct lde_addr *); +static void lde_address_list_free(struct lde_nbr *); + +RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare) + +struct ldpd_conf *ldeconf; +struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs); + +static struct imsgev *iev_ldpe; +static struct imsgev *iev_main; + +/* ARGSUSED */ +static void +lde_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + lde_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* label decision engine */ +void +lde(int debug, int verbose) +{ + struct event ev_sigint, ev_sigterm; + struct timeval now; + struct passwd *pw; + + ldeconf = config_new_empty(); + + log_init(debug); + log_verbose(verbose); + + setproctitle("label decision engine"); + ldpd_process = PROC_LDE_ENGINE; + + if ((pw = getpwnam(LDPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + if (pledge("stdio recvfd", NULL) == -1) + fatal("pledge"); + + event_init(); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, lde_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, lde_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* setup pipe and event handler to the parent process */ + if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_main->ibuf, 3); + iev_main->handler = lde_dispatch_parent; + iev_main->events = EV_READ; + event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, + iev_main->handler, iev_main); + event_add(&iev_main->ev, NULL); + + /* setup and start the LIB garbage collector */ + evtimer_set(&gc_timer, lde_gc_timer, NULL); + lde_gc_start_timer(); + + gettimeofday(&now, NULL); + global.uptime = now.tv_sec; + + event_dispatch(); + + lde_shutdown(); +} + +static __dead void +lde_shutdown(void) +{ + /* close pipes */ + msgbuf_clear(&iev_ldpe->ibuf.w); + close(iev_ldpe->ibuf.fd); + msgbuf_clear(&iev_main->ibuf.w); + close(iev_main->ibuf.fd); + + lde_gc_stop_timer(); + lde_nbr_clear(); + fec_tree_clear(); + + config_clear(ldeconf); + + free(iev_ldpe); + free(iev_main); + + log_info("label decision engine exiting"); + exit(0); +} + +/* imesg */ +static int +lde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen) +{ + return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen)); +} + +int +lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data, + uint16_t datalen) +{ + return (imsg_compose_event(iev_ldpe, type, peerid, pid, + -1, data, datalen)); +} + +/* ARGSUSED */ +static void +lde_dispatch_imsg(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + struct lde_nbr *ln; + struct map map; + struct lde_addr lde_addr; + struct notify_msg nm; + ssize_t n; + int shut = 0, verbose; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* connection closed */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("lde_dispatch_imsg: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_LABEL_MAPPING_FULL: + ln = lde_nbr_find(imsg.hdr.peerid); + if (ln == NULL) { + log_debug("%s: cannot find lde neighbor", + __func__); + break; + } + + fec_snap(ln); + break; + case IMSG_LABEL_MAPPING: + case IMSG_LABEL_REQUEST: + case IMSG_LABEL_RELEASE: + case IMSG_LABEL_WITHDRAW: + case IMSG_LABEL_ABORT: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("lde_dispatch_imsg: wrong imsg len"); + memcpy(&map, imsg.data, sizeof(map)); + + ln = lde_nbr_find(imsg.hdr.peerid); + if (ln == NULL) { + log_debug("%s: cannot find lde neighbor", + __func__); + break; + } + + switch (imsg.hdr.type) { + case IMSG_LABEL_MAPPING: + lde_check_mapping(&map, ln); + break; + case IMSG_LABEL_REQUEST: + 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); + break; + case IMSG_LABEL_WITHDRAW: + if (map.type == MAP_TYPE_WILDCARD) + lde_check_withdraw_wcard(&map, ln); + else + lde_check_withdraw(&map, ln); + break; + case IMSG_LABEL_ABORT: + /* not necessary */ + break; + } + break; + case IMSG_ADDRESS_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr)) + fatalx("lde_dispatch_imsg: wrong imsg len"); + memcpy(&lde_addr, imsg.data, sizeof(lde_addr)); + + ln = lde_nbr_find(imsg.hdr.peerid); + if (ln == NULL) { + log_debug("%s: cannot find lde neighbor", + __func__); + break; + } + 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)); + } + break; + case IMSG_ADDRESS_DEL: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr)) + fatalx("lde_dispatch_imsg: wrong imsg len"); + memcpy(&lde_addr, imsg.data, sizeof(lde_addr)); + + ln = lde_nbr_find(imsg.hdr.peerid); + if (ln == NULL) { + log_debug("%s: cannot find lde neighbor", + __func__); + break; + } + 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)); + } + break; + case IMSG_NOTIFICATION: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm)) + fatalx("lde_dispatch_imsg: wrong imsg len"); + memcpy(&nm, imsg.data, sizeof(nm)); + + ln = lde_nbr_find(imsg.hdr.peerid); + if (ln == NULL) { + log_debug("%s: cannot find lde neighbor", + __func__); + break; + } + + switch (nm.status_code) { + case S_PW_STATUS: + l2vpn_recv_pw_status(ln, &nm); + break; + default: + break; + } + break; + case IMSG_NEIGHBOR_UP: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct lde_nbr)) + fatalx("lde_dispatch_imsg: wrong imsg len"); + + if (lde_nbr_find(imsg.hdr.peerid)) + fatalx("lde_dispatch_imsg: " + "neighbor already exists"); + lde_nbr_new(imsg.hdr.peerid, imsg.data); + break; + case IMSG_NEIGHBOR_DOWN: + lde_nbr_del(lde_nbr_find(imsg.hdr.peerid)); + break; + case IMSG_CTL_SHOW_LIB: + rt_dump(imsg.hdr.pid); + + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, + imsg.hdr.pid, NULL, 0); + break; + case IMSG_CTL_SHOW_L2VPN_PW: + l2vpn_pw_ctl(imsg.hdr.pid); + + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, + imsg.hdr.pid, NULL, 0); + break; + case IMSG_CTL_SHOW_L2VPN_BINDING: + l2vpn_binding_ctl(imsg.hdr.pid); + + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, + imsg.hdr.pid, NULL, 0); + break; + case IMSG_CTL_LOG_VERBOSE: + /* already checked by ldpe */ + memcpy(&verbose, imsg.data, sizeof(verbose)); + log_verbose(verbose); + break; + default: + log_debug("%s: unexpected imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +static void +lde_dispatch_parent(int fd, short event, void *bula) +{ + static struct ldpd_conf *nconf; + struct iface *niface; + struct tnbr *ntnbr; + struct nbr_params *nnbrp; + static struct l2vpn *nl2vpn; + struct l2vpn_if *nlif; + struct l2vpn_pw *npw; + struct imsg imsg; + struct kroute kr; + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + ssize_t n; + int shut = 0; + struct fec fec; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* connection closed */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("lde_dispatch_parent: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_NETWORK_ADD: + case IMSG_NETWORK_DEL: + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + memcpy(&kr, imsg.data, sizeof(kr)); + + switch (kr.af) { + case AF_INET: + fec.type = FEC_TYPE_IPV4; + 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; + break; + default: + fatalx("lde_dispatch_parent: unknown af"); + } + + switch (imsg.hdr.type) { + case IMSG_NETWORK_ADD: + lde_kernel_insert(&fec, kr.af, &kr.nexthop, + kr.priority, kr.flags & F_CONNECTED, NULL); + break; + case IMSG_NETWORK_DEL: + lde_kernel_remove(&fec, kr.af, &kr.nexthop, + kr.priority); + break; + } + break; + case IMSG_SOCKET_IPC: + if (iev_ldpe) { + log_warnx("%s: received unexpected imsg fd " + "to ldpe", __func__); + break; + } + if ((fd = imsg.fd) == -1) { + log_warnx("%s: expected to receive imsg fd to " + "ldpe but didn't receive any", __func__); + break; + } + + if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_ldpe->ibuf, fd); + iev_ldpe->handler = lde_dispatch_imsg; + iev_ldpe->events = EV_READ; + event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd, + iev_ldpe->events, iev_ldpe->handler, iev_ldpe); + event_add(&iev_ldpe->ev, NULL); + break; + case IMSG_RECONF_CONF: + if ((nconf = malloc(sizeof(struct ldpd_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); + + LIST_INIT(&nconf->iface_list); + LIST_INIT(&nconf->tnbr_list); + LIST_INIT(&nconf->nbrp_list); + LIST_INIT(&nconf->l2vpn_list); + break; + case IMSG_RECONF_IFACE: + if ((niface = malloc(sizeof(struct iface))) == NULL) + fatal(NULL); + memcpy(niface, imsg.data, sizeof(struct iface)); + + LIST_INIT(&niface->addr_list); + LIST_INIT(&niface->ipv4.adj_list); + LIST_INIT(&niface->ipv6.adj_list); + niface->ipv4.iface = niface; + niface->ipv6.iface = niface; + + LIST_INSERT_HEAD(&nconf->iface_list, niface, entry); + break; + case IMSG_RECONF_TNBR: + if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL) + fatal(NULL); + memcpy(ntnbr, imsg.data, sizeof(struct tnbr)); + + LIST_INSERT_HEAD(&nconf->tnbr_list, ntnbr, entry); + break; + case IMSG_RECONF_NBRP: + if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL) + fatal(NULL); + memcpy(nnbrp, imsg.data, sizeof(struct nbr_params)); + + LIST_INSERT_HEAD(&nconf->nbrp_list, nnbrp, entry); + break; + case IMSG_RECONF_L2VPN: + if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL) + fatal(NULL); + memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn)); + + LIST_INIT(&nl2vpn->if_list); + LIST_INIT(&nl2vpn->pw_list); + + LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry); + break; + case IMSG_RECONF_L2VPN_IF: + if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL) + fatal(NULL); + memcpy(nlif, imsg.data, sizeof(struct l2vpn_if)); + + nlif->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->if_list, nlif, entry); + break; + case IMSG_RECONF_L2VPN_PW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + npw->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry); + break; + case IMSG_RECONF_END: + merge_config(ldeconf, nconf); + nconf = NULL; + break; + default: + log_debug("%s: unexpected imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +uint32_t +lde_assign_label(void) +{ + static uint32_t label = MPLS_LABEL_RESERVED_MAX; + + /* XXX some checks needed */ + label++; + return (label); +} + +void +lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) +{ + struct kroute kr; + struct kpw kpw; + struct l2vpn_pw *pw; + + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + memset(&kr, 0, sizeof(kr)); + kr.af = AF_INET; + kr.prefix.v4 = fn->fec.u.ipv4.prefix; + kr.prefixlen = fn->fec.u.ipv4.prefixlen; + kr.nexthop.v4 = fnh->nexthop.v4; + kr.local_label = fn->local_label; + kr.remote_label = fnh->remote_label; + kr.priority = fnh->priority; + + lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, + sizeof(kr)); + + if (fn->fec.u.ipv4.prefixlen == 32) + l2vpn_sync_pws(AF_INET, (union ldpd_addr *) + &fn->fec.u.ipv4.prefix); + break; + case FEC_TYPE_IPV6: + memset(&kr, 0, sizeof(kr)); + kr.af = AF_INET6; + kr.prefix.v6 = fn->fec.u.ipv6.prefix; + kr.prefixlen = fn->fec.u.ipv6.prefixlen; + kr.nexthop.v6 = fnh->nexthop.v6; + kr.local_label = fn->local_label; + kr.remote_label = fnh->remote_label; + kr.priority = fnh->priority; + + lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, + sizeof(kr)); + + if (fn->fec.u.ipv6.prefixlen == 128) + l2vpn_sync_pws(AF_INET6, (union ldpd_addr *) + &fn->fec.u.ipv6.prefix); + break; + case FEC_TYPE_PWID: + if (fn->local_label == NO_LABEL || + fnh->remote_label == NO_LABEL) + return; + + pw = (struct l2vpn_pw *) fn->data; + pw->flags |= F_PW_STATUS_UP; + + memset(&kpw, 0, sizeof(kpw)); + kpw.ifindex = pw->ifindex; + kpw.pw_type = fn->fec.u.pwid.type; + kpw.af = pw->af; + kpw.nexthop = pw->addr; + kpw.local_label = fn->local_label; + kpw.remote_label = fnh->remote_label; + kpw.flags = pw->flags; + + lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw, + sizeof(kpw)); + break; + } +} + +void +lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) +{ + struct kroute kr; + struct kpw kpw; + struct l2vpn_pw *pw; + + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + memset(&kr, 0, sizeof(kr)); + kr.af = AF_INET; + kr.prefix.v4 = fn->fec.u.ipv4.prefix; + kr.prefixlen = fn->fec.u.ipv4.prefixlen; + kr.nexthop.v4 = fnh->nexthop.v4; + kr.local_label = fn->local_label; + kr.remote_label = fnh->remote_label; + kr.priority = fnh->priority; + + lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, + sizeof(kr)); + + if (fn->fec.u.ipv4.prefixlen == 32) + l2vpn_sync_pws(AF_INET, (union ldpd_addr *) + &fn->fec.u.ipv4.prefix); + break; + case FEC_TYPE_IPV6: + memset(&kr, 0, sizeof(kr)); + kr.af = AF_INET6; + kr.prefix.v6 = fn->fec.u.ipv6.prefix; + kr.prefixlen = fn->fec.u.ipv6.prefixlen; + kr.nexthop.v6 = fnh->nexthop.v6; + kr.local_label = fn->local_label; + kr.remote_label = fnh->remote_label; + kr.priority = fnh->priority; + + lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, + sizeof(kr)); + + if (fn->fec.u.ipv6.prefixlen == 128) + l2vpn_sync_pws(AF_INET6, (union ldpd_addr *) + &fn->fec.u.ipv6.prefix); + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fn->data; + if (!(pw->flags & F_PW_STATUS_UP)) + return; + pw->flags &= ~F_PW_STATUS_UP; + + memset(&kpw, 0, sizeof(kpw)); + kpw.ifindex = pw->ifindex; + kpw.pw_type = fn->fec.u.pwid.type; + kpw.af = pw->af; + kpw.nexthop = pw->addr; + kpw.local_label = fn->local_label; + kpw.remote_label = fnh->remote_label; + kpw.flags = pw->flags; + + lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw, + sizeof(kpw)); + break; + } +} + +void +lde_fec2map(struct fec *fec, struct map *map) +{ + memset(map, 0, sizeof(*map)); + + switch (fec->type) { + case FEC_TYPE_IPV4: + map->type = MAP_TYPE_PREFIX; + map->fec.prefix.af = AF_INET; + map->fec.prefix.prefix.v4 = fec->u.ipv4.prefix; + map->fec.prefix.prefixlen = fec->u.ipv4.prefixlen; + break; + case FEC_TYPE_IPV6: + map->type = MAP_TYPE_PREFIX; + map->fec.prefix.af = AF_INET6; + map->fec.prefix.prefix.v6 = fec->u.ipv6.prefix; + map->fec.prefix.prefixlen = fec->u.ipv6.prefixlen; + break; + case FEC_TYPE_PWID: + map->type = MAP_TYPE_PWID; + map->fec.pwid.type = fec->u.pwid.type; + map->fec.pwid.group_id = 0; + map->flags |= F_MAP_PW_ID; + map->fec.pwid.pwid = fec->u.pwid.pwid; + break; + } +} + +void +lde_map2fec(struct map *map, struct in_addr lsr_id, struct fec *fec) +{ + memset(fec, 0, sizeof(*fec)); + + switch (map->type) { + case MAP_TYPE_PREFIX: + switch (map->fec.prefix.af) { + case AF_INET: + fec->type = FEC_TYPE_IPV4; + fec->u.ipv4.prefix = map->fec.prefix.prefix.v4; + fec->u.ipv4.prefixlen = map->fec.prefix.prefixlen; + break; + case AF_INET6: + fec->type = FEC_TYPE_IPV6; + fec->u.ipv6.prefix = map->fec.prefix.prefix.v6; + fec->u.ipv6.prefixlen = map->fec.prefix.prefixlen; + break; + default: + fatalx("lde_map2fec: unknown af"); + break; + } + break; + case MAP_TYPE_PWID: + fec->type = FEC_TYPE_PWID; + fec->u.pwid.type = map->fec.pwid.type; + fec->u.pwid.pwid = map->fec.pwid.pwid; + fec->u.pwid.lsr_id = lsr_id; + break; + } +} + +void +lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) +{ + struct lde_req *lre; + struct lde_map *me; + struct map map; + struct l2vpn_pw *pw; + + /* + * This function skips SL.1 - 3 and SL.9 - 14 because the label + * allocation is done way earlier (because of the merging nature of + * ldpd). + */ + + lde_fec2map(&fn->fec, &map); + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + if (!ln->v4_enabled) + return; + break; + case FEC_TYPE_IPV6: + if (!ln->v6_enabled) + return; + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr) + /* not the remote end of the pseudowire */ + return; + + map.flags |= F_MAP_PW_IFMTU; + map.fec.pwid.ifmtu = pw->l2vpn->mtu; + if (pw->flags & F_PW_CWORD) + map.flags |= F_MAP_PW_CWORD; + if (pw->flags & F_PW_STATUSTLV) { + map.flags |= F_MAP_PW_STATUS; + /* VPLS are always up */ + map.pw_status = PW_FORWARDING; + } + break; + } + map.label = fn->local_label; + + /* SL.6: is there a pending request for this mapping? */ + lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec); + if (lre) { + /* set label request msg id in the mapping response. */ + map.requestid = lre->msg_id; + map.flags = F_MAP_REQ_ID; + + /* SL.7: delete record of pending request */ + lde_req_del(ln, lre, 0); + } + + /* SL.4: send label mapping */ + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD, ln->peerid, 0, + &map, sizeof(map)); + if (single) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, + NULL, 0); + + /* SL.5: record sent label mapping */ + me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec); + if (me == NULL) + me = lde_map_add(ln, fn, 1); + me->map = map; +} + +void +lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label, + struct status_tlv *st) +{ + struct lde_wdraw *lw; + struct map map; + struct fec *f; + struct l2vpn_pw *pw; + + if (fn) { + lde_fec2map(&fn->fec, &map); + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + if (!ln->v4_enabled) + return; + break; + case FEC_TYPE_IPV6: + if (!ln->v6_enabled) + return; + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr) + /* not the remote end of the pseudowire */ + return; + + if (pw->flags & F_PW_CWORD) + map.flags |= F_MAP_PW_CWORD; + break; + } + map.label = fn->local_label; + } else { + memset(&map, 0, sizeof(map)); + map.type = MAP_TYPE_WILDCARD; + map.label = label; + } + + if (st) { + map.st.status_code = st->status_code; + map.st.msg_id = st->msg_id; + map.st.msg_type = st->msg_type; + map.flags |= F_MAP_STATUS; + } + + /* SWd.1: send label withdraw. */ + lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD, ln->peerid, 0, + &map, sizeof(map)); + lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD_END, ln->peerid, 0, NULL, 0); + + /* SWd.2: record label withdraw. */ + if (fn) { + lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); + if (lw == NULL) + lw = lde_wdraw_add(ln, fn); + lw->label = map.label; + } else { + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, + &fn->fec); + if (lw == NULL) + lw = lde_wdraw_add(ln, fn); + lw->label = map.label; + } + } +} + +void +lde_send_labelwithdraw_all(struct fec_node *fn, uint32_t label) +{ + struct lde_nbr *ln; + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, label, NULL); +} + +void +lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label) +{ + struct map map; + struct l2vpn_pw *pw; + + if (fn) { + lde_fec2map(&fn->fec, &map); + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + if (!ln->v4_enabled) + return; + break; + case FEC_TYPE_IPV6: + if (!ln->v6_enabled) + return; + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr) + /* not the remote end of the pseudowire */ + return; + + if (pw->flags & F_PW_CWORD) + map.flags |= F_MAP_PW_CWORD; + break; + } + } else { + memset(&map, 0, sizeof(map)); + map.type = MAP_TYPE_WILDCARD; + } + map.label = label; + + lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0, + &map, sizeof(map)); + lde_imsg_compose_ldpe(IMSG_RELEASE_ADD_END, ln->peerid, 0, NULL, 0); +} + +void +lde_send_notification(uint32_t peerid, uint32_t status_code, uint32_t msg_id, + uint16_t msg_type) +{ + struct notify_msg nm; + + memset(&nm, 0, sizeof(nm)); + nm.status_code = status_code; + /* 'msg_id' and 'msg_type' should be in network byte order */ + nm.msg_id = msg_id; + nm.msg_type = msg_type; + + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0, + &nm, sizeof(nm)); +} + +static __inline int +lde_nbr_compare(struct lde_nbr *a, struct lde_nbr *b) +{ + return (a->peerid - b->peerid); +} + +static struct lde_nbr * +lde_nbr_new(uint32_t peerid, struct lde_nbr *new) +{ + struct lde_nbr *ln; + + if ((ln = calloc(1, sizeof(*ln))) == NULL) + fatal(__func__); + + ln->id = new->id; + ln->v4_enabled = new->v4_enabled; + ln->v6_enabled = new->v6_enabled; + ln->peerid = peerid; + fec_init(&ln->recv_map); + fec_init(&ln->sent_map); + fec_init(&ln->recv_req); + fec_init(&ln->sent_req); + fec_init(&ln->sent_wdraw); + + TAILQ_INIT(&ln->addr_list); + + if (RB_INSERT(nbr_tree, &lde_nbrs, ln) != NULL) + fatalx("lde_nbr_new: RB_INSERT failed"); + + return (ln); +} + +static void +lde_nbr_del(struct lde_nbr *ln) +{ + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + struct l2vpn_pw *pw; + + if (ln == NULL) + return; + + /* uninstall received mappings */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + switch (f->type) { + case FEC_TYPE_IPV4: + case FEC_TYPE_IPV6: + if (!lde_address_find(ln, fnh->af, + &fnh->nexthop)) + continue; + break; + case FEC_TYPE_PWID: + if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr) + continue; + pw = (struct l2vpn_pw *) fn->data; + if (pw) + l2vpn_pw_reset(pw); + break; + default: + break; + } + + lde_send_delete_klabel(fn, fnh); + fnh->remote_label = NO_LABEL; + } + } + + lde_address_list_free(ln); + + fec_clear(&ln->recv_map, lde_map_free); + fec_clear(&ln->sent_map, lde_map_free); + fec_clear(&ln->recv_req, free); + fec_clear(&ln->sent_req, free); + fec_clear(&ln->sent_wdraw, free); + + RB_REMOVE(nbr_tree, &lde_nbrs, ln); + + free(ln); +} + +static struct lde_nbr * +lde_nbr_find(uint32_t peerid) +{ + struct lde_nbr ln; + + ln.peerid = peerid; + + return (RB_FIND(nbr_tree, &lde_nbrs, &ln)); +} + +struct lde_nbr * +lde_nbr_find_by_lsrid(struct in_addr addr) +{ + struct lde_nbr *ln; + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + if (ln->id.s_addr == addr.s_addr) + return (ln); + + return (NULL); +} + +struct lde_nbr * +lde_nbr_find_by_addr(int af, union ldpd_addr *addr) +{ + struct lde_nbr *ln; + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + if (lde_address_find(ln, af, addr) != NULL) + return (ln); + + return (NULL); +} + +static void +lde_nbr_clear(void) +{ + struct lde_nbr *ln; + + while ((ln = RB_ROOT(&lde_nbrs)) != NULL) + lde_nbr_del(ln); +} + +static void +lde_nbr_addr_update(struct lde_nbr *ln, struct lde_addr *lde_addr, int removed) +{ + struct fec *fec; + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_map *me; + + RB_FOREACH(fec, fec_tree, &ln->recv_map) { + fn = (struct fec_node *)fec_find(&ft, fec); + switch (fec->type) { + case FEC_TYPE_IPV4: + if (lde_addr->af != AF_INET) + continue; + break; + case FEC_TYPE_IPV6: + if (lde_addr->af != AF_INET6) + continue; + break; + default: + continue; + } + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + if (ldp_addrcmp(fnh->af, &fnh->nexthop, + &lde_addr->addr)) + continue; + + if (removed) { + lde_send_delete_klabel(fn, fnh); + fnh->remote_label = NO_LABEL; + } else { + me = (struct lde_map *)fec; + fnh->remote_label = me->map.label; + lde_send_change_klabel(fn, fnh); + } + break; + } + } +} + +struct lde_map * +lde_map_add(struct lde_nbr *ln, struct fec_node *fn, int sent) +{ + struct lde_map *me; + + me = calloc(1, sizeof(*me)); + if (me == NULL) + fatal(__func__); + + me->fec = fn->fec; + me->nexthop = ln; + + if (sent) { + LIST_INSERT_HEAD(&fn->upstream, me, entry); + if (fec_insert(&ln->sent_map, &me->fec)) + log_warnx("failed to add %s to sent map", + log_fec(&me->fec)); + /* XXX on failure more cleanup is needed */ + } else { + LIST_INSERT_HEAD(&fn->downstream, me, entry); + if (fec_insert(&ln->recv_map, &me->fec)) + log_warnx("failed to add %s to recv map", + log_fec(&me->fec)); + } + + return (me); +} + +void +lde_map_del(struct lde_nbr *ln, struct lde_map *me, int sent) +{ + if (sent) + fec_remove(&ln->sent_map, &me->fec); + else + fec_remove(&ln->recv_map, &me->fec); + + lde_map_free(me); +} + +static void +lde_map_free(void *ptr) +{ + struct lde_map *map = ptr; + + LIST_REMOVE(map, entry); + free(map); +} + +struct lde_req * +lde_req_add(struct lde_nbr *ln, struct fec *fec, int sent) +{ + struct fec_tree *t; + struct lde_req *lre; + + t = sent ? &ln->sent_req : &ln->recv_req; + + lre = calloc(1, sizeof(*lre)); + if (lre != NULL) { + lre->fec = *fec; + + if (fec_insert(t, &lre->fec)) { + log_warnx("failed to add %s to %s req", + log_fec(&lre->fec), sent ? "sent" : "recv"); + free(lre); + return (NULL); + } + } + + return (lre); +} + +void +lde_req_del(struct lde_nbr *ln, struct lde_req *lre, int sent) +{ + if (sent) + fec_remove(&ln->sent_req, &lre->fec); + else + fec_remove(&ln->recv_req, &lre->fec); + + free(lre); +} + +struct lde_wdraw * +lde_wdraw_add(struct lde_nbr *ln, struct fec_node *fn) +{ + struct lde_wdraw *lw; + + lw = calloc(1, sizeof(*lw)); + if (lw == NULL) + fatal(__func__); + + lw->fec = fn->fec; + + if (fec_insert(&ln->sent_wdraw, &lw->fec)) + log_warnx("failed to add %s to sent wdraw", + log_fec(&lw->fec)); + + return (lw); +} + +void +lde_wdraw_del(struct lde_nbr *ln, struct lde_wdraw *lw) +{ + fec_remove(&ln->sent_wdraw, &lw->fec); + free(lw); +} + +void +lde_change_egress_label(int af, int was_implicit) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) { + /* explicit withdraw */ + if (was_implicit) + lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IMPLNULL, + NULL); + else { + if (ln->v4_enabled) + lde_send_labelwithdraw(ln, NULL, + MPLS_LABEL_IPV4NULL, NULL); + if (ln->v6_enabled) + lde_send_labelwithdraw(ln, NULL, + MPLS_LABEL_IPV6NULL, NULL); + } + + /* advertise new label of connected prefixes */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (fn->local_label > MPLS_LABEL_RESERVED_MAX) + continue; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + fatalx("lde_change_egress_label: unknown af"); + } + + fn->local_label = egress_label(fn->fec.type); + lde_send_labelmapping(ln, fn, 0); + } + + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, + NULL, 0); + } +} + +static int +lde_address_add(struct lde_nbr *ln, struct lde_addr *lde_addr) +{ + struct lde_addr *new; + + if (lde_address_find(ln, lde_addr->af, &lde_addr->addr) != NULL) + return (-1); + + if ((new = calloc(1, sizeof(*new))) == NULL) + fatal(__func__); + + new->af = lde_addr->af; + new->addr = lde_addr->addr; + TAILQ_INSERT_TAIL(&ln->addr_list, new, entry); + + /* reevaluate the previously received mappings from this neighbor */ + lde_nbr_addr_update(ln, lde_addr, 0); + + return (0); +} + +static int +lde_address_del(struct lde_nbr *ln, struct lde_addr *lde_addr) +{ + lde_addr = lde_address_find(ln, lde_addr->af, &lde_addr->addr); + if (lde_addr == NULL) + return (-1); + + /* reevaluate the previously received mappings from this neighbor */ + lde_nbr_addr_update(ln, lde_addr, 1); + + TAILQ_REMOVE(&ln->addr_list, lde_addr, entry); + free(lde_addr); + + return (0); +} + +struct lde_addr * +lde_address_find(struct lde_nbr *ln, int af, union ldpd_addr *addr) +{ + struct lde_addr *lde_addr; + + TAILQ_FOREACH(lde_addr, &ln->addr_list, entry) + if (lde_addr->af == af && + ldp_addrcmp(af, &lde_addr->addr, addr) == 0) + return (lde_addr); + + return (NULL); +} + +static void +lde_address_list_free(struct lde_nbr *ln) +{ + struct lde_addr *lde_addr; + + while ((lde_addr = TAILQ_FIRST(&ln->addr_list)) != NULL) { + TAILQ_REMOVE(&ln->addr_list, lde_addr, entry); + free(lde_addr); + } +} diff --git a/ldpd/lde.h b/ldpd/lde.h new file mode 100644 index 0000000000..b0f7b20439 --- /dev/null +++ b/ldpd/lde.h @@ -0,0 +1,202 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2004, 2005 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDE_H_ +#define _LDE_H_ + +#include +#include +#include + +enum fec_type { + FEC_TYPE_IPV4, + FEC_TYPE_IPV6, + FEC_TYPE_PWID +}; + +struct fec { + RB_ENTRY(fec) entry; + enum fec_type type; + union { + struct { + struct in_addr prefix; + uint8_t prefixlen; + } ipv4; + struct { + struct in6_addr prefix; + uint8_t prefixlen; + } ipv6; + struct { + uint16_t type; + uint32_t pwid; + struct in_addr lsr_id; + } pwid; + } u; +}; +RB_HEAD(fec_tree, fec); +RB_PROTOTYPE(fec_tree, fec, entry, fec_compare) + +/* request entries */ +struct lde_req { + struct fec fec; + uint32_t msg_id; +}; + +/* mapping entries */ +struct lde_map { + struct fec fec; + LIST_ENTRY(lde_map) entry; + struct lde_nbr *nexthop; + struct map map; +}; + +/* withdraw entries */ +struct lde_wdraw { + struct fec fec; + uint32_t label; +}; + +/* Addresses belonging to neighbor */ +struct lde_addr { + TAILQ_ENTRY(lde_addr) entry; + int af; + union ldpd_addr addr; +}; + +/* just the info LDE needs */ +struct lde_nbr { + RB_ENTRY(lde_nbr) entry; + uint32_t peerid; + struct in_addr id; + int v4_enabled; /* announce/process v4 msgs */ + int v6_enabled; /* announce/process v6 msgs */ + struct fec_tree recv_req; + struct fec_tree sent_req; + struct fec_tree recv_map; + struct fec_tree sent_map; + struct fec_tree sent_wdraw; + TAILQ_HEAD(, lde_addr) addr_list; +}; +RB_HEAD(nbr_tree, lde_nbr); +RB_PROTOTYPE(nbr_tree, lde_nbr, entry, lde_nbr_compare) + +struct fec_nh { + LIST_ENTRY(fec_nh) entry; + int af; + union ldpd_addr nexthop; + uint32_t remote_label; + uint8_t priority; +}; + +struct fec_node { + struct fec fec; + + LIST_HEAD(, fec_nh) nexthops; /* fib nexthops */ + LIST_HEAD(, lde_map) downstream; /* recv mappings */ + LIST_HEAD(, lde_map) upstream; /* sent mappings */ + + uint32_t local_label; + void *data; /* fec specific data */ +}; + +#define LDE_GC_INTERVAL 300 + +extern struct ldpd_conf *ldeconf; +extern struct fec_tree ft; +extern struct nbr_tree lde_nbrs; +extern struct event gc_timer; + +/* lde.c */ +void lde(int, int); +int lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t); +uint32_t lde_assign_label(void); +void lde_send_change_klabel(struct fec_node *, struct fec_nh *); +void lde_send_delete_klabel(struct fec_node *, struct fec_nh *); +void lde_fec2map(struct fec *, struct map *); +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 *, + uint32_t); +void lde_send_notification(uint32_t, uint32_t, uint32_t, 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); +void lde_map_del(struct lde_nbr *, struct lde_map *, int); +struct lde_req *lde_req_add(struct lde_nbr *, struct fec *, int); +void lde_req_del(struct lde_nbr *, struct lde_req *, int); +struct lde_wdraw *lde_wdraw_add(struct lde_nbr *, struct fec_node *); +void lde_wdraw_del(struct lde_nbr *, struct lde_wdraw *); +void lde_change_egress_label(int, int); +struct lde_addr *lde_address_find(struct lde_nbr *, int, + union ldpd_addr *); + +/* lde_lib.c */ +void fec_init(struct fec_tree *); +struct fec *fec_find(struct fec_tree *, struct fec *); +int fec_insert(struct fec_tree *, struct fec *); +int fec_remove(struct fec_tree *, struct fec *); +void fec_clear(struct fec_tree *, void (*)(void *)); +void rt_dump(pid_t); +void fec_snap(struct lde_nbr *); +void fec_tree_clear(void); +struct fec_nh *fec_nh_find(struct fec_node *, int, union ldpd_addr *, + uint8_t); +uint32_t egress_label(enum fec_type); +void lde_kernel_insert(struct fec *, int, union ldpd_addr *, + uint8_t, int, void *); +void lde_kernel_remove(struct fec *, int, union ldpd_addr *, + uint8_t); +void lde_check_mapping(struct map *, struct lde_nbr *); +void lde_check_request(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 *); +void lde_gc_timer(int, short, void *); +void lde_gc_start_timer(void); +void lde_gc_stop_timer(void); + +/* l2vpn.c */ +struct l2vpn *l2vpn_new(const char *); +struct l2vpn *l2vpn_find(struct ldpd_conf *, const char *); +void l2vpn_del(struct l2vpn *); +void l2vpn_init(struct l2vpn *); +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_pw *l2vpn_pw_new(struct l2vpn *, struct kif *); +struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, unsigned int); +void l2vpn_pw_init(struct l2vpn_pw *); +void l2vpn_pw_exit(struct l2vpn_pw *); +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_recv_pw_status(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); + +#endif /* _LDE_H_ */ diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c new file mode 100644 index 0000000000..d9c1f544f1 --- /dev/null +++ b/ldpd/lde_lib.c @@ -0,0 +1,784 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "lde.h" +#include "log.h" + +static __inline int fec_compare(struct fec *, struct fec *); +static int lde_nbr_is_nexthop(struct fec_node *, + struct lde_nbr *); +static void fec_free(void *); +static struct fec_node *fec_add(struct fec *fec); +static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *, + uint8_t priority); +static void fec_nh_del(struct fec_nh *); + +RB_GENERATE(fec_tree, fec, entry, fec_compare) + +struct fec_tree ft = RB_INITIALIZER(&ft); +struct event gc_timer; + +/* FEC tree functions */ +void +fec_init(struct fec_tree *fh) +{ + RB_INIT(fh); +} + +static __inline int +fec_compare(struct fec *a, struct fec *b) +{ + if (a->type < b->type) + return (-1); + if (a->type > b->type) + return (1); + + switch (a->type) { + case FEC_TYPE_IPV4: + if (ntohl(a->u.ipv4.prefix.s_addr) < + ntohl(b->u.ipv4.prefix.s_addr)) + return (-1); + if (ntohl(a->u.ipv4.prefix.s_addr) > + ntohl(b->u.ipv4.prefix.s_addr)) + return (1); + if (a->u.ipv4.prefixlen < b->u.ipv4.prefixlen) + return (-1); + if (a->u.ipv4.prefixlen > b->u.ipv4.prefixlen) + return (1); + return (0); + case FEC_TYPE_IPV6: + if (memcmp(&a->u.ipv6.prefix, &b->u.ipv6.prefix, + sizeof(struct in6_addr)) < 0) + return (-1); + if (memcmp(&a->u.ipv6.prefix, &b->u.ipv6.prefix, + sizeof(struct in6_addr)) > 0) + return (1); + if (a->u.ipv6.prefixlen < b->u.ipv6.prefixlen) + return (-1); + if (a->u.ipv6.prefixlen > b->u.ipv6.prefixlen) + return (1); + return (0); + case FEC_TYPE_PWID: + if (a->u.pwid.type < b->u.pwid.type) + return (-1); + if (a->u.pwid.type > b->u.pwid.type) + return (1); + if (a->u.pwid.pwid < b->u.pwid.pwid) + return (-1); + if (a->u.pwid.pwid > b->u.pwid.pwid) + return (1); + if (ntohl(a->u.pwid.lsr_id.s_addr) < + ntohl(b->u.pwid.lsr_id.s_addr)) + return (-1); + if (ntohl(a->u.pwid.lsr_id.s_addr) > + ntohl(b->u.pwid.lsr_id.s_addr)) + return (1); + return (0); + } + + return (-1); +} + +struct fec * +fec_find(struct fec_tree *fh, struct fec *f) +{ + return (RB_FIND(fec_tree, fh, f)); +} + +int +fec_insert(struct fec_tree *fh, struct fec *f) +{ + if (RB_INSERT(fec_tree, fh, f) != NULL) + return (-1); + return (0); +} + +int +fec_remove(struct fec_tree *fh, struct fec *f) +{ + if (RB_REMOVE(fec_tree, fh, f) == NULL) { + log_warnx("%s failed for %s", __func__, log_fec(f)); + return (-1); + } + return (0); +} + +void +fec_clear(struct fec_tree *fh, void (*free_cb)(void *)) +{ + struct fec *f; + + while ((f = RB_ROOT(fh)) != NULL) { + fec_remove(fh, f); + free_cb(f); + } +} + +/* routing table functions */ +static int +lde_nbr_is_nexthop(struct fec_node *fn, struct lde_nbr *ln) +{ + struct fec_nh *fnh; + + LIST_FOREACH(fnh, &fn->nexthops, entry) + if (lde_address_find(ln, fnh->af, &fnh->nexthop)) + return (1); + + return (0); +} + +void +rt_dump(pid_t pid) +{ + struct fec *f; + struct fec_node *fn; + struct lde_map *me; + static struct ctl_rt rtctl; + + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (fn->local_label == NO_LABEL && + LIST_EMPTY(&fn->downstream)) + continue; + + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + rtctl.af = AF_INET; + rtctl.prefix.v4 = fn->fec.u.ipv4.prefix; + rtctl.prefixlen = fn->fec.u.ipv4.prefixlen; + break; + case FEC_TYPE_IPV6: + rtctl.af = AF_INET6; + rtctl.prefix.v6 = fn->fec.u.ipv6.prefix; + rtctl.prefixlen = fn->fec.u.ipv6.prefixlen; + break; + default: + continue; + } + + rtctl.local_label = fn->local_label; + LIST_FOREACH(me, &fn->downstream, entry) { + rtctl.in_use = lde_nbr_is_nexthop(fn, me->nexthop); + rtctl.nexthop = me->nexthop->id; + rtctl.remote_label = me->map.label; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid, + &rtctl, sizeof(rtctl)); + } + if (LIST_EMPTY(&fn->downstream)) { + rtctl.in_use = 0; + rtctl.nexthop.s_addr = INADDR_ANY; + rtctl.remote_label = NO_LABEL; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid, + &rtctl, sizeof(rtctl)); + } + } +} + +void +fec_snap(struct lde_nbr *ln) +{ + struct fec *f; + struct fec_node *fn; + + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (fn->local_label == NO_LABEL) + continue; + + lde_send_labelmapping(ln, fn, 0); + } + + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); +} + +static void +fec_free(void *arg) +{ + struct fec_node *fn = arg; + struct fec_nh *fnh; + + while ((fnh = LIST_FIRST(&fn->nexthops))) + fec_nh_del(fnh); + if (!LIST_EMPTY(&fn->downstream)) + log_warnx("%s: fec %s downstream list not empty", __func__, + log_fec(&fn->fec)); + if (!LIST_EMPTY(&fn->upstream)) + log_warnx("%s: fec %s upstream list not empty", __func__, + log_fec(&fn->fec)); + + free(fn); +} + +void +fec_tree_clear(void) +{ + fec_clear(&ft, fec_free); +} + +static struct fec_node * +fec_add(struct fec *fec) +{ + struct fec_node *fn; + + fn = calloc(1, sizeof(*fn)); + if (fn == NULL) + fatal(__func__); + + fn->fec = *fec; + fn->local_label = NO_LABEL; + LIST_INIT(&fn->upstream); + LIST_INIT(&fn->downstream); + LIST_INIT(&fn->nexthops); + + if (fec_insert(&ft, &fn->fec)) + log_warnx("failed to add %s to ft tree", + log_fec(&fn->fec)); + + return (fn); +} + +struct fec_nh * +fec_nh_find(struct fec_node *fn, int af, union ldpd_addr *nexthop, + uint8_t priority) +{ + struct fec_nh *fnh; + + LIST_FOREACH(fnh, &fn->nexthops, entry) + if (fnh->af == af && + ldp_addrcmp(af, &fnh->nexthop, nexthop) == 0 && + fnh->priority == priority) + return (fnh); + + return (NULL); +} + +static struct fec_nh * +fec_nh_add(struct fec_node *fn, int af, union ldpd_addr *nexthop, + uint8_t priority) +{ + struct fec_nh *fnh; + + fnh = calloc(1, sizeof(*fnh)); + if (fnh == NULL) + fatal(__func__); + + fnh->af = af; + fnh->nexthop = *nexthop; + fnh->remote_label = NO_LABEL; + fnh->priority = priority; + LIST_INSERT_HEAD(&fn->nexthops, fnh, entry); + + return (fnh); +} + +static void +fec_nh_del(struct fec_nh *fnh) +{ + LIST_REMOVE(fnh, entry); + free(fnh); +} + +uint32_t +egress_label(enum fec_type fec_type) +{ + switch (fec_type) { + case FEC_TYPE_IPV4: + if (ldeconf->ipv4.flags & F_LDPD_AF_EXPNULL) + return (MPLS_LABEL_IPV4NULL); + break; + case FEC_TYPE_IPV6: + if (ldeconf->ipv6.flags & F_LDPD_AF_EXPNULL) + return (MPLS_LABEL_IPV6NULL); + break; + default: + fatalx("egress_label: unexpected fec type"); + } + + return (MPLS_LABEL_IMPLNULL); +} + +void +lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, + uint8_t priority, int connected, void *data) +{ + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_map *me; + struct lde_nbr *ln; + + fn = (struct fec_node *)fec_find(&ft, fec); + if (fn == NULL) + fn = fec_add(fec); + if (fec_nh_find(fn, af, nexthop, priority) != NULL) + return; + + log_debug("lde add fec %s nexthop %s", + log_fec(&fn->fec), log_addr(af, nexthop)); + + if (fn->fec.type == FEC_TYPE_PWID) + fn->data = data; + + if (fn->local_label == NO_LABEL) { + if (connected) + fn->local_label = egress_label(fn->fec.type); + else + fn->local_label = lde_assign_label(); + + /* FEC.1: perform lsr label distribution procedure */ + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 1); + } + + fnh = fec_nh_add(fn, af, nexthop, priority); + lde_send_change_klabel(fn, fnh); + + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + case FEC_TYPE_IPV6: + ln = lde_nbr_find_by_addr(af, &fnh->nexthop); + break; + case FEC_TYPE_PWID: + ln = lde_nbr_find_by_lsrid(fn->fec.u.pwid.lsr_id); + break; + default: + ln = NULL; + break; + } + + if (ln) { + /* FEC.2 */ + me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); + if (me) + /* FEC.5 */ + lde_check_mapping(&me->map, ln); + } +} + +void +lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop, + uint8_t priority) +{ + struct fec_node *fn; + struct fec_nh *fnh; + + fn = (struct fec_node *)fec_find(&ft, fec); + if (fn == NULL) + /* route lost */ + return; + fnh = fec_nh_find(fn, af, nexthop, priority); + if (fnh == NULL) + /* route lost */ + return; + + log_debug("lde remove fec %s nexthop %s", + log_fec(&fn->fec), log_addr(af, nexthop)); + + lde_send_delete_klabel(fn, fnh); + fec_nh_del(fnh); + if (LIST_EMPTY(&fn->nexthops)) { + lde_send_labelwithdraw_all(fn, NO_LABEL); + fn->local_label = NO_LABEL; + if (fn->fec.type == FEC_TYPE_PWID) + fn->data = NULL; + } +} + +void +lde_check_mapping(struct map *map, struct lde_nbr *ln) +{ + struct fec fec; + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_req *lre; + struct lde_map *me; + struct l2vpn_pw *pw; + int msgsource = 0; + + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + fn = fec_add(&fec); + + /* LMp.1: first check if we have a pending request running */ + lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec); + if (lre) + /* LMp.2: delete record of outstanding label request */ + lde_req_del(ln, lre, 1); + + /* RFC 4447 control word and status tlv negotiation */ + if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) + return; + + /* + * LMp.3 - LMp.8: loop detection - unnecessary for frame-mode + * mpls networks. + */ + + /* LMp.9 */ + me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); + if (me) { + /* LMp.10 */ + if (me->map.label != map->label && lre == NULL) { + /* LMp.10a */ + lde_send_labelrelease(ln, fn, me->map.label); + + /* + * Can not use lde_nbr_find_by_addr() because there's + * the possibility of multipath. + */ + LIST_FOREACH(fnh, &fn->nexthops, entry) { + if (lde_address_find(ln, fnh->af, + &fnh->nexthop) == NULL) + continue; + + lde_send_delete_klabel(fn, fnh); + fnh->remote_label = NO_LABEL; + } + } + } + + /* + * LMp.11 - 12: consider multiple nexthops in order to + * support multipath + */ + LIST_FOREACH(fnh, &fn->nexthops, entry) { + /* LMp.15: install FEC in FIB */ + switch (fec.type) { + case FEC_TYPE_IPV4: + case FEC_TYPE_IPV6: + if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) + continue; + + fnh->remote_label = map->label; + lde_send_change_klabel(fn, fnh); + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL) + continue; + + pw->remote_group = map->fec.pwid.group_id; + if (map->flags & F_MAP_PW_IFMTU) + pw->remote_mtu = map->fec.pwid.ifmtu; + if (map->flags & F_MAP_PW_STATUS) + pw->remote_status = map->pw_status; + fnh->remote_label = map->label; + if (l2vpn_pw_ok(pw, fnh)) + lde_send_change_klabel(fn, fnh); + break; + default: + break; + } + + msgsource = 1; + } + /* LMp.13 & LMp.16: Record the mapping from this peer */ + if (me == NULL) + me = lde_map_add(ln, fn, 0); + me->map = *map; + + if (msgsource == 0) + /* LMp.13: just return since we use liberal lbl retention */ + return; + + /* + * LMp.17 - LMp.27 are unnecessary since we don't need to implement + * loop detection. LMp.28 - LMp.30 are unnecessary because we are + * merging capable. + */ +} + +void +lde_check_request(struct map *map, struct lde_nbr *ln) +{ + struct fec fec; + struct lde_req *lre; + struct fec_node *fn; + struct fec_nh *fnh; + + /* LRq.1: skip loop detection (not necessary) */ + + /* LRq.2: is there a next hop for fec? */ + lde_map2fec(map, ln->id, &fec); + 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, + htons(MSG_TYPE_LABELREQUEST)); + return; + } + + /* LRq.3: is MsgSource the next hop? */ + LIST_FOREACH(fnh, &fn->nexthops, entry) { + switch (fec.type) { + case FEC_TYPE_IPV4: + case FEC_TYPE_IPV6: + if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) + continue; + + /* LRq.4: send Loop Detected notification */ + lde_send_notification(ln->peerid, S_LOOP_DETECTED, + map->msg_id, htons(MSG_TYPE_LABELREQUEST)); + return; + default: + break; + } + } + + /* 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 */ + return; + + /* 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); + + /* + * LRq.10: do nothing (Request Never) since we use liberal + * label retention. + * LRq.11 - 12 are unnecessary since we are merging capable. + */ +} + +void +lde_check_release(struct map *map, struct lde_nbr *ln) +{ + struct fec fec; + struct fec_node *fn; + struct lde_wdraw *lw; + struct lde_map *me; + + /* TODO group wildcard */ + if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID)) + return; + + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + /* LRl.1: does FEC match a known FEC? */ + if (fn == NULL) + return; + + /* 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))) { + /* LRl.4: delete record of outstanding label 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); + + /* + * LRl.11 - 13 are unnecessary since we remove the label from + * forwarding/switching as soon as the FEC is unreachable. + */ +} + +void +lde_check_release_wcard(struct map *map, struct lde_nbr *ln) +{ + struct fec *f; + struct fec_node *fn; + struct lde_wdraw *lw; + struct lde_map *me; + + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + /* 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))) { + /* 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); + + /* + * LRl.11 - 13 are unnecessary since we remove the label from + * forwarding/switching as soon as the FEC is unreachable. + */ + } +} + +void +lde_check_withdraw(struct map *map, struct lde_nbr *ln) +{ + struct fec fec; + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_map *me; + struct l2vpn_pw *pw; + + /* TODO group wildcard */ + if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID)) + return; + + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + fn = fec_add(&fec); + + /* LWd.1: remove label from forwarding/switching use */ + LIST_FOREACH(fnh, &fn->nexthops, entry) { + switch (fec.type) { + case FEC_TYPE_IPV4: + case FEC_TYPE_IPV6: + if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) + continue; + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL) + continue; + break; + default: + break; + } + lde_send_delete_klabel(fn, fnh); + fnh->remote_label = NO_LABEL; + } + + /* LWd.2: send label release */ + lde_send_labelrelease(ln, fn, map->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)) + /* LWd.4: remove record of previously received lbl mapping */ + lde_map_del(ln, me, 0); +} + +void +lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) +{ + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_map *me; + + /* LWd.2: send label release */ + lde_send_labelrelease(ln, NULL, map->label); + + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + /* LWd.1: remove label from forwarding/switching use */ + LIST_FOREACH(fnh, &fn->nexthops, entry) { + switch (f->type) { + case FEC_TYPE_IPV4: + case FEC_TYPE_IPV6: + if (!lde_address_find(ln, fnh->af, + &fnh->nexthop)) + continue; + break; + case FEC_TYPE_PWID: + if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr) + continue; + break; + default: + break; + } + 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)) + /* + * LWd.4: remove record of previously received + * label mapping + */ + lde_map_del(ln, me, 0); + } +} + +/* gabage collector timer: timer to remove dead entries from the LIB */ + +/* ARGSUSED */ +void +lde_gc_timer(int fd, short event, void *arg) +{ + struct fec *fec, *safe; + struct fec_node *fn; + int count = 0; + + RB_FOREACH_SAFE(fec, fec_tree, &ft, safe) { + fn = (struct fec_node *) fec; + + if (!LIST_EMPTY(&fn->nexthops) || + !LIST_EMPTY(&fn->downstream) || + !LIST_EMPTY(&fn->upstream)) + continue; + + fec_remove(&ft, &fn->fec); + free(fn); + count++; + } + + if (count > 0) + log_debug("%s: %u entries removed", __func__, count); + + lde_gc_start_timer(); +} + +void +lde_gc_start_timer(void) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = LDE_GC_INTERVAL; + if (evtimer_add(&gc_timer, &tv) == -1) + fatal(__func__); +} + +void +lde_gc_stop_timer(void) +{ + if (evtimer_pending(&gc_timer, NULL) && + evtimer_del(&gc_timer) == -1) + fatal(__func__); +} diff --git a/ldpd/ldp.h b/ldpd/ldp.h new file mode 100644 index 0000000000..77034b30a5 --- /dev/null +++ b/ldpd/ldp.h @@ -0,0 +1,304 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* LDP protocol definitions */ + +#ifndef _LDP_H_ +#define _LDP_H_ + +#include + +/* misc */ +#define LDP_VERSION 1 +#define LDP_PORT 646 +#define LDP_MAX_LEN 4096 + +/* All Routers on this Subnet group multicast addresses */ +#define AllRouters_v4 "224.0.0.2" +#define AllRouters_v6 "ff02::2" + +#define LINK_DFLT_HOLDTIME 15 +#define TARGETED_DFLT_HOLDTIME 45 +#define MIN_HOLDTIME 3 +#define MAX_HOLDTIME 0xffff +#define INFINITE_HOLDTIME 0xffff + +#define DEFAULT_KEEPALIVE 180 +#define MIN_KEEPALIVE 3 +#define MAX_KEEPALIVE 0xffff +#define KEEPALIVE_PER_PERIOD 3 +#define INIT_FSM_TIMEOUT 15 + +#define DEFAULT_HELLO_INTERVAL 5 +#define MIN_HELLO_INTERVAL 1 +#define MAX_HELLO_INTERVAL 0xffff + +#define INIT_DELAY_TMR 15 +#define MAX_DELAY_TMR 120 + +#define MIN_PWID_ID 1 +#define MAX_PWID_ID 0xffffffff + +#define DEFAULT_L2VPN_MTU 1500 +#define MIN_L2VPN_MTU 512 +#define MAX_L2VPN_MTU 0xffff + +/* LDP message types */ +#define MSG_TYPE_NOTIFICATION 0x0001 +#define MSG_TYPE_HELLO 0x0100 +#define MSG_TYPE_INIT 0x0200 +#define MSG_TYPE_KEEPALIVE 0x0201 +#define MSG_TYPE_ADDR 0x0300 +#define MSG_TYPE_ADDRWITHDRAW 0x0301 +#define MSG_TYPE_LABELMAPPING 0x0400 +#define MSG_TYPE_LABELREQUEST 0x0401 +#define MSG_TYPE_LABELWITHDRAW 0x0402 +#define MSG_TYPE_LABELRELEASE 0x0403 +#define MSG_TYPE_LABELABORTREQ 0x0404 + +/* LDP TLV types */ +#define TLV_TYPE_FEC 0x0100 +#define TLV_TYPE_ADDRLIST 0x0101 +#define TLV_TYPE_HOPCOUNT 0x0103 +#define TLV_TYPE_PATHVECTOR 0x0104 +#define TLV_TYPE_GENERICLABEL 0x0200 +#define TLV_TYPE_ATMLABEL 0x0201 +#define TLV_TYPE_FRLABEL 0x0202 +#define TLV_TYPE_STATUS 0x0300 +#define TLV_TYPE_EXTSTATUS 0x0301 +#define TLV_TYPE_RETURNEDPDU 0x0302 +#define TLV_TYPE_RETURNEDMSG 0x0303 +#define TLV_TYPE_COMMONHELLO 0x0400 +#define TLV_TYPE_IPV4TRANSADDR 0x0401 +#define TLV_TYPE_CONFIG 0x0402 +#define TLV_TYPE_IPV6TRANSADDR 0x0403 +#define TLV_TYPE_COMMONSESSION 0x0500 +#define TLV_TYPE_ATMSESSIONPAR 0x0501 +#define TLV_TYPE_FRSESSION 0x0502 +#define TLV_TYPE_LABELREQUEST 0x0600 +/* RFC 4447 */ +#define TLV_TYPE_PW_STATUS 0x096A +#define TLV_TYPE_PW_IF_PARAM 0x096B +#define TLV_TYPE_PW_GROUP_ID 0x096C +/* RFC 7552 */ +#define TLV_TYPE_DUALSTACK 0x8701 + +/* LDP header */ +struct ldp_hdr { + uint16_t version; + uint16_t length; + uint32_t lsr_id; + uint16_t lspace_id; +} __packed; + +#define LDP_HDR_SIZE 10 /* actual size of the LDP header */ +#define LDP_HDR_PDU_LEN 6 /* minimum "PDU Length" */ +#define LDP_HDR_DEAD_LEN 4 + +/* TLV record */ +struct tlv { + uint16_t type; + uint16_t length; +}; +#define TLV_HDR_SIZE 4 + +struct ldp_msg { + uint16_t type; + uint16_t length; + uint32_t id; + /* Mandatory Parameters */ + /* Optional Parameters */ +} __packed; + +#define LDP_MSG_SIZE 8 /* minimum size of LDP message */ +#define LDP_MSG_LEN 4 /* minimum "Message Length" */ +#define LDP_MSG_DEAD_LEN 4 + +#define UNKNOWN_FLAG 0x8000 +#define FORWARD_FLAG 0xc000 + +struct hello_prms_tlv { + uint16_t type; + uint16_t length; + uint16_t holdtime; + uint16_t flags; +}; +#define F_HELLO_TARGETED 0x8000 +#define F_HELLO_REQ_TARG 0x4000 +#define F_HELLO_GTSM 0x2000 + +struct hello_prms_opt4_tlv { + uint16_t type; + uint16_t length; + uint32_t value; +}; + +struct hello_prms_opt16_tlv { + uint16_t type; + uint16_t length; + uint8_t value[16]; +}; + +#define DUAL_STACK_LDPOV4 4 +#define DUAL_STACK_LDPOV6 6 + +#define F_HELLO_TLV_RCVD_ADDR 0x01 +#define F_HELLO_TLV_RCVD_CONF 0x02 +#define F_HELLO_TLV_RCVD_DS 0x04 + +#define S_SUCCESS 0x00000000 +#define S_BAD_LDP_ID 0x80000001 +#define S_BAD_PROTO_VER 0x80000002 +#define S_BAD_PDU_LEN 0x80000003 +#define S_UNKNOWN_MSG 0x00000004 +#define S_BAD_MSG_LEN 0x80000005 +#define S_UNKNOWN_TLV 0x00000006 +#define S_BAD_TLV_LEN 0x80000007 +#define S_BAD_TLV_VAL 0x80000008 +#define S_HOLDTIME_EXP 0x80000009 +#define S_SHUTDOWN 0x8000000A +#define S_LOOP_DETECTED 0x0000000B +#define S_UNKNOWN_FEC 0x0000000C +#define S_NO_ROUTE 0x0000000D +#define S_NO_LABEL_RES 0x0000000E +#define S_AVAILABLE 0x0000000F +#define S_NO_HELLO 0x80000010 +#define S_PARM_ADV_MODE 0x80000011 +#define S_MAX_PDU_LEN 0x80000012 +#define S_PARM_L_RANGE 0x80000013 +#define S_KEEPALIVE_TMR 0x80000014 +#define S_LAB_REQ_ABRT 0x00000015 +#define S_MISS_MSG 0x00000016 +#define S_UNSUP_ADDR 0x00000017 +#define S_KEEPALIVE_BAD 0x80000018 +#define S_INTERN_ERR 0x80000019 +/* RFC 4447 */ +#define S_ILLEGAL_CBIT 0x00000024 +#define S_WRONG_CBIT 0x00000025 +#define S_INCPT_BITRATE 0x00000026 +#define S_CEP_MISCONF 0x00000027 +#define S_PW_STATUS 0x00000028 +#define S_UNASSIGN_TAI 0x00000029 +#define S_MISCONF_ERR 0x0000002A +#define S_WITHDRAW_MTHD 0x0000002B +/* RFC 7552 */ +#define S_TRANS_MISMTCH 0x80000032 +#define S_DS_NONCMPLNCE 0x80000033 + +struct sess_prms_tlv { + uint16_t type; + uint16_t length; + uint16_t proto_version; + uint16_t keepalive_time; + uint8_t reserved; + uint8_t pvlim; + uint16_t max_pdu_len; + uint32_t lsr_id; + uint16_t lspace_id; +} __packed; + +#define SESS_PRMS_SIZE 18 +#define SESS_PRMS_LEN 14 + +struct status_tlv { + uint16_t type; + uint16_t length; + uint32_t status_code; + uint32_t msg_id; + uint16_t msg_type; +} __packed; + +#define STATUS_SIZE 14 +#define STATUS_TLV_LEN 10 +#define STATUS_FATAL 0x80000000 + +#define AF_IPV4 0x1 +#define AF_IPV6 0x2 + +struct address_list_tlv { + uint16_t type; + uint16_t length; + uint16_t family; + /* address entries */ +} __packed; + +#define ADDR_LIST_SIZE 6 + +#define FEC_ELM_WCARD_LEN 1 +#define FEC_ELM_PREFIX_MIN_LEN 4 +#define FEC_PWID_ELM_MIN_LEN 8 + +#define MAP_TYPE_WILDCARD 0x01 +#define MAP_TYPE_PREFIX 0x02 +#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 DEFAULT_PW_TYPE PW_TYPE_ETHERNET + +/* RFC 4447 Sub-TLV record */ +struct subtlv { + uint8_t type; + uint8_t length; +}; +#define SUBTLV_HDR_SIZE 2 + +#define SUBTLV_IFMTU 0x01 +#define SUBTLV_VLANID 0x06 + +#define FEC_SUBTLV_IFMTU_SIZE 4 +#define FEC_SUBTLV_VLANID_SIZE 4 + +struct label_tlv { + uint16_t type; + uint16_t length; + uint32_t label; +}; +#define LABEL_TLV_SIZE 8 +#define LABEL_TLV_LEN 4 + +struct reqid_tlv { + uint16_t type; + uint16_t length; + uint32_t reqid; +}; +#define REQID_TLV_SIZE 8 +#define REQID_TLV_LEN 4 + +struct pw_status_tlv { + uint16_t type; + uint16_t length; + uint32_t value; +}; +#define PW_STATUS_TLV_SIZE 8 +#define PW_STATUS_TLV_LEN 4 + +#define PW_FORWARDING 0 +#define PW_NOT_FORWARDING (1 << 0) +#define PW_LOCAL_RX_FAULT (1 << 1) +#define PW_LOCAL_TX_FAULT (1 << 2) +#define PW_PSN_RX_FAULT (1 << 3) +#define PW_PSN_TX_FAULT (1 << 4) + +#define NO_LABEL UINT32_MAX + +#endif /* !_LDP_H_ */ diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c new file mode 100644 index 0000000000..4e14491989 --- /dev/null +++ b/ldpd/ldpd.c @@ -0,0 +1,1227 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004, 2008 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +static void main_sig_handler(int, short, void *); +static __dead void usage(void); +static __dead void ldpd_shutdown(void); +static pid_t start_child(enum ldpd_process, char *, int, int, int); +static void main_dispatch_ldpe(int, short, void *); +static void main_dispatch_lde(int, short, void *); +static int main_imsg_compose_both(enum imsg_type, void *, + uint16_t); +static int main_imsg_send_ipc_sockets(struct imsgbuf *, + struct imsgbuf *); +static void main_imsg_send_net_sockets(int); +static void main_imsg_send_net_socket(int, enum socket_type); +static int main_imsg_send_config(struct ldpd_conf *); +static int ldp_reload(void); +static void merge_global(struct ldpd_conf *, struct ldpd_conf *); +static void merge_af(int, struct ldpd_af_conf *, + struct ldpd_af_conf *); +static void merge_ifaces(struct ldpd_conf *, struct ldpd_conf *); +static void merge_iface_af(struct iface_af *, struct iface_af *); +static void merge_tnbrs(struct ldpd_conf *, struct ldpd_conf *); +static void merge_nbrps(struct ldpd_conf *, struct ldpd_conf *); +static void merge_l2vpns(struct ldpd_conf *, struct ldpd_conf *); +static void merge_l2vpn(struct ldpd_conf *, struct l2vpn *, + struct l2vpn *); + +struct ldpd_global global; +struct ldpd_conf *ldpd_conf; + +static char *conffile; +static struct imsgev *iev_ldpe; +static struct imsgev *iev_lde; +static pid_t ldpe_pid; +static pid_t lde_pid; + +/* ARGSUSED */ +static void +main_sig_handler(int sig, short event, void *arg) +{ + /* signal handler rules don't apply, libevent decouples for us */ + switch (sig) { + case SIGTERM: + case SIGINT: + ldpd_shutdown(); + /* NOTREACHED */ + case SIGHUP: + if (ldp_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); + break; + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +static __dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", + __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sighup; + char *saved_argv0; + int ch; + int debug = 0, lflag = 0, eflag = 0; + int pipe_parent2ldpe[2]; + int pipe_parent2lde[2]; + + conffile = CONF_FILE; + ldpd_process = PROC_MAIN; + + log_init(1); /* log to stderr until daemonized */ + log_verbose(1); + + saved_argv0 = argv[0]; + if (saved_argv0 == NULL) + saved_argv0 = "ldpd"; + + while ((ch = getopt(argc, argv, "dD:f:nvLE")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'D': + if (cmdline_symset(optarg) < 0) + log_warnx("could not parse macro definition %s", + optarg); + break; + case 'f': + conffile = optarg; + break; + case 'n': + global.cmd_opts |= LDPD_OPT_NOACTION; + break; + case 'v': + if (global.cmd_opts & LDPD_OPT_VERBOSE) + global.cmd_opts |= LDPD_OPT_VERBOSE2; + global.cmd_opts |= LDPD_OPT_VERBOSE; + break; + case 'L': + lflag = 1; + break; + case 'E': + eflag = 1; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + if (argc > 0 || (lflag && eflag)) + usage(); + + if (lflag) + lde(debug, global.cmd_opts & LDPD_OPT_VERBOSE); + else if (eflag) + ldpe(debug, global.cmd_opts & LDPD_OPT_VERBOSE); + + /* fetch interfaces early */ + kif_init(); + + /* parse config file */ + if ((ldpd_conf = parse_config(conffile)) == NULL ) { + kif_clear(); + exit(1); + } + + if (global.cmd_opts & LDPD_OPT_NOACTION) { + if (global.cmd_opts & LDPD_OPT_VERBOSE) + print_config(ldpd_conf); + else + fprintf(stderr, "configuration OK\n"); + kif_clear(); + exit(0); + } + + /* check for root privileges */ + if (geteuid()) + errx(1, "need root privileges"); + + /* check for ldpd user */ + if (getpwnam(LDPD_USER) == NULL) + errx(1, "unknown user %s", LDPD_USER); + + log_init(debug); + log_verbose(global.cmd_opts & (LDPD_OPT_VERBOSE | LDPD_OPT_VERBOSE2)); + + if (!debug) + daemon(1, 0); + + log_info("startup"); + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, + PF_UNSPEC, pipe_parent2ldpe) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, + PF_UNSPEC, pipe_parent2lde) == -1) + fatal("socketpair"); + + /* start children */ + lde_pid = start_child(PROC_LDE_ENGINE, saved_argv0, + pipe_parent2lde[1], debug, global.cmd_opts & LDPD_OPT_VERBOSE); + ldpe_pid = start_child(PROC_LDP_ENGINE, saved_argv0, + pipe_parent2ldpe[1], debug, global.cmd_opts & LDPD_OPT_VERBOSE); + + event_init(); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sighup, NULL); + signal(SIGPIPE, SIG_IGN); + + /* setup pipes to children */ + if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL || + (iev_lde = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]); + iev_ldpe->handler = main_dispatch_ldpe; + imsg_init(&iev_lde->ibuf, pipe_parent2lde[0]); + iev_lde->handler = main_dispatch_lde; + + /* setup event handler */ + iev_ldpe->events = EV_READ; + event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd, iev_ldpe->events, + iev_ldpe->handler, iev_ldpe); + event_add(&iev_ldpe->ev, NULL); + + iev_lde->events = EV_READ; + event_set(&iev_lde->ev, iev_lde->ibuf.fd, iev_lde->events, + iev_lde->handler, iev_lde); + event_add(&iev_lde->ev, NULL); + + if (main_imsg_send_ipc_sockets(&iev_ldpe->ibuf, &iev_lde->ibuf)) + fatal("could not establish imsg links"); + main_imsg_send_config(ldpd_conf); + + /* notify ldpe about existing interfaces and addresses */ + kif_redistribute(NULL); + + if (kr_init(!(ldpd_conf->flags & F_LDPD_NO_FIB_UPDATE)) == -1) + fatalx("kr_init failed"); + + if (ldpd_conf->ipv4.flags & F_LDPD_AF_ENABLED) + main_imsg_send_net_sockets(AF_INET); + if (ldpd_conf->ipv6.flags & F_LDPD_AF_ENABLED) + main_imsg_send_net_sockets(AF_INET6); + + /* remove unneded stuff from config */ + /* ... */ + + event_dispatch(); + + ldpd_shutdown(); + /* NOTREACHED */ + return (0); +} + +static __dead void +ldpd_shutdown(void) +{ + pid_t pid; + int status; + + /* close pipes */ + msgbuf_clear(&iev_ldpe->ibuf.w); + close(iev_ldpe->ibuf.fd); + msgbuf_clear(&iev_lde->ibuf.w); + close(iev_lde->ibuf.fd); + + kr_shutdown(); + config_clear(ldpd_conf); + + log_debug("waiting for children to terminate"); + do { + pid = wait(&status); + if (pid == -1) { + if (errno != EINTR && errno != ECHILD) + fatal("wait"); + } else if (WIFSIGNALED(status)) + log_warnx("%s terminated; signal %d", + (pid == lde_pid) ? "label decision engine" : + "ldp engine", WTERMSIG(status)); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + + free(iev_ldpe); + free(iev_lde); + + log_info("terminating"); + exit(0); +} + +static pid_t +start_child(enum ldpd_process p, char *argv0, int fd, int debug, int verbose) +{ + char *argv[5]; + int argc = 0; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + case 0: + break; + default: + close(fd); + return (pid); + } + + if (dup2(fd, 3) == -1) + fatal("cannot setup imsg fd"); + + argv[argc++] = argv0; + switch (p) { + case PROC_MAIN: + fatalx("Can not start main process"); + case PROC_LDE_ENGINE: + argv[argc++] = "-L"; + break; + case PROC_LDP_ENGINE: + argv[argc++] = "-E"; + break; + } + if (debug) + argv[argc++] = "-d"; + if (verbose) + argv[argc++] = "-v"; + argv[argc++] = NULL; + + execvp(argv0, argv); + fatal("execvp"); +} + +/* imsg handling */ +/* ARGSUSED */ +static void +main_dispatch_ldpe(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + int af; + ssize_t n; + int shut = 0, verbose; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_REQUEST_SOCKETS: + af = imsg.hdr.pid; + main_imsg_send_net_sockets(af); + break; + case IMSG_CTL_RELOAD: + if (ldp_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); + break; + case IMSG_CTL_FIB_COUPLE: + kr_fib_couple(); + break; + case IMSG_CTL_FIB_DECOUPLE: + kr_fib_decouple(); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + kr_show_route(&imsg); + break; + case IMSG_CTL_IFINFO: + if (imsg.hdr.len == IMSG_HEADER_SIZE) + kr_ifinfo(NULL, imsg.hdr.pid); + else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ) + kr_ifinfo(imsg.data, imsg.hdr.pid); + else + log_warnx("IFINFO request with wrong len"); + break; + case IMSG_CTL_LOG_VERBOSE: + /* already checked by ldpe */ + memcpy(&verbose, imsg.data, sizeof(verbose)); + log_verbose(verbose); + break; + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +static void +main_dispatch_lde(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + ssize_t n; + int shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_KLABEL_CHANGE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kroute)) + fatalx("invalid size of IMSG_KLABEL_CHANGE"); + if (kr_change(imsg.data)) + log_warnx("%s: error changing route", __func__); + break; + case IMSG_KLABEL_DELETE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kroute)) + fatalx("invalid size of IMSG_KLABEL_DELETE"); + if (kr_delete(imsg.data)) + log_warnx("%s: error deleting route", __func__); + break; + case IMSG_KPWLABEL_CHANGE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kpw)) + fatalx("invalid size of IMSG_KPWLABEL_CHANGE"); + if (kmpw_set(imsg.data)) + log_warnx("%s: error changing pseudowire", + __func__); + break; + case IMSG_KPWLABEL_DELETE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kpw)) + fatalx("invalid size of IMSG_KPWLABEL_DELETE"); + if (kmpw_unset(imsg.data)) + log_warnx("%s: error unsetting pseudowire", + __func__); + break; + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +main_imsg_compose_ldpe(int type, pid_t pid, void *data, uint16_t datalen) +{ + if (iev_ldpe == NULL) + return; + imsg_compose_event(iev_ldpe, type, 0, pid, -1, data, datalen); +} + +void +main_imsg_compose_lde(int type, pid_t pid, void *data, uint16_t datalen) +{ + imsg_compose_event(iev_lde, type, 0, pid, -1, data, datalen); +} + +static int +main_imsg_compose_both(enum imsg_type type, void *buf, uint16_t len) +{ + if (imsg_compose_event(iev_ldpe, type, 0, 0, -1, buf, len) == -1) + return (-1); + if (imsg_compose_event(iev_lde, type, 0, 0, -1, buf, len) == -1) + return (-1); + return (0); +} + +void +imsg_event_add(struct imsgev *iev) +{ + iev->events = EV_READ; + if (iev->ibuf.w.queued) + iev->events |= EV_WRITE; + + event_del(&iev->ev); + event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); + event_add(&iev->ev, NULL); +} + +int +imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, + pid_t pid, int fd, void *data, uint16_t datalen) +{ + int ret; + + if ((ret = imsg_compose(&iev->ibuf, type, peerid, + pid, fd, data, datalen)) != -1) + imsg_event_add(iev); + return (ret); +} + +void +evbuf_enqueue(struct evbuf *eb, struct ibuf *buf) +{ + ibuf_close(&eb->wbuf, buf); + evbuf_event_add(eb); +} + +void +evbuf_event_add(struct evbuf *eb) +{ + if (eb->wbuf.queued) + event_add(&eb->ev, NULL); +} + +void +evbuf_init(struct evbuf *eb, int fd, void (*handler)(int, short, void *), + void *arg) +{ + msgbuf_init(&eb->wbuf); + eb->wbuf.fd = fd; + event_set(&eb->ev, eb->wbuf.fd, EV_WRITE, handler, arg); +} + +void +evbuf_clear(struct evbuf *eb) +{ + event_del(&eb->ev); + msgbuf_clear(&eb->wbuf); + eb->wbuf.fd = -1; +} + +static int +main_imsg_send_ipc_sockets(struct imsgbuf *ldpe_buf, struct imsgbuf *lde_buf) +{ + int pipe_ldpe2lde[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_ldpe2lde) == -1) + return (-1); + + if (imsg_compose(ldpe_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[0], + NULL, 0) == -1) + return (-1); + if (imsg_compose(lde_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[1], + NULL, 0) == -1) + return (-1); + + return (0); +} + +static void +main_imsg_send_net_sockets(int af) +{ + main_imsg_send_net_socket(af, LDP_SOCKET_DISC); + main_imsg_send_net_socket(af, LDP_SOCKET_EDISC); + main_imsg_send_net_socket(af, LDP_SOCKET_SESSION); + imsg_compose_event(iev_ldpe, IMSG_SETUP_SOCKETS, af, 0, -1, NULL, 0); +} + +static void +main_imsg_send_net_socket(int af, enum socket_type type) +{ + int fd; + + fd = ldp_create_socket(af, type); + if (fd == -1) { + log_warnx("%s: failed to create %s socket for address-family " + "%s", __func__, socket_name(type), af_name(af)); + return; + } + + imsg_compose_event(iev_ldpe, IMSG_SOCKET_NET, af, 0, fd, &type, + sizeof(type)); +} + +struct ldpd_af_conf * +ldp_af_conf_get(struct ldpd_conf *xconf, int af) +{ + switch (af) { + case AF_INET: + return (&xconf->ipv4); + case AF_INET6: + return (&xconf->ipv6); + default: + fatalx("ldp_af_conf_get: unknown af"); + } +} + +struct ldpd_af_global * +ldp_af_global_get(struct ldpd_global *xglobal, int af) +{ + switch (af) { + case AF_INET: + return (&xglobal->ipv4); + case AF_INET6: + return (&xglobal->ipv6); + default: + fatalx("ldp_af_global_get: unknown af"); + } +} + +int +ldp_is_dual_stack(struct ldpd_conf *xconf) +{ + return ((xconf->ipv4.flags & F_LDPD_AF_ENABLED) && + (xconf->ipv6.flags & F_LDPD_AF_ENABLED)); +} + +static int +main_imsg_send_config(struct ldpd_conf *xconf) +{ + struct iface *iface; + struct tnbr *tnbr; + struct nbr_params *nbrp; + struct l2vpn *l2vpn; + struct l2vpn_if *lif; + struct l2vpn_pw *pw; + + if (main_imsg_compose_both(IMSG_RECONF_CONF, xconf, + sizeof(*xconf)) == -1) + return (-1); + + LIST_FOREACH(iface, &xconf->iface_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_IFACE, iface, + sizeof(*iface)) == -1) + return (-1); + } + + LIST_FOREACH(tnbr, &xconf->tnbr_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_TNBR, tnbr, + sizeof(*tnbr)) == -1) + return (-1); + } + + LIST_FOREACH(nbrp, &xconf->nbrp_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_NBRP, nbrp, + sizeof(*nbrp)) == -1) + return (-1); + } + + LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_L2VPN, l2vpn, + sizeof(*l2vpn)) == -1) + return (-1); + + LIST_FOREACH(lif, &l2vpn->if_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_L2VPN_IF, lif, + sizeof(*lif)) == -1) + return (-1); + } + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_L2VPN_PW, pw, + sizeof(*pw)) == -1) + return (-1); + } + } + + if (main_imsg_compose_both(IMSG_RECONF_END, NULL, 0) == -1) + return (-1); + + return (0); +} + +static int +ldp_reload(void) +{ + struct ldpd_conf *xconf; + + if ((xconf = parse_config(conffile)) == NULL) + return (-1); + + if (main_imsg_send_config(xconf) == -1) + return (-1); + + merge_config(ldpd_conf, xconf); + + return (0); +} + +void +merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + merge_global(conf, xconf); + merge_af(AF_INET, &conf->ipv4, &xconf->ipv4); + merge_af(AF_INET6, &conf->ipv6, &xconf->ipv6); + merge_ifaces(conf, xconf); + merge_tnbrs(conf, xconf); + merge_nbrps(conf, xconf); + merge_l2vpns(conf, xconf); + free(xconf); +} + +static void +merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + /* change of router-id requires resetting all neighborships */ + if (conf->rtr_id.s_addr != xconf->rtr_id.s_addr) { + if (ldpd_process == PROC_LDP_ENGINE) { + ldpe_reset_nbrs(AF_INET); + ldpe_reset_nbrs(AF_INET6); + if (conf->rtr_id.s_addr == INADDR_ANY || + xconf->rtr_id.s_addr == INADDR_ANY) { + if_update_all(AF_UNSPEC); + tnbr_update_all(AF_UNSPEC); + } + } + conf->rtr_id = xconf->rtr_id; + } + + if (conf->trans_pref != xconf->trans_pref) { + if (ldpd_process == PROC_LDP_ENGINE) + ldpe_reset_ds_nbrs(); + conf->trans_pref = xconf->trans_pref; + } + + if ((conf->flags & F_LDPD_DS_CISCO_INTEROP) != + (xconf->flags & F_LDPD_DS_CISCO_INTEROP)) { + if (ldpd_process == PROC_LDP_ENGINE) + ldpe_reset_ds_nbrs(); + } + + conf->flags = xconf->flags; +} + +static void +merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) +{ + int egress_label_changed = 0; + int update_sockets = 0; + + if (af_conf->keepalive != xa->keepalive) { + af_conf->keepalive = xa->keepalive; + if (ldpd_process == PROC_LDP_ENGINE) + ldpe_stop_init_backoff(af); + } + af_conf->thello_holdtime = xa->thello_holdtime; + af_conf->thello_interval = xa->thello_interval; + + /* update flags */ + if (ldpd_process == PROC_LDP_ENGINE && + (af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) && + !(xa->flags & F_LDPD_AF_THELLO_ACCEPT)) + ldpe_remove_dynamic_tnbrs(af); + + if ((af_conf->flags & F_LDPD_AF_NO_GTSM) != + (xa->flags & F_LDPD_AF_NO_GTSM)) { + if (af == AF_INET6) + /* need to set/unset IPV6_MINHOPCOUNT */ + update_sockets = 1; + else if (ldpd_process == PROC_LDP_ENGINE) + /* for LDPv4 just resetting the neighbors is enough */ + ldpe_reset_nbrs(af); + } + + if ((af_conf->flags & F_LDPD_AF_EXPNULL) != + (xa->flags & F_LDPD_AF_EXPNULL)) + egress_label_changed = 1; + + af_conf->flags = xa->flags; + + if (egress_label_changed) { + switch (ldpd_process) { + case PROC_LDE_ENGINE: + lde_change_egress_label(af, af_conf->flags & + F_LDPD_AF_EXPNULL); + break; + case PROC_MAIN: + kr_change_egress_label(af, af_conf->flags & + F_LDPD_AF_EXPNULL); + break; + default: + break; + } + } + + if (ldp_addrcmp(af, &af_conf->trans_addr, &xa->trans_addr)) { + af_conf->trans_addr = xa->trans_addr; + update_sockets = 1; + } + + if (ldpd_process == PROC_MAIN && update_sockets) + imsg_compose_event(iev_ldpe, IMSG_CLOSE_SOCKETS, af, 0, -1, + NULL, 0); +} + +static void +merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + struct iface *iface, *itmp, *xi; + + LIST_FOREACH_SAFE(iface, &conf->iface_list, entry, itmp) { + /* find deleted interfaces */ + if ((xi = if_lookup(xconf, iface->ifindex)) == NULL) { + LIST_REMOVE(iface, entry); + if (ldpd_process == PROC_LDP_ENGINE) + if_exit(iface); + free(iface); + } + } + LIST_FOREACH_SAFE(xi, &xconf->iface_list, entry, itmp) { + /* find new interfaces */ + if ((iface = if_lookup(conf, xi->ifindex)) == NULL) { + LIST_REMOVE(xi, entry); + LIST_INSERT_HEAD(&conf->iface_list, xi, entry); + + /* resend addresses to activate new interfaces */ + if (ldpd_process == PROC_MAIN) + kif_redistribute(xi->name); + continue; + } + + /* update existing interfaces */ + merge_iface_af(&iface->ipv4, &xi->ipv4); + merge_iface_af(&iface->ipv6, &xi->ipv6); + LIST_REMOVE(xi, entry); + free(xi); + } +} + +static void +merge_iface_af(struct iface_af *ia, struct iface_af *xi) +{ + if (ia->enabled != xi->enabled) { + ia->enabled = xi->enabled; + if (ldpd_process == PROC_LDP_ENGINE) + if_update(ia->iface, ia->af); + } + ia->hello_holdtime = xi->hello_holdtime; + ia->hello_interval = xi->hello_interval; +} + +static void +merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + struct tnbr *tnbr, *ttmp, *xt; + + LIST_FOREACH_SAFE(tnbr, &conf->tnbr_list, entry, ttmp) { + if (!(tnbr->flags & F_TNBR_CONFIGURED)) + continue; + + /* find deleted tnbrs */ + if ((xt = tnbr_find(xconf, tnbr->af, &tnbr->addr)) == NULL) { + if (ldpd_process == PROC_LDP_ENGINE) { + tnbr->flags &= ~F_TNBR_CONFIGURED; + tnbr_check(tnbr); + } else { + LIST_REMOVE(tnbr, entry); + free(tnbr); + } + } + } + LIST_FOREACH_SAFE(xt, &xconf->tnbr_list, entry, ttmp) { + /* find new tnbrs */ + if ((tnbr = tnbr_find(conf, xt->af, &xt->addr)) == NULL) { + LIST_REMOVE(xt, entry); + LIST_INSERT_HEAD(&conf->tnbr_list, xt, entry); + + if (ldpd_process == PROC_LDP_ENGINE) + tnbr_update(xt); + continue; + } + + /* update existing tnbrs */ + if (!(tnbr->flags & F_TNBR_CONFIGURED)) + tnbr->flags |= F_TNBR_CONFIGURED; + tnbr->hello_holdtime = xt->hello_holdtime; + tnbr->hello_interval = xt->hello_interval; + LIST_REMOVE(xt, entry); + free(xt); + } +} + +static void +merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + struct nbr_params *nbrp, *ntmp, *xn; + struct nbr *nbr; + int nbrp_changed; + + LIST_FOREACH_SAFE(nbrp, &conf->nbrp_list, entry, ntmp) { + /* find deleted nbrps */ + if ((xn = nbr_params_find(xconf, nbrp->lsr_id)) == NULL) { + if (ldpd_process == PROC_LDP_ENGINE) { + nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); + if (nbr) { + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + pfkey_remove(nbr); + if (nbr_session_active_role(nbr)) + nbr_establish_connection(nbr); + } + } + LIST_REMOVE(nbrp, entry); + free(nbrp); + } + } + LIST_FOREACH_SAFE(xn, &xconf->nbrp_list, entry, ntmp) { + /* find new nbrps */ + if ((nbrp = nbr_params_find(conf, xn->lsr_id)) == NULL) { + LIST_REMOVE(xn, entry); + LIST_INSERT_HEAD(&conf->nbrp_list, xn, entry); + + if (ldpd_process == PROC_LDP_ENGINE) { + nbr = nbr_find_ldpid(xn->lsr_id.s_addr); + if (nbr) { + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + if (pfkey_establish(nbr, xn) == -1) + fatalx("pfkey setup failed"); + if (nbr_session_active_role(nbr)) + nbr_establish_connection(nbr); + } + } + continue; + } + + /* update existing nbrps */ + if (nbrp->flags != xn->flags || + nbrp->keepalive != xn->keepalive || + nbrp->gtsm_enabled != xn->gtsm_enabled || + nbrp->gtsm_hops != xn->gtsm_hops || + nbrp->auth.method != xn->auth.method || + strcmp(nbrp->auth.md5key, xn->auth.md5key) != 0) + nbrp_changed = 1; + else + nbrp_changed = 0; + + nbrp->keepalive = xn->keepalive; + nbrp->gtsm_enabled = xn->gtsm_enabled; + nbrp->gtsm_hops = xn->gtsm_hops; + nbrp->auth.method = xn->auth.method; + strlcpy(nbrp->auth.md5key, xn->auth.md5key, + sizeof(nbrp->auth.md5key)); + nbrp->auth.md5key_len = xn->auth.md5key_len; + nbrp->flags = xn->flags; + + if (ldpd_process == PROC_LDP_ENGINE) { + nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); + if (nbr && nbrp_changed) { + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + pfkey_remove(nbr); + if (pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); + if (nbr_session_active_role(nbr)) + nbr_establish_connection(nbr); + } + } + LIST_REMOVE(xn, entry); + free(xn); + } +} + +static void +merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + struct l2vpn *l2vpn, *ltmp, *xl; + + LIST_FOREACH_SAFE(l2vpn, &conf->l2vpn_list, entry, ltmp) { + /* find deleted l2vpns */ + if ((xl = l2vpn_find(xconf, l2vpn->name)) == NULL) { + LIST_REMOVE(l2vpn, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_exit(l2vpn); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_exit(l2vpn); + break; + case PROC_MAIN: + break; + } + l2vpn_del(l2vpn); + } + } + LIST_FOREACH_SAFE(xl, &xconf->l2vpn_list, entry, ltmp) { + /* find new l2vpns */ + if ((l2vpn = l2vpn_find(conf, xl->name)) == NULL) { + LIST_REMOVE(xl, entry); + LIST_INSERT_HEAD(&conf->l2vpn_list, xl, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_init(xl); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_init(xl); + break; + case PROC_MAIN: + break; + } + continue; + } + + /* update existing l2vpns */ + merge_l2vpn(conf, l2vpn, xl); + LIST_REMOVE(xl, entry); + free(xl); + } +} + +static void +merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) +{ + struct l2vpn_if *lif, *ftmp, *xf; + struct l2vpn_pw *pw, *ptmp, *xp; + struct nbr *nbr; + int reset_nbr, reinstall_pwfec, reinstall_tnbr; + int previous_pw_type, previous_mtu; + + previous_pw_type = l2vpn->pw_type; + previous_mtu = l2vpn->mtu; + + /* merge intefaces */ + LIST_FOREACH_SAFE(lif, &l2vpn->if_list, entry, ftmp) { + /* find deleted interfaces */ + if ((xf = l2vpn_if_find(xl, lif->ifindex)) == NULL) { + LIST_REMOVE(lif, entry); + free(lif); + } + } + LIST_FOREACH_SAFE(xf, &xl->if_list, entry, ftmp) { + /* find new interfaces */ + if ((lif = l2vpn_if_find(l2vpn, xf->ifindex)) == NULL) { + LIST_REMOVE(xf, entry); + LIST_INSERT_HEAD(&l2vpn->if_list, xf, entry); + xf->l2vpn = l2vpn; + continue; + } + + LIST_REMOVE(xf, entry); + free(xf); + } + + /* merge pseudowires */ + LIST_FOREACH_SAFE(pw, &l2vpn->pw_list, entry, ptmp) { + /* find deleted pseudowires */ + if ((xp = l2vpn_pw_find(xl, pw->ifindex)) == NULL) { + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_exit(pw); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_pw_exit(pw); + break; + case PROC_MAIN: + break; + } + + LIST_REMOVE(pw, entry); + free(pw); + } + } + LIST_FOREACH_SAFE(xp, &xl->pw_list, entry, ptmp) { + /* find new pseudowires */ + if ((pw = l2vpn_pw_find(l2vpn, xp->ifindex)) == NULL) { + LIST_REMOVE(xp, entry); + LIST_INSERT_HEAD(&l2vpn->pw_list, xp, entry); + xp->l2vpn = l2vpn; + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_init(xp); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_pw_init(xp); + break; + case PROC_MAIN: + break; + } + continue; + } + + /* update existing pseudowire */ + if (pw->af != xp->af || + ldp_addrcmp(pw->af, &pw->addr, &xp->addr)) + reinstall_tnbr = 1; + else + reinstall_tnbr = 0; + + /* changes that require a session restart */ + if ((pw->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)) != + (xp->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF))) + reset_nbr = 1; + else + reset_nbr = 0; + + if (l2vpn->pw_type != xl->pw_type || l2vpn->mtu != xl->mtu || + pw->pwid != xp->pwid || reinstall_tnbr || reset_nbr || + pw->lsr_id.s_addr != xp->lsr_id.s_addr) + reinstall_pwfec = 1; + else + reinstall_pwfec = 0; + + if (ldpd_process == PROC_LDP_ENGINE) { + if (reinstall_tnbr) + ldpe_l2vpn_pw_exit(pw); + if (reset_nbr) { + nbr = nbr_find_ldpid(pw->lsr_id.s_addr); + if (nbr && nbr->state == NBR_STA_OPER) + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } + } + if (ldpd_process == PROC_LDE_ENGINE && + !reset_nbr && reinstall_pwfec) + l2vpn_pw_exit(pw); + pw->lsr_id = xp->lsr_id; + pw->af = xp->af; + pw->addr = xp->addr; + pw->pwid = xp->pwid; + strlcpy(pw->ifname, xp->ifname, sizeof(pw->ifname)); + pw->ifindex = xp->ifindex; + if (xp->flags & F_PW_CWORD_CONF) + pw->flags |= F_PW_CWORD_CONF; + else + pw->flags &= ~F_PW_CWORD_CONF; + if (xp->flags & F_PW_STATUSTLV_CONF) + pw->flags |= F_PW_STATUSTLV_CONF; + else + pw->flags &= ~F_PW_STATUSTLV_CONF; + if (ldpd_process == PROC_LDP_ENGINE && reinstall_tnbr) + ldpe_l2vpn_pw_init(pw); + if (ldpd_process == PROC_LDE_ENGINE && + !reset_nbr && reinstall_pwfec) { + l2vpn->pw_type = xl->pw_type; + l2vpn->mtu = xl->mtu; + l2vpn_pw_init(pw); + l2vpn->pw_type = previous_pw_type; + l2vpn->mtu = previous_mtu; + } + + LIST_REMOVE(xp, entry); + free(xp); + } + + l2vpn->pw_type = xl->pw_type; + l2vpn->mtu = xl->mtu; + strlcpy(l2vpn->br_ifname, xl->br_ifname, sizeof(l2vpn->br_ifname)); + l2vpn->br_ifindex = xl->br_ifindex; +} + +struct ldpd_conf * +config_new_empty(void) +{ + struct ldpd_conf *xconf; + + xconf = calloc(1, sizeof(*xconf)); + if (xconf == NULL) + fatal(NULL); + + LIST_INIT(&xconf->iface_list); + LIST_INIT(&xconf->tnbr_list); + LIST_INIT(&xconf->nbrp_list); + LIST_INIT(&xconf->l2vpn_list); + + return (xconf); +} + +void +config_clear(struct ldpd_conf *conf) +{ + struct ldpd_conf *xconf; + + /* + * Merge current config with an empty config, this will deactivate + * and deallocate all the interfaces, pseudowires and so on. Before + * merging, copy the router-id and other variables to avoid some + * unnecessary operations, like trying to reset the neighborships. + */ + xconf = config_new_empty(); + xconf->ipv4 = conf->ipv4; + xconf->ipv6 = conf->ipv6; + xconf->rtr_id = conf->rtr_id; + xconf->trans_pref = conf->trans_pref; + xconf->flags = conf->flags; + merge_config(conf, xconf); + free(conf); +} diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h new file mode 100644 index 0000000000..d32d23c6c9 --- /dev/null +++ b/ldpd/ldpd.h @@ -0,0 +1,606 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDPD_H_ +#define _LDPD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldp.h" + +#define CONF_FILE "/etc/ldpd.conf" +#define LDPD_SOCKET "/var/run/ldpd.sock" +#define LDPD_USER "_ldpd" + +#define LDPD_OPT_VERBOSE 0x00000001 +#define LDPD_OPT_VERBOSE2 0x00000002 +#define LDPD_OPT_NOACTION 0x00000004 + +#define TCP_MD5_KEY_LEN 80 +#define L2VPN_NAME_LEN 32 + +#define RT_BUF_SIZE 16384 +#define MAX_RTSOCK_BUF 128 * 1024 +#define LDP_BACKLOG 128 + +#define F_LDPD_INSERTED 0x0001 +#define F_CONNECTED 0x0002 +#define F_STATIC 0x0004 +#define F_DYNAMIC 0x0008 +#define F_REJECT 0x0010 +#define F_BLACKHOLE 0x0020 +#define F_REDISTRIBUTED 0x0040 + +struct evbuf { + struct msgbuf wbuf; + struct event ev; +}; + +struct imsgev { + struct imsgbuf ibuf; + void (*handler)(int, short, void *); + struct event ev; + short events; +}; + +enum imsg_type { + IMSG_NONE, + IMSG_CTL_RELOAD, + IMSG_CTL_SHOW_INTERFACE, + IMSG_CTL_SHOW_DISCOVERY, + IMSG_CTL_SHOW_NBR, + IMSG_CTL_SHOW_LIB, + IMSG_CTL_SHOW_L2VPN_PW, + IMSG_CTL_SHOW_L2VPN_BINDING, + IMSG_CTL_CLEAR_NBR, + IMSG_CTL_FIB_COUPLE, + IMSG_CTL_FIB_DECOUPLE, + IMSG_CTL_KROUTE, + IMSG_CTL_KROUTE_ADDR, + IMSG_CTL_IFINFO, + IMSG_CTL_END, + IMSG_CTL_LOG_VERBOSE, + IMSG_KLABEL_CHANGE, + IMSG_KLABEL_DELETE, + IMSG_KPWLABEL_CHANGE, + IMSG_KPWLABEL_DELETE, + IMSG_IFSTATUS, + IMSG_NEWADDR, + IMSG_DELADDR, + IMSG_LABEL_MAPPING, + IMSG_LABEL_MAPPING_FULL, + IMSG_LABEL_REQUEST, + IMSG_LABEL_RELEASE, + IMSG_LABEL_WITHDRAW, + IMSG_LABEL_ABORT, + IMSG_REQUEST_ADD, + IMSG_REQUEST_ADD_END, + IMSG_MAPPING_ADD, + IMSG_MAPPING_ADD_END, + IMSG_RELEASE_ADD, + IMSG_RELEASE_ADD_END, + IMSG_WITHDRAW_ADD, + IMSG_WITHDRAW_ADD_END, + IMSG_ADDRESS_ADD, + IMSG_ADDRESS_DEL, + IMSG_NOTIFICATION, + IMSG_NOTIFICATION_SEND, + IMSG_NEIGHBOR_UP, + IMSG_NEIGHBOR_DOWN, + IMSG_NETWORK_ADD, + IMSG_NETWORK_DEL, + IMSG_SOCKET_IPC, + IMSG_SOCKET_NET, + IMSG_CLOSE_SOCKETS, + IMSG_REQUEST_SOCKETS, + IMSG_SETUP_SOCKETS, + IMSG_RECONF_CONF, + IMSG_RECONF_IFACE, + IMSG_RECONF_TNBR, + IMSG_RECONF_NBRP, + IMSG_RECONF_L2VPN, + IMSG_RECONF_L2VPN_IF, + IMSG_RECONF_L2VPN_PW, + IMSG_RECONF_END +}; + +union ldpd_addr { + struct in_addr v4; + struct in6_addr v6; +}; + +#define IN6_IS_SCOPE_EMBED(a) \ + ((IN6_IS_ADDR_LINKLOCAL(a)) || \ + (IN6_IS_ADDR_MC_LINKLOCAL(a)) || \ + (IN6_IS_ADDR_MC_INTFACELOCAL(a))) + +/* interface states */ +#define IF_STA_DOWN 0x01 +#define IF_STA_ACTIVE 0x02 + +/* targeted neighbor states */ +#define TNBR_STA_DOWN 0x01 +#define TNBR_STA_ACTIVE 0x02 + +/* interface types */ +enum iface_type { + IF_TYPE_POINTOPOINT, + IF_TYPE_BROADCAST +}; + +/* neighbor states */ +#define NBR_STA_PRESENT 0x0001 +#define NBR_STA_INITIAL 0x0002 +#define NBR_STA_OPENREC 0x0004 +#define NBR_STA_OPENSENT 0x0008 +#define NBR_STA_OPER 0x0010 +#define NBR_STA_SESSION (NBR_STA_INITIAL | NBR_STA_OPENREC | \ + NBR_STA_OPENSENT | NBR_STA_OPER) + +/* neighbor events */ +enum nbr_event { + NBR_EVT_NOTHING, + NBR_EVT_MATCH_ADJ, + NBR_EVT_CONNECT_UP, + NBR_EVT_CLOSE_SESSION, + NBR_EVT_INIT_RCVD, + NBR_EVT_KEEPALIVE_RCVD, + NBR_EVT_PDU_RCVD, + NBR_EVT_PDU_SENT, + NBR_EVT_INIT_SENT +}; + +/* neighbor actions */ +enum nbr_action { + NBR_ACT_NOTHING, + NBR_ACT_RST_KTIMEOUT, + NBR_ACT_SESSION_EST, + NBR_ACT_RST_KTIMER, + NBR_ACT_CONNECT_SETUP, + NBR_ACT_PASSIVE_INIT, + NBR_ACT_KEEPALIVE_SEND, + NBR_ACT_CLOSE_SESSION +}; + +TAILQ_HEAD(mapping_head, mapping_entry); + +struct map { + uint8_t type; + uint32_t msg_id; + union { + struct { + uint16_t af; + union ldpd_addr prefix; + uint8_t prefixlen; + } prefix; + struct { + uint16_t type; + uint32_t pwid; + uint32_t group_id; + uint16_t ifmtu; + } pwid; + } fec; + struct { + uint32_t status_code; + uint32_t msg_id; + uint16_t msg_type; + } st; + uint32_t label; + uint32_t requestid; + uint32_t pw_status; + uint8_t flags; +}; +#define F_MAP_REQ_ID 0x01 /* optional request message id present */ +#define F_MAP_STATUS 0x02 /* status */ +#define F_MAP_PW_CWORD 0x04 /* pseudowire control word */ +#define F_MAP_PW_ID 0x08 /* pseudowire connection id */ +#define F_MAP_PW_IFMTU 0x10 /* pseudowire interface parameter */ +#define F_MAP_PW_STATUS 0x20 /* pseudowire status */ + +struct notify_msg { + uint32_t status_code; + uint32_t msg_id; /* network byte order */ + uint16_t msg_type; /* network byte order */ + uint32_t pw_status; + struct map fec; + uint8_t flags; +}; +#define F_NOTIF_PW_STATUS 0x01 /* pseudowire status tlv present */ +#define F_NOTIF_FEC 0x02 /* fec tlv present */ + +struct if_addr { + LIST_ENTRY(if_addr) entry; + int af; + union ldpd_addr addr; + uint8_t prefixlen; + union ldpd_addr dstbrd; +}; +LIST_HEAD(if_addr_head, if_addr); + +struct iface_af { + struct iface *iface; + int af; + int enabled; + int state; + LIST_HEAD(, adj) adj_list; + time_t uptime; + struct event hello_timer; + uint16_t hello_holdtime; + uint16_t hello_interval; +}; + +struct iface { + LIST_ENTRY(iface) entry; + char name[IF_NAMESIZE]; + unsigned int ifindex; + struct if_addr_head addr_list; + struct in6_addr linklocal; + enum iface_type type; + uint8_t if_type; + uint16_t flags; + uint8_t linkstate; + struct iface_af ipv4; + struct iface_af ipv6; +}; + +/* source of targeted hellos */ +struct tnbr { + LIST_ENTRY(tnbr) entry; + struct event hello_timer; + struct adj *adj; + int af; + union ldpd_addr addr; + int state; + uint16_t hello_holdtime; + uint16_t hello_interval; + uint16_t pw_count; + uint8_t flags; +}; +#define F_TNBR_CONFIGURED 0x01 +#define F_TNBR_DYNAMIC 0x02 + +enum auth_method { + AUTH_NONE, + AUTH_MD5SIG +}; + +/* neighbor specific parameters */ +struct nbr_params { + LIST_ENTRY(nbr_params) entry; + struct in_addr lsr_id; + uint16_t keepalive; + int gtsm_enabled; + uint8_t gtsm_hops; + struct { + enum auth_method method; + char md5key[TCP_MD5_KEY_LEN]; + uint8_t md5key_len; + } auth; + uint8_t flags; +}; +#define F_NBRP_KEEPALIVE 0x01 +#define F_NBRP_GTSM 0x02 +#define F_NBRP_GTSM_HOPS 0x04 + +struct l2vpn_if { + LIST_ENTRY(l2vpn_if) entry; + struct l2vpn *l2vpn; + char ifname[IF_NAMESIZE]; + unsigned int ifindex; + uint16_t flags; + uint8_t link_state; +}; + +struct l2vpn_pw { + LIST_ENTRY(l2vpn_pw) entry; + struct l2vpn *l2vpn; + struct in_addr lsr_id; + int af; + union ldpd_addr addr; + uint32_t pwid; + char ifname[IF_NAMESIZE]; + unsigned int ifindex; + uint32_t remote_group; + uint16_t remote_mtu; + uint32_t remote_status; + uint8_t flags; +}; +#define F_PW_STATUSTLV_CONF 0x01 /* status tlv configured */ +#define F_PW_STATUSTLV 0x02 /* status tlv negotiated */ +#define F_PW_CWORD_CONF 0x04 /* control word configured */ +#define F_PW_CWORD 0x08 /* control word negotiated */ +#define F_PW_STATUS_UP 0x10 /* pseudowire is operational */ + +struct l2vpn { + LIST_ENTRY(l2vpn) entry; + char name[L2VPN_NAME_LEN]; + int type; + int pw_type; + int mtu; + char br_ifname[IF_NAMESIZE]; + unsigned int br_ifindex; + LIST_HEAD(, l2vpn_if) if_list; + LIST_HEAD(, l2vpn_pw) pw_list; +}; +#define L2VPN_TYPE_VPWS 1 +#define L2VPN_TYPE_VPLS 2 + +/* ldp_conf */ +enum ldpd_process { + PROC_MAIN, + PROC_LDP_ENGINE, + PROC_LDE_ENGINE +} ldpd_process; + +enum socket_type { + LDP_SOCKET_DISC, + LDP_SOCKET_EDISC, + LDP_SOCKET_SESSION +}; + +enum hello_type { + HELLO_LINK, + HELLO_TARGETED +}; + +struct ldpd_af_conf { + uint16_t keepalive; + uint16_t thello_holdtime; + uint16_t thello_interval; + union ldpd_addr trans_addr; + int flags; +}; +#define F_LDPD_AF_ENABLED 0x0001 +#define F_LDPD_AF_THELLO_ACCEPT 0x0002 +#define F_LDPD_AF_EXPNULL 0x0004 +#define F_LDPD_AF_NO_GTSM 0x0008 + +struct ldpd_conf { + struct in_addr rtr_id; + struct ldpd_af_conf ipv4; + struct ldpd_af_conf ipv6; + LIST_HEAD(, iface) iface_list; + LIST_HEAD(, tnbr) tnbr_list; + LIST_HEAD(, nbr_params) nbrp_list; + LIST_HEAD(, l2vpn) l2vpn_list; + uint16_t trans_pref; + int flags; +}; +#define F_LDPD_NO_FIB_UPDATE 0x0001 +#define F_LDPD_DS_CISCO_INTEROP 0x0002 + +struct ldpd_af_global { + struct event disc_ev; + struct event edisc_ev; + int ldp_disc_socket; + int ldp_edisc_socket; + int ldp_session_socket; +}; + +struct ldpd_global { + int cmd_opts; + time_t uptime; + struct ldpd_af_global ipv4; + struct ldpd_af_global ipv6; + uint32_t conf_seqnum; + int pfkeysock; + struct if_addr_head addr_list; + LIST_HEAD(, adj) adj_list; + struct in_addr mcast_addr_v4; + struct in6_addr mcast_addr_v6; + TAILQ_HEAD(, pending_conn) pending_conns; +}; + +/* kroute */ +struct kroute { + int af; + union ldpd_addr prefix; + uint8_t prefixlen; + union ldpd_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + unsigned short ifindex; + uint8_t priority; + uint16_t flags; +}; + +struct kpw { + unsigned short ifindex; + int pw_type; + int af; + union ldpd_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; +}; + +struct kaddr { + unsigned short ifindex; + int af; + union ldpd_addr addr; + uint8_t prefixlen; + union ldpd_addr dstbrd; +}; + +struct kif { + char ifname[IF_NAMESIZE]; + unsigned short ifindex; + int flags; + uint8_t link_state; + int mtu; + uint8_t if_type; + uint64_t baudrate; +}; + +/* control data structures */ +struct ctl_iface { + int af; + char name[IF_NAMESIZE]; + unsigned int ifindex; + int state; + uint16_t flags; + uint8_t linkstate; + enum iface_type type; + uint8_t if_type; + uint16_t hello_holdtime; + uint16_t hello_interval; + time_t uptime; + uint16_t adj_cnt; +}; + +struct ctl_adj { + int af; + struct in_addr id; + enum hello_type type; + char ifname[IF_NAMESIZE]; + union ldpd_addr src_addr; + uint16_t holdtime; + union ldpd_addr trans_addr; +}; + +struct ctl_nbr { + int af; + struct in_addr id; + union ldpd_addr laddr; + union ldpd_addr raddr; + time_t uptime; + int nbr_state; +}; + +struct ctl_rt { + int af; + union ldpd_addr prefix; + uint8_t prefixlen; + struct in_addr nexthop; /* lsr-id */ + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + uint8_t in_use; +}; + +struct ctl_pw { + uint16_t type; + char ifname[IF_NAMESIZE]; + uint32_t pwid; + struct in_addr lsr_id; + uint32_t local_label; + uint32_t local_gid; + uint16_t local_ifmtu; + uint32_t remote_label; + uint32_t remote_gid; + uint16_t remote_ifmtu; + uint32_t status; +}; + +extern struct ldpd_conf *ldpd_conf; +extern struct ldpd_global global; + +/* parse.y */ +struct ldpd_conf *parse_config(char *); +int cmdline_symset(char *); + +/* kroute.c */ +int kif_init(void); +int kr_init(int); +void kif_redistribute(const char *); +int kr_change(struct kroute *); +int kr_delete(struct kroute *); +void kr_shutdown(void); +void kr_fib_couple(void); +void kr_fib_decouple(void); +void kr_change_egress_label(int, int); +void kr_show_route(struct imsg *); +void kr_ifinfo(char *, pid_t); +struct kif *kif_findname(char *); +void kif_clear(void); +int kmpw_set(struct kpw *); +int kmpw_unset(struct kpw *); + +/* util.c */ +uint8_t mask2prefixlen(in_addr_t); +uint8_t mask2prefixlen6(struct sockaddr_in6 *); +in_addr_t prefixlen2mask(uint8_t); +struct in6_addr *prefixlen2mask6(uint8_t); +void ldp_applymask(int, union ldpd_addr *, + const union ldpd_addr *, int); +int ldp_addrcmp(int, const union ldpd_addr *, + const union ldpd_addr *); +int ldp_addrisset(int, const union ldpd_addr *); +int ldp_prefixcmp(int, const union ldpd_addr *, + const union ldpd_addr *, uint8_t); +int bad_addr_v4(struct in_addr); +int bad_addr_v6(struct in6_addr *); +int bad_addr(int, union ldpd_addr *); +void embedscope(struct sockaddr_in6 *); +void recoverscope(struct sockaddr_in6 *); +void addscope(struct sockaddr_in6 *, uint32_t); +void clearscope(struct in6_addr *); +struct sockaddr *addr2sa(int af, union ldpd_addr *, uint16_t); +void sa2addr(struct sockaddr *, int *, union ldpd_addr *); + +/* ldpd.c */ +void main_imsg_compose_ldpe(int, pid_t, void *, uint16_t); +void main_imsg_compose_lde(int, pid_t, void *, uint16_t); +void imsg_event_add(struct imsgev *); +int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t, + int, void *, uint16_t); +void evbuf_enqueue(struct evbuf *, struct ibuf *); +void evbuf_event_add(struct evbuf *); +void evbuf_init(struct evbuf *, int, void (*)(int, short, void *), void *); +void evbuf_clear(struct evbuf *); +struct ldpd_af_conf *ldp_af_conf_get(struct ldpd_conf *, int); +struct ldpd_af_global *ldp_af_global_get(struct ldpd_global *, int); +int ldp_is_dual_stack(struct ldpd_conf *); +void merge_config(struct ldpd_conf *, struct ldpd_conf *); +struct ldpd_conf *config_new_empty(void); +void config_clear(struct ldpd_conf *); + +/* socket.c */ +int ldp_create_socket(int, enum socket_type); +void sock_set_recvbuf(int); +int sock_set_reuse(int, int); +int sock_set_bindany(int, int); +int sock_set_ipv4_tos(int, int); +int sock_set_ipv4_recvif(int, int); +int sock_set_ipv4_minttl(int, int); +int sock_set_ipv4_ucast_ttl(int fd, int); +int sock_set_ipv4_mcast_ttl(int, uint8_t); +int sock_set_ipv4_mcast(struct iface *); +int sock_set_ipv4_mcast_loop(int); +int sock_set_ipv6_dscp(int, int); +int sock_set_ipv6_pktinfo(int, int); +int sock_set_ipv6_minhopcount(int, int); +int sock_set_ipv6_ucast_hops(int, int); +int sock_set_ipv6_mcast_hops(int, int); +int sock_set_ipv6_mcast(struct iface *); +int sock_set_ipv6_mcast_loop(int); + +/* printconf.c */ +void print_config(struct ldpd_conf *); + +#endif /* _LDPD_H_ */ diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c new file mode 100644 index 0000000000..1b4d2a67e7 --- /dev/null +++ b/ldpd/ldpe.c @@ -0,0 +1,808 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004, 2008 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "control.h" +#include "log.h" + +static void ldpe_sig_handler(int, short, void *); +static __dead void ldpe_shutdown(void); +static void ldpe_dispatch_main(int, short, void *); +static void ldpe_dispatch_lde(int, short, void *); +static void ldpe_dispatch_pfkey(int, short, void *); +static void ldpe_setup_sockets(int, int, int, int); +static void ldpe_close_sockets(int); +static void ldpe_iface_af_ctl(struct ctl_conn *, int, unsigned int); + +struct ldpd_conf *leconf; +struct ldpd_sysdep sysdep; + +static struct imsgev *iev_main; +static struct imsgev *iev_lde; +static struct event pfkey_ev; + +/* ARGSUSED */ +static void +ldpe_sig_handler(int sig, short event, void *bula) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + ldpe_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* label distribution protocol engine */ +void +ldpe(int debug, int verbose) +{ + struct passwd *pw; + struct event ev_sigint, ev_sigterm; + + leconf = config_new_empty(); + + log_init(debug); + log_verbose(verbose); + + setproctitle("ldp engine"); + ldpd_process = PROC_LDP_ENGINE; + + /* create ldpd control socket outside chroot */ + if (control_init() == -1) + fatalx("control socket setup failed"); + + LIST_INIT(&global.addr_list); + LIST_INIT(&global.adj_list); + TAILQ_INIT(&global.pending_conns); + if (inet_pton(AF_INET, AllRouters_v4, &global.mcast_addr_v4) != 1) + fatal("inet_pton"); + if (inet_pton(AF_INET6, AllRouters_v6, &global.mcast_addr_v6) != 1) + fatal("inet_pton"); + global.pfkeysock = pfkey_init(); + + if ((pw = getpwnam(LDPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + if (pledge("stdio cpath inet mcast recvfd", NULL) == -1) + fatal("pledge"); + + event_init(); + accept_init(); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, ldpe_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, ldpe_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* setup pipe and event handler to the parent process */ + if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_main->ibuf, 3); + iev_main->handler = ldpe_dispatch_main; + iev_main->events = EV_READ; + event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, + iev_main->handler, iev_main); + event_add(&iev_main->ev, NULL); + + if (sysdep.no_pfkey == 0) { + event_set(&pfkey_ev, global.pfkeysock, EV_READ | EV_PERSIST, + ldpe_dispatch_pfkey, NULL); + event_add(&pfkey_ev, NULL); + } + + /* mark sockets as closed */ + global.ipv4.ldp_disc_socket = -1; + global.ipv4.ldp_edisc_socket = -1; + global.ipv4.ldp_session_socket = -1; + global.ipv6.ldp_disc_socket = -1; + global.ipv6.ldp_edisc_socket = -1; + global.ipv6.ldp_session_socket = -1; + + /* listen on ldpd control socket */ + TAILQ_INIT(&ctl_conns); + control_listen(); + + if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL) + fatal(__func__); + + event_dispatch(); + + ldpe_shutdown(); +} + +static __dead void +ldpe_shutdown(void) +{ + struct if_addr *if_addr; + struct adj *adj; + + /* close pipes */ + msgbuf_write(&iev_lde->ibuf.w); + msgbuf_clear(&iev_lde->ibuf.w); + close(iev_lde->ibuf.fd); + msgbuf_write(&iev_main->ibuf.w); + msgbuf_clear(&iev_main->ibuf.w); + close(iev_main->ibuf.fd); + + control_cleanup(); + config_clear(leconf); + + if (sysdep.no_pfkey == 0) { + event_del(&pfkey_ev); + close(global.pfkeysock); + } + ldpe_close_sockets(AF_INET); + ldpe_close_sockets(AF_INET6); + + /* remove addresses from global list */ + while ((if_addr = LIST_FIRST(&global.addr_list)) != NULL) { + LIST_REMOVE(if_addr, entry); + free(if_addr); + } + while ((adj = LIST_FIRST(&global.adj_list)) != NULL) + adj_del(adj, S_SHUTDOWN); + + /* clean up */ + free(iev_lde); + free(iev_main); + free(pkt_ptr); + + log_info("ldp engine exiting"); + exit(0); +} + +/* imesg */ +int +ldpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen) +{ + return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen)); +} + +int +ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data, + uint16_t datalen) +{ + return (imsg_compose_event(iev_lde, type, peerid, pid, -1, + data, datalen)); +} + +/* ARGSUSED */ +static void +ldpe_dispatch_main(int fd, short event, void *bula) +{ + static struct ldpd_conf *nconf; + struct iface *niface; + struct tnbr *ntnbr; + struct nbr_params *nnbrp; + static struct l2vpn *nl2vpn; + struct l2vpn_if *nlif; + struct l2vpn_pw *npw; + struct imsg imsg; + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct iface *iface = NULL; + struct kif *kif; + int af; + enum socket_type *socket_type; + static int disc_socket = -1; + static int edisc_socket = -1; + static int session_socket = -1; + struct nbr *nbr; + struct nbr_params *nbrp; + int n, shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("ldpe_dispatch_main: msgbuf_write"); + if (n == 0) + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("ldpe_dispatch_main: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_IFSTATUS: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kif)) + fatalx("IFSTATUS imsg with wrong len"); + kif = imsg.data; + + iface = if_lookup(leconf, kif->ifindex); + if (!iface) + break; + + iface->flags = kif->flags; + iface->linkstate = kif->link_state; + if_update(iface, AF_UNSPEC); + break; + case IMSG_NEWADDR: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kaddr)) + fatalx("NEWADDR imsg with wrong len"); + + if_addr_add(imsg.data); + break; + case IMSG_DELADDR: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kaddr)) + fatalx("DELADDR imsg with wrong len"); + + if_addr_del(imsg.data); + break; + case IMSG_SOCKET_IPC: + if (iev_lde) { + log_warnx("%s: received unexpected imsg fd " + "to lde", __func__); + break; + } + if ((fd = imsg.fd) == -1) { + log_warnx("%s: expected to receive imsg fd to " + "lde but didn't receive any", __func__); + break; + } + + if ((iev_lde = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_lde->ibuf, fd); + iev_lde->handler = ldpe_dispatch_lde; + iev_lde->events = EV_READ; + event_set(&iev_lde->ev, iev_lde->ibuf.fd, + iev_lde->events, iev_lde->handler, iev_lde); + event_add(&iev_lde->ev, NULL); + break; + case IMSG_CLOSE_SOCKETS: + af = imsg.hdr.peerid; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af != af) + continue; + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + pfkey_remove(nbr); + } + ldpe_close_sockets(af); + if_update_all(af); + tnbr_update_all(af); + + disc_socket = -1; + edisc_socket = -1; + session_socket = -1; + if ((ldp_af_conf_get(leconf, af))->flags & + F_LDPD_AF_ENABLED) + ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, + af, NULL, 0); + break; + case IMSG_SOCKET_NET: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(enum socket_type)) + fatalx("SOCKET_NET imsg with wrong len"); + socket_type = imsg.data; + + switch (*socket_type) { + case LDP_SOCKET_DISC: + disc_socket = imsg.fd; + break; + case LDP_SOCKET_EDISC: + edisc_socket = imsg.fd; + break; + case LDP_SOCKET_SESSION: + session_socket = imsg.fd; + break; + } + break; + case IMSG_SETUP_SOCKETS: + af = imsg.hdr.peerid; + if (disc_socket == -1 || edisc_socket == -1 || + session_socket == -1) { + if (disc_socket != -1) + close(disc_socket); + if (edisc_socket != -1) + close(edisc_socket); + if (session_socket != -1) + close(session_socket); + break; + } + + ldpe_setup_sockets(af, disc_socket, edisc_socket, + session_socket); + if_update_all(af); + tnbr_update_all(af); + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af != af) + continue; + nbr->laddr = (ldp_af_conf_get(leconf, + af))->trans_addr; + nbrp = nbr_params_find(leconf, nbr->id); + if (nbrp && pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); + if (nbr_session_active_role(nbr)) + nbr_establish_connection(nbr); + } + break; + case IMSG_RECONF_CONF: + if ((nconf = malloc(sizeof(struct ldpd_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); + + LIST_INIT(&nconf->iface_list); + LIST_INIT(&nconf->tnbr_list); + LIST_INIT(&nconf->nbrp_list); + LIST_INIT(&nconf->l2vpn_list); + break; + case IMSG_RECONF_IFACE: + if ((niface = malloc(sizeof(struct iface))) == NULL) + fatal(NULL); + memcpy(niface, imsg.data, sizeof(struct iface)); + + LIST_INIT(&niface->addr_list); + LIST_INIT(&niface->ipv4.adj_list); + LIST_INIT(&niface->ipv6.adj_list); + niface->ipv4.iface = niface; + niface->ipv6.iface = niface; + + LIST_INSERT_HEAD(&nconf->iface_list, niface, entry); + break; + case IMSG_RECONF_TNBR: + if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL) + fatal(NULL); + memcpy(ntnbr, imsg.data, sizeof(struct tnbr)); + + LIST_INSERT_HEAD(&nconf->tnbr_list, ntnbr, entry); + break; + case IMSG_RECONF_NBRP: + if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL) + fatal(NULL); + memcpy(nnbrp, imsg.data, sizeof(struct nbr_params)); + + LIST_INSERT_HEAD(&nconf->nbrp_list, nnbrp, entry); + break; + case IMSG_RECONF_L2VPN: + if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL) + fatal(NULL); + memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn)); + + LIST_INIT(&nl2vpn->if_list); + LIST_INIT(&nl2vpn->pw_list); + + LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry); + break; + case IMSG_RECONF_L2VPN_IF: + if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL) + fatal(NULL); + memcpy(nlif, imsg.data, sizeof(struct l2vpn_if)); + + nlif->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->if_list, nlif, entry); + break; + case IMSG_RECONF_L2VPN_PW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + npw->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry); + break; + case IMSG_RECONF_END: + merge_config(leconf, nconf); + nconf = NULL; + global.conf_seqnum++; + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_IFINFO: + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + default: + log_debug("ldpe_dispatch_main: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +static void +ldpe_dispatch_lde(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + struct map map; + struct notify_msg nm; + int n, shut = 0; + struct nbr *nbr = NULL; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("ldpe_dispatch_lde: msgbuf_write"); + if (n == 0) + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("ldpe_dispatch_lde: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD: + case IMSG_RELEASE_ADD: + case IMSG_REQUEST_ADD: + case IMSG_WITHDRAW_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("invalid size of map request"); + memcpy(&map, imsg.data, sizeof(map)); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + break; + } + if (nbr->state != NBR_STA_OPER) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD: + mapping_list_add(&nbr->mapping_list, &map); + break; + case IMSG_RELEASE_ADD: + mapping_list_add(&nbr->release_list, &map); + break; + case IMSG_REQUEST_ADD: + mapping_list_add(&nbr->request_list, &map); + break; + case IMSG_WITHDRAW_ADD: + mapping_list_add(&nbr->withdraw_list, &map); + break; + } + break; + case IMSG_MAPPING_ADD_END: + case IMSG_RELEASE_ADD_END: + case IMSG_REQUEST_ADD_END: + case IMSG_WITHDRAW_ADD_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + break; + } + if (nbr->state != NBR_STA_OPER) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELMAPPING, + &nbr->mapping_list); + break; + case IMSG_RELEASE_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELRELEASE, + &nbr->release_list); + break; + case IMSG_REQUEST_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELREQUEST, + &nbr->request_list); + break; + case IMSG_WITHDRAW_ADD_END: + send_labelmessage(nbr, MSG_TYPE_LABELWITHDRAW, + &nbr->withdraw_list); + break; + } + break; + case IMSG_NOTIFICATION_SEND: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm)) + fatalx("invalid size of OE request"); + memcpy(&nm, imsg.data, sizeof(nm)); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + break; + } + if (nbr->state != NBR_STA_OPER) + break; + + send_notification_full(nbr->tcp, &nm); + break; + case IMSG_CTL_END: + case IMSG_CTL_SHOW_LIB: + case IMSG_CTL_SHOW_L2VPN_PW: + case IMSG_CTL_SHOW_L2VPN_BINDING: + control_imsg_relay(&imsg); + break; + default: + log_debug("ldpe_dispatch_lde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +static void +ldpe_dispatch_pfkey(int fd, short event, void *bula) +{ + if (event & EV_READ) { + if (pfkey_read(fd, NULL) == -1) { + fatal("pfkey_read failed, exiting..."); + } + } +} + +static void +ldpe_setup_sockets(int af, int disc_socket, int edisc_socket, + int session_socket) +{ + struct ldpd_af_global *af_global; + + af_global = ldp_af_global_get(&global, af); + + /* discovery socket */ + af_global->ldp_disc_socket = disc_socket; + event_set(&af_global->disc_ev, af_global->ldp_disc_socket, + EV_READ|EV_PERSIST, disc_recv_packet, NULL); + event_add(&af_global->disc_ev, NULL); + + /* extended discovery socket */ + af_global->ldp_edisc_socket = edisc_socket; + event_set(&af_global->edisc_ev, af_global->ldp_edisc_socket, + EV_READ|EV_PERSIST, disc_recv_packet, NULL); + event_add(&af_global->edisc_ev, NULL); + + /* session socket */ + af_global->ldp_session_socket = session_socket; + accept_add(af_global->ldp_session_socket, session_accept, NULL); +} + +static void +ldpe_close_sockets(int af) +{ + struct ldpd_af_global *af_global; + + af_global = ldp_af_global_get(&global, af); + + /* discovery socket */ + if (event_initialized(&af_global->disc_ev)) + event_del(&af_global->disc_ev); + if (af_global->ldp_disc_socket != -1) { + close(af_global->ldp_disc_socket); + af_global->ldp_disc_socket = -1; + } + + /* extended discovery socket */ + if (event_initialized(&af_global->edisc_ev)) + event_del(&af_global->edisc_ev); + if (af_global->ldp_edisc_socket != -1) { + close(af_global->ldp_edisc_socket); + af_global->ldp_edisc_socket = -1; + } + + /* session socket */ + if (af_global->ldp_session_socket != -1) { + accept_del(af_global->ldp_session_socket); + close(af_global->ldp_session_socket); + af_global->ldp_session_socket = -1; + } +} + +void +ldpe_reset_nbrs(int af) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af == af) + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } +} + +void +ldpe_reset_ds_nbrs(void) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->ds_tlv) + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } +} + +void +ldpe_remove_dynamic_tnbrs(int af) +{ + struct tnbr *tnbr, *safe; + + LIST_FOREACH_SAFE(tnbr, &leconf->tnbr_list, entry, safe) { + if (tnbr->af != af) + continue; + + tnbr->flags &= ~F_TNBR_DYNAMIC; + tnbr_check(tnbr); + } +} + +void +ldpe_stop_init_backoff(int af) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { + if (nbr->af == af && nbr_pending_idtimer(nbr)) { + nbr_stop_idtimer(nbr); + nbr_establish_connection(nbr); + } + } +} + +static void +ldpe_iface_af_ctl(struct ctl_conn *c, int af, unsigned int idx) +{ + struct iface *iface; + struct iface_af *ia; + struct ctl_iface *ictl; + + LIST_FOREACH(iface, &leconf->iface_list, entry) { + if (idx == 0 || idx == iface->ifindex) { + ia = iface_af_get(iface, af); + if (!ia->enabled) + continue; + + ictl = if_to_ctl(ia); + imsg_compose_event(&c->iev, + IMSG_CTL_SHOW_INTERFACE, + 0, 0, -1, ictl, sizeof(struct ctl_iface)); + } + } +} + +void +ldpe_iface_ctl(struct ctl_conn *c, unsigned int idx) +{ + ldpe_iface_af_ctl(c, AF_INET, idx); + ldpe_iface_af_ctl(c, AF_INET6, idx); +} + +void +ldpe_adj_ctl(struct ctl_conn *c) +{ + struct nbr *nbr; + struct adj *adj; + struct ctl_adj *actl; + + RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { + LIST_FOREACH(adj, &nbr->adj_list, nbr_entry) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, + 0, 0, -1, actl, sizeof(struct ctl_adj)); + } + } + /* show adjacencies not associated with any neighbor */ + LIST_FOREACH(adj, &global.adj_list, global_entry) { + if (adj->nbr != NULL) + continue; + + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, + -1, actl, sizeof(struct ctl_adj)); + } + + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void +ldpe_nbr_ctl(struct ctl_conn *c) +{ + struct nbr *nbr; + struct ctl_nbr *nctl; + + RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { + nctl = nbr_to_ctl(nbr); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl, + sizeof(struct ctl_nbr)); + } + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void +mapping_list_add(struct mapping_head *mh, struct map *map) +{ + struct mapping_entry *me; + + me = calloc(1, sizeof(*me)); + if (me == NULL) + fatal(__func__); + me->map = *map; + + TAILQ_INSERT_TAIL(mh, me, entry); +} + +void +mapping_list_clr(struct mapping_head *mh) +{ + struct mapping_entry *me; + + while ((me = TAILQ_FIRST(mh)) != NULL) { + TAILQ_REMOVE(mh, me, entry); + free(me); + } +} diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h new file mode 100644 index 0000000000..e640dd67db --- /dev/null +++ b/ldpd/ldpe.h @@ -0,0 +1,282 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDPE_H_ +#define _LDPE_H_ + +#include +#include +#include +#include + +#include "ldpd.h" + +#define min(x,y) ((x) <= (y) ? (x) : (y)) +#define max(x,y) ((x) > (y) ? (x) : (y)) + +struct hello_source { + enum hello_type type; + struct { + struct iface_af *ia; + union ldpd_addr src_addr; + } link; + struct tnbr *target; +}; + +struct adj { + LIST_ENTRY(adj) global_entry; + LIST_ENTRY(adj) nbr_entry; + LIST_ENTRY(adj) ia_entry; + struct in_addr lsr_id; + struct nbr *nbr; + int ds_tlv; + struct hello_source source; + struct event inactivity_timer; + uint16_t holdtime; + union ldpd_addr trans_addr; +}; + +struct tcp_conn { + struct nbr *nbr; + int fd; + struct ibuf_read *rbuf; + struct evbuf wbuf; + struct event rev; +}; + +struct nbr { + RB_ENTRY(nbr) id_tree, addr_tree, pid_tree; + struct tcp_conn *tcp; + LIST_HEAD(, adj) adj_list; /* adjacencies */ + struct event ev_connect; + struct event keepalive_timer; + struct event keepalive_timeout; + struct event init_timeout; + struct event initdelay_timer; + + struct mapping_head mapping_list; + struct mapping_head withdraw_list; + struct mapping_head request_list; + struct mapping_head release_list; + struct mapping_head abortreq_list; + + uint32_t peerid; /* unique ID in DB */ + int af; + int ds_tlv; + int v4_enabled; /* announce/process v4 msgs */ + int v6_enabled; /* announce/process v6 msgs */ + struct in_addr id; /* lsr id */ + union ldpd_addr laddr; /* local address */ + union ldpd_addr raddr; /* remote address */ + uint32_t raddr_scope; /* remote address scope (v6) */ + time_t uptime; + int fd; + int state; + uint32_t conf_seqnum; + int idtimer_cnt; + uint16_t keepalive; + uint16_t max_pdu_len; + + struct { + uint8_t established; + uint32_t spi_in; + uint32_t spi_out; + enum auth_method method; + char md5key[TCP_MD5_KEY_LEN]; + } auth; + int flags; +}; +#define F_NBR_GTSM_NEGOTIATED 0x01 + +RB_HEAD(nbr_id_head, nbr); +RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare) +RB_HEAD(nbr_addr_head, nbr); +RB_PROTOTYPE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare) +RB_HEAD(nbr_pid_head, nbr); +RB_PROTOTYPE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare) + +struct pending_conn { + TAILQ_ENTRY(pending_conn) entry; + int fd; + int af; + union ldpd_addr addr; + struct event ev_timeout; +}; +#define PENDING_CONN_TIMEOUT 5 + +struct mapping_entry { + TAILQ_ENTRY(mapping_entry) entry; + struct map map; +}; + +struct ldpd_sysdep { + uint8_t no_pfkey; + uint8_t no_md5sig; +}; + +extern struct ldpd_conf *leconf; +extern struct ldpd_sysdep sysdep; +extern struct nbr_id_head nbrs_by_id; +extern struct nbr_addr_head nbrs_by_addr; +extern struct nbr_pid_head nbrs_by_pid; + +/* accept.c */ +void accept_init(void); +int accept_add(int, void (*)(int, short, void *), void *); +void accept_del(int); +void accept_pause(void); +void accept_unpause(void); + +/* hello.c */ +int send_hello(enum hello_type, struct iface_af *, struct tnbr *); +void recv_hello(struct in_addr, struct ldp_msg *, int, union ldpd_addr *, + struct iface *, int, char *, uint16_t); + +/* init.c */ +void send_init(struct nbr *); +int recv_init(struct nbr *, char *, uint16_t); + +/* keepalive.c */ +void send_keepalive(struct nbr *); +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); +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); +int recv_address(struct nbr *, char *, uint16_t); + +/* labelmapping.c */ +#define PREFIX_SIZE(x) (((x) + 7) / 8) +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); +int gen_fec_tlv(struct ibuf *, struct map *); +int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *, + uint16_t, struct map *); + +/* ldpe.c */ +void ldpe(int, int); +int ldpe_imsg_compose_parent(int, pid_t, void *, + uint16_t); +int ldpe_imsg_compose_lde(int, uint32_t, pid_t, void *, + uint16_t); +void ldpe_reset_nbrs(int); +void ldpe_reset_ds_nbrs(void); +void ldpe_remove_dynamic_tnbrs(int); +void ldpe_stop_init_backoff(int); +struct ctl_conn; +void ldpe_iface_ctl(struct ctl_conn *, unsigned int); +void ldpe_adj_ctl(struct ctl_conn *); +void ldpe_nbr_ctl(struct ctl_conn *); +void mapping_list_add(struct mapping_head *, struct map *); +void mapping_list_clr(struct mapping_head *); + +/* interface.c */ +struct iface *if_new(struct kif *); +void if_exit(struct iface *); +struct iface *if_lookup(struct ldpd_conf *, unsigned short); +struct iface_af *iface_af_get(struct iface *, int); +void if_addr_add(struct kaddr *); +void if_addr_del(struct kaddr *); +void if_update(struct iface *, int); +void if_update_all(int); +struct ctl_iface *if_to_ctl(struct iface_af *); +in_addr_t if_get_ipv4_addr(struct iface *); + +/* adjacency.c */ +struct adj *adj_new(struct in_addr, struct hello_source *, + union ldpd_addr *); +void adj_del(struct adj *, uint32_t); +struct adj *adj_find(struct hello_source *); +int adj_get_af(struct adj *adj); +void adj_start_itimer(struct adj *); +void adj_stop_itimer(struct adj *); +struct tnbr *tnbr_new(struct ldpd_conf *, int, union ldpd_addr *); +struct tnbr *tnbr_find(struct ldpd_conf *, int, union ldpd_addr *); +struct tnbr *tnbr_check(struct tnbr *); +void tnbr_update(struct tnbr *); +void tnbr_update_all(int); +struct ctl_adj *adj_to_ctl(struct adj *); + +/* neighbor.c */ +int nbr_fsm(struct nbr *, enum nbr_event); +struct nbr *nbr_new(struct in_addr, int, int, union ldpd_addr *, + uint32_t); +void nbr_del(struct nbr *); +struct nbr *nbr_find_ldpid(uint32_t); +struct nbr *nbr_find_addr(int, union ldpd_addr *); +struct nbr *nbr_find_peerid(uint32_t); +int nbr_adj_count(struct nbr *, int); +int nbr_session_active_role(struct nbr *); +void nbr_stop_ktimer(struct nbr *); +void nbr_stop_ktimeout(struct nbr *); +void nbr_stop_itimeout(struct nbr *); +void nbr_start_idtimer(struct nbr *); +void nbr_stop_idtimer(struct nbr *); +int nbr_pending_idtimer(struct nbr *); +int nbr_pending_connect(struct nbr *); +int nbr_establish_connection(struct nbr *); +int nbr_gtsm_enabled(struct nbr *, struct nbr_params *); +int nbr_gtsm_setup(int, int, struct nbr_params *); +int nbr_gtsm_check(int, struct nbr *, struct nbr_params *); +struct nbr_params *nbr_params_new(struct in_addr); +struct nbr_params *nbr_params_find(struct ldpd_conf *, struct in_addr); +uint16_t nbr_get_keepalive(int, struct in_addr); +struct ctl_nbr *nbr_to_ctl(struct nbr *); +void nbr_clear_ctl(struct ctl_nbr *); + +/* packet.c */ +int gen_ldp_hdr(struct ibuf *, uint16_t); +int gen_msg_hdr(struct ibuf *, uint16_t, uint16_t); +int send_packet(int, int, union ldpd_addr *, + struct iface_af *, void *, size_t); +void disc_recv_packet(int, short, void *); +void session_accept(int, short, void *); +void session_accept_nbr(struct nbr *, int); +void session_shutdown(struct nbr *, uint32_t, uint32_t, + uint32_t); +void session_close(struct nbr *); +struct tcp_conn *tcp_new(int, struct nbr *); +void pending_conn_del(struct pending_conn *); +struct pending_conn *pending_conn_find(int, union ldpd_addr *); + +char *pkt_ptr; /* packet buffer */ + +/* pfkey.c */ +int pfkey_read(int, struct sadb_msg *); +int pfkey_establish(struct nbr *, struct nbr_params *); +int pfkey_remove(struct nbr *); +int pfkey_init(void); + +/* l2vpn.c */ +void ldpe_l2vpn_init(struct l2vpn *); +void ldpe_l2vpn_exit(struct l2vpn *); +void ldpe_l2vpn_pw_init(struct l2vpn_pw *); +void ldpe_l2vpn_pw_exit(struct l2vpn_pw *); + +#endif /* _LDPE_H_ */ diff --git a/ldpd/log.c b/ldpd/log.c new file mode 100644 index 0000000000..e14b6e51ea --- /dev/null +++ b/ldpd/log.c @@ -0,0 +1,600 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +static const char * const procnames[] = { + "parent", + "ldpe", + "lde" +}; + +static void vlog(int, const char *, va_list); + +static int debug; +static int verbose; + +void +log_init(int n_debug) +{ + extern char *__progname; + + debug = n_debug; + + if (!debug) + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + + tzset(); +} + +void +log_verbose(int v) +{ + verbose = v; +} + +void +logit(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, fmt, ap); + va_end(ap); +} + +static void +vlog(int pri, const char *fmt, va_list ap) +{ + char *nfmt; + + if (debug) { + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s\n", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); + } else + vsyslog(pri, fmt, ap); +} + +void +log_warn(const char *emsg, ...) +{ + char *nfmt; + va_list ap; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_CRIT, "%s", strerror(errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { + /* we tried it... */ + vlog(LOG_CRIT, emsg, ap); + logit(LOG_CRIT, "%s", strerror(errno)); + } else { + vlog(LOG_CRIT, nfmt, ap); + free(nfmt); + } + va_end(ap); + } +} + +void +log_warnx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_CRIT, emsg, ap); + va_end(ap); +} + +void +log_info(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_INFO, emsg, ap); + va_end(ap); +} + +void +log_debug(const char *emsg, ...) +{ + va_list ap; + + if (verbose & LDPD_OPT_VERBOSE) { + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); + } +} + +void +fatal(const char *emsg) +{ + if (emsg == NULL) + logit(LOG_CRIT, "fatal in %s: %s", procnames[ldpd_process], + strerror(errno)); + else + if (errno) + logit(LOG_CRIT, "fatal in %s: %s: %s", + procnames[ldpd_process], emsg, strerror(errno)); + else + logit(LOG_CRIT, "fatal in %s: %s", + procnames[ldpd_process], emsg); + + exit(1); +} + +void +fatalx(const char *emsg) +{ + errno = 0; + fatal(emsg); +} + +#define NUM_LOGS 4 +const char * +log_sockaddr(void *vp) +{ + static char buf[NUM_LOGS][NI_MAXHOST]; + static int round = 0; + struct sockaddr *sa = vp; + + round = (round + 1) % NUM_LOGS; + + if (getnameinfo(sa, sa->sa_len, buf[round], NI_MAXHOST, NULL, 0, + NI_NUMERICHOST)) + return ("(unknown)"); + else + return (buf[round]); +} + +const char * +log_in6addr(const struct in6_addr *addr) +{ + struct sockaddr_in6 sa_in6; + + memset(&sa_in6, 0, sizeof(sa_in6)); + sa_in6.sin6_len = sizeof(sa_in6); + sa_in6.sin6_family = AF_INET6; + sa_in6.sin6_addr = *addr; + + recoverscope(&sa_in6); + + return (log_sockaddr(&sa_in6)); +} + +const char * +log_in6addr_scope(const struct in6_addr *addr, unsigned int ifindex) +{ + struct sockaddr_in6 sa_in6; + + memset(&sa_in6, 0, sizeof(sa_in6)); + sa_in6.sin6_len = sizeof(sa_in6); + sa_in6.sin6_family = AF_INET6; + sa_in6.sin6_addr = *addr; + + addscope(&sa_in6, ifindex); + + return (log_sockaddr(&sa_in6)); +} + +const char * +log_addr(int af, const union ldpd_addr *addr) +{ + static char buf[NUM_LOGS][INET6_ADDRSTRLEN]; + static int round = 0; + + switch (af) { + case AF_INET: + round = (round + 1) % NUM_LOGS; + if (inet_ntop(AF_INET, &addr->v4, buf[round], + sizeof(buf[round])) == NULL) + return ("???"); + return (buf[round]); + case AF_INET6: + return (log_in6addr(&addr->v6)); + default: + break; + } + + return ("???"); +} + +#define TF_BUFS 4 +#define TF_LEN 32 + +char * +log_label(uint32_t label) +{ + char *buf; + static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ + static int idx = 0; + + buf = tfbuf[idx++]; + if (idx == TF_BUFS) + idx = 0; + + switch (label) { + case NO_LABEL: + snprintf(buf, TF_LEN, "-"); + break; + case MPLS_LABEL_IMPLNULL: + snprintf(buf, TF_LEN, "imp-null"); + break; + case MPLS_LABEL_IPV4NULL: + case MPLS_LABEL_IPV6NULL: + snprintf(buf, TF_LEN, "exp-null"); + break; + default: + snprintf(buf, TF_LEN, "%u", label); + break; + } + + return (buf); +} + +char * +log_hello_src(const struct hello_source *src) +{ + static char buf[64]; + + switch (src->type) { + case HELLO_LINK: + snprintf(buf, sizeof(buf), "iface %s", + src->link.ia->iface->name); + break; + case HELLO_TARGETED: + snprintf(buf, sizeof(buf), "source %s", + log_addr(src->target->af, &src->target->addr)); + break; + } + + return (buf); +} + +const char * +log_map(const struct map *map) +{ + static char buf[64]; + + switch (map->type) { + case MAP_TYPE_WILDCARD: + if (snprintf(buf, sizeof(buf), "wildcard") < 0) + return ("???"); + break; + case MAP_TYPE_PREFIX: + if (snprintf(buf, sizeof(buf), "%s/%u", + log_addr(map->fec.prefix.af, &map->fec.prefix.prefix), + map->fec.prefix.prefixlen) == -1) + return ("???"); + break; + case MAP_TYPE_PWID: + if (snprintf(buf, sizeof(buf), "pwid %u (%s)", + map->fec.pwid.pwid, + pw_type_name(map->fec.pwid.type)) == -1) + return ("???"); + break; + default: + return ("???"); + } + + return (buf); +} + +const char * +log_fec(const struct fec *fec) +{ + static char buf[64]; + union ldpd_addr addr; + + switch (fec->type) { + case FEC_TYPE_IPV4: + addr.v4 = fec->u.ipv4.prefix; + if (snprintf(buf, sizeof(buf), "ipv4 %s/%u", + log_addr(AF_INET, &addr), fec->u.ipv4.prefixlen) == -1) + return ("???"); + break; + case FEC_TYPE_IPV6: + addr.v6 = fec->u.ipv6.prefix; + if (snprintf(buf, sizeof(buf), "ipv6 %s/%u", + log_addr(AF_INET6, &addr), fec->u.ipv6.prefixlen) == -1) + return ("???"); + break; + case FEC_TYPE_PWID: + if (snprintf(buf, sizeof(buf), + "pwid %u (%s) - %s", + fec->u.pwid.pwid, pw_type_name(fec->u.pwid.type), + inet_ntoa(fec->u.pwid.lsr_id)) == -1) + return ("???"); + break; + default: + return ("???"); + } + + return (buf); +} + +/* names */ +const char * +af_name(int af) +{ + switch (af) { + case AF_INET: + return ("ipv4"); + case AF_INET6: + return ("ipv6"); + case AF_MPLS: + return ("mpls"); + default: + return ("UNKNOWN"); + } +} + +const char * +socket_name(int type) +{ + switch (type) { + case LDP_SOCKET_DISC: + return ("discovery"); + case LDP_SOCKET_EDISC: + return ("extended discovery"); + case LDP_SOCKET_SESSION: + return ("session"); + default: + return ("UNKNOWN"); + } +} + +const char * +nbr_state_name(int state) +{ + switch (state) { + case NBR_STA_PRESENT: + return ("PRESENT"); + case NBR_STA_INITIAL: + return ("INITIALIZED"); + case NBR_STA_OPENREC: + return ("OPENREC"); + case NBR_STA_OPENSENT: + return ("OPENSENT"); + case NBR_STA_OPER: + return ("OPERATIONAL"); + default: + return ("UNKNOWN"); + } +} + +const char * +if_state_name(int state) +{ + switch (state) { + case IF_STA_DOWN: + return ("DOWN"); + case IF_STA_ACTIVE: + return ("ACTIVE"); + default: + return ("UNKNOWN"); + } +} + +const char * +if_type_name(enum iface_type type) +{ + switch (type) { + case IF_TYPE_POINTOPOINT: + return ("POINTOPOINT"); + case IF_TYPE_BROADCAST: + return ("BROADCAST"); + } + /* NOTREACHED */ + return ("UNKNOWN"); +} + +const char * +msg_name(uint16_t msg) +{ + static char buf[16]; + + switch (msg) { + case MSG_TYPE_NOTIFICATION: + return ("notification"); + case MSG_TYPE_HELLO: + return ("hello"); + case MSG_TYPE_INIT: + return ("initialization"); + case MSG_TYPE_KEEPALIVE: + return ("keepalive"); + case MSG_TYPE_ADDR: + return ("address"); + case MSG_TYPE_ADDRWITHDRAW: + return ("address withdraw"); + case MSG_TYPE_LABELMAPPING: + return ("label mapping"); + case MSG_TYPE_LABELREQUEST: + return ("label request"); + case MSG_TYPE_LABELWITHDRAW: + return ("label withdraw"); + case MSG_TYPE_LABELRELEASE: + return ("label release"); + case MSG_TYPE_LABELABORTREQ: + default: + snprintf(buf, sizeof(buf), "[%08x]", msg); + return (buf); + } +} + +const char * +status_code_name(uint32_t status) +{ + static char buf[16]; + + switch (status) { + case S_SUCCESS: + return ("Success"); + case S_BAD_LDP_ID: + return ("Bad LDP Identifier"); + case S_BAD_PROTO_VER: + return ("Bad Protocol Version"); + case S_BAD_PDU_LEN: + return ("Bad PDU Length"); + case S_UNKNOWN_MSG: + return ("Unknown Message Type"); + case S_BAD_MSG_LEN: + return ("Bad Message Length"); + case S_UNKNOWN_TLV: + return ("Unknown TLV"); + case S_BAD_TLV_LEN: + return ("Bad TLV Length"); + case S_BAD_TLV_VAL: + return ("Malformed TLV Value"); + case S_HOLDTIME_EXP: + return ("Hold Timer Expired"); + case S_SHUTDOWN: + return ("Shutdown"); + case S_LOOP_DETECTED: + return ("Loop Detected"); + case S_UNKNOWN_FEC: + return ("Unknown FEC"); + case S_NO_ROUTE: + return ("No Route"); + case S_NO_LABEL_RES: + return ("No Label Resources"); + case S_AVAILABLE: + return ("Label Resources Available"); + case S_NO_HELLO: + return ("Session Rejected, No Hello"); + case S_PARM_ADV_MODE: + return ("Rejected Advertisement Mode Parameter"); + case S_MAX_PDU_LEN: + return ("Rejected Max PDU Length Parameter"); + case S_PARM_L_RANGE: + return ("Rejected Label Range Parameter"); + case S_KEEPALIVE_TMR: + return ("KeepAlive Timer Expired"); + case S_LAB_REQ_ABRT: + return ("Label Request Aborted"); + case S_MISS_MSG: + return ("Missing Message Parameters"); + case S_UNSUP_ADDR: + return ("Unsupported Address Family"); + case S_KEEPALIVE_BAD: + return ("Bad KeepAlive Time"); + case S_INTERN_ERR: + return ("Internal Error"); + case S_ILLEGAL_CBIT: + return ("Illegal C-Bit"); + case S_WRONG_CBIT: + return ("Wrong C-Bit"); + case S_INCPT_BITRATE: + return ("Incompatible bit-rate"); + case S_CEP_MISCONF: + return ("CEP-TDM mis-configuration"); + case S_PW_STATUS: + return ("PW Status"); + case S_UNASSIGN_TAI: + return ("Unassigned/Unrecognized TAI"); + case S_MISCONF_ERR: + return ("Generic Misconfiguration Error"); + case S_WITHDRAW_MTHD: + return ("Label Withdraw PW Status Method"); + case S_TRANS_MISMTCH: + return ("Transport Connection Mismatch"); + case S_DS_NONCMPLNCE: + return ("Dual-Stack Noncompliance"); + default: + snprintf(buf, sizeof(buf), "[%08x]", status); + return (buf); + } +} + +const char * +pw_type_name(uint16_t pw_type) +{ + static char buf[64]; + + switch (pw_type) { + case PW_TYPE_ETHERNET_TAGGED: + return ("Eth Tagged"); + case PW_TYPE_ETHERNET: + return ("Ethernet"); + default: + snprintf(buf, sizeof(buf), "[%0x]", pw_type); + return (buf); + } +} + +static char *msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + "RTM_IFANNOUNCE: iface arrival/departure", + "RTM_DESYNC: route socket overflow", +}; + +void +log_rtmsg(unsigned char rtm_type) +{ + if (!(verbose & LDPD_OPT_VERBOSE2)) + return; + + if (rtm_type > 0 && + rtm_type < sizeof(msgtypes)/sizeof(msgtypes[0])) + log_debug("kernel message: %s", msgtypes[rtm_type]); + else + log_debug("kernel message: rtm_type %d out of range", + rtm_type); +} diff --git a/ldpd/log.h b/ldpd/log.h new file mode 100644 index 0000000000..94c463041d --- /dev/null +++ b/ldpd/log.h @@ -0,0 +1,63 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include + +struct in6_addr; +union ldpd_addr; +struct hello_source; +struct fec; + +void log_init(int); +void log_verbose(int); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void fatal(const char *) __dead + __attribute__((__format__ (printf, 1, 0))); +void fatalx(const char *) __dead + __attribute__((__format__ (printf, 1, 0))); +const char *log_sockaddr(void *); +const char *log_in6addr(const struct in6_addr *); +const char *log_in6addr_scope(const struct in6_addr *, unsigned int); +const char *log_addr(int, const union ldpd_addr *); +char *log_label(uint32_t); +char *log_hello_src(const struct hello_source *); +const char *log_map(const struct map *); +const char *log_fec(const struct fec *); +const char *af_name(int); +const char *socket_name(int); +const char *nbr_state_name(int); +const char *if_state_name(int); +const char *if_type_name(enum iface_type); +const char *msg_name(uint16_t); +const char *status_code_name(uint32_t); +const char *pw_type_name(uint16_t); +void log_rtmsg(unsigned char); + +#endif /* _LOG_H_ */ diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c new file mode 100644 index 0000000000..d3f83734f5 --- /dev/null +++ b/ldpd/neighbor.c @@ -0,0 +1,827 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +static __inline int nbr_id_compare(struct nbr *, struct nbr *); +static __inline int nbr_addr_compare(struct nbr *, struct nbr *); +static __inline int nbr_pid_compare(struct nbr *, struct nbr *); +static void nbr_update_peerid(struct nbr *); +static void nbr_ktimer(int, short, void *); +static void nbr_start_ktimer(struct nbr *); +static void nbr_ktimeout(int, short, void *); +static void nbr_start_ktimeout(struct nbr *); +static void nbr_itimeout(int, short, void *); +static void nbr_start_itimeout(struct nbr *); +static void nbr_idtimer(int, short, void *); +static int nbr_act_session_operational(struct nbr *); +static void nbr_send_labelmappings(struct nbr *); + +RB_GENERATE(nbr_id_head, nbr, id_tree, nbr_id_compare) +RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare) +RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare) + +struct { + int state; + enum nbr_event event; + enum nbr_action action; + int new_state; +} nbr_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ +/* Passive Role */ + {NBR_STA_PRESENT, NBR_EVT_MATCH_ADJ, NBR_ACT_NOTHING, NBR_STA_INITIAL}, + {NBR_STA_INITIAL, NBR_EVT_INIT_RCVD, NBR_ACT_PASSIVE_INIT, NBR_STA_OPENREC}, + {NBR_STA_OPENREC, NBR_EVT_KEEPALIVE_RCVD, NBR_ACT_SESSION_EST, NBR_STA_OPER}, +/* Active Role */ + {NBR_STA_PRESENT, NBR_EVT_CONNECT_UP, NBR_ACT_CONNECT_SETUP, NBR_STA_INITIAL}, + {NBR_STA_INITIAL, NBR_EVT_INIT_SENT, NBR_ACT_NOTHING, NBR_STA_OPENSENT}, + {NBR_STA_OPENSENT, NBR_EVT_INIT_RCVD, NBR_ACT_KEEPALIVE_SEND, NBR_STA_OPENREC}, +/* Session Maintenance */ + {NBR_STA_OPER, NBR_EVT_PDU_RCVD, NBR_ACT_RST_KTIMEOUT, 0}, + {NBR_STA_SESSION, NBR_EVT_PDU_RCVD, NBR_ACT_NOTHING, 0}, + {NBR_STA_OPER, NBR_EVT_PDU_SENT, NBR_ACT_RST_KTIMER, 0}, + {NBR_STA_SESSION, NBR_EVT_PDU_SENT, NBR_ACT_NOTHING, 0}, +/* Session Close */ + {NBR_STA_PRESENT, NBR_EVT_CLOSE_SESSION, NBR_ACT_NOTHING, 0}, + {NBR_STA_SESSION, NBR_EVT_CLOSE_SESSION, NBR_ACT_CLOSE_SESSION, NBR_STA_PRESENT}, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "ADJACENCY MATCHED", + "CONNECTION UP", + "SESSION CLOSE", + "INIT RECEIVED", + "KEEPALIVE RECEIVED", + "PDU RECEIVED", + "PDU SENT", + "INIT SENT" +}; + +const char * const nbr_action_names[] = { + "NOTHING", + "RESET KEEPALIVE TIMEOUT", + "START NEIGHBOR SESSION", + "RESET KEEPALIVE TIMER", + "SETUP NEIGHBOR CONNECTION", + "SEND INIT AND KEEPALIVE", + "SEND KEEPALIVE", + "CLOSE SESSION" +}; + +struct nbr_id_head nbrs_by_id = RB_INITIALIZER(&nbrs_by_id); +struct nbr_addr_head nbrs_by_addr = RB_INITIALIZER(&nbrs_by_addr); +struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid); + +static __inline int +nbr_id_compare(struct nbr *a, struct nbr *b) +{ + return (ntohl(a->id.s_addr) - ntohl(b->id.s_addr)); +} + +static __inline int +nbr_addr_compare(struct nbr *a, struct nbr *b) +{ + if (a->af < b->af) + return (-1); + if (a->af > b->af) + return (1); + + return (ldp_addrcmp(a->af, &a->raddr, &b->raddr)); +} + +static __inline int +nbr_pid_compare(struct nbr *a, struct nbr *b) +{ + return (a->peerid - b->peerid); +} + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + struct timeval now; + int old_state; + int new_state = 0; + int i; + + old_state = nbr->state; + for (i = 0; nbr_fsm_tbl[i].state != -1; i++) + if ((nbr_fsm_tbl[i].state & old_state) && + (nbr_fsm_tbl[i].event == event)) { + new_state = nbr_fsm_tbl[i].new_state; + break; + } + + if (nbr_fsm_tbl[i].state == -1) { + /* event outside of the defined fsm, ignore it. */ + log_warnx("%s: lsr-id %s, event %s not expected in " + "state %s", __func__, inet_ntoa(nbr->id), + nbr_event_names[event], nbr_state_name(old_state)); + return (0); + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + log_debug("%s: event %s resulted in action %s and " + "changing state for lsr-id %s from %s to %s", + __func__, nbr_event_names[event], + nbr_action_names[nbr_fsm_tbl[i].action], + inet_ntoa(nbr->id), nbr_state_name(old_state), + nbr_state_name(nbr->state)); + + if (nbr->state == NBR_STA_OPER) { + gettimeofday(&now, NULL); + nbr->uptime = now.tv_sec; + } + } + + if (nbr->state == NBR_STA_OPER || nbr->state == NBR_STA_PRESENT) + nbr_stop_itimeout(nbr); + else + nbr_start_itimeout(nbr); + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_RST_KTIMEOUT: + nbr_start_ktimeout(nbr); + break; + case NBR_ACT_RST_KTIMER: + nbr_start_ktimer(nbr); + break; + case NBR_ACT_SESSION_EST: + nbr_act_session_operational(nbr); + nbr_start_ktimer(nbr); + nbr_start_ktimeout(nbr); + if (nbr->v4_enabled) + send_address_all(nbr, AF_INET); + if (nbr->v6_enabled) + send_address_all(nbr, AF_INET6); + nbr_send_labelmappings(nbr); + break; + case NBR_ACT_CONNECT_SETUP: + nbr->tcp = tcp_new(nbr->fd, nbr); + + /* trigger next state */ + send_init(nbr); + nbr_fsm(nbr, NBR_EVT_INIT_SENT); + break; + case NBR_ACT_PASSIVE_INIT: + send_init(nbr); + send_keepalive(nbr); + break; + case NBR_ACT_KEEPALIVE_SEND: + nbr_start_ktimeout(nbr); + send_keepalive(nbr); + break; + case NBR_ACT_CLOSE_SESSION: + ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, + NULL, 0); + session_close(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + return (0); +} + +struct nbr * +nbr_new(struct in_addr id, int af, int ds_tlv, union ldpd_addr *addr, + uint32_t scope_id) +{ + struct nbr *nbr; + struct nbr_params *nbrp; + struct adj *adj; + struct pending_conn *pconn; + + log_debug("%s: lsr-id %s transport-address %s", __func__, + inet_ntoa(id), log_addr(af, addr)); + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal(__func__); + + LIST_INIT(&nbr->adj_list); + nbr->state = NBR_STA_PRESENT; + nbr->peerid = 0; + nbr->af = af; + nbr->ds_tlv = ds_tlv; + if (af == AF_INET || ds_tlv) + nbr->v4_enabled = 1; + if (af == AF_INET6 || ds_tlv) + nbr->v6_enabled = 1; + nbr->id = id; + nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr; + nbr->raddr = *addr; + nbr->raddr_scope = scope_id; + nbr->conf_seqnum = 0; + + LIST_FOREACH(adj, &global.adj_list, global_entry) { + if (adj->lsr_id.s_addr == nbr->id.s_addr) { + adj->nbr = nbr; + LIST_INSERT_HEAD(&nbr->adj_list, adj, nbr_entry); + } + } + + if (RB_INSERT(nbr_id_head, &nbrs_by_id, nbr) != NULL) + fatalx("nbr_new: RB_INSERT(nbrs_by_id) failed"); + if (RB_INSERT(nbr_addr_head, &nbrs_by_addr, nbr) != NULL) + fatalx("nbr_new: RB_INSERT(nbrs_by_addr) failed"); + + TAILQ_INIT(&nbr->mapping_list); + TAILQ_INIT(&nbr->withdraw_list); + TAILQ_INIT(&nbr->request_list); + TAILQ_INIT(&nbr->release_list); + TAILQ_INIT(&nbr->abortreq_list); + + /* set event structures */ + evtimer_set(&nbr->keepalive_timeout, nbr_ktimeout, nbr); + evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr); + evtimer_set(&nbr->init_timeout, nbr_itimeout, nbr); + evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr); + + nbrp = nbr_params_find(leconf, nbr->id); + if (nbrp && pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); + + pconn = pending_conn_find(nbr->af, &nbr->raddr); + if (pconn) { + session_accept_nbr(nbr, pconn->fd); + pending_conn_del(pconn); + } + + return (nbr); +} + +void +nbr_del(struct nbr *nbr) +{ + log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + pfkey_remove(nbr); + + if (nbr_pending_connect(nbr)) + event_del(&nbr->ev_connect); + nbr_stop_ktimer(nbr); + nbr_stop_ktimeout(nbr); + nbr_stop_itimeout(nbr); + nbr_stop_idtimer(nbr); + + mapping_list_clr(&nbr->mapping_list); + mapping_list_clr(&nbr->withdraw_list); + mapping_list_clr(&nbr->request_list); + mapping_list_clr(&nbr->release_list); + mapping_list_clr(&nbr->abortreq_list); + + if (nbr->peerid) + RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr); + RB_REMOVE(nbr_id_head, &nbrs_by_id, nbr); + RB_REMOVE(nbr_addr_head, &nbrs_by_addr, nbr); + + free(nbr); +} + +static void +nbr_update_peerid(struct nbr *nbr) +{ + static uint32_t peercnt = 1; + + if (nbr->peerid) + RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr); + + /* get next unused peerid */ + while (nbr_find_peerid(++peercnt)) + ; + nbr->peerid = peercnt; + + if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL) + fatalx("nbr_update_peerid: RB_INSERT(nbrs_by_pid) failed"); +} + +struct nbr * +nbr_find_ldpid(uint32_t lsr_id) +{ + struct nbr n; + n.id.s_addr = lsr_id; + return (RB_FIND(nbr_id_head, &nbrs_by_id, &n)); +} + +struct nbr * +nbr_find_addr(int af, union ldpd_addr *addr) +{ + struct nbr n; + n.af = af; + n.raddr = *addr; + return (RB_FIND(nbr_addr_head, &nbrs_by_addr, &n)); +} + +struct nbr * +nbr_find_peerid(uint32_t peerid) +{ + struct nbr n; + n.peerid = peerid; + return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n)); +} + +int +nbr_adj_count(struct nbr *nbr, int af) +{ + struct adj *adj; + int total = 0; + + LIST_FOREACH(adj, &nbr->adj_list, nbr_entry) + if (adj_get_af(adj) == af) + total++; + + return (total); +} + +int +nbr_session_active_role(struct nbr *nbr) +{ + if (ldp_addrcmp(nbr->af, &nbr->laddr, &nbr->raddr) > 0) + return (1); + + return (0); +} + +/* timers */ + +/* Keepalive timer: timer to send keepalive message to neighbors */ + +static void +nbr_ktimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + send_keepalive(nbr); + nbr_start_ktimer(nbr); +} + +static void +nbr_start_ktimer(struct nbr *nbr) +{ + struct timeval tv; + + /* send three keepalives per period */ + timerclear(&tv); + tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD); + if (evtimer_add(&nbr->keepalive_timer, &tv) == -1) + fatal(__func__); +} + +void +nbr_stop_ktimer(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->keepalive_timer, NULL) && + evtimer_del(&nbr->keepalive_timer) == -1) + fatal(__func__); +} + +/* Keepalive timeout: if the nbr hasn't sent keepalive */ + +static void +nbr_ktimeout(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + + session_shutdown(nbr, S_KEEPALIVE_TMR, 0, 0); +} + +static void +nbr_start_ktimeout(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->keepalive; + + if (evtimer_add(&nbr->keepalive_timeout, &tv) == -1) + fatal(__func__); +} + +void +nbr_stop_ktimeout(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->keepalive_timeout, NULL) && + evtimer_del(&nbr->keepalive_timeout) == -1) + fatal(__func__); +} + +/* Session initialization timeout: if nbr got stuck in the initialization FSM */ + +static void +nbr_itimeout(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); +} + +static void +nbr_start_itimeout(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = INIT_FSM_TIMEOUT; + if (evtimer_add(&nbr->init_timeout, &tv) == -1) + fatal(__func__); +} + +void +nbr_stop_itimeout(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->init_timeout, NULL) && + evtimer_del(&nbr->init_timeout) == -1) + fatal(__func__); +} + +/* Init delay timer: timer to retry to iniziatize session */ + +static void +nbr_idtimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + + nbr_establish_connection(nbr); +} + +void +nbr_start_idtimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + + tv.tv_sec = INIT_DELAY_TMR; + switch(nbr->idtimer_cnt) { + default: + /* do not further increase the counter */ + tv.tv_sec = MAX_DELAY_TMR; + break; + case 2: + tv.tv_sec *= 2; + /* FALLTHROUGH */ + case 1: + tv.tv_sec *= 2; + /* FALLTHROUGH */ + case 0: + nbr->idtimer_cnt++; + break; + } + + if (evtimer_add(&nbr->initdelay_timer, &tv) == -1) + fatal(__func__); +} + +void +nbr_stop_idtimer(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->initdelay_timer, NULL) && + evtimer_del(&nbr->initdelay_timer) == -1) + fatal(__func__); +} + +int +nbr_pending_idtimer(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->initdelay_timer, NULL)) + return (1); + + return (0); +} + +int +nbr_pending_connect(struct nbr *nbr) +{ + if (event_initialized(&nbr->ev_connect) && + event_pending(&nbr->ev_connect, EV_WRITE, NULL)) + return (1); + + return (0); +} + +static void +nbr_connect_cb(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + int error; + socklen_t len; + + len = sizeof(error); + if (getsockopt(nbr->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + log_warn("%s: getsockopt SOL_SOCKET SO_ERROR", __func__); + return; + } + + if (error) { + close(nbr->fd); + errno = error; + log_debug("%s: error while connecting to %s: %s", __func__, + log_addr(nbr->af, &nbr->raddr), strerror(errno)); + return; + } + + nbr_fsm(nbr, NBR_EVT_CONNECT_UP); +} + +int +nbr_establish_connection(struct nbr *nbr) +{ + struct sockaddr_storage local_sa; + struct sockaddr_storage remote_sa; + struct adj *adj; + struct nbr_params *nbrp; + int opt = 1; + + nbr->fd = socket(nbr->af, + SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (nbr->fd == -1) { + log_warn("%s: error while creating socket", __func__); + return (-1); + } + + nbrp = nbr_params_find(leconf, nbr->id); + if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { + if (sysdep.no_pfkey || sysdep.no_md5sig) { + log_warnx("md5sig configured but not available"); + close(nbr->fd); + return (-1); + } + if (setsockopt(nbr->fd, IPPROTO_TCP, TCP_MD5SIG, + &opt, sizeof(opt)) == -1) { + log_warn("setsockopt md5sig"); + close(nbr->fd); + return (-1); + } + } + + memcpy(&local_sa, addr2sa(nbr->af, &nbr->laddr, 0), sizeof(local_sa)); + memcpy(&remote_sa, addr2sa(nbr->af, &nbr->raddr, LDP_PORT), + sizeof(local_sa)); + if (nbr->af == AF_INET6 && nbr->raddr_scope) + addscope((struct sockaddr_in6 *)&remote_sa, nbr->raddr_scope); + + if (bind(nbr->fd, (struct sockaddr *)&local_sa, + local_sa.ss_len) == -1) { + log_warn("%s: error while binding socket to %s", __func__, + log_sockaddr((struct sockaddr *)&local_sa)); + close(nbr->fd); + return (-1); + } + + if (nbr_gtsm_check(nbr->fd, nbr, nbrp)) { + close(nbr->fd); + return (-1); + } + + /* + * Send an extra hello to guarantee that the remote peer has formed + * an adjacency as well. + */ + LIST_FOREACH(adj, &nbr->adj_list, nbr_entry) + send_hello(adj->source.type, adj->source.link.ia, + adj->source.target); + + if (connect(nbr->fd, (struct sockaddr *)&remote_sa, + remote_sa.ss_len) == -1) { + if (errno == EINPROGRESS) { + event_set(&nbr->ev_connect, nbr->fd, EV_WRITE, + nbr_connect_cb, nbr); + event_add(&nbr->ev_connect, NULL); + return (0); + } + log_warn("%s: error while connecting to %s", __func__, + log_sockaddr((struct sockaddr *)&remote_sa)); + close(nbr->fd); + return (-1); + } + + /* connection completed immediately */ + nbr_fsm(nbr, NBR_EVT_CONNECT_UP); + + return (0); +} + +int +nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp) +{ + /* + * RFC 6720 - Section 3: + * "This document allows for the implementation to provide an option to + * statically (e.g., via configuration) and/or dynamically override the + * default behavior and enable/disable GTSM on a per-peer basis". + */ + if (nbrp && (nbrp->flags & F_NBRP_GTSM)) + return (nbrp->gtsm_enabled); + + if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM) + return (0); + + /* By default, GTSM support has to be negotiated for LDPv4 */ + if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED)) + return (0); + + return (1); +} + +int +nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp) +{ + int ttl = 255; + + if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS)) + ttl = 256 - nbrp->gtsm_hops; + + switch (af) { + case AF_INET: + if (sock_set_ipv4_minttl(fd, ttl) == -1) + return (-1); + ttl = 255; + if (sock_set_ipv4_ucast_ttl(fd, ttl) == -1) + return (-1); + break; + case AF_INET6: + if (sock_set_ipv6_minhopcount(fd, ttl) == -1) + return (-1); + ttl = 255; + if (sock_set_ipv6_ucast_hops(fd, ttl) == -1) + return (-1); + break; + default: + fatalx("nbr_gtsm_setup: unknown af"); + } + + return (0); +} + +int +nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp) +{ + if (!nbr_gtsm_enabled(nbr, nbrp)) { + switch (nbr->af) { + case AF_INET: + sock_set_ipv4_ucast_ttl(fd, -1); + break; + case AF_INET6: + /* + * Send packets with a Hop Limit of 255 even when GSTM + * is disabled to guarantee interoperability. + */ + sock_set_ipv6_ucast_hops(fd, 255); + break; + default: + fatalx("nbr_gtsm_check: unknown af"); + break; + } + return (0); + } + + if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) { + log_warnx("%s: error enabling GTSM for lsr-id %s", __func__, + inet_ntoa(nbr->id)); + return (-1); + } + + return (0); +} + +static int +nbr_act_session_operational(struct nbr *nbr) +{ + struct lde_nbr lde_nbr; + + nbr->idtimer_cnt = 0; + + /* this is necessary to avoid ipc synchronization issues */ + nbr_update_peerid(nbr); + + memset(&lde_nbr, 0, sizeof(lde_nbr)); + lde_nbr.id = nbr->id; + lde_nbr.v4_enabled = nbr->v4_enabled; + lde_nbr.v6_enabled = nbr->v6_enabled; + return (ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, + &lde_nbr, sizeof(lde_nbr))); +} + +static void +nbr_send_labelmappings(struct nbr *nbr) +{ + ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, + NULL, 0); +} + +struct nbr_params * +nbr_params_new(struct in_addr lsr_id) +{ + struct nbr_params *nbrp; + + if ((nbrp = calloc(1, sizeof(*nbrp))) == NULL) + fatal(__func__); + + nbrp->lsr_id = lsr_id; + nbrp->auth.method = AUTH_NONE; + + return (nbrp); +} + +struct nbr_params * +nbr_params_find(struct ldpd_conf *xconf, struct in_addr lsr_id) +{ + struct nbr_params *nbrp; + + LIST_FOREACH(nbrp, &xconf->nbrp_list, entry) + if (nbrp->lsr_id.s_addr == lsr_id.s_addr) + return (nbrp); + + return (NULL); +} + +uint16_t +nbr_get_keepalive(int af, struct in_addr lsr_id) +{ + struct nbr_params *nbrp; + + nbrp = nbr_params_find(leconf, lsr_id); + if (nbrp && (nbrp->flags & F_NBRP_KEEPALIVE)) + return (nbrp->keepalive); + + return ((ldp_af_conf_get(leconf, af))->keepalive); +} + +struct ctl_nbr * +nbr_to_ctl(struct nbr *nbr) +{ + static struct ctl_nbr nctl; + struct timeval now; + + nctl.af = nbr->af; + nctl.id = nbr->id; + nctl.laddr = nbr->laddr; + nctl.raddr = nbr->raddr; + nctl.nbr_state = nbr->state; + + gettimeofday(&now, NULL); + if (nbr->state == NBR_STA_OPER) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; + + return (&nctl); +} + +void +nbr_clear_ctl(struct ctl_nbr *nctl) +{ + struct nbr *nbr; + + RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { + if (ldp_addrisset(nctl->af, &nctl->raddr) && + ldp_addrcmp(nctl->af, &nctl->raddr, &nbr->raddr)) + continue; + + log_debug("%s: neighbor %s manually cleared", __func__, + log_addr(nbr->af, &nbr->raddr)); + session_shutdown(nbr, S_SHUTDOWN, 0, 0); + } +} diff --git a/ldpd/notification.c b/ldpd/notification.c new file mode 100644 index 0000000000..f30646bb83 --- /dev/null +++ b/ldpd/notification.c @@ -0,0 +1,239 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Michele Marchetto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +void +send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) +{ + struct ibuf *buf; + uint16_t size; + int err = 0; + + /* calculate size */ + 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 ((buf = ibuf_open(size)) == NULL) + fatal(__func__); + + err |= gen_ldp_hdr(buf, size); + size -= LDP_HDR_SIZE; + err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size); + err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type); + /* optional tlvs */ + if (nm->flags & F_NOTIF_PW_STATUS) + err |= gen_pw_status_tlv(buf, nm->pw_status); + if (nm->flags & F_NOTIF_FEC) + err |= gen_fec_tlv(buf, &nm->fec); + if (err) { + ibuf_free(buf); + return; + } + + if (tcp->nbr) + log_debug("msg-out: notification: lsr-id %s, status %s%s", + inet_ntoa(tcp->nbr->id), status_code_name(nm->status_code), + (nm->status_code & STATUS_FATAL) ? " (fatal)" : ""); + + 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, + uint16_t msg_type) +{ + struct notify_msg nm; + + memset(&nm, 0, sizeof(nm)); + nm.status_code = status_code; + nm.msg_id = msg_id; + nm.msg_type = msg_type; + + send_notification_full(tcp, &nm); +} + +void +send_notification_nbr(struct nbr *nbr, uint32_t status_code, uint32_t msg_id, + uint16_t msg_type) +{ + send_notification(status_code, nbr->tcp, msg_id, msg_type); + nbr_fsm(nbr, NBR_EVT_PDU_SENT); +} + +int +recv_notification(struct nbr *nbr, char *buf, uint16_t len) +{ + struct ldp_msg msg; + struct status_tlv st; + struct notify_msg nm; + int tlen; + + memcpy(&msg, buf, sizeof(msg)); + buf += LDP_MSG_SIZE; + len -= LDP_MSG_SIZE; + + if (len < STATUS_SIZE) { + session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type); + return (-1); + } + memcpy(&st, buf, sizeof(st)); + + if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE || + ntohs(st.length) > len - TLV_HDR_SIZE) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + return (-1); + } + buf += STATUS_SIZE; + len -= STATUS_SIZE; + + memset(&nm, 0, sizeof(nm)); + nm.status_code = ntohl(st.status_code); + + /* Optional Parameters */ + while (len > 0) { + struct tlv tlv; + 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_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 (ntohs(tlv.type)) { + case TLV_TYPE_EXTSTATUS: + case TLV_TYPE_RETURNEDPDU: + case TLV_TYPE_RETURNEDMSG: + /* TODO is there any use for this? */ + break; + case TLV_TYPE_PW_STATUS: + if (tlv_len != 4) { + session_shutdown(nbr, S_BAD_TLV_LEN, + msg.id, msg.type); + return (-1); + } + + nm.pw_status = ntohl(*(uint32_t *)buf); + nm.flags |= F_NOTIF_PW_STATUS; + break; + case TLV_TYPE_FEC: + if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, + tlv_len, &nm.fec)) == -1) + return (-1); + /* allow only one fec element */ + if (tlen != tlv_len) { + session_shutdown(nbr, S_BAD_TLV_VAL, + msg.id, msg.type); + return (-1); + } + nm.flags |= F_NOTIF_FEC; + break; + default: + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + send_notification_nbr(nbr, S_UNKNOWN_TLV, + msg.id, msg.type); + /* ignore unknown tlv */ + break; + } + buf += tlv_len; + len -= tlv_len; + } + + if (nm.status_code == S_PW_STATUS) { + if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) { + send_notification_nbr(nbr, S_MISS_MSG, + msg.id, msg.type); + return (-1); + } + + switch (nm.fec.type) { + case MAP_TYPE_PWID: + break; + default: + send_notification_nbr(nbr, S_BAD_TLV_VAL, + msg.id, msg.type); + return (-1); + } + } + + log_warnx("msg-in: notification: lsr-id %s, status %s%s", + inet_ntoa(nbr->id), status_code_name(ntohl(st.status_code)), + (st.status_code & htonl(STATUS_FATAL)) ? " (fatal)" : ""); + + if (st.status_code & htonl(STATUS_FATAL)) { + if (nbr->state == NBR_STA_OPENSENT) + nbr_start_idtimer(nbr); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + return (-1); + } + + if (nm.status_code == S_PW_STATUS) + ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, + &nm, sizeof(nm)); + + return (0); +} + +int +gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id, + uint16_t msg_type) +{ + struct status_tlv st; + + memset(&st, 0, sizeof(st)); + st.type = htons(TLV_TYPE_STATUS); + st.length = htons(STATUS_TLV_LEN); + st.status_code = htonl(status_code); + /* + * For convenience, msg_id and msg_type are already in network + * byte order. + */ + st.msg_id = msg_id; + st.msg_type = msg_type; + + return (ibuf_add(buf, &st, STATUS_SIZE)); +} diff --git a/ldpd/packet.c b/ldpd/packet.c new file mode 100644 index 0000000000..7cc375c317 --- /dev/null +++ b/ldpd/packet.c @@ -0,0 +1,788 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2013, 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2004, 2005, 2008 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static struct iface *disc_find_iface(unsigned int, int, + union ldpd_addr *, int); +static void session_read(int, short, void *); +static void session_write(int, short, void *); +static ssize_t session_get_pdu(struct ibuf_read *, char **); +static void tcp_close(struct tcp_conn *); +static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); +static void pending_conn_timeout(int, short, void *); + +int +gen_ldp_hdr(struct ibuf *buf, uint16_t size) +{ + struct ldp_hdr ldp_hdr; + + memset(&ldp_hdr, 0, sizeof(ldp_hdr)); + ldp_hdr.version = htons(LDP_VERSION); + /* exclude the 'Version' and 'PDU Length' fields from the total */ + ldp_hdr.length = htons(size - LDP_HDR_DEAD_LEN); + ldp_hdr.lsr_id = leconf->rtr_id.s_addr; + ldp_hdr.lspace_id = 0; + + return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE)); +} + +int +gen_msg_hdr(struct ibuf *buf, uint16_t type, uint16_t size) +{ + static int msgcnt = 0; + struct ldp_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.type = htons(type); + /* exclude the 'Type' and 'Length' fields from the total */ + msg.length = htons(size - LDP_MSG_DEAD_LEN); + msg.id = htonl(++msgcnt); + + return (ibuf_add(buf, &msg, sizeof(msg))); +} + +/* send packets */ +int +send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, + void *pkt, size_t len) +{ + struct sockaddr *sa; + + switch (af) { + case AF_INET: + if (ia && IN_MULTICAST(ntohl(dst->v4.s_addr))) { + /* set outgoing interface for multicast traffic */ + if (sock_set_ipv4_mcast(ia->iface) == -1) { + log_debug("%s: error setting multicast " + "interface, %s", __func__, ia->iface->name); + return (-1); + } + } + break; + case AF_INET6: + if (ia && IN6_IS_ADDR_MULTICAST(&dst->v6)) { + /* set outgoing interface for multicast traffic */ + if (sock_set_ipv6_mcast(ia->iface) == -1) { + log_debug("%s: error setting multicast " + "interface, %s", __func__, ia->iface->name); + return (-1); + } + } + break; + default: + fatalx("send_packet: unknown af"); + } + + sa = addr2sa(af, dst, LDP_PORT); + if (sendto(fd, pkt, len, 0, sa, sa->sa_len) == -1) { + log_warn("%s: error sending packet to %s", __func__, + log_sockaddr(sa)); + return (-1); + } + + return (0); +} + +/* Discovery functions */ +#define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo)) +void +disc_recv_packet(int fd, short event, void *bula) +{ + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(CMSG_MAXLEN)]; + } cmsgbuf; + struct msghdr m; + struct sockaddr_storage from; + struct iovec iov; + char *buf; + struct cmsghdr *cmsg; + ssize_t r; + int multicast; + int af; + union ldpd_addr src; + unsigned int ifindex = 0; + struct iface *iface; + uint16_t len; + struct ldp_hdr ldp_hdr; + uint16_t pdu_len; + struct ldp_msg msg; + uint16_t msg_len; + struct in_addr lsr_id; + + if (event != EV_READ) + return; + + /* setup buffer */ + memset(&m, 0, sizeof(m)); + iov.iov_base = buf = pkt_ptr; + iov.iov_len = IBUF_READ_SIZE; + m.msg_name = &from; + m.msg_namelen = sizeof(from); + m.msg_iov = &iov; + m.msg_iovlen = 1; + m.msg_control = &cmsgbuf.buf; + m.msg_controllen = sizeof(cmsgbuf.buf); + + if ((r = recvmsg(fd, &m, 0)) == -1) { + if (errno != EAGAIN && errno != EINTR) + log_debug("%s: read error: %s", __func__, + strerror(errno)); + return; + } + + multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0; + sa2addr((struct sockaddr *)&from, &af, &src); + if (bad_addr(af, &src)) { + log_debug("%s: invalid source address: %s", __func__, + log_addr(af, &src)); + return; + } + + for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; + cmsg = CMSG_NXTHDR(&m, cmsg)) { + if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVIF) { + ifindex = ((struct sockaddr_dl *) + CMSG_DATA(cmsg))->sdl_index; + break; + } + if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + ifindex = ((struct in6_pktinfo *) + CMSG_DATA(cmsg))->ipi6_ifindex; + break; + } + } + + /* find a matching interface */ + iface = disc_find_iface(ifindex, af, &src, multicast); + if (iface == NULL) + return; + + /* check packet size */ + len = (uint16_t)r; + if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) { + log_debug("%s: bad packet size, source %s", __func__, + log_addr(af, &src)); + return; + } + + /* LDP header sanity checks */ + memcpy(&ldp_hdr, buf, sizeof(ldp_hdr)); + if (ntohs(ldp_hdr.version) != LDP_VERSION) { + log_debug("%s: invalid LDP version %d, source %s", __func__, + ntohs(ldp_hdr.version), log_addr(af, &src)); + return; + } + if (ntohs(ldp_hdr.lspace_id) != 0) { + log_debug("%s: invalid label space %u, source %s", __func__, + ntohs(ldp_hdr.lspace_id), log_addr(af, &src)); + return; + } + /* check "PDU Length" field */ + pdu_len = ntohs(ldp_hdr.length); + if ((pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE)) || + (pdu_len > (len - LDP_HDR_DEAD_LEN))) { + log_debug("%s: invalid LDP packet length %u, source %s", + __func__, ntohs(ldp_hdr.length), log_addr(af, &src)); + return; + } + buf += LDP_HDR_SIZE; + len -= LDP_HDR_SIZE; + + lsr_id.s_addr = ldp_hdr.lsr_id; + + /* + * For UDP, we process only the first message of each packet. This does + * not impose any restrictions since LDP uses UDP only for sending Hello + * packets. + */ + memcpy(&msg, buf, sizeof(msg)); + + /* check "Message Length" field */ + msg_len = ntohs(msg.length); + if (msg_len < LDP_MSG_LEN || ((msg_len + LDP_MSG_DEAD_LEN) > pdu_len)) { + log_debug("%s: invalid LDP message length %u, source %s", + __func__, ntohs(msg.length), log_addr(af, &src)); + return; + } + buf += LDP_MSG_SIZE; + len -= LDP_MSG_SIZE; + + /* switch LDP packet type */ + switch (ntohs(msg.type)) { + case MSG_TYPE_HELLO: + recv_hello(lsr_id, &msg, af, &src, iface, multicast, buf, len); + break; + default: + log_debug("%s: unknown LDP packet type, source %s", __func__, + log_addr(af, &src)); + } +} + +static struct iface * +disc_find_iface(unsigned int ifindex, int af, union ldpd_addr *src, + int multicast) +{ + struct iface *iface; + struct iface_af *ia; + struct if_addr *if_addr; + in_addr_t mask; + + iface = if_lookup(leconf, ifindex); + if (iface == NULL) + return (NULL); + + /* + * For unicast packets, we just need to make sure that the interface + * is enabled for the given address-family. + */ + if (!multicast) { + ia = iface_af_get(iface, af); + if (ia->enabled) + return (iface); + return (NULL); + } + + switch (af) { + case AF_INET: + LIST_FOREACH(if_addr, &iface->addr_list, entry) { + if (if_addr->af != AF_INET) + continue; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + if (if_addr->dstbrd.v4.s_addr == src->v4.s_addr) + return (iface); + break; + default: + mask = prefixlen2mask(if_addr->prefixlen); + if ((if_addr->addr.v4.s_addr & mask) == + (src->v4.s_addr & mask)) + return (iface); + break; + } + } + break; + case AF_INET6: + if (IN6_IS_ADDR_LINKLOCAL(&src->v6)) + return (iface); + break; + default: + fatalx("disc_find_iface: unknown af"); + } + + return (NULL); +} + +void +session_accept(int fd, short event, void *bula) +{ + struct sockaddr_storage src; + socklen_t len = sizeof(src); + int newfd; + int af; + union ldpd_addr addr; + struct nbr *nbr; + struct pending_conn *pconn; + + if (!(event & EV_READ)) + return; + + newfd = accept4(fd, (struct sockaddr *)&src, &len, + SOCK_NONBLOCK | SOCK_CLOEXEC); + if (newfd == -1) { + /* + * Pause accept if we are out of file descriptors, or + * libevent will haunt us here too. + */ + if (errno == ENFILE || errno == EMFILE) { + accept_pause(); + } else if (errno != EWOULDBLOCK && errno != EINTR && + errno != ECONNABORTED) + log_debug("%s: accept error: %s", __func__, + strerror(errno)); + return; + } + + sa2addr((struct sockaddr *)&src, &af, &addr); + + /* + * Since we don't support label spaces, we can identify this neighbor + * just by its source address. This way we don't need to wait for its + * Initialization message to know who we are talking to. + */ + nbr = nbr_find_addr(af, &addr); + if (nbr == NULL) { + /* + * According to RFC 5036, we would need to send a No Hello + * Error Notification message and close this TCP connection + * right now. But doing so would trigger the backoff exponential + * timer in the remote peer, which would considerably slow down + * the session establishment process. The trick here is to wait + * five seconds before sending the Notification Message. There's + * a good chance that the remote peer will send us a Hello + * message within this interval, so it's worth waiting before + * taking a more drastic measure. + */ + pconn = pending_conn_find(af, &addr); + if (pconn) + close(newfd); + else + pending_conn_new(newfd, af, &addr); + return; + } + /* protection against buggy implementations */ + if (nbr_session_active_role(nbr)) { + close(newfd); + return; + } + if (nbr->state != NBR_STA_PRESENT) { + log_debug("%s: lsr-id %s: rejecting additional transport " + "connection", __func__, inet_ntoa(nbr->id)); + close(newfd); + return; + } + + session_accept_nbr(nbr, newfd); +} + +void +session_accept_nbr(struct nbr *nbr, int fd) +{ + struct nbr_params *nbrp; + int opt; + socklen_t len; + + nbrp = nbr_params_find(leconf, nbr->id); + if (nbr_gtsm_check(fd, nbr, nbrp)) { + close(fd); + return; + } + + if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { + if (sysdep.no_pfkey || sysdep.no_md5sig) { + log_warnx("md5sig configured but not available"); + close(fd); + return; + } + + len = sizeof(opt); + if (getsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, &len) == -1) + fatal("getsockopt TCP_MD5SIG"); + if (!opt) { /* non-md5'd connection! */ + log_warnx("connection attempt without md5 signature"); + close(fd); + return; + } + } + + nbr->tcp = tcp_new(fd, nbr); + nbr_fsm(nbr, NBR_EVT_MATCH_ADJ); +} + +static void +session_read(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct tcp_conn *tcp = nbr->tcp; + struct ldp_hdr *ldp_hdr; + struct ldp_msg *msg; + char *buf, *pdu; + ssize_t n, len; + uint16_t pdu_len, msg_len, msg_size, max_pdu_len; + int ret; + + if (event != EV_READ) + return; + + if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos, + sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) { + if (errno != EINTR && errno != EAGAIN) { + log_warn("%s: read error", __func__); + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + return; + } + /* retry read */ + return; + } + if (n == 0) { + /* connection closed */ + log_debug("%s: connection closed by remote end", __func__); + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + return; + } + tcp->rbuf->wpos += n; + + while ((len = session_get_pdu(tcp->rbuf, &buf)) > 0) { + pdu = buf; + ldp_hdr = (struct ldp_hdr *)pdu; + if (ntohs(ldp_hdr->version) != LDP_VERSION) { + session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0); + free(buf); + return; + } + + pdu_len = ntohs(ldp_hdr->length); + /* + * RFC 5036 - Section 3.5.3: + * "Prior to completion of the negotiation, the maximum + * allowable length is 4096 bytes". + */ + if (nbr->state == NBR_STA_OPER) + max_pdu_len = nbr->max_pdu_len; + else + max_pdu_len = LDP_MAX_LEN; + if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || + pdu_len > max_pdu_len) { + session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); + free(buf); + return; + } + pdu_len -= LDP_HDR_PDU_LEN; + if (ldp_hdr->lsr_id != nbr->id.s_addr || + ldp_hdr->lspace_id != 0) { + session_shutdown(nbr, S_BAD_LDP_ID, 0, 0); + free(buf); + return; + } + pdu += LDP_HDR_SIZE; + len -= LDP_HDR_SIZE; + + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + while (len >= LDP_MSG_SIZE) { + uint16_t type; + + msg = (struct ldp_msg *)pdu; + type = ntohs(msg->type); + msg_len = ntohs(msg->length); + if (msg_len < LDP_MSG_LEN || + (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, + msg->type); + free(buf); + return; + } + msg_size = msg_len + LDP_MSG_DEAD_LEN; + pdu_len -= msg_size; + + /* check for error conditions earlier */ + switch (type) { + case MSG_TYPE_INIT: + if ((nbr->state != NBR_STA_INITIAL) && + (nbr->state != NBR_STA_OPENSENT)) { + session_shutdown(nbr, S_SHUTDOWN, + msg->id, msg->type); + free(buf); + return; + } + break; + case MSG_TYPE_KEEPALIVE: + if ((nbr->state == NBR_STA_INITIAL) || + (nbr->state == NBR_STA_OPENSENT)) { + session_shutdown(nbr, S_SHUTDOWN, + msg->id, msg->type); + free(buf); + return; + } + 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: + if (nbr->state != NBR_STA_OPER) { + session_shutdown(nbr, S_SHUTDOWN, + msg->id, msg->type); + free(buf); + return; + } + break; + default: + break; + } + + /* switch LDP packet type */ + switch (type) { + case MSG_TYPE_NOTIFICATION: + ret = recv_notification(nbr, pdu, msg_size); + break; + case MSG_TYPE_INIT: + ret = recv_init(nbr, pdu, msg_size); + break; + case MSG_TYPE_KEEPALIVE: + ret = recv_keepalive(nbr, pdu, msg_size); + break; + case MSG_TYPE_ADDR: + case MSG_TYPE_ADDRWITHDRAW: + ret = recv_address(nbr, pdu, msg_size); + break; + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELREQUEST: + case MSG_TYPE_LABELWITHDRAW: + case MSG_TYPE_LABELRELEASE: + case MSG_TYPE_LABELABORTREQ: + ret = recv_labelmessage(nbr, pdu, msg_size, + type); + break; + default: + log_debug("%s: unknown LDP message from nbr %s", + __func__, inet_ntoa(nbr->id)); + if (!(ntohs(msg->type) & UNKNOWN_FLAG)) + send_notification_nbr(nbr, + S_UNKNOWN_MSG, msg->id, msg->type); + /* ignore the message */ + ret = 0; + break; + } + + if (ret == -1) { + /* parser failed, giving up */ + free(buf); + return; + } + + /* Analyse the next message */ + pdu += msg_size; + len -= msg_size; + } + free(buf); + if (len != 0) { + session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); + return; + } + } +} + +static void +session_write(int fd, short event, void *arg) +{ + struct tcp_conn *tcp = arg; + struct nbr *nbr = tcp->nbr; + + if (!(event & EV_WRITE)) + return; + + if (msgbuf_write(&tcp->wbuf.wbuf) <= 0) + if (errno != EAGAIN && nbr) + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + + if (nbr == NULL && !tcp->wbuf.wbuf.queued) { + /* + * We are done sending the notification message, now we can + * close the socket. + */ + tcp_close(tcp); + return; + } + + evbuf_event_add(&tcp->wbuf); +} + +void +session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, + uint32_t msg_type) +{ + switch (nbr->state) { + case NBR_STA_PRESENT: + if (nbr_pending_connect(nbr)) + event_del(&nbr->ev_connect); + break; + case NBR_STA_INITIAL: + case NBR_STA_OPENREC: + case NBR_STA_OPENSENT: + case NBR_STA_OPER: + log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + + send_notification_nbr(nbr, status, msg_id, msg_type); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + break; + default: + fatalx("session_shutdown: unknown neighbor state"); + } +} + +void +session_close(struct nbr *nbr) +{ + log_debug("%s: closing session with lsr-id %s", __func__, + inet_ntoa(nbr->id)); + + tcp_close(nbr->tcp); + nbr_stop_ktimer(nbr); + nbr_stop_ktimeout(nbr); + nbr_stop_itimeout(nbr); +} + +static ssize_t +session_get_pdu(struct ibuf_read *r, char **b) +{ + struct ldp_hdr l; + size_t av, dlen, left; + + av = r->wpos; + if (av < sizeof(l)) + return (0); + + memcpy(&l, r->buf, sizeof(l)); + dlen = ntohs(l.length) + LDP_HDR_DEAD_LEN; + if (dlen > av) + return (0); + + if ((*b = malloc(dlen)) == NULL) + return (-1); + + memcpy(*b, r->buf, dlen); + if (dlen < av) { + left = av - dlen; + memmove(r->buf, r->buf + dlen, left); + r->wpos = left; + } else + r->wpos = 0; + + return (dlen); +} + +struct tcp_conn * +tcp_new(int fd, struct nbr *nbr) +{ + struct tcp_conn *tcp; + + if ((tcp = calloc(1, sizeof(*tcp))) == NULL) + fatal(__func__); + + tcp->fd = fd; + evbuf_init(&tcp->wbuf, tcp->fd, session_write, tcp); + + if (nbr) { + if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL) + fatal(__func__); + + event_set(&tcp->rev, tcp->fd, EV_READ | EV_PERSIST, + session_read, nbr); + event_add(&tcp->rev, NULL); + tcp->nbr = nbr; + } + + return (tcp); +} + +static void +tcp_close(struct tcp_conn *tcp) +{ + /* try to flush write buffer */ + msgbuf_write(&tcp->wbuf.wbuf); + evbuf_clear(&tcp->wbuf); + + if (tcp->nbr) { + event_del(&tcp->rev); + free(tcp->rbuf); + tcp->nbr->tcp = NULL; + } + + close(tcp->fd); + accept_unpause(); + free(tcp); +} + +static struct pending_conn * +pending_conn_new(int fd, int af, union ldpd_addr *addr) +{ + struct pending_conn *pconn; + struct timeval tv; + + if ((pconn = calloc(1, sizeof(*pconn))) == NULL) + fatal(__func__); + + pconn->fd = fd; + pconn->af = af; + pconn->addr = *addr; + evtimer_set(&pconn->ev_timeout, pending_conn_timeout, pconn); + TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry); + + timerclear(&tv); + tv.tv_sec = PENDING_CONN_TIMEOUT; + if (evtimer_add(&pconn->ev_timeout, &tv) == -1) + fatal(__func__); + + return (pconn); +} + +void +pending_conn_del(struct pending_conn *pconn) +{ + if (evtimer_pending(&pconn->ev_timeout, NULL) && + evtimer_del(&pconn->ev_timeout) == -1) + fatal(__func__); + + TAILQ_REMOVE(&global.pending_conns, pconn, entry); + free(pconn); +} + +struct pending_conn * +pending_conn_find(int af, union ldpd_addr *addr) +{ + struct pending_conn *pconn; + + TAILQ_FOREACH(pconn, &global.pending_conns, entry) + if (af == pconn->af && + ldp_addrcmp(af, addr, &pconn->addr) == 0) + return (pconn); + + return (NULL); +} + +static void +pending_conn_timeout(int fd, short event, void *arg) +{ + struct pending_conn *pconn = arg; + struct tcp_conn *tcp; + + log_debug("%s: no adjacency with remote end: %s", __func__, + log_addr(pconn->af, &pconn->addr)); + + /* + * Create a write buffer detached from any neighbor to send a + * notification message reliably. + */ + tcp = tcp_new(pconn->fd, NULL); + send_notification(S_NO_HELLO, tcp, 0, 0); + msgbuf_write(&tcp->wbuf.wbuf); + + pending_conn_del(pconn); +} diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c new file mode 100644 index 0000000000..f0f16c867f --- /dev/null +++ b/ldpd/pfkey.c @@ -0,0 +1,466 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * Copyright (c) 2003, 2004 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +static int pfkey_send(int, uint8_t, uint8_t, uint8_t, + int, union ldpd_addr *, union ldpd_addr *, + uint32_t, uint8_t, int, char *, uint8_t, int, char *, + uint16_t, uint16_t); +static int pfkey_reply(int, uint32_t *); +static int pfkey_sa_add(int, union ldpd_addr *, union ldpd_addr *, + uint8_t, char *, uint32_t *); +static int pfkey_sa_remove(int, union ldpd_addr *, union ldpd_addr *, + uint32_t *); +static int pfkey_md5sig_establish(struct nbr *, struct nbr_params *nbrp); +static int pfkey_md5sig_remove(struct nbr *); + +#define PFKEY2_CHUNK sizeof(uint64_t) +#define ROUNDUP(x) (((x) + (PFKEY2_CHUNK - 1)) & ~(PFKEY2_CHUNK - 1)) +#define IOV_CNT 20 + +static uint32_t sadb_msg_seq; +static uint32_t pid; /* should pid_t but pfkey needs uint32_t */ +static int fd; + +static int +pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir, + int af, union ldpd_addr *src, union ldpd_addr *dst, uint32_t spi, + uint8_t aalg, int alen, char *akey, uint8_t ealg, int elen, char *ekey, + uint16_t sport, uint16_t dport) +{ + struct sadb_msg smsg; + struct sadb_sa sa; + struct sadb_address sa_src, sa_dst; + struct sadb_key sa_akey, sa_ekey; + struct sadb_spirange sa_spirange; + struct iovec iov[IOV_CNT]; + ssize_t n; + int len = 0; + int iov_cnt; + struct sockaddr_storage ssrc, sdst, smask, dmask; + struct sockaddr *saptr; + + if (!pid) + pid = getpid(); + + /* we need clean sockaddr... no ports set */ + memset(&ssrc, 0, sizeof(ssrc)); + memset(&smask, 0, sizeof(smask)); + if ((saptr = addr2sa(af, src, 0))) + memcpy(&ssrc, saptr, sizeof(ssrc)); + switch (af) { + case AF_INET: + memset(&((struct sockaddr_in *)&smask)->sin_addr, 0xff, 32/8); + break; + case AF_INET6: + memset(&((struct sockaddr_in6 *)&smask)->sin6_addr, 0xff, + 128/8); + break; + default: + return (-1); + } + smask.ss_family = ssrc.ss_family; + smask.ss_len = ssrc.ss_len; + + memset(&sdst, 0, sizeof(sdst)); + memset(&dmask, 0, sizeof(dmask)); + if ((saptr = addr2sa(af, dst, 0))) + memcpy(&sdst, saptr, sizeof(sdst)); + switch (af) { + case AF_INET: + memset(&((struct sockaddr_in *)&dmask)->sin_addr, 0xff, 32/8); + break; + case AF_INET6: + memset(&((struct sockaddr_in6 *)&dmask)->sin6_addr, 0xff, + 128/8); + break; + default: + return (-1); + } + dmask.ss_family = sdst.ss_family; + dmask.ss_len = sdst.ss_len; + + memset(&smsg, 0, sizeof(smsg)); + smsg.sadb_msg_version = PF_KEY_V2; + smsg.sadb_msg_seq = ++sadb_msg_seq; + smsg.sadb_msg_pid = pid; + smsg.sadb_msg_len = sizeof(smsg) / 8; + smsg.sadb_msg_type = mtype; + smsg.sadb_msg_satype = satype; + + switch (mtype) { + case SADB_GETSPI: + memset(&sa_spirange, 0, sizeof(sa_spirange)); + sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE; + sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8; + sa_spirange.sadb_spirange_min = 0x100; + sa_spirange.sadb_spirange_max = 0xffffffff; + sa_spirange.sadb_spirange_reserved = 0; + break; + case SADB_ADD: + case SADB_UPDATE: + case SADB_DELETE: + memset(&sa, 0, sizeof(sa)); + 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_state = SADB_SASTATE_MATURE; + break; + } + + memset(&sa_src, 0, sizeof(sa_src)); + sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; + sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8; + + memset(&sa_dst, 0, sizeof(sa_dst)); + sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; + sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8; + + sa.sadb_sa_auth = aalg; + sa.sadb_sa_encrypt = SADB_X_EALG_AES; /* XXX */ + + switch (mtype) { + case SADB_ADD: + case SADB_UPDATE: + memset(&sa_akey, 0, sizeof(sa_akey)); + sa_akey.sadb_key_exttype = SADB_EXT_KEY_AUTH; + sa_akey.sadb_key_len = (sizeof(sa_akey) + + ((alen + 7) / 8) * 8) / 8; + sa_akey.sadb_key_bits = 8 * alen; + + memset(&sa_ekey, 0, sizeof(sa_ekey)); + sa_ekey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; + sa_ekey.sadb_key_len = (sizeof(sa_ekey) + + ((elen + 7) / 8) * 8) / 8; + sa_ekey.sadb_key_bits = 8 * elen; + + break; + } + + iov_cnt = 0; + + /* msghdr */ + iov[iov_cnt].iov_base = &smsg; + iov[iov_cnt].iov_len = sizeof(smsg); + iov_cnt++; + + switch (mtype) { + case SADB_ADD: + case SADB_UPDATE: + case SADB_DELETE: + /* SA hdr */ + iov[iov_cnt].iov_base = &sa; + iov[iov_cnt].iov_len = sizeof(sa); + smsg.sadb_msg_len += sa.sadb_sa_len; + iov_cnt++; + break; + case SADB_GETSPI: + /* SPI range */ + iov[iov_cnt].iov_base = &sa_spirange; + iov[iov_cnt].iov_len = sizeof(sa_spirange); + smsg.sadb_msg_len += sa_spirange.sadb_spirange_len; + iov_cnt++; + break; + } + + /* dest addr */ + iov[iov_cnt].iov_base = &sa_dst; + iov[iov_cnt].iov_len = sizeof(sa_dst); + iov_cnt++; + iov[iov_cnt].iov_base = &sdst; + iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len); + smsg.sadb_msg_len += sa_dst.sadb_address_len; + iov_cnt++; + + /* src addr */ + iov[iov_cnt].iov_base = &sa_src; + iov[iov_cnt].iov_len = sizeof(sa_src); + iov_cnt++; + iov[iov_cnt].iov_base = &ssrc; + iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len); + smsg.sadb_msg_len += sa_src.sadb_address_len; + iov_cnt++; + + switch (mtype) { + case SADB_ADD: + case SADB_UPDATE: + if (alen) { + /* auth key */ + iov[iov_cnt].iov_base = &sa_akey; + iov[iov_cnt].iov_len = sizeof(sa_akey); + iov_cnt++; + iov[iov_cnt].iov_base = akey; + iov[iov_cnt].iov_len = ((alen + 7) / 8) * 8; + smsg.sadb_msg_len += sa_akey.sadb_key_len; + iov_cnt++; + } + if (elen) { + /* encryption key */ + iov[iov_cnt].iov_base = &sa_ekey; + iov[iov_cnt].iov_len = sizeof(sa_ekey); + iov_cnt++; + iov[iov_cnt].iov_base = ekey; + iov[iov_cnt].iov_len = ((elen + 7) / 8) * 8; + smsg.sadb_msg_len += sa_ekey.sadb_key_len; + iov_cnt++; + } + break; + } + + len = smsg.sadb_msg_len * 8; + do { + n = writev(sd, iov, iov_cnt); + } while (n == -1 && (errno == EAGAIN || errno == EINTR)); + + if (n == -1) { + log_warn("writev (%d/%d)", iov_cnt, len); + return (-1); + } + + return (0); +} + +int +pfkey_read(int sd, struct sadb_msg *h) +{ + struct sadb_msg hdr; + + if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { + if (errno == EAGAIN || errno == EINTR) + return (0); + log_warn("pfkey peek"); + return (-1); + } + + /* XXX: Only one message can be outstanding. */ + if (hdr.sadb_msg_seq == sadb_msg_seq && + hdr.sadb_msg_pid == pid) { + if (h) + *h = hdr; + return (0); + } + + /* not ours, discard */ + if (read(sd, &hdr, sizeof(hdr)) == -1) { + if (errno == EAGAIN || errno == EINTR) + return (0); + log_warn("pfkey read"); + return (-1); + } + + return (1); +} + +static int +pfkey_reply(int sd, uint32_t *spip) +{ + struct sadb_msg hdr, *msg; + struct sadb_ext *ext; + struct sadb_sa *sa; + uint8_t *data; + ssize_t len; + int rv; + + do { + rv = pfkey_read(sd, &hdr); + if (rv == -1) + return (-1); + } while (rv); + + if (hdr.sadb_msg_errno != 0) { + errno = hdr.sadb_msg_errno; + if (errno == ESRCH) + return (0); + else { + log_warn("pfkey"); + return (-1); + } + } + if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEY2_CHUNK)) == NULL) { + log_warn("pfkey malloc"); + return (-1); + } + len = hdr.sadb_msg_len * PFKEY2_CHUNK; + if (read(sd, data, len) != len) { + log_warn("pfkey read"); + explicit_bzero(data, len); + free(data); + return (-1); + } + + if (hdr.sadb_msg_type == SADB_GETSPI) { + if (spip == NULL) { + explicit_bzero(data, len); + free(data); + return (0); + } + + msg = (struct sadb_msg *)data; + for (ext = (struct sadb_ext *)(msg + 1); + (size_t)((uint8_t *)ext - (uint8_t *)msg) < + msg->sadb_msg_len * PFKEY2_CHUNK; + ext = (struct sadb_ext *)((uint8_t *)ext + + ext->sadb_ext_len * PFKEY2_CHUNK)) { + if (ext->sadb_ext_type == SADB_EXT_SA) { + sa = (struct sadb_sa *) ext; + *spip = sa->sadb_sa_spi; + break; + } + } + } + explicit_bzero(data, len); + free(data); + return (0); +} + +static int +pfkey_sa_add(int af, union ldpd_addr *src, union ldpd_addr *dst, uint8_t keylen, + char *key, uint32_t *spi) +{ + if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_GETSPI, 0, + af, src, dst, 0, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0) + return (-1); + if (pfkey_reply(fd, spi) < 0) + return (-1); + if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_UPDATE, 0, + af, src, dst, *spi, 0, keylen, key, 0, 0, NULL, 0, 0) < 0) + return (-1); + if (pfkey_reply(fd, NULL) < 0) + return (-1); + return (0); +} + +static int +pfkey_sa_remove(int af, union ldpd_addr *src, union ldpd_addr *dst, + uint32_t *spi) +{ + if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_DELETE, 0, + af, src, dst, *spi, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0) + return (-1); + if (pfkey_reply(fd, NULL) < 0) + return (-1); + *spi = 0; + return (0); +} + +static int +pfkey_md5sig_establish(struct nbr *nbr, struct nbr_params *nbrp) +{ + sleep(1); + + if (!nbr->auth.spi_out) + if (pfkey_sa_add(nbr->af, &nbr->laddr, &nbr->raddr, + nbrp->auth.md5key_len, nbrp->auth.md5key, + &nbr->auth.spi_out) == -1) + return (-1); + if (!nbr->auth.spi_in) + if (pfkey_sa_add(nbr->af, &nbr->raddr, &nbr->laddr, + nbrp->auth.md5key_len, nbrp->auth.md5key, + &nbr->auth.spi_in) == -1) + return (-1); + + nbr->auth.established = 1; + return (0); +} + +static int +pfkey_md5sig_remove(struct nbr *nbr) +{ + if (nbr->auth.spi_out) + if (pfkey_sa_remove(nbr->af, &nbr->laddr, &nbr->raddr, + &nbr->auth.spi_out) == -1) + return (-1); + if (nbr->auth.spi_in) + if (pfkey_sa_remove(nbr->af, &nbr->raddr, &nbr->laddr, + &nbr->auth.spi_in) == -1) + return (-1); + + nbr->auth.established = 0; + nbr->auth.spi_in = 0; + nbr->auth.spi_out = 0; + nbr->auth.method = AUTH_NONE; + memset(nbr->auth.md5key, 0, sizeof(nbr->auth.md5key)); + + return (0); +} + +int +pfkey_establish(struct nbr *nbr, struct nbr_params *nbrp) +{ + if (nbrp->auth.method == AUTH_NONE) + return (0); + + /* + * make sure we keep copies of everything we need to + * remove SAs and flows later again. + */ + nbr->auth.method = nbrp->auth.method; + + switch (nbr->auth.method) { + case AUTH_MD5SIG: + strlcpy(nbr->auth.md5key, nbrp->auth.md5key, + sizeof(nbr->auth.md5key)); + return (pfkey_md5sig_establish(nbr, nbrp)); + default: + break; + } + + return (0); +} + +int +pfkey_remove(struct nbr *nbr) +{ + if (nbr->auth.method == AUTH_NONE || !nbr->auth.established) + return (0); + + switch (nbr->auth.method) { + case AUTH_MD5SIG: + return (pfkey_md5sig_remove(nbr)); + default: + break; + } + + return (0); +} + +int +pfkey_init(void) +{ + if ((fd = socket(PF_KEY, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_KEY_V2)) == -1) { + if (errno == EPROTONOSUPPORT) { + log_warnx("PF_KEY not available"); + sysdep.no_pfkey = 1; + return (-1); + } else + fatal("pfkey setup failed"); + } + return (fd); +} diff --git a/ldpd/socket.c b/ldpd/socket.c new file mode 100644 index 0000000000..8f26771df7 --- /dev/null +++ b/ldpd/socket.c @@ -0,0 +1,391 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2016 Renato Westphal + * Copyright (c) 2009 Michele Marchetto + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +int +ldp_create_socket(int af, enum socket_type type) +{ + int fd, domain, proto; + union ldpd_addr addr; + struct sockaddr_storage local_sa; + int opt; + + /* create socket */ + switch (type) { + case LDP_SOCKET_DISC: + case LDP_SOCKET_EDISC: + domain = SOCK_DGRAM; + proto = IPPROTO_UDP; + break; + case LDP_SOCKET_SESSION: + domain = SOCK_STREAM; + proto = IPPROTO_TCP; + break; + default: + fatalx("ldp_create_socket: unknown socket type"); + } + fd = socket(af, domain | SOCK_NONBLOCK | SOCK_CLOEXEC, proto); + if (fd == -1) { + log_warn("%s: error creating socket", __func__); + return (-1); + } + + /* bind to a local address/port */ + switch (type) { + case LDP_SOCKET_DISC: + /* listen on all addresses */ + memset(&addr, 0, sizeof(addr)); + memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT), + sizeof(local_sa)); + break; + case LDP_SOCKET_EDISC: + case LDP_SOCKET_SESSION: + addr = (ldp_af_conf_get(ldpd_conf, af))->trans_addr; + memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT), + sizeof(local_sa)); + if (sock_set_bindany(fd, 1) == -1) { + close(fd); + return (-1); + } + break; + } + if (sock_set_reuse(fd, 1) == -1) { + close(fd); + return (-1); + } + if (bind(fd, (struct sockaddr *)&local_sa, local_sa.ss_len) == -1) { + log_warn("%s: error binding socket", __func__); + close(fd); + return (-1); + } + + /* set options */ + switch (af) { + case AF_INET: + if (sock_set_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL) == -1) { + close(fd); + return (-1); + } + if (type == LDP_SOCKET_DISC) { + if (sock_set_ipv4_mcast_ttl(fd, + IP_DEFAULT_MULTICAST_TTL) == -1) { + close(fd); + return (-1); + } + if (sock_set_ipv4_mcast_loop(fd) == -1) { + close(fd); + return (-1); + } + } + if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) { + if (sock_set_ipv4_recvif(fd, 1) == -1) { + close(fd); + return (-1); + } + } + if (type == LDP_SOCKET_SESSION) { + if (sock_set_ipv4_ucast_ttl(fd, 255) == -1) { + close(fd); + return (-1); + } + } + break; + case AF_INET6: + if (sock_set_ipv6_dscp(fd, IPTOS_PREC_INTERNETCONTROL) == -1) { + close(fd); + return (-1); + } + if (type == LDP_SOCKET_DISC) { + if (sock_set_ipv6_mcast_loop(fd) == -1) { + close(fd); + return (-1); + } + if (sock_set_ipv6_mcast_hops(fd, 255) == -1) { + close(fd); + return (-1); + } + if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) { + if (sock_set_ipv6_minhopcount(fd, 255) == -1) { + close(fd); + return (-1); + } + } + } + if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) { + if (sock_set_ipv6_pktinfo(fd, 1) == -1) { + close(fd); + return (-1); + } + } + if (type == LDP_SOCKET_SESSION) { + if (sock_set_ipv6_ucast_hops(fd, 255) == -1) { + close(fd); + return (-1); + } + } + break; + } + switch (type) { + case LDP_SOCKET_DISC: + case LDP_SOCKET_EDISC: + sock_set_recvbuf(fd); + break; + case LDP_SOCKET_SESSION: + if (listen(fd, LDP_BACKLOG) == -1) + log_warn("%s: error listening on socket", __func__); + + opt = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, + sizeof(opt)) == -1) { + if (errno == ENOPROTOOPT) { /* system w/o md5sig */ + log_warnx("md5sig not available, disabling"); + sysdep.no_md5sig = 1; + } else { + close(fd); + return (-1); + } + } + break; + } + + return (fd); +} + +void +sock_set_recvbuf(int fd) +{ + int bsize; + + bsize = 65535; + while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, + sizeof(bsize)) == -1) + bsize /= 2; +} + +int +sock_set_reuse(int fd, int enable) +{ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, + sizeof(int)) < 0) { + log_warn("%s: error setting SO_REUSEADDR", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_bindany(int fd, int enable) +{ + if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, + sizeof(int)) < 0) { + log_warn("%s: error setting SO_BINDANY", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv4_tos(int fd, int tos) +{ + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { + log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos); + return (-1); + } + + return (0); +} + +int +sock_set_ipv4_recvif(int fd, int enable) +{ + if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, + sizeof(enable)) < 0) { + log_warn("%s: error setting IP_RECVIF", __func__); + return (-1); + } + return (0); +} + +int +sock_set_ipv4_minttl(int fd, int ttl) +{ + if (setsockopt(fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) { + log_warn("%s: error setting IP_MINTTL", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv4_ucast_ttl(int fd, int ttl) +{ + if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { + log_warn("%s: error setting IP_TTL", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl) +{ + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) { + log_warn("%s: error setting IP_MULTICAST_TTL to %d", + __func__, ttl); + return (-1); + } + + return (0); +} + +int +sock_set_ipv4_mcast(struct iface *iface) +{ + in_addr_t addr; + + addr = if_get_ipv4_addr(iface); + + if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, IP_MULTICAST_IF, + &addr, sizeof(addr)) < 0) { + log_warn("%s: error setting IP_MULTICAST_IF, interface %s", + __func__, iface->name); + return (-1); + } + + return (0); +} + +int +sock_set_ipv4_mcast_loop(int fd) +{ + uint8_t loop = 0; + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) { + log_warn("%s: error setting IP_MULTICAST_LOOP", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_dscp(int fd, int dscp) +{ + if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp, + sizeof(dscp)) < 0) { + log_warn("%s: error setting IPV6_TCLASS", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_pktinfo(int fd, int enable) +{ + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable, + sizeof(enable)) < 0) { + log_warn("%s: error setting IPV6_RECVPKTINFO", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_minhopcount(int fd, int hoplimit) +{ + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT, + &hoplimit, sizeof(hoplimit)) < 0) { + log_warn("%s: error setting IPV6_MINHOPCOUNT", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_ucast_hops(int fd, int hoplimit) +{ + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &hoplimit, sizeof(hoplimit)) < 0) { + log_warn("%s: error setting IPV6_UNICAST_HOPS", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_mcast_hops(int fd, int hoplimit) +{ + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &hoplimit, sizeof(hoplimit)) < 0) { + log_warn("%s: error setting IPV6_MULTICAST_HOPS", __func__); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_mcast(struct iface *iface) +{ + if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6, + IPV6_MULTICAST_IF, &iface->ifindex, sizeof(iface->ifindex)) < 0) { + log_warn("%s: error setting IPV6_MULTICAST_IF, interface %s", + __func__, iface->name); + return (-1); + } + + return (0); +} + +int +sock_set_ipv6_mcast_loop(int fd) +{ + unsigned int loop = 0; + + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &loop, sizeof(loop)) < 0) { + log_warn("%s: error setting IPV6_MULTICAST_LOOP", __func__); + return (-1); + } + + return (0); +} diff --git a/ldpd/util.c b/ldpd/util.c new file mode 100644 index 0000000000..981a5c8c21 --- /dev/null +++ b/ldpd/util.c @@ -0,0 +1,356 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Renato Westphal + * Copyright (c) 2012 Alexander Bluhm + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "ldpd.h" +#include "log.h" + +uint8_t +mask2prefixlen(in_addr_t ina) +{ + if (ina == 0) + return (0); + else + return (33 - ffs(ntohl(ina))); +} + +uint8_t +mask2prefixlen6(struct sockaddr_in6 *sa_in6) +{ + uint8_t l = 0, *ap, *ep; + + /* + * sin6_len is the size of the sockaddr so substract the offset of + * the possibly truncated sin6_addr struct. + */ + ap = (uint8_t *)&sa_in6->sin6_addr; + ep = (uint8_t *)sa_in6 + sa_in6->sin6_len; + for (; ap < ep; ap++) { + /* this "beauty" is adopted from sbin/route/show.c ... */ + switch (*ap) { + case 0xff: + l += 8; + break; + case 0xfe: + l += 7; + return (l); + case 0xfc: + l += 6; + return (l); + case 0xf8: + l += 5; + return (l); + case 0xf0: + l += 4; + return (l); + case 0xe0: + l += 3; + return (l); + case 0xc0: + l += 2; + return (l); + case 0x80: + l += 1; + return (l); + case 0x00: + return (l); + default: + fatalx("non contiguous inet6 netmask"); + } + } + + return (l); +} + +in_addr_t +prefixlen2mask(uint8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + return (htonl(0xffffffff << (32 - prefixlen))); +} + +struct in6_addr * +prefixlen2mask6(uint8_t prefixlen) +{ + static struct in6_addr mask; + int i; + + memset(&mask, 0, sizeof(mask)); + for (i = 0; i < prefixlen / 8; i++) + mask.s6_addr[i] = 0xff; + i = prefixlen % 8; + if (i) + mask.s6_addr[prefixlen / 8] = 0xff00 >> i; + + return (&mask); +} + +void +ldp_applymask(int af, union ldpd_addr *dest, const union ldpd_addr *src, + int prefixlen) +{ + struct in6_addr mask; + int i; + + switch (af) { + case AF_INET: + dest->v4.s_addr = src->v4.s_addr & prefixlen2mask(prefixlen); + break; + case AF_INET6: + memset(&mask, 0, sizeof(mask)); + for (i = 0; i < prefixlen / 8; i++) + mask.s6_addr[i] = 0xff; + i = prefixlen % 8; + if (i) + mask.s6_addr[prefixlen / 8] = 0xff00 >> i; + + for (i = 0; i < 16; i++) + dest->v6.s6_addr[i] = src->v6.s6_addr[i] & + mask.s6_addr[i]; + break; + default: + fatalx("ldp_applymask: unknown af"); + } +} + +int +ldp_addrcmp(int af, const union ldpd_addr *a, const union ldpd_addr *b) +{ + switch (af) { + case AF_INET: + if (a->v4.s_addr == b->v4.s_addr) + return (0); + return ((ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr)) ? 1 : -1); + case AF_INET6: + return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr))); + default: + fatalx("ldp_addrcmp: unknown af"); + } +} + +int +ldp_addrisset(int af, const union ldpd_addr *addr) +{ + switch (af) { + case AF_UNSPEC: + return (0); + case AF_INET: + if (addr->v4.s_addr != INADDR_ANY) + return (1); + break; + case AF_INET6: + if (!IN6_IS_ADDR_UNSPECIFIED(&addr->v6)) + return (1); + break; + default: + fatalx("ldp_addrisset: unknown af"); + } + + return (0); +} + +int +ldp_prefixcmp(int af, const union ldpd_addr *a, const union ldpd_addr *b, + uint8_t prefixlen) +{ + in_addr_t mask, aa, ba; + int i; + uint8_t m; + + switch (af) { + case AF_INET: + if (prefixlen == 0) + return (0); + if (prefixlen > 32) + fatalx("ldp_prefixcmp: bad IPv4 prefixlen"); + mask = htonl(prefixlen2mask(prefixlen)); + aa = htonl(a->v4.s_addr) & mask; + ba = htonl(b->v4.s_addr) & mask; + return (aa - ba); + case AF_INET6: + if (prefixlen == 0) + return (0); + if (prefixlen > 128) + fatalx("ldp_prefixcmp: bad IPv6 prefixlen"); + for (i = 0; i < prefixlen / 8; i++) + if (a->v6.s6_addr[i] != b->v6.s6_addr[i]) + return (a->v6.s6_addr[i] - b->v6.s6_addr[i]); + i = prefixlen % 8; + if (i) { + m = 0xff00 >> i; + if ((a->v6.s6_addr[prefixlen / 8] & m) != + (b->v6.s6_addr[prefixlen / 8] & m)) + return ((a->v6.s6_addr[prefixlen / 8] & m) - + (b->v6.s6_addr[prefixlen / 8] & m)); + } + return (0); + default: + fatalx("ldp_prefixcmp: unknown af"); + } + return (-1); +} + +int +bad_addr_v4(struct in_addr addr) +{ + uint32_t a = ntohl(addr.s_addr); + + if (((a >> IN_CLASSA_NSHIFT) == 0) || + ((a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) || + IN_MULTICAST(a) || IN_BADCLASS(a)) + return (1); + + return (0); +} + +int +bad_addr_v6(struct in6_addr *addr) +{ + if (IN6_IS_ADDR_UNSPECIFIED(addr) || + IN6_IS_ADDR_LOOPBACK(addr) || + IN6_IS_ADDR_MULTICAST(addr) || + IN6_IS_ADDR_SITELOCAL(addr) || + IN6_IS_ADDR_V4MAPPED(addr) || + IN6_IS_ADDR_V4COMPAT(addr)) + return (1); + + return (0); +} + +int +bad_addr(int af, union ldpd_addr *addr) +{ + switch (af) { + case AF_INET: + return (bad_addr_v4(addr->v4)); + case AF_INET6: + return (bad_addr_v6(&addr->v6)); + default: + fatalx("bad_addr: unknown af"); + } +} + +void +embedscope(struct sockaddr_in6 *sin6) +{ + uint16_t tmp16; + + if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) { + memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16)); + if (tmp16 != 0) { + log_warnx("%s: address %s already has embeded scope %u", + __func__, log_sockaddr(sin6), ntohs(tmp16)); + } + tmp16 = htons(sin6->sin6_scope_id); + memcpy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16)); + sin6->sin6_scope_id = 0; + } +} + +void +recoverscope(struct sockaddr_in6 *sin6) +{ + uint16_t tmp16; + + if (sin6->sin6_scope_id != 0) + log_warnx("%s: address %s already has scope id %u", + __func__, log_sockaddr(sin6), sin6->sin6_scope_id); + + if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) { + memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16)); + sin6->sin6_scope_id = ntohs(tmp16); + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + } +} + +void +addscope(struct sockaddr_in6 *sin6, uint32_t id) +{ + if (sin6->sin6_scope_id != 0) + log_warnx("%s: address %s already has scope id %u", __func__, + log_sockaddr(sin6), sin6->sin6_scope_id); + + if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) + sin6->sin6_scope_id = id; +} + +void +clearscope(struct in6_addr *in6) +{ + if (IN6_IS_SCOPE_EMBED(in6)) { + in6->s6_addr[2] = 0; + in6->s6_addr[3] = 0; + } +} + +struct sockaddr * +addr2sa(int af, union ldpd_addr *addr, uint16_t port) +{ + static struct sockaddr_storage ss; + struct sockaddr_in *sa_in = (struct sockaddr_in *)&ss; + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)&ss; + + memset(&ss, 0, sizeof(ss)); + switch (af) { + case AF_INET: + sa_in->sin_family = AF_INET; + sa_in->sin_len = sizeof(struct sockaddr_in); + sa_in->sin_addr = addr->v4; + sa_in->sin_port = htons(port); + break; + case AF_INET6: + sa_in6->sin6_family = AF_INET6; + sa_in6->sin6_len = sizeof(struct sockaddr_in6); + sa_in6->sin6_addr = addr->v6; + sa_in6->sin6_port = htons(port); + break; + default: + fatalx("addr2sa: unknown af"); + } + + return ((struct sockaddr *)&ss); +} + +void +sa2addr(struct sockaddr *sa, int *af, union ldpd_addr *addr) +{ + struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; + + memset(addr, 0, sizeof(*addr)); + switch (sa->sa_family) { + case AF_INET: + *af = AF_INET; + addr->v4 = sa_in->sin_addr; + break; + case AF_INET6: + *af = AF_INET6; + addr->v6 = sa_in6->sin6_addr; + break; + default: + fatalx("sa2addr: unknown af"); + } +} diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c new file mode 100644 index 0000000000..61b2c095ae --- /dev/null +++ b/lib/imsg-buffer.c @@ -0,0 +1,309 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "imsg.h" + +int ibuf_realloc(struct ibuf *, size_t); +void ibuf_enqueue(struct msgbuf *, struct ibuf *); +void ibuf_dequeue(struct msgbuf *, struct ibuf *); + +struct ibuf * +ibuf_open(size_t len) +{ + struct ibuf *buf; + + if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = buf->max = len; + buf->fd = -1; + + return (buf); +} + +struct ibuf * +ibuf_dynamic(size_t len, size_t max) +{ + struct ibuf *buf; + + if (max < len) + return (NULL); + + if ((buf = ibuf_open(len)) == NULL) + return (NULL); + + if (max > 0) + buf->max = max; + + return (buf); +} + +int +ibuf_realloc(struct ibuf *buf, size_t len) +{ + u_char *b; + + /* on static buffers max is eq size and so the following fails */ + if (buf->wpos + len > buf->max) { + errno = ERANGE; + return (-1); + } + + b = realloc(buf->buf, buf->wpos + len); + if (b == NULL) + return (-1); + buf->buf = b; + buf->size = buf->wpos + len; + + return (0); +} + +int +ibuf_add(struct ibuf *buf, const void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + if (ibuf_realloc(buf, len) == -1) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +void * +ibuf_reserve(struct ibuf *buf, size_t len) +{ + void *b; + + if (buf->wpos + len > buf->size) + if (ibuf_realloc(buf, len) == -1) + return (NULL); + + b = buf->buf + buf->wpos; + buf->wpos += len; + return (b); +} + +void * +ibuf_seek(struct ibuf *buf, size_t pos, size_t len) +{ + /* only allowed to seek in already written parts */ + if (pos + len > buf->wpos) + return (NULL); + + return (buf->buf + pos); +} + +size_t +ibuf_size(struct ibuf *buf) +{ + return (buf->wpos); +} + +size_t +ibuf_left(struct ibuf *buf) +{ + return (buf->max - buf->wpos); +} + +void +ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) +{ + ibuf_enqueue(msgbuf, buf); +} + +int +ibuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct ibuf *buf; + unsigned int i = 0; + ssize_t n; + + memset(&iov, 0, sizeof(iov)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->wpos - buf->rpos; + i++; + } + +again: + if ((n = writev(msgbuf->fd, iov, i)) == -1) { + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (0); + } + + msgbuf_drain(msgbuf, n); + + return (1); +} + +void +ibuf_free(struct ibuf *buf) +{ + if (buf == NULL) + return; + free(buf->buf); + free(buf); +} + +void +msgbuf_init(struct msgbuf *msgbuf) +{ + msgbuf->queued = 0; + msgbuf->fd = -1; + TAILQ_INIT(&msgbuf->bufs); +} + +void +msgbuf_drain(struct msgbuf *msgbuf, size_t n) +{ + struct ibuf *buf, *next; + + for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; + buf = next) { + next = TAILQ_NEXT(buf, entry); + if (buf->rpos + n >= buf->wpos) { + n -= buf->wpos - buf->rpos; + ibuf_dequeue(msgbuf, buf); + } else { + buf->rpos += n; + n = 0; + } + } +} + +void +msgbuf_clear(struct msgbuf *msgbuf) +{ + struct ibuf *buf; + + while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) + ibuf_dequeue(msgbuf, buf); +} + +int +msgbuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct ibuf *buf; + unsigned int i = 0; + ssize_t n; + struct msghdr msg; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + + memset(&iov, 0, sizeof(iov)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->wpos - buf->rpos; + i++; + if (buf->fd != -1) + break; + } + + msg.msg_iov = iov; + msg.msg_iovlen = i; + + if (buf != NULL && buf->fd != -1) { + msg.msg_control = (caddr_t)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = buf->fd; + } + +again: + if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (0); + } + + /* + * assumption: fd got sent if sendmsg sent anything + * this works because fds are passed one at a time + */ + if (buf != NULL && buf->fd != -1) { + close(buf->fd); + buf->fd = -1; + } + + msgbuf_drain(msgbuf, n); + + return (1); +} + +void +ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) +{ + TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); + msgbuf->queued++; +} + +void +ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) +{ + TAILQ_REMOVE(&msgbuf->bufs, buf, entry); + + if (buf->fd != -1) + close(buf->fd); + + msgbuf->queued--; + ibuf_free(buf); +} diff --git a/lib/imsg.c b/lib/imsg.c new file mode 100644 index 0000000000..0c1cb8220c --- /dev/null +++ b/lib/imsg.c @@ -0,0 +1,302 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "imsg.h" + +int imsg_fd_overhead = 0; + +int imsg_get_fd(struct imsgbuf *); + +void +imsg_init(struct imsgbuf *ibuf, int fd) +{ + msgbuf_init(&ibuf->w); + memset(&ibuf->r, 0, sizeof(ibuf->r)); + ibuf->fd = fd; + ibuf->w.fd = fd; + ibuf->pid = getpid(); + TAILQ_INIT(&ibuf->fds); +} + +ssize_t +imsg_read(struct imsgbuf *ibuf) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int) * 1)]; + } cmsgbuf; + struct iovec iov; + ssize_t n = -1; + int fd; + struct imsg_fd *ifd; + + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + + iov.iov_base = ibuf->r.buf + ibuf->r.wpos; + iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) + return (-1); + +again: + if (getdtablecount() + imsg_fd_overhead + + (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)) + >= getdtablesize()) { + errno = EAGAIN; + free(ifd); + return (-1); + } + + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { + if (errno == EINTR) + goto again; + goto fail; + } + + ibuf->r.wpos += n; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + int i; + int j; + + /* + * We only accept one file descriptor. Due to C + * padding rules, our control buffer might contain + * more than one fd, and we must close them. + */ + j = ((char *)cmsg + cmsg->cmsg_len - + (char *)CMSG_DATA(cmsg)) / sizeof(int); + for (i = 0; i < j; i++) { + fd = ((int *)CMSG_DATA(cmsg))[i]; + if (ifd != NULL) { + ifd->fd = fd; + TAILQ_INSERT_TAIL(&ibuf->fds, ifd, + entry); + ifd = NULL; + } else + close(fd); + } + } + /* we do not handle other ctl data level */ + } + +fail: + free(ifd); + return (n); +} + +ssize_t +imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) +{ + size_t av, left, datalen; + + av = ibuf->r.wpos; + + if (IMSG_HEADER_SIZE > av) + return (0); + + memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); + if (imsg->hdr.len < IMSG_HEADER_SIZE || + imsg->hdr.len > MAX_IMSGSIZE) { + errno = ERANGE; + return (-1); + } + if (imsg->hdr.len > av) + return (0); + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; + if (datalen == 0) + imsg->data = NULL; + else if ((imsg->data = malloc(datalen)) == NULL) + return (-1); + + if (imsg->hdr.flags & IMSGF_HASFD) + imsg->fd = imsg_get_fd(ibuf); + else + imsg->fd = -1; + + memcpy(imsg->data, ibuf->r.rptr, datalen); + + if (imsg->hdr.len < av) { + left = av - imsg->hdr.len; + memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); + ibuf->r.wpos = left; + } else + ibuf->r.wpos = 0; + + return (datalen + IMSG_HEADER_SIZE); +} + +int +imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, + pid_t pid, int fd, const void *data, u_int16_t datalen) +{ + struct ibuf *wbuf; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + if (imsg_add(wbuf, data, datalen) == -1) + return (-1); + + wbuf->fd = fd; + + imsg_close(ibuf, wbuf); + + return (1); +} + +int +imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, + pid_t pid, int fd, const struct iovec *iov, int iovcnt) +{ + struct ibuf *wbuf; + int i, datalen = 0; + + for (i = 0; i < iovcnt; i++) + datalen += iov[i].iov_len; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + for (i = 0; i < iovcnt; i++) + if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) + return (-1); + + wbuf->fd = fd; + + imsg_close(ibuf, wbuf); + + return (1); +} + +/* ARGSUSED */ +struct ibuf * +imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, + pid_t pid, u_int16_t datalen) +{ + struct ibuf *wbuf; + struct imsg_hdr hdr; + + datalen += IMSG_HEADER_SIZE; + if (datalen > MAX_IMSGSIZE) { + errno = ERANGE; + return (NULL); + } + + hdr.type = type; + hdr.flags = 0; + hdr.peerid = peerid; + if ((hdr.pid = pid) == 0) + hdr.pid = ibuf->pid; + if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { + return (NULL); + } + if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) + return (NULL); + + return (wbuf); +} + +int +imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) +{ + if (datalen) + if (ibuf_add(msg, data, datalen) == -1) { + ibuf_free(msg); + return (-1); + } + return (datalen); +} + +void +imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) +{ + struct imsg_hdr *hdr; + + hdr = (struct imsg_hdr *)msg->buf; + + hdr->flags &= ~IMSGF_HASFD; + if (msg->fd != -1) + hdr->flags |= IMSGF_HASFD; + + hdr->len = (u_int16_t)msg->wpos; + + ibuf_close(&ibuf->w, msg); +} + +void +imsg_free(struct imsg *imsg) +{ + free(imsg->data); +} + +int +imsg_get_fd(struct imsgbuf *ibuf) +{ + int fd; + struct imsg_fd *ifd; + + if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) + return (-1); + + fd = ifd->fd; + TAILQ_REMOVE(&ibuf->fds, ifd, entry); + free(ifd); + + return (fd); +} + +int +imsg_flush(struct imsgbuf *ibuf) +{ + while (ibuf->w.queued) + if (msgbuf_write(&ibuf->w) <= 0) + return (-1); + return (0); +} + +void +imsg_clear(struct imsgbuf *ibuf) +{ + int fd; + + msgbuf_clear(&ibuf->w); + while ((fd = imsg_get_fd(ibuf)) != -1) + close(fd); +} diff --git a/lib/imsg.h b/lib/imsg.h new file mode 100644 index 0000000000..d053d01956 --- /dev/null +++ b/lib/imsg.h @@ -0,0 +1,112 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2006, 2007 Pierre-Yves Ritschard + * Copyright (c) 2006, 2007, 2008 Reyk Floeter + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _IMSG_H_ +#define _IMSG_H_ + +#define IBUF_READ_SIZE 65535 +#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) +#define MAX_IMSGSIZE 16384 + +struct ibuf { + TAILQ_ENTRY(ibuf) entry; + u_char *buf; + size_t size; + size_t max; + size_t wpos; + size_t rpos; + int fd; +}; + +struct msgbuf { + TAILQ_HEAD(, ibuf) bufs; + u_int32_t queued; + int fd; +}; + +struct ibuf_read { + u_char buf[IBUF_READ_SIZE]; + u_char *rptr; + size_t wpos; +}; + +struct imsg_fd { + TAILQ_ENTRY(imsg_fd) entry; + int fd; +}; + +struct imsgbuf { + TAILQ_HEAD(, imsg_fd) fds; + struct ibuf_read r; + struct msgbuf w; + int fd; + pid_t pid; +}; + +#define IMSGF_HASFD 1 + +struct imsg_hdr { + u_int32_t type; + u_int16_t len; + u_int16_t flags; + u_int32_t peerid; + u_int32_t pid; +}; + +struct imsg { + struct imsg_hdr hdr; + int fd; + void *data; +}; + + +/* buffer.c */ +struct ibuf *ibuf_open(size_t); +struct ibuf *ibuf_dynamic(size_t, size_t); +int ibuf_add(struct ibuf *, const void *, size_t); +void *ibuf_reserve(struct ibuf *, size_t); +void *ibuf_seek(struct ibuf *, size_t, size_t); +size_t ibuf_size(struct ibuf *); +size_t ibuf_left(struct ibuf *); +void ibuf_close(struct msgbuf *, struct ibuf *); +int ibuf_write(struct msgbuf *); +void ibuf_free(struct ibuf *); +void msgbuf_init(struct msgbuf *); +void msgbuf_clear(struct msgbuf *); +int msgbuf_write(struct msgbuf *); +void msgbuf_drain(struct msgbuf *, size_t); + +/* imsg.c */ +void imsg_init(struct imsgbuf *, int); +ssize_t imsg_read(struct imsgbuf *); +ssize_t imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, + int, const void *, u_int16_t); +int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, + int, const struct iovec *, int); +struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, + u_int16_t); +int imsg_add(struct ibuf *, const void *, u_int16_t); +void imsg_close(struct imsgbuf *, struct ibuf *); +void imsg_free(struct imsg *); +int imsg_flush(struct imsgbuf *); +void imsg_clear(struct imsgbuf *); + +#endif diff --git a/lib/openbsd-queue.h b/lib/openbsd-queue.h new file mode 100644 index 0000000000..5e81fdd13d --- /dev/null +++ b/lib/openbsd-queue.h @@ -0,0 +1,533 @@ +/* $OpenBSD: queue.h,v 1.43 2015/12/28 19:38:40 millert Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues and XOR simple queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * An XOR simple queue is used in the same way as a regular simple queue. + * The difference is that the head structure also includes a "cookie" that + * is XOR'd with the queue pointer (first, last or next) to generate the + * real pointer value. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + _Q_INVALIDATE((elm)->field.sle_next); \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/lib/openbsd-tree.h b/lib/openbsd-tree.h new file mode 100644 index 0000000000..e6502b1e74 --- /dev/null +++ b/lib/openbsd-tree.h @@ -0,0 +1,748 @@ +/* $OpenBSD: tree.h,v 1.14 2015/05/25 03:07:49 deraadt Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ From e30090a678e45aee2755c492212c5a478b9dcfcc Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 29 Mar 2016 11:37:01 -0300 Subject: [PATCH 020/136] ldpd: sun is a reserved word on Solaris On a SUN/Solaris system the string "sun" is a preprocessor define and can't be used for program variables. Signed-off-by: Renato Westphal --- ldpd/control.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ldpd/control.c b/ldpd/control.c index 42322eb08c..b7cb3f1063 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -44,7 +44,7 @@ static int control_fd; int control_init(void) { - struct sockaddr_un sun; + struct sockaddr_un s_un; int fd; mode_t old_umask; @@ -54,9 +54,9 @@ control_init(void) return (-1); } - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - strlcpy(sun.sun_path, LDPD_SOCKET, sizeof(sun.sun_path)); + memset(&s_un, 0, sizeof(s_un)); + s_un.sun_family = AF_UNIX; + strlcpy(s_un.sun_path, LDPD_SOCKET, sizeof(s_un.sun_path)); if (unlink(LDPD_SOCKET) == -1) if (errno != ENOENT) { @@ -66,7 +66,7 @@ control_init(void) } old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); - if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { log_warn("%s: bind: %s", __func__, LDPD_SOCKET); close(fd); umask(old_umask); @@ -111,11 +111,11 @@ control_accept(int listenfd, short event, void *bula) { int connfd; socklen_t len; - struct sockaddr_un sun; + struct sockaddr_un s_un; struct ctl_conn *c; - len = sizeof(sun); - if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len, + len = sizeof(s_un); + if ((connfd = accept4(listenfd, (struct sockaddr *)&s_un, &len, SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) { /* * Pause accept if we are out of file descriptors, or From eac6e3f027356c25a8c8fddf921f769b79945fcc Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 1 Mar 2016 15:31:28 -0300 Subject: [PATCH 021/136] ldpd: adapt the code for Quagga Signed-off-by: Renato Westphal --- Makefile.am | 4 +- configure.ac | 43 +- doc/Makefile.am | 6 +- doc/ldpd-basic-test-setup.md | 681 +++++++++++++ doc/ldpd.8 | 109 +++ ldpd/.gitignore | 18 + ldpd/Makefile.am | 26 + ldpd/accept.c | 55 +- ldpd/address.c | 13 +- ldpd/adjacency.c | 84 +- ldpd/control.c | 96 +- ldpd/control.h | 3 +- ldpd/hello.c | 35 +- ldpd/init.c | 11 +- ldpd/interface.c | 128 ++- ldpd/keepalive.c | 8 +- ldpd/l2vpn.c | 51 +- ldpd/labelmapping.c | 17 +- ldpd/lde.c | 251 ++--- ldpd/lde.h | 14 +- ldpd/lde_lib.c | 38 +- ldpd/ldp.h | 12 +- ldpd/ldp_debug.c | 198 ++++ ldpd/ldp_debug.h | 131 +++ ldpd/ldp_vty.h | 81 ++ ldpd/ldp_vty.xml | 379 ++++++++ ldpd/ldp_vty_cmds.c | 1738 ++++++++++++++++++++++++++++++++++ ldpd/ldp_vty_conf.c | 1637 ++++++++++++++++++++++++++++++++ ldpd/ldp_vty_exec.c | 667 +++++++++++++ ldpd/ldp_zebra.c | 466 +++++++++ ldpd/ldpd.c | 972 ++++++++++++++----- ldpd/ldpd.conf.sample | 46 + ldpd/ldpd.h | 134 ++- ldpd/ldpe.c | 384 +++++--- ldpd/ldpe.h | 43 +- ldpd/log.c | 162 ++-- ldpd/log.h | 12 +- ldpd/neighbor.c | 192 ++-- ldpd/notification.c | 11 +- ldpd/packet.c | 236 +++-- ldpd/pfkey.c | 3 + ldpd/socket.c | 207 +++- ldpd/util.c | 47 +- lib/Makefile.am | 5 +- lib/command.c | 35 + lib/command.h | 7 + lib/imsg-buffer.c | 14 +- lib/imsg.c | 50 +- lib/log.c | 3 +- lib/log.h | 2 + lib/mpls.h | 9 + lib/privs.c | 9 +- lib/route_types.txt | 2 + lib/vty.c | 40 + lib/vty.h | 11 + tools/xml2cli.pl | 436 +++++++++ zebra/zserv.c | 6 +- 57 files changed, 8870 insertions(+), 1208 deletions(-) create mode 100644 doc/ldpd-basic-test-setup.md create mode 100644 doc/ldpd.8 create mode 100644 ldpd/.gitignore create mode 100644 ldpd/Makefile.am create mode 100644 ldpd/ldp_debug.c create mode 100644 ldpd/ldp_debug.h create mode 100644 ldpd/ldp_vty.h create mode 100644 ldpd/ldp_vty.xml create mode 100644 ldpd/ldp_vty_cmds.c create mode 100644 ldpd/ldp_vty_conf.c create mode 100644 ldpd/ldp_vty_exec.c create mode 100644 ldpd/ldp_zebra.c create mode 100644 ldpd/ldpd.conf.sample create mode 100755 tools/xml2cli.pl diff --git a/Makefile.am b/Makefile.am index 1a39844cb1..a3cbdc919c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,10 @@ ## Process this file with automake to produce Makefile.in. -SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ \ +SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ redhat @SOLARIS@ tests tools cumulus -DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d \ +DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d ldpd \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ solaris pimd tools cumulus diff --git a/configure.ac b/configure.ac index 937d7b8d0f..eb1f1de6c1 100755 --- a/configure.ac +++ b/configure.ac @@ -246,6 +246,8 @@ AC_ARG_ENABLE(ospfd, AS_HELP_STRING([--disable-ospfd], [do not build ospfd])) AC_ARG_ENABLE(ospf6d, AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d])) +AC_ARG_ENABLE(ldpd, + AS_HELP_STRING([--enable-ldpd], [build ldpd])) AC_ARG_ENABLE(watchquagga, AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga])) AC_ARG_ENABLE(isisd, @@ -838,7 +840,7 @@ AC_CHECK_FUNCS([dup2 ftruncate getcwd gethostbyname getpagesize gettimeofday \ strtol strtoul strlcat strlcpy \ daemon snprintf vsnprintf \ if_nametoindex if_indextoname getifaddrs \ - uname fcntl getgrouplist]) + uname fcntl getgrouplist pledge]) AC_CHECK_HEADER([asm-generic/unistd.h], [AC_CHECK_DECL(__NR_setns, @@ -1231,6 +1233,13 @@ else fi AM_CONDITIONAL(OSPFD, test "x$OSPFD" = "xospfd") +if test "${enable_ldpd}" = "yes";then + LDPD="ldpd" +else + LDPD="" +fi +AM_CONDITIONAL(LDPD, test "x$LDPD" = "xldpd") + if test "${enable_watchquagga}" = "no";then WATCHQUAGGA="" else @@ -1286,6 +1295,7 @@ AC_SUBST(RIPD) AC_SUBST(RIPNGD) AC_SUBST(OSPFD) AC_SUBST(OSPF6D) +AC_SUBST(LDPD) AC_SUBST(WATCHQUAGGA) AC_SUBST(ISISD) AC_SUBST(PIMD) @@ -1432,6 +1442,32 @@ AC_TRY_COMPILE([#include ], [ AC_MSG_RESULT(no) ]) +dnl ---------------------- +dnl checking for SO_BINDANY +dnl ---------------------- +AC_MSG_CHECKING(for SO_BINDANY) +AC_TRY_COMPILE([#include ], [ + int opt = SO_BINDANY; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SO_BINDANY, 1, [Have SO_BINDANY]) +], [ + AC_MSG_RESULT(no) +]) + +dnl ---------------------- +dnl checking for IP_FREEBIND +dnl ---------------------- +AC_MSG_CHECKING(for IP_FREEBIND) +AC_TRY_COMPILE([#include ], [ + int opt = IP_FREEBIND; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_FREEBIND, 1, [Have IP_FREEBIND]) +], [ + AC_MSG_RESULT(no) +]) + dnl -------------------------------------- dnl checking for getrusage struct and call dnl -------------------------------------- @@ -1580,6 +1616,8 @@ AC_DEFINE_UNQUOTED(PATH_RIPNGD_PID, "$quagga_statedir/ripngd.pid",ripngd PID) AC_DEFINE_UNQUOTED(PATH_BGPD_PID, "$quagga_statedir/bgpd.pid",bgpd PID) AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$quagga_statedir/ospfd.pid",ospfd PID) AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$quagga_statedir/ospf6d.pid",ospf6d PID) +AC_DEFINE_UNQUOTED(PATH_LDPD_PID, "$quagga_statedir/ldpd.pid",ldpd PID) +AC_DEFINE_UNQUOTED(LDPD_SOCKET, "$quagga_statedir/ldpd.sock",ldpd control socket) AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$quagga_statedir/isisd.pid",isisd PID) AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$quagga_statedir/pimd.pid",pimd PID) AC_DEFINE_UNQUOTED(PATH_WATCHQUAGGA_PID, "$quagga_statedir/watchquagga.pid",watchquagga PID) @@ -1590,6 +1628,7 @@ AC_DEFINE_UNQUOTED(RIPNG_VTYSH_PATH, "$quagga_statedir/ripngd.vty",ripng vty soc AC_DEFINE_UNQUOTED(BGP_VTYSH_PATH, "$quagga_statedir/bgpd.vty",bgpd vty socket) AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socket) AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket) +AC_DEFINE_UNQUOTED(LDP_VTYSH_PATH, "$quagga_statedir/ldpd.vty",ldpd vty socket) AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socket) AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket) AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory) @@ -1615,7 +1654,7 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile - ospf6d/Makefile isisd/Makefile vtysh/Makefile + ospf6d/Makefile ldpd/Makefile isisd/Makefile vtysh/Makefile doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile pimd/Makefile tests/bgpd.tests/Makefile diff --git a/doc/Makefile.am b/doc/Makefile.am index 4a39f0b011..ffb0bf4651 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -87,6 +87,10 @@ if OSPFD man_MANS += ospfd.8 endif +if LDPD +man_MANS += ldpd.8 +endif + if RIPD man_MANS += ripd.8 endif @@ -108,7 +112,7 @@ man_MANS += zebra.8 endif EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \ - bgpd.8 isisd.8 ospf6d.8 ospfclient.8 ospfd.8 ripd.8 \ + bgpd.8 isisd.8 ospf6d.8 ospfclient.8 ospfd.8 ldpd.8 ripd.8 \ ripngd.8 pimd.8 vtysh.1 watchquagga.8 zebra.8 quagga.1 \ mpls/ChangeLog.opaque.txt mpls/cli_summary.txt \ mpls/opaque_lsa.txt mpls/ospfd.conf \ diff --git a/doc/ldpd-basic-test-setup.md b/doc/ldpd-basic-test-setup.md new file mode 100644 index 0000000000..e5e987f9e3 --- /dev/null +++ b/doc/ldpd-basic-test-setup.md @@ -0,0 +1,681 @@ +## Topology + +The goal of this test is to verify that the all the basic functionality +of ldpd is working as expected, be it running on Linux or OpenBSD. In +addition to that, more advanced features are also tested, like LDP +sessions over IPv6, MD5 authentication and pseudowire signaling. + +In the topology below there are 3 PE routers, 3 CE routers and one P +router (not attached to any consumer site). + +All routers have IPv4 addresses and OSPF is used as the IGP. The +three routers from the bottom of the picture, P, PE2 and PE3, are also +configured for IPv6 (dual-stack) and static IPv6 routes are used to +provide connectivity among them. + +The three CEs share the same VPLS membership. LDP is used to set up the +LSPs among the PEs and to signal the pseudowires. MD5 authentication is +used to protect all LDP sessions. + +``` + CE1 172.16.1.1/24 + + + | + +---+---+ + | PE1 | + | IOS XE| + | | + +---+---+ + | + | 10.0.1.0/24 + | + +---+---+ + | P | + +------+ IOS XR+------+ + | | | | + | +-------+ | + 10.0.2.0/24 | | 10.0.3.0/24 +2001:db8:2::/64 | | 2001:db8:3::/64 + | | + +---+---+ +---+---+ + | PE2 | | PE3 | + |OpenBSD+-------------+ Linux | + | | | | + +---+---+ 10.0.4.0/24 +---+---+ + | 2001:db8:4::/64 | + + + + 172.16.1.2/24 CE2 CE3 172.16.1.3/24 +``` + +## Configuration + +#### Linux +1 - Enable IPv4/v6 forwarding: +``` +# sysctl -w net.ipv4.ip_forward=1 +# sysctl -w net.ipv6.conf.all.forwarding=1 +``` + +2 - Enable MPLS forwarding: +``` +# modprobe mpls-router +# modprobe mpls-iptunnel +# echo 100000 > /proc/sys/net/mpls/platform_labels +# echo 1 > /proc/sys/net/mpls/conf/eth1/input +# echo 1 > /proc/sys/net/mpls/conf/eth2/input +``` + +3 - Set up the interfaces: +``` +# ip link add name lo1 type dummy +# ip link set dev lo1 up +# ip addr add 4.4.4.4/32 dev lo1 +# ip -6 addr add 4:4:4::4/128 dev lo1 +# ip link set dev eth1 up +# ip addr add 10.0.4.4/24 dev eth1 +# ip -6 addr add 2001:db8:4::4/64 dev eth1 +# ip link set dev eth2 up +# ip addr add 10.0.3.4/24 dev eth2 +# ip -6 addr add 2001:db8:3::4/64 dev eth2 +``` + +4 - Set up the bridge and pseudowire interfaces: +``` +# ip link add type bridge +# ip link set dev bridge0 up +# ip link set dev eth0 up +# ip link set dev eth0 master bridge0 +# ip link add name mpw0 type dummy +# ip link set dev mpw0 up +# ip link set dev mpw0 master bridge0 +# ip link add name mpw1 type dummy +# ip link set dev mpw1 up +# ip link set dev mpw1 master bridge0 +``` + +> NOTE: MPLS support in the Linux kernel is very recent and it still +doesn't support pseudowire interfaces. We are using here dummy interfaces +just to show how the VPLS configuration should look like in the future. + +5 - Add static IPv6 routes for the remote loopbacks: +``` +# ip -6 route add 2:2:2::2/128 via 2001:db8:3::2 +# ip -6 route add 3:3:3::3/128 via 2001:db8:4::3 +``` + +6 - Edit /etc/quagga/ospfd.conf: +``` +router ospf + network 4.4.4.4/32 area 0.0.0.0 + network 10.0.3.4/24 area 0.0.0.0 + network 10.0.4.4/24 area 0.0.0.0 +! +``` + +7 - Edit /etc/quagga/ldpd.conf: +``` +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 4.4.4.4 + dual-stack cisco-interop + neighbor 1.1.1.1 password opensourcerouting + neighbor 2.2.2.2 password opensourcerouting + neighbor 3.3.3.3 password opensourcerouting + ! + address-family ipv4 + discovery transport-address 4.4.4.4 + label local advertise explicit-null + ! + interface eth2 + ! + interface eth1 + ! + ! + address-family ipv6 + discovery transport-address 4:4:4::4 + ttl-security disable + ! + interface eth2 + ! + interface eth1 + ! + ! +! +l2vpn ENG type vpls + bridge br0 + member interface eth0 + ! + member pseudowire mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! + member pseudowire mpw1 + neighbor lsr-id 3.3.3.3 + neighbor address 3:3:3::3 + pw-id 100 + ! +! +``` + +> NOTE: We have to disable ttl-security under the ipv6 address-family +in order to interoperate with the IOS-XR router. GTSM is mandatory for +LDPv6 but the IOS-XR implementation is not RFC compliant in this regard. + +8 - Run zebra, ospfd and ldpd. + +#### OpenBSD +1 - Enable IPv4/v6 forwarding: +``` +# sysctl net.inet.ip.forwarding=1 +# sysctl net.inet6.ip6.forwarding=1 +``` + +2 - Enable MPLS forwarding: +``` +# ifconfig em2 10.0.2.3/24 mpls +# ifconfig em3 10.0.4.3/24 mpls +``` + +3 - Set up the interfaces: +``` +# ifconfig lo1 alias 3.3.3.3 netmask 255.255.255.255 +# ifconfig lo1 inet6 3:3:3::3/128 +# ifconfig em2 inet6 2001:db8:2::3/64 +# ifconfig em3 inet6 2001:db8:4::3/64 +``` + +4 - Set up the bridge and pseudowire interfaces: +``` +# ifconfig bridge0 create +# ifconfig bridge0 up +# ifconfig em1 up +# ifconfig bridge0 add em1 +# ifconfig mpw0 create +# ifconfig mpw0 up +# ifconfig bridge0 add mpw0 +# ifconfig mpw1 create +# ifconfig mpw1 up +# ifconfig bridge0 add mpw1 +``` + +5 - Add static IPv6 routes for the remote loopbacks: +``` +# route -n add 4:4:4::4/128 2001:db8:4::4 +# route -n add 2:2:2::2/128 2001:db8:2::2 +``` + +6 - Edit /etc/quagga/ospfd.conf: +``` +router ospf + network 10.0.2.3/24 area 0 + network 10.0.4.3/24 area 0 + network 3.3.3.3/32 area 0 +! +``` + +7 - Edit /etc/quagga/ldpd.conf: +``` +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp zebra +! +mpls ldp + router-id 3.3.3.3 + dual-stack cisco-interop + neighbor 1.1.1.1 password opensourcerouting + neighbor 2.2.2.2 password opensourcerouting + neighbor 4.4.4.4 password opensourcerouting + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + label local advertise explicit-null + ! + interface em3 + ! + interface em2 + ! + ! + address-family ipv6 + discovery transport-address 3:3:3::3 + ttl-security disable + ! + interface em3 + ! + interface em2 + ! + ! +! +l2vpn ENG type vpls + bridge br0 + member interface em1 + ! + member pseudowire mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! + member pseudowire mpw1 + neighbor lsr-id 4.4.4.4 + neighbor address 4:4:4::4 + pw-id 100 + ! +! +``` + +8 - Run zebra, ospfd and ldpd. + +#### Cisco routers +CE1 (IOS): +``` +interface FastEthernet0/0 + ip address 172.16.1.1 255.255.255.0 + ! +! +``` + +CE2 (IOS): +``` +interface FastEthernet0/0 + ip address 172.16.1.2 255.255.255.0 + ! +! +``` + +CE3 (IOS): +``` +interface FastEthernet0/0 + ip address 172.16.1.3 255.255.255.0 + ! +! +``` + +PE1 - IOS-XE (1): +``` +mpls ldp neighbor 2.2.2.2 password opensourcerouting +mpls ldp neighbor 3.3.3.3 password opensourcerouting +mpls ldp neighbor 4.4.4.4 password opensourcerouting +! +l2vpn vfi context VFI + vpn id 1 + member pseudowire2 + member pseudowire1 +! +bridge-domain 1 + member GigabitEthernet1 service-instance 1 + member vfi VFI +! +interface Loopback1 + ip address 1.1.1.1 255.255.255.255 +! +interface pseudowire1 + encapsulation mpls + neighbor 3.3.3.3 100 +! +interface pseudowire2 + encapsulation mpls + neighbor 4.4.4.4 100 +! +interface GigabitEthernet3 + ip address 10.0.1.1 255.255.255.0 + mpls ip +! +router ospf 1 + network 0.0.0.0 255.255.255.255 area 0 +! +``` + +P - IOS-XR (2): +``` +interface Loopback1 + ipv4 address 2.2.2.2 255.255.255.255 + ipv6 address 2:2:2::2/128 +! +interface GigabitEthernet0/0/0/0 + ipv4 address 10.0.1.2 255.255.255.0 +! +interface GigabitEthernet0/0/0/1 + ipv4 address 10.0.2.2 255.255.255.0 + ipv6 address 2001:db8:2::2/64 + ipv6 enable +! +interface GigabitEthernet0/0/0/2 + ipv4 address 10.0.3.2 255.255.255.0 + ipv6 address 2001:db8:3::2/64 + ipv6 enable +! +router static + address-family ipv6 unicast + 3:3:3::3/128 2001:db8:2::3 + 4:4:4::4/128 2001:db8:3::4 + ! +! +router ospf 1 + router-id 2.2.2.2 + address-family ipv4 unicast + area 0 + interface Loopback1 + ! + interface GigabitEthernet0/0/0/0 + ! + interface GigabitEthernet0/0/0/1 + ! + interface GigabitEthernet0/0/0/2 + ! + ! +! +mpls ldp + router-id 2.2.2.2 + neighbor + 1.1.1.1:0 password clear opensourcerouting + 3.3.3.3:0 password clear opensourcerouting + 4.4.4.4:0 password clear opensourcerouting + ! + address-family ipv4 + ! + address-family ipv6 + discovery transport-address 2:2:2::2 + ! + interface GigabitEthernet0/0/0/0 + address-family ipv4 + ! + ! + interface GigabitEthernet0/0/0/1 + address-family ipv4 + ! + address-family ipv6 + ! + ! + interface GigabitEthernet0/0/0/2 + address-family ipv4 + ! + address-family ipv6 + ! + ! +! +``` + +## Verification - Control Plane + +Using the CLI on the Linux box, the goal is to ensure that everything +is working as expected. + +First, verify that all the required adjacencies and neighborships sessions +were established: + +``` +linux# show mpls ldp discovery +Local LDP Identifier: 4.4.4.4:0 +Discovery Sources: + Interfaces: + eth1: xmit/recv + LDP Id: 3.3.3.3:0, Transport address: 3.3.3.3 + Hold time: 15 sec + LDP Id: 3.3.3.3:0, Transport address: 3:3:3::3 + Hold time: 15 sec + eth2: xmit/recv + LDP Id: 2.2.2.2:0, Transport address: 2.2.2.2 + Hold time: 15 sec + LDP Id: 2.2.2.2:0, Transport address: 2:2:2::2 + Hold time: 15 sec + Targeted Hellos: + 4.4.4.4 -> 1.1.1.1: xmit/recv + LDP Id: 1.1.1.1:0, Transport address: 1.1.1.1 + Hold time: 45 sec + 4:4:4::4 -> 3:3:3::3: xmit/recv + LDP Id: 3.3.3.3:0, Transport address: 3:3:3::3 + Hold time: 45 sec + +linux# show mpls ldp neighbor +Peer LDP Identifier: 1.1.1.1:0 + TCP connection: 4.4.4.4:40921 - 1.1.1.1:646 + Session Holdtime: 180 sec + State: OPERATIONAL; Downstream-Unsolicited + Up time: 00:06:02 + LDP Discovery Sources: + IPv4: + Targeted Hello: 1.1.1.1 + +Peer LDP Identifier: 2.2.2.2:0 + TCP connection: 4:4:4::4:52286 - 2:2:2::2:646 + Session Holdtime: 180 sec + State: OPERATIONAL; Downstream-Unsolicited + Up time: 00:06:02 + LDP Discovery Sources: + IPv4: + Interface: eth2 + IPv6: + Interface: eth2 + +Peer LDP Identifier: 3.3.3.3:0 + TCP connection: 4:4:4::4:60575 - 3:3:3::3:646 + Session Holdtime: 180 sec + State: OPERATIONAL; Downstream-Unsolicited + Up time: 00:05:57 + LDP Discovery Sources: + IPv4: + Interface: eth1 + IPv6: + Targeted Hello: 3:3:3::3 + Interface: eth1 +``` + +Note that the neighborships with the P and PE2 routers were established +over IPv6, since this is the default behavior for dual-stack LSRs, as +specified in RFC 7552. If desired, the **dual-stack transport-connection +prefer ipv4** command can be used to establish these sessions over IPv4 +(the command should be applied an all routers). + +Now, verify that there's a remote label for each PE address: +``` +linux# show mpls ldp binding +1.1.1.1/32 + Local binding: label: 20 + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 imp-null + 2.2.2.2 24000 + 3.3.3.3 20 +2.2.2.2/32 + Local binding: label: 21 + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 18 + 2.2.2.2 imp-null + 3.3.3.3 21 +3.3.3.3/32 + Local binding: label: 22 + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 21 + 2.2.2.2 24003 + 3.3.3.3 imp-null +4.4.4.4/32 + Local binding: label: imp-null + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 22 + 2.2.2.2 24001 + 3.3.3.3 22 +10.0.1.0/24 + Local binding: label: 23 + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 imp-null + 2.2.2.2 imp-null + 3.3.3.3 23 +10.0.2.0/24 + Local binding: label: 24 + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 20 + 2.2.2.2 imp-null + 3.3.3.3 imp-null +10.0.3.0/24 + Local binding: label: imp-null + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 19 + 2.2.2.2 imp-null + 3.3.3.3 24 +10.0.4.0/24 + Local binding: label: imp-null + Remote bindings: + Peer Label + ----------------- --------- + 1.1.1.1 23 + 2.2.2.2 24002 + 3.3.3.3 imp-null +2:2:2::2/128 + Local binding: label: 18 + Remote bindings: + Peer Label + ----------------- --------- + 2.2.2.2 imp-null + 3.3.3.3 18 +3:3:3::3/128 + Local binding: label: 19 + Remote bindings: + Peer Label + ----------------- --------- + 2.2.2.2 24007 +4:4:4::4/128 + Local binding: label: imp-null + Remote bindings: + Peer Label + ----------------- --------- + 2.2.2.2 24006 + 3.3.3.3 19 +2001:db8:2::/64 + Local binding: label: - + Remote bindings: + Peer Label + ----------------- --------- + 2.2.2.2 imp-null + 3.3.3.3 imp-null +2001:db8:3::/64 + Local binding: label: imp-null + Remote bindings: + Peer Label + ----------------- --------- + 2.2.2.2 imp-null +2001:db8:4::/64 + Local binding: label: imp-null + Remote bindings: + Peer Label + ----------------- --------- + 3.3.3.3 imp-null +``` + +Check if the pseudowires are up: +``` +linux# show l2vpn atom vc +Interface Peer ID VC ID Name Status +--------- --------------- ---------- ---------------- ---------- +mpw1 3.3.3.3 100 ENG UP +mpw0 1.1.1.1 100 ENG UP +``` + +Check the label bindings of the pseudowires: +``` +linux# show l2vpn atom binding + Destination Address: 1.1.1.1, VC ID: 100 + Local Label: 25 + Cbit: 1, VC Type: Ethernet, GroupID: 0 + MTU: 1500 + Remote Label: 16 + Cbit: 1, VC Type: Ethernet, GroupID: 0 + MTU: 1500 + Destination Address: 3.3.3.3, VC ID: 100 + Local Label: 26 + Cbit: 1, VC Type: Ethernet, GroupID: 0 + MTU: 1500 + Remote Label: 26 + Cbit: 1, VC Type: Ethernet, GroupID: 0 + MTU: 1500 +``` + +## Verification - Data Plane + +Verify that all the exchanged label mappings were installed in zebra: +``` +linux# show mpls table + Inbound Outbound + Label Type Nexthop Label +-------- ------- --------------- -------- + 17 LDP 2001:db8:3::2 3 + 19 LDP 2001:db8:3::2 24005 + 20 LDP 10.0.3.2 24000 + 21 LDP 10.0.3.2 3 + 22 LDP 10.0.3.2 24001 + 23 LDP 10.0.3.2 3 + 24 LDP 10.0.3.2 3 + 25 LDP 10.0.3.2 3 + +linux# show ip route ldp +Codes: K - kernel route, C - connected, S - static, R - RIP, + O - OSPF, I - IS-IS, B - BGP, P - PIM, A - Babel, L - LDP, + > - selected route, * - FIB route + +L>* 1.1.1.1/32 [0/0] via 10.0.3.2, eth2 label 24000 +L>* 3.3.3.3/32 [0/0] via 10.0.3.2, eth2 label 24001 +``` + +Verify that all the exchanged label mappings were installed in the kernel: +``` +$ ip -M ro +17 via inet6 2001:db8:3::2 dev eth2 proto zebra +19 as to 24005 via inet6 2001:db8:3::2 dev eth2 proto zebra +20 as to 24000 via inet 10.0.3.2 dev eth2 proto zebra +21 via inet 10.0.3.2 dev eth2 proto zebra +22 as to 24001 via inet 10.0.3.2 dev eth2 proto zebra +23 via inet 10.0.3.2 dev eth2 proto zebra +24 via inet 10.0.3.2 dev eth2 proto zebra +25 via inet 10.0.3.2 dev eth2 proto zebra +$ +$ ip route | grep mpls +1.1.1.1 encap mpls 24000 via 10.0.3.2 dev eth2 proto zebra metric 20 +3.3.3.3 encap mpls 24001 via 10.0.3.2 dev eth2 proto zebra metric 20 +``` + +Now ping PE1's loopback using lo1's address as a source address: +``` +$ ping -c 5 -I 4.4.4.4 1.1.1.1 +PING 1.1.1.1 (1.1.1.1) from 4.4.4.4 : 56(84) bytes of data. +64 bytes from 1.1.1.1: icmp_seq=1 ttl=253 time=3.02 ms +64 bytes from 1.1.1.1: icmp_seq=2 ttl=253 time=3.13 ms +64 bytes from 1.1.1.1: icmp_seq=3 ttl=253 time=3.19 ms +64 bytes from 1.1.1.1: icmp_seq=4 ttl=253 time=3.07 ms +64 bytes from 1.1.1.1: icmp_seq=5 ttl=253 time=3.27 ms + +--- 1.1.1.1 ping statistics --- +5 packets transmitted, 5 received, 0% packet loss, time 4005ms +rtt min/avg/max/mdev = 3.022/3.140/3.278/0.096 ms +``` + +Verify that the ICMP echo request packets are leaving with the MPLS +label advertised by the P router. Also, verify that the ICMP echo reply +packets are arriving with an explicit-null MPLS label: +``` +# tcpdump -n -i eth2 mpls and icmp +tcpdump: verbose output suppressed, use -v or -vv for full protocol decode +listening on eth2, link-type EN10MB (Ethernet), capture size 262144 bytes +10:01:40.758771 MPLS (label 24000, exp 0, [S], ttl 64) IP 4.4.4.4 > 1.1.1.1: ICMP echo request, id 13370, seq 1, length 64 +10:01:40.761777 MPLS (label 0, exp 0, [S], ttl 254) IP 1.1.1.1 > 4.4.4.4: ICMP echo reply, id 13370, seq 1, length 64 +10:01:41.760343 MPLS (label 24000, exp 0, [S], ttl 64) IP 4.4.4.4 > 1.1.1.1: ICMP echo request, id 13370, seq 2, length 64 +10:01:41.763448 MPLS (label 0, exp 0, [S], ttl 254) IP 1.1.1.1 > 4.4.4.4: ICMP echo reply, id 13370, seq 2, length 64 +10:01:42.761758 MPLS (label 24000, exp 0, [S], ttl 64) IP 4.4.4.4 > 1.1.1.1: ICMP echo request, id 13370, seq 3, length 64 +10:01:42.764924 MPLS (label 0, exp 0, [S], ttl 254) IP 1.1.1.1 > 4.4.4.4: ICMP echo reply, id 13370, seq 3, length 64 +10:01:43.763193 MPLS (label 24000, exp 0, [S], ttl 64) IP 4.4.4.4 > 1.1.1.1: ICMP echo request, id 13370, seq 4, length 64 +10:01:43.766237 MPLS (label 0, exp 0, [S], ttl 254) IP 1.1.1.1 > 4.4.4.4: ICMP echo reply, id 13370, seq 4, length 64 +10:01:44.764552 MPLS (label 24000, exp 0, [S], ttl 64) IP 4.4.4.4 > 1.1.1.1: ICMP echo request, id 13370, seq 5, length 64 +10:01:44.767803 MPLS (label 0, exp 0, [S], ttl 254) IP 1.1.1.1 > 4.4.4.4: ICMP echo reply, id 13370, seq 5, length 64 +``` diff --git a/doc/ldpd.8 b/doc/ldpd.8 new file mode 100644 index 0000000000..092ff39d49 --- /dev/null +++ b/doc/ldpd.8 @@ -0,0 +1,109 @@ +.TH LDPD 8 "29 March 2016" "Quagga LDP daemon" "Version 1.0.20160309" +.SH NAME +ldpd \- an LDP engine for use with Quagga routing software. +.SH SYNOPSIS +.B ldpd +[ +.B \-dhv +] [ +.B \-f +.I config-file +] [ +.B \-i +.I pid-file +] [ +.B \-P +.I port-number +] [ +.B \-A +.I vty-address +] [ +.B \-u +.I user +] [ +.B \-g +.I group +] +.SH DESCRIPTION +.B ldpd +is a component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B ldpd +command: +.TP +\fB\-d\fR, \fB\-\-daemon\fR +Runs in daemon mode, forking and exiting from tty. +.TP +\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR +Specifies the config file to use for startup. If not specified this +option will likely default to \fB\fI/usr/local/etc/ldpd.conf\fR. +.TP +\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR +Specify the group to run as. Default is \fIquagga\fR. +.TP +\fB\-h\fR, \fB\-\-help\fR +A brief message. +.TP +\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR +When ldpd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart ldpd. The likely default is \fB\fI/var/run/ldpd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the ldpd VTY will listen on. This defaults to +2612, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the ldpd VTY will listen on. Default is all +interfaces. +.TP +\fB\-u\fR, \fB\-\-user \fR\fIuser\fR +Specify the user to run as. Default is \fIquagga\fR. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.SH FILES +.TP +.BI /usr/local/sbin/ldpd +The default location of the +.B ldpd +binary. +.TP +.BI /usr/local/etc/ldpd.conf +The default location of the +.B ldpd +config file. +.TP +.BI $(PWD)/ldpd.log +If the +.B ldpd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBldpd\fR. +.SH WARNING +This man page is intended to be a quick reference for command line +options. The definitive document is the Info file \fBQuagga\fR. +.SH DIAGNOSTICS +The ldpd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBldpd\fR supports many +debugging options, see the Info file, or the source for details. +.SH "SEE ALSO" +.BR bgpd (8), +.BR ripd (8), +.BR ripngd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR isisd (8), +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +.B ldpd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/ldpd/.gitignore b/ldpd/.gitignore new file mode 100644 index 0000000000..be90d42119 --- /dev/null +++ b/ldpd/.gitignore @@ -0,0 +1,18 @@ +Makefile +Makefile.in +*.o +ldpd +ldpd.conf +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.a +*.libs +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/ldpd/Makefile.am b/ldpd/Makefile.am new file mode 100644 index 0000000000..c292adf6fc --- /dev/null +++ b/ldpd/Makefile.am @@ -0,0 +1,26 @@ +## Process this file with automake to produce Makefile.in. + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +INSTALL_SDATA=@INSTALL@ -m 600 + +AM_CFLAGS = $(WERROR) + +noinst_LIBRARIES = libldp.a +sbin_PROGRAMS = ldpd + +libldp_a_SOURCES = \ + accept.c address.c adjacency.c control.c hello.c init.c interface.c \ + keepalive.c l2vpn.c labelmapping.c lde.c lde_lib.c ldpd.c \ + ldpe.c log.c neighbor.c notification.c packet.c pfkey.c \ + socket.c util.c ldp_vty_cmds.c ldp_vty_conf.c ldp_vty_exec.c \ + ldp_debug.c ldp_zebra.c + +noinst_HEADERS = \ + control.h lde.h ldpd.h ldpe.h ldp.h log.h ldp_debug.h ldp_vty.h + +ldpd_SOURCES = ldpd.c +ldpd_LDADD = libldp.a ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = ldpd.conf.sample diff --git a/ldpd/accept.c b/ldpd/accept.c index bc13ad49e7..4cb461b908 100644 --- a/ldpd/accept.c +++ b/ldpd/accept.c @@ -16,8 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include +#include #include "ldpd.h" #include "ldpe.h" @@ -25,31 +24,30 @@ struct accept_ev { LIST_ENTRY(accept_ev) entry; - struct event ev; - void (*accept_cb)(int, short, void *); + struct thread *ev; + int (*accept_cb)(struct thread *); void *arg; int fd; }; struct { - LIST_HEAD(, accept_ev) queue; - struct event evt; + LIST_HEAD(, accept_ev) queue; + struct thread *evt; } accept_queue; static void accept_arm(void); static void accept_unarm(void); -static void accept_cb(int, short, void *); -static void accept_timeout(int, short, void *); +static int accept_cb(struct thread *); +static int accept_timeout(struct thread *); void accept_init(void) { LIST_INIT(&accept_queue.queue); - evtimer_set(&accept_queue.evt, accept_timeout, NULL); } int -accept_add(int fd, void (*cb)(int, short, void *), void *arg) +accept_add(int fd, int (*cb)(struct thread *), void *arg) { struct accept_ev *av; @@ -60,8 +58,7 @@ accept_add(int fd, void (*cb)(int, short, void *), void *arg) av->arg = arg; LIST_INSERT_HEAD(&accept_queue.queue, av, entry); - event_set(&av->ev, av->fd, EV_READ, accept_cb, av); - event_add(&av->ev, NULL); + av->ev = thread_add_read(master, accept_cb, av, av->fd); log_debug("%s: accepting on fd %d", __func__, fd); @@ -76,7 +73,7 @@ accept_del(int fd) LIST_FOREACH(av, &accept_queue.queue, entry) if (av->fd == fd) { log_debug("%s: %d removed from queue", __func__, fd); - event_del(&av->ev); + THREAD_READ_OFF(av->ev); LIST_REMOVE(av, entry); free(av); return; @@ -86,19 +83,17 @@ accept_del(int fd) void accept_pause(void) { - struct timeval evtpause = { 1, 0 }; - log_debug(__func__); accept_unarm(); - evtimer_add(&accept_queue.evt, &evtpause); + accept_queue.evt = thread_add_timer(master, accept_timeout, NULL, 1); } void accept_unpause(void) { - if (evtimer_pending(&accept_queue.evt, NULL)) { + if (accept_queue.evt != NULL) { log_debug(__func__); - evtimer_del(&accept_queue.evt); + THREAD_TIMER_OFF(accept_queue.evt); accept_arm(); } } @@ -108,7 +103,7 @@ accept_arm(void) { struct accept_ev *av; LIST_FOREACH(av, &accept_queue.queue, entry) - event_add(&av->ev, NULL); + av->ev = thread_add_read(master, accept_cb, av, av->fd); } static void @@ -116,20 +111,26 @@ accept_unarm(void) { struct accept_ev *av; LIST_FOREACH(av, &accept_queue.queue, entry) - event_del(&av->ev); + THREAD_READ_OFF(av->ev); } -static void -accept_cb(int fd, short event, void *arg) +static int +accept_cb(struct thread *thread) { - struct accept_ev *av = arg; - event_add(&av->ev, NULL); - av->accept_cb(fd, event, av->arg); + struct accept_ev *av = THREAD_ARG(thread); + av->ev = thread_add_read(master, accept_cb, av, av->fd); + av->accept_cb(thread); + + return (0); } -static void -accept_timeout(int fd, short event, void *bula) +static int +accept_timeout(struct thread *thread) { + accept_queue.evt = NULL; + log_debug(__func__); accept_arm(); + + return (0); } diff --git a/ldpd/address.c b/ldpd/address.c index 5e95fcc271..1c4c116f21 100644 --- a/ldpd/address.c +++ b/ldpd/address.c @@ -16,15 +16,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "lde.h" #include "log.h" +#include "ldp_debug.h" static void send_address(struct nbr *, int, struct if_addr_head *, unsigned int, int); @@ -94,7 +92,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, } while ((if_addr = LIST_FIRST(addr_list)) != NULL) { - log_debug("msg-out: %s: lsr-id %s, address %s", + debug_msg_send("%s: lsr-id %s address %s", msg_name(msg_type), inet_ntoa(nbr->id), log_addr(af, &if_addr->addr)); @@ -225,9 +223,8 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len) fatalx("recv_address: unknown af"); } - log_debug("msg-in: %s: lsr-id %s, address %s", - msg_name(msg_type), inet_ntoa(nbr->id), - log_addr(lde_addr.af, &lde_addr.addr)); + 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)); ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr, sizeof(lde_addr)); diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c index 266717729f..3607ee96b3 100644 --- a/ldpd/adjacency.c +++ b/ldpd/adjacency.c @@ -19,19 +19,15 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" -static void adj_del_single(struct adj *); -static void adj_itimer(int, short, void *); +static int adj_itimer(struct thread *); static void tnbr_del(struct tnbr *); -static void tnbr_hello_timer(int, short, void *); +static int tnbr_hello_timer(struct thread *); static void tnbr_start_hello_timer(struct tnbr *); static void tnbr_stop_hello_timer(struct tnbr *); @@ -52,8 +48,6 @@ adj_new(struct in_addr lsr_id, struct hello_source *source, adj->source = *source; adj->trans_addr = *addr; - evtimer_set(&adj->inactivity_timer, adj_itimer, adj); - LIST_INSERT_HEAD(&global.adj_list, adj, global_entry); switch (source->type) { @@ -154,10 +148,12 @@ adj_get_af(struct adj *adj) /* adjacency timers */ /* ARGSUSED */ -static void -adj_itimer(int fd, short event, void *arg) +static int +adj_itimer(struct thread *thread) { - struct adj *adj = arg; + struct adj *adj = THREAD_ARG(thread); + + adj->inactivity_timer = NULL; log_debug("%s: lsr-id %s", __func__, inet_ntoa(adj->lsr_id)); @@ -166,37 +162,34 @@ adj_itimer(int fd, short event, void *arg) adj->source.target->pw_count == 0) { /* remove dynamic targeted neighbor */ tnbr_del(adj->source.target); - return; + return (0); } adj->source.target->adj = NULL; } adj_del(adj, S_HOLDTIME_EXP); + + return (0); } void adj_start_itimer(struct adj *adj) { - struct timeval tv; - - timerclear(&tv); - tv.tv_sec = adj->holdtime; - if (evtimer_add(&adj->inactivity_timer, &tv) == -1) - fatal(__func__); + THREAD_TIMER_OFF(adj->inactivity_timer); + adj->inactivity_timer = thread_add_timer(master, adj_itimer, adj, + adj->holdtime); } void adj_stop_itimer(struct adj *adj) { - if (evtimer_pending(&adj->inactivity_timer, NULL) && - evtimer_del(&adj->inactivity_timer) == -1) - fatal(__func__); + THREAD_TIMER_OFF(adj->inactivity_timer); } /* targeted neighbors */ struct tnbr * -tnbr_new(struct ldpd_conf *xconf, int af, union ldpd_addr *addr) +tnbr_new(int af, union ldpd_addr *addr) { struct tnbr *tnbr; @@ -206,8 +199,6 @@ tnbr_new(struct ldpd_conf *xconf, int af, union ldpd_addr *addr) tnbr->af = af; tnbr->addr = *addr; tnbr->state = TNBR_STA_DOWN; - tnbr->hello_holdtime = (ldp_af_conf_get(xconf, af))->thello_holdtime; - tnbr->hello_interval = (ldp_af_conf_get(xconf, af))->thello_interval; return (tnbr); } @@ -257,7 +248,7 @@ tnbr_update(struct tnbr *tnbr) else socket_ok = 0; - if (leconf->rtr_id.s_addr != INADDR_ANY) + if (ldp_rtr_id_get(leconf) != INADDR_ANY) rtr_id_ok = 1; else rtr_id_ok = 0; @@ -269,7 +260,6 @@ tnbr_update(struct tnbr *tnbr) tnbr->state = TNBR_STA_ACTIVE; send_hello(HELLO_TARGETED, NULL, tnbr); - evtimer_set(&tnbr->hello_timer, tnbr_hello_timer, tnbr); tnbr_start_hello_timer(tnbr); } else if (tnbr->state == TNBR_STA_ACTIVE) { if (socket_ok && rtr_id_ok) @@ -291,35 +281,51 @@ tnbr_update_all(int af) tnbr_update(tnbr); } +uint16_t +tnbr_get_hello_holdtime(struct tnbr *tnbr) +{ + if ((ldp_af_conf_get(leconf, tnbr->af))->thello_holdtime != 0) + return ((ldp_af_conf_get(leconf, tnbr->af))->thello_holdtime); + + return (leconf->thello_holdtime); +} + +uint16_t +tnbr_get_hello_interval(struct tnbr *tnbr) +{ + if ((ldp_af_conf_get(leconf, tnbr->af))->thello_interval != 0) + return ((ldp_af_conf_get(leconf, tnbr->af))->thello_interval); + + return (leconf->thello_interval); +} + /* target neighbors timers */ /* ARGSUSED */ -static void -tnbr_hello_timer(int fd, short event, void *arg) +static int +tnbr_hello_timer(struct thread *thread) { - struct tnbr *tnbr = arg; + struct tnbr *tnbr = THREAD_ARG(thread); + tnbr->hello_timer = NULL; send_hello(HELLO_TARGETED, NULL, tnbr); tnbr_start_hello_timer(tnbr); + + return (0); } static void tnbr_start_hello_timer(struct tnbr *tnbr) { - struct timeval tv; - - timerclear(&tv); - tv.tv_sec = tnbr->hello_interval; - if (evtimer_add(&tnbr->hello_timer, &tv) == -1) - fatal(__func__); + THREAD_TIMER_OFF(tnbr->hello_timer); + tnbr->hello_timer = thread_add_timer(master, tnbr_hello_timer, tnbr, + tnbr_get_hello_interval(tnbr)); } static void tnbr_stop_hello_timer(struct tnbr *tnbr) { - if (evtimer_pending(&tnbr->hello_timer, NULL) && - evtimer_del(&tnbr->hello_timer) == -1) - fatal(__func__); + THREAD_TIMER_OFF(tnbr->hello_timer); } struct ctl_adj * diff --git a/ldpd/control.c b/ldpd/control.c index b7cb3f1063..ba303cc12c 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -16,13 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include +#include #include -#include -#include -#include -#include #include "ldpd.h" #include "ldpe.h" @@ -31,11 +26,11 @@ #define CONTROL_BACKLOG 5 -static void control_accept(int, short, void *); +static int control_accept(struct thread *); static struct ctl_conn *control_connbyfd(int); static struct ctl_conn *control_connbypid(pid_t); static void control_close(int); -static void control_dispatch_imsg(int, short, void *); +static int control_dispatch_imsg(struct thread *); struct ctl_conns ctl_conns; @@ -48,11 +43,11 @@ control_init(void) int fd; mode_t old_umask; - if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - 0)) == -1) { + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { log_warn("%s: socket", __func__); return (-1); } + sock_set_nonblock(fd); memset(&s_un, 0, sizeof(s_un)); s_un.sun_family = AF_UNIX; @@ -106,8 +101,8 @@ control_cleanup(void) } /* ARGSUSED */ -static void -control_accept(int listenfd, short event, void *bula) +static int +control_accept(struct thread *thread) { int connfd; socklen_t len; @@ -115,8 +110,8 @@ control_accept(int listenfd, short event, void *bula) struct ctl_conn *c; len = sizeof(s_un); - if ((connfd = accept4(listenfd, (struct sockaddr *)&s_un, &len, - SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) { + if ((connfd = accept(THREAD_FD(thread), (struct sockaddr *)&s_un, + &len)) == -1) { /* * Pause accept if we are out of file descriptors, or * libevent will haunt us here too. @@ -125,24 +120,27 @@ control_accept(int listenfd, short event, void *bula) accept_pause(); else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) - log_warn("%s: accept4", __func__); - return; + log_warn("%s: accept", __func__); + return (0); } + sock_set_nonblock(connfd); if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { log_warn(__func__); close(connfd); - return; + return (0); } imsg_init(&c->iev.ibuf, connfd); - c->iev.handler = control_dispatch_imsg; - c->iev.events = EV_READ; - event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, - c->iev.handler, &c->iev); - event_add(&c->iev.ev, NULL); + c->iev.handler_read = control_dispatch_imsg; + c->iev.ev_read = thread_add_read(master, c->iev.handler_read, + &c->iev, c->iev.ibuf.fd); + c->iev.handler_write = ldp_write_handler; + c->iev.ev_write = NULL; TAILQ_INSERT_TAIL(&ctl_conns, c, entry); + + return (0); } static struct ctl_conn * @@ -182,45 +180,40 @@ control_close(int fd) msgbuf_clear(&c->iev.ibuf.w); TAILQ_REMOVE(&ctl_conns, c, entry); - event_del(&c->iev.ev); + THREAD_READ_OFF(c->iev.ev_read); + THREAD_WRITE_OFF(c->iev.ev_write); close(c->iev.ibuf.fd); accept_unpause(); free(c); } /* ARGSUSED */ -static void -control_dispatch_imsg(int fd, short event, void *bula) +static int +control_dispatch_imsg(struct thread *thread) { + int fd = THREAD_FD(thread); struct ctl_conn *c; struct imsg imsg; ssize_t n; unsigned int ifidx; - int verbose; if ((c = control_connbyfd(fd)) == NULL) { log_warnx("%s: fd %d: not found", __func__, fd); - return; + return (0); } - if (event & EV_READ) { - if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || - n == 0) { - control_close(fd); - return; - } - } - if (event & EV_WRITE) { - if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) { - control_close(fd); - return; - } + c->iev.ev_read = NULL; + + if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || + n == 0) { + control_close(fd); + return (0); } for (;;) { if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { control_close(fd); - return; + return (0); } if (n == 0) @@ -230,16 +223,10 @@ control_dispatch_imsg(int fd, short event, void *bula) case IMSG_CTL_FIB_COUPLE: case IMSG_CTL_FIB_DECOUPLE: case IMSG_CTL_RELOAD: - c->iev.ibuf.pid = imsg.hdr.pid; - ldpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); - break; case IMSG_CTL_KROUTE: case IMSG_CTL_KROUTE_ADDR: case IMSG_CTL_IFINFO: - c->iev.ibuf.pid = imsg.hdr.pid; - ldpe_imsg_compose_parent(imsg.hdr.type, - imsg.hdr.pid, imsg.data, - imsg.hdr.len - IMSG_HEADER_SIZE); + /* ignore */ break; case IMSG_CTL_SHOW_INTERFACE: if (imsg.hdr.len == IMSG_HEADER_SIZE + @@ -271,18 +258,7 @@ control_dispatch_imsg(int fd, short event, void *bula) nbr_clear_ctl(imsg.data); break; case IMSG_CTL_LOG_VERBOSE: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(verbose)) - break; - - /* forward to other processes */ - ldpe_imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid, - imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); - ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid, - imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); - - memcpy(&verbose, imsg.data, sizeof(verbose)); - log_verbose(verbose); + /* ignore */ break; default: log_debug("%s: error handling imsg %d", __func__, @@ -293,6 +269,8 @@ control_dispatch_imsg(int fd, short event, void *bula) } imsg_event_add(&c->iev); + + return (0); } int diff --git a/ldpd/control.h b/ldpd/control.h index fd6e47071d..32c49fdf87 100644 --- a/ldpd/control.h +++ b/ldpd/control.h @@ -19,8 +19,7 @@ #ifndef _CONTROL_H_ #define _CONTROL_H_ -#include -#include +#include "openbsd-queue.h" struct ctl_conn { TAILQ_ENTRY(ctl_conn) entry; diff --git a/ldpd/hello.c b/ldpd/hello.c index d0daed347d..755b25aa85 100644 --- a/ldpd/hello.c +++ b/ldpd/hello.c @@ -17,13 +17,12 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "ldp_debug.h" static int gen_hello_prms_tlv(struct ibuf *buf, uint16_t, uint16_t); static int gen_opt4_hello_prms_tlv(struct ibuf *, uint16_t, uint32_t); @@ -46,7 +45,7 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) switch (type) { case HELLO_LINK: af = ia->af; - holdtime = ia->hello_holdtime; + holdtime = if_get_hello_holdtime(ia); flags = 0; fd = (ldp_af_global_get(&global, af))->ldp_disc_socket; @@ -66,7 +65,7 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) break; case HELLO_TARGETED: af = tnbr->af; - holdtime = tnbr->hello_holdtime; + holdtime = tnbr_get_hello_holdtime(tnbr); flags = F_HELLO_TARGETED; if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count) flags |= F_HELLO_REQ_TARG; @@ -139,6 +138,20 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) return (-1); } + switch (type) { + case HELLO_LINK: + debug_hello_send("iface %s (%s) holdtime %u", ia->iface->name, + af_name(ia->af), holdtime); + break; + case HELLO_TARGETED: + debug_hello_send("targeted-neighbor %s (%s) holdtime %u", + log_addr(tnbr->af, &tnbr->addr), af_name(tnbr->af), + holdtime); + break; + default: + fatalx("send_hello: unknown hello type"); + } + send_packet(fd, af, &dst, ia, buf->buf, buf->wpos); ibuf_free(buf); @@ -152,7 +165,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, { struct adj *adj = NULL; struct nbr *nbr, *nbrt; - uint16_t holdtime, flags; + uint16_t holdtime = 0, flags = 0; int tlvs_rcvd; int ds_tlv; union ldpd_addr trans_addr; @@ -257,7 +270,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, F_LDPD_AF_THELLO_ACCEPT))) return; - tnbr = tnbr_new(leconf, af, src); + tnbr = tnbr_new(af, src); tnbr->flags |= F_TNBR_DYNAMIC; tnbr_update(tnbr); LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry); @@ -387,19 +400,23 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, if (holdtime == 0) holdtime = LINK_DFLT_HOLDTIME; - adj->holdtime = min(ia->hello_holdtime, holdtime); + adj->holdtime = min(if_get_hello_holdtime(ia), holdtime); break; case HELLO_TARGETED: if (holdtime == 0) holdtime = TARGETED_DFLT_HOLDTIME; - adj->holdtime = min(tnbr->hello_holdtime, holdtime); + adj->holdtime = min(tnbr_get_hello_holdtime(tnbr), holdtime); } if (adj->holdtime != INFINITE_HOLDTIME) adj_start_itimer(adj); else adj_stop_itimer(adj); + debug_hello_recv("%s lsr-id %s transport-address %s holdtime %u%s", + log_hello_src(&source), inet_ntoa(lsr_id), log_addr(af, &trans_addr), + holdtime, (ds_tlv) ? " (dual stack TLV present)" : ""); + if (nbr && nbr->state == NBR_STA_PRESENT && !nbr_pending_idtimer(nbr) && nbr_session_active_role(nbr) && !nbr_pending_connect(nbr)) nbr_establish_connection(nbr); diff --git a/ldpd/init.c b/ldpd/init.c index eb22bf52ac..ed6b53c02d 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -16,13 +16,12 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "ldp_debug.h" static int gen_init_prms_tlv(struct ibuf *, struct nbr *); @@ -33,7 +32,7 @@ send_init(struct nbr *nbr) uint16_t size; int err = 0; - log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + debug_msg_send("initialization: lsr-id %s", inet_ntoa(nbr->id)); size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE; if ((buf = ibuf_open(size)) == NULL) @@ -59,7 +58,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) struct sess_prms_tlv sess; uint16_t max_pdu_len; - log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); + debug_msg_recv("initialization: lsr-id %s", inet_ntoa(nbr->id)); memcpy(&msg, buf, sizeof(msg)); buf += LDP_MSG_SIZE; @@ -82,7 +81,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) session_shutdown(nbr, S_KEEPALIVE_BAD, msg.id, msg.type); return (-1); } - if (sess.lsr_id != leconf->rtr_id.s_addr || + if (sess.lsr_id != ldp_rtr_id_get(leconf) || ntohs(sess.lspace_id) != 0) { session_shutdown(nbr, S_NO_HELLO, msg.id, msg.type); return (-1); diff --git a/ldpd/interface.c b/ldpd/interface.c index ff4c8f169c..b6472fe5e8 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -18,22 +18,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "sockopt.h" + static struct if_addr *if_addr_new(struct kaddr *); static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *); static int if_start(struct iface *, int); static int if_reset(struct iface *, int); static void if_update_af(struct iface_af *, int); -static void if_hello_timer(int, short, void *); +static int if_hello_timer(struct thread *); static void if_start_hello_timer(struct iface_af *); static void if_stop_hello_timer(struct iface_af *); static int if_join_ipv4_group(struct iface *, struct in_addr *); @@ -50,20 +48,9 @@ if_new(struct kif *kif) fatal("if_new: calloc"); strlcpy(iface->name, kif->ifname, sizeof(iface->name)); - - /* get type */ - if (kif->flags & IFF_POINTOPOINT) - iface->type = IF_TYPE_POINTOPOINT; - if (kif->flags & IFF_BROADCAST && - kif->flags & IFF_MULTICAST) - iface->type = IF_TYPE_BROADCAST; - - /* get index and flags */ LIST_INIT(&iface->addr_list); - iface->ifindex = kif->ifindex; - iface->flags = kif->flags; - iface->linkstate = kif->link_state; - iface->if_type = kif->if_type; + if (kif->ifindex) + if_update_info(iface, kif); /* ipv4 */ iface->ipv4.af = AF_INET; @@ -112,6 +99,33 @@ if_exit(struct iface *iface) } } +struct iface * +if_lookup_name(struct ldpd_conf *xconf, const char *ifname) +{ + struct iface *iface; + + LIST_FOREACH(iface, &xconf->iface_list, entry) + if (strcmp(iface->name, ifname) == 0) + return (iface); + + return (NULL); +} + +void +if_update_info(struct iface *iface, struct kif *kif) +{ + /* get type */ + if (kif->flags & IFF_POINTOPOINT) + iface->type = IF_TYPE_POINTOPOINT; + if (kif->flags & IFF_BROADCAST && + kif->flags & IFF_MULTICAST) + iface->type = IF_TYPE_BROADCAST; + + /* get index and flags */ + iface->ifindex = kif->ifindex; + iface->flags = kif->flags; +} + struct iface_af * iface_af_get(struct iface *iface, int af) { @@ -258,7 +272,6 @@ if_start(struct iface *iface, int af) send_hello(HELLO_LINK, ia, NULL); - evtimer_set(&ia->hello_timer, if_hello_timer, ia); if_start_hello_timer(ia); return (0); } @@ -328,7 +341,7 @@ if_update_af(struct iface_af *ia, int link_ok) else socket_ok = 0; - if (leconf->rtr_id.s_addr != INADDR_ANY) + if (ldp_rtr_id_get(leconf) != INADDR_ANY) rtr_id_ok = 1; else rtr_id_ok = 0; @@ -354,8 +367,7 @@ if_update(struct iface *iface, int af) { int link_ok; - link_ok = (iface->flags & IFF_UP) && - LINK_STATE_IS_UP(iface->linkstate); + link_ok = (iface->flags & IFF_UP) && (iface->flags & IFF_RUNNING); if (af == AF_INET || af == AF_UNSPEC) if_update_af(&iface->ipv4, link_ok); @@ -372,34 +384,56 @@ if_update_all(int af) if_update(iface, af); } +uint16_t +if_get_hello_holdtime(struct iface_af *ia) +{ + if (ia->hello_holdtime != 0) + return (ia->hello_holdtime); + + if ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime != 0) + return ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime); + + return (leconf->lhello_holdtime); +} + +uint16_t +if_get_hello_interval(struct iface_af *ia) +{ + if (ia->hello_interval != 0) + return (ia->hello_interval); + + if ((ldp_af_conf_get(leconf, ia->af))->lhello_interval != 0) + return ((ldp_af_conf_get(leconf, ia->af))->lhello_interval); + + return (leconf->lhello_interval); +} + /* timers */ /* ARGSUSED */ -static void -if_hello_timer(int fd, short event, void *arg) +static int +if_hello_timer(struct thread *thread) { - struct iface_af *ia = arg; + struct iface_af *ia = THREAD_ARG(thread); + ia->hello_timer = NULL; send_hello(HELLO_LINK, ia, NULL); if_start_hello_timer(ia); + + return (0); } static void if_start_hello_timer(struct iface_af *ia) { - struct timeval tv; - - timerclear(&tv); - tv.tv_sec = ia->hello_interval; - if (evtimer_add(&ia->hello_timer, &tv) == -1) - fatal(__func__); + THREAD_TIMER_OFF(ia->hello_timer); + ia->hello_timer = thread_add_timer(master, if_hello_timer, ia, + if_get_hello_interval(ia)); } static void if_stop_hello_timer(struct iface_af *ia) { - if (evtimer_pending(&ia->hello_timer, NULL) && - evtimer_del(&ia->hello_timer) == -1) - fatal(__func__); + THREAD_TIMER_OFF(ia->hello_timer); } struct ctl_iface * @@ -414,11 +448,9 @@ if_to_ctl(struct iface_af *ia) ictl.ifindex = ia->iface->ifindex; ictl.state = ia->state; ictl.flags = ia->iface->flags; - ictl.linkstate = ia->iface->linkstate; ictl.type = ia->iface->type; - ictl.if_type = ia->iface->if_type; - ictl.hello_holdtime = ia->hello_holdtime; - ictl.hello_interval = ia->hello_interval; + ictl.hello_holdtime = if_get_hello_holdtime(ia); + ictl.hello_interval = if_get_hello_interval(ia); gettimeofday(&now, NULL); if (ia->state != IF_STA_DOWN && @@ -450,16 +482,15 @@ if_get_ipv4_addr(struct iface *iface) static int if_join_ipv4_group(struct iface *iface, struct in_addr *addr) { - struct ip_mreq mreq; + struct in_addr if_addr; log_debug("%s: interface %s addr %s", __func__, iface->name, inet_ntoa(*addr)); - mreq.imr_multiaddr = *addr; - mreq.imr_interface.s_addr = if_get_ipv4_addr(iface); + if_addr.s_addr = if_get_ipv4_addr(iface); - if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, - IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { + if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket, + IP_ADD_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) { log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s", __func__, iface->name, inet_ntoa(*addr)); return (-1); @@ -470,16 +501,15 @@ if_join_ipv4_group(struct iface *iface, struct in_addr *addr) static int if_leave_ipv4_group(struct iface *iface, struct in_addr *addr) { - struct ip_mreq mreq; + struct in_addr if_addr; log_debug("%s: interface %s addr %s", __func__, iface->name, inet_ntoa(*addr)); - mreq.imr_multiaddr = *addr; - mreq.imr_interface.s_addr = if_get_ipv4_addr(iface); + if_addr.s_addr = if_get_ipv4_addr(iface); - if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, - IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { + if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket, + IP_DROP_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) { log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s " "address %s", __func__, iface->name, inet_ntoa(*addr)); return (-1); diff --git a/ldpd/keepalive.c b/ldpd/keepalive.c index 4cd49d485b..f9a7d850fd 100644 --- a/ldpd/keepalive.c +++ b/ldpd/keepalive.c @@ -16,12 +16,12 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "ldp_debug.h" void send_keepalive(struct nbr *nbr) @@ -37,6 +37,8 @@ send_keepalive(struct nbr *nbr) size -= LDP_HDR_SIZE; gen_msg_hdr(buf, MSG_TYPE_KEEPALIVE, size); + debug_kalive_send("keepalive: lsr-id %s", inet_ntoa(nbr->id)); + evbuf_enqueue(&nbr->tcp->wbuf, buf); } @@ -51,6 +53,8 @@ recv_keepalive(struct nbr *nbr, char *buf, uint16_t len) return (-1); } + debug_kalive_recv("keepalive: lsr-id %s", inet_ntoa(nbr->id)); + if (nbr->state != NBR_STA_OPER) nbr_fsm(nbr, NBR_EVT_KEEPALIVE_RCVD); diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 22c98745e2..db382e484f 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -19,10 +19,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" @@ -47,6 +44,7 @@ l2vpn_new(const char *name) LIST_INIT(&l2vpn->if_list); LIST_INIT(&l2vpn->pw_list); + LIST_INIT(&l2vpn->pw_inactive_list); return (l2vpn); } @@ -77,6 +75,10 @@ l2vpn_del(struct l2vpn *l2vpn) LIST_REMOVE(pw, entry); free(pw); } + while ((pw = LIST_FIRST(&l2vpn->pw_inactive_list)) != NULL) { + LIST_REMOVE(pw, entry); + free(pw); + } free(l2vpn); } @@ -111,7 +113,6 @@ l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif) strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname)); lif->ifindex = kif->ifindex; lif->flags = kif->flags; - lif->link_state = kif->link_state; return (lif); } @@ -128,6 +129,19 @@ l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex) return (NULL); } +struct l2vpn_if * +l2vpn_if_find_name(struct l2vpn *l2vpn, const char *ifname) +{ + struct l2vpn_if *lif; + + LIST_FOREACH(lif, &l2vpn->if_list, entry) + if (strcmp(lif->ifname, ifname) == 0) + return (lif); + + return (NULL); +} + + struct l2vpn_pw * l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif) { @@ -151,6 +165,24 @@ l2vpn_pw_find(struct l2vpn *l2vpn, unsigned int ifindex) LIST_FOREACH(pw, &l2vpn->pw_list, entry) if (pw->ifindex == ifindex) return (pw); + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) + if (pw->ifindex == ifindex) + return (pw); + + return (NULL); +} + +struct l2vpn_pw * +l2vpn_pw_find_name(struct l2vpn *l2vpn, const char *ifname) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + if (strcmp(pw->ifname, ifname) == 0) + return (pw); + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) + if (strcmp(pw->ifname, ifname) == 0) + return (pw); return (NULL); } @@ -399,6 +431,8 @@ l2vpn_pw_ctl(pid_t pid) LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) LIST_FOREACH(pw, &l2vpn->pw_list, entry) { memset(&pwctl, 0, sizeof(pwctl)); + strlcpy(pwctl.l2vpn_name, pw->l2vpn->name, + sizeof(pwctl.l2vpn_name)); strlcpy(pwctl.ifname, pw->ifname, sizeof(pwctl.ifname)); pwctl.pwid = pw->pwid; @@ -438,6 +472,8 @@ l2vpn_binding_ctl(pid_t pid) pwctl.local_label = fn->local_label; pwctl.local_gid = 0; pwctl.local_ifmtu = pw->l2vpn->mtu; + pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ? + 1 : 0; } else pwctl.local_label = NO_LABEL; @@ -450,6 +486,9 @@ l2vpn_binding_ctl(pid_t pid) pwctl.remote_gid = me->map.fec.pwid.group_id; if (me->map.flags & F_MAP_PW_IFMTU) pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu; + if (pw) + pwctl.remote_cword = (pw->flags & F_PW_CWORD) ? + 1 : 0; lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, 0, pid, &pwctl, sizeof(pwctl)); @@ -489,7 +528,7 @@ ldpe_l2vpn_pw_init(struct l2vpn_pw *pw) tnbr = tnbr_find(leconf, pw->af, &pw->addr); if (tnbr == NULL) { - tnbr = tnbr_new(leconf, pw->af, &pw->addr); + tnbr = tnbr_new(pw->af, &pw->addr); tnbr_update(tnbr); LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry); } diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index 88e64071bb..62f2a620d2 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -17,17 +17,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "ldp_debug.h" + +#include "mpls.h" static void enqueue_pdu(struct nbr *, struct ibuf *, uint16_t); static int gen_label_tlv(struct ibuf *, uint32_t); @@ -127,8 +124,8 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) return; } - log_debug("msg-out: %s: lsr-id %s, fec %s, label %s", - msg_name(type), inet_ntoa(nbr->id), log_map(&me->map), + 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)); TAILQ_REMOVE(mh, me, entry); @@ -399,7 +396,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; - log_debug("msg-in: label mapping: lsr-id %s, fec %s, label %s", + 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)); diff --git a/ldpd/lde.c b/ldpd/lde.c index 8859ed25b8..ae29ef6a7d 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -19,31 +19,24 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "ldp.h" #include "ldpd.h" #include "ldpe.h" #include "log.h" #include "lde.h" +#include "ldp_debug.h" -static void lde_sig_handler(int sig, short, void *); -static __dead void lde_shutdown(void); -static int lde_imsg_compose_parent(int, pid_t, void *, uint16_t); -static void lde_dispatch_imsg(int, short, void *); -static void lde_dispatch_parent(int, short, void *); +#include +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "mpls.h" + +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 *, struct lde_nbr *); static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *); @@ -65,89 +58,101 @@ struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs); static struct imsgev *iev_ldpe; static struct imsgev *iev_main; -/* ARGSUSED */ -static void -lde_sig_handler(int sig, short event, void *arg) -{ - /* - * signal handler rules don't apply, libevent decouples for us - */ +/* Master of threads. */ +struct thread_master *master; - switch (sig) { - case SIGINT: - case SIGTERM: - lde_shutdown(); - /* NOTREACHED */ - default: - fatalx("unexpected signal"); - } +/* lde privileges */ +static zebra_capabilities_t _caps_p [] = +{ + /* none */ +}; + +static struct zebra_privs_t lde_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0 +}; + +/* SIGINT / SIGTERM handler. */ +static void +sigint(void) +{ + lde_shutdown(); } +static struct quagga_signal_t lde_signals[] = +{ + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + /* label decision engine */ void -lde(int debug, int verbose) +lde(const char *user, const char *group) { - struct event ev_sigint, ev_sigterm; + struct thread thread; struct timeval now; - struct passwd *pw; ldeconf = config_new_empty(); - log_init(debug); - log_verbose(verbose); - +#ifdef HAVE_SETPROCTITLE setproctitle("label decision engine"); +#endif ldpd_process = PROC_LDE_ENGINE; - if ((pw = getpwnam(LDPD_USER)) == NULL) - fatal("getpwnam"); - - if (chroot(pw->pw_dir) == -1) - fatal("chroot"); - if (chdir("/") == -1) - fatal("chdir(\"/\")"); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("can't drop privileges"); + /* drop privileges */ + if (user) + lde_privs.user = user; + if (group) + lde_privs.group = group; + zprivs_init(&lde_privs); +#ifdef HAVE_PLEDGE if (pledge("stdio recvfd", NULL) == -1) fatal("pledge"); +#endif - event_init(); + master = thread_master_create(); /* setup signal handler */ - signal_set(&ev_sigint, SIGINT, lde_sig_handler, NULL); - signal_set(&ev_sigterm, SIGTERM, lde_sig_handler, NULL); - signal_add(&ev_sigint, NULL); - signal_add(&ev_sigterm, NULL); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); + signal_init(master, array_size(lde_signals), lde_signals); /* setup pipe and event handler to the parent process */ if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_main->ibuf, 3); - iev_main->handler = lde_dispatch_parent; - iev_main->events = EV_READ; - event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, - iev_main->handler, iev_main); - event_add(&iev_main->ev, NULL); + iev_main->handler_read = lde_dispatch_parent; + iev_main->ev_read = thread_add_read(master, iev_main->handler_read, + iev_main, iev_main->ibuf.fd); + iev_main->handler_write = ldp_write_handler; + iev_main->ev_write = NULL; - /* setup and start the LIB garbage collector */ - evtimer_set(&gc_timer, lde_gc_timer, NULL); + /* start the LIB garbage collector */ lde_gc_start_timer(); gettimeofday(&now, NULL); global.uptime = now.tv_sec; - event_dispatch(); - - lde_shutdown(); + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); } -static __dead void +static void lde_shutdown(void) { /* close pipes */ @@ -170,7 +175,7 @@ lde_shutdown(void) } /* imesg */ -static int +int lde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen) { return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen)); @@ -185,10 +190,10 @@ lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data, } /* ARGSUSED */ -static void -lde_dispatch_imsg(int fd, short event, void *bula) +static int +lde_dispatch_imsg(struct thread *thread) { - struct imsgev *iev = bula; + struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct lde_nbr *ln; @@ -196,20 +201,14 @@ lde_dispatch_imsg(int fd, short event, void *bula) struct lde_addr lde_addr; struct notify_msg nm; ssize_t n; - int shut = 0, verbose; + int shut = 0; - if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatal("imsg_read error"); - if (n == 0) /* connection closed */ - shut = 1; - } - if (event & EV_WRITE) { - if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) - fatal("msgbuf_write"); - if (n == 0) /* connection closed */ - shut = 1; - } + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -353,11 +352,6 @@ lde_dispatch_imsg(int fd, short event, void *bula) lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0); break; - case IMSG_CTL_LOG_VERBOSE: - /* already checked by ldpe */ - memcpy(&verbose, imsg.data, sizeof(verbose)); - log_verbose(verbose); - break; default: log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); @@ -368,15 +362,18 @@ lde_dispatch_imsg(int fd, short event, void *bula) if (!shut) imsg_event_add(iev); else { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + lde_shutdown(); } + + return (0); } /* ARGSUSED */ -static void -lde_dispatch_parent(int fd, short event, void *bula) +static int +lde_dispatch_parent(struct thread *thread) { static struct ldpd_conf *nconf; struct iface *niface; @@ -387,24 +384,19 @@ lde_dispatch_parent(int fd, short event, void *bula) struct l2vpn_pw *npw; struct imsg imsg; struct kroute kr; - struct imsgev *iev = bula; + int fd = THREAD_FD(thread); + struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; ssize_t n; int shut = 0; struct fec fec; - if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatal("imsg_read error"); - if (n == 0) /* connection closed */ - shut = 1; - } - if (event & EV_WRITE) { - if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) - fatal("msgbuf_write"); - if (n == 0) /* connection closed */ - shut = 1; - } + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -462,11 +454,11 @@ lde_dispatch_parent(int fd, short event, void *bula) if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_ldpe->ibuf, fd); - iev_ldpe->handler = lde_dispatch_imsg; - iev_ldpe->events = EV_READ; - event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd, - iev_ldpe->events, iev_ldpe->handler, iev_ldpe); - event_add(&iev_ldpe->ev, NULL); + iev_ldpe->handler_read = lde_dispatch_imsg; + iev_ldpe->ev_read = thread_add_read(master, + iev_ldpe->handler_read, iev_ldpe, iev_ldpe->ibuf.fd); + iev_ldpe->handler_write = ldp_write_handler; + iev_ldpe->ev_write = NULL; break; case IMSG_RECONF_CONF: if ((nconf = malloc(sizeof(struct ldpd_conf))) == @@ -513,6 +505,7 @@ lde_dispatch_parent(int fd, short event, void *bula) LIST_INIT(&nl2vpn->if_list); LIST_INIT(&nl2vpn->pw_list); + LIST_INIT(&nl2vpn->pw_inactive_list); LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry); break; @@ -532,10 +525,26 @@ lde_dispatch_parent(int fd, short event, void *bula) npw->l2vpn = nl2vpn; LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry); break; + case IMSG_RECONF_L2VPN_IPW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + npw->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->pw_inactive_list, npw, entry); + break; case IMSG_RECONF_END: merge_config(ldeconf, nconf); nconf = NULL; break; + case IMSG_DEBUG_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(ldp_debug)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); + break; default: log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); @@ -546,10 +555,13 @@ lde_dispatch_parent(int fd, short event, void *bula) if (!shut) imsg_event_add(iev); else { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + lde_shutdown(); } + + return (0); } uint32_t @@ -557,7 +569,10 @@ lde_assign_label(void) { static uint32_t label = MPLS_LABEL_RESERVED_MAX; - /* XXX some checks needed */ + /* + * TODO: request label to zebra or define a range of labels for ldpd. + */ + label++; return (label); } diff --git a/ldpd/lde.h b/ldpd/lde.h index b0f7b20439..0fce5565a2 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -21,9 +21,8 @@ #ifndef _LDE_H_ #define _LDE_H_ -#include -#include -#include +#include "openbsd-queue.h" +#include "openbsd-tree.h" enum fec_type { FEC_TYPE_IPV4, @@ -121,10 +120,11 @@ struct fec_node { extern struct ldpd_conf *ldeconf; extern struct fec_tree ft; extern struct nbr_tree lde_nbrs; -extern struct event gc_timer; +extern struct thread *gc_timer; /* lde.c */ -void lde(int, int); +void lde(const char *, const char *); +int lde_imsg_compose_parent(int, pid_t, void *, uint16_t); int lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t); uint32_t lde_assign_label(void); void lde_send_change_klabel(struct fec_node *, struct fec_nh *); @@ -173,7 +173,7 @@ 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 *); -void lde_gc_timer(int, short, void *); +int lde_gc_timer(struct thread *); void lde_gc_start_timer(void); void lde_gc_stop_timer(void); @@ -185,8 +185,10 @@ void l2vpn_init(struct l2vpn *); 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 *); 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 *); void l2vpn_pw_init(struct l2vpn_pw *); void l2vpn_pw_exit(struct l2vpn_pw *); void l2vpn_pw_reset(struct l2vpn_pw *); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index d9c1f544f1..568761bd61 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -17,17 +17,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "lde.h" #include "log.h" +#include "mpls.h" + static __inline int fec_compare(struct fec *, struct fec *); static int lde_nbr_is_nexthop(struct fec_node *, struct lde_nbr *); @@ -40,7 +37,7 @@ static void fec_nh_del(struct fec_nh *); RB_GENERATE(fec_tree, fec, entry, fec_compare) struct fec_tree ft = RB_INITIALIZER(&ft); -struct event gc_timer; +struct thread *gc_timer; /* FEC tree functions */ void @@ -165,6 +162,7 @@ rt_dump(pid_t pid) LIST_EMPTY(&fn->downstream)) continue; + rtctl.first = 1; switch (fn->fec.type) { case FEC_TYPE_IPV4: rtctl.af = AF_INET; @@ -188,6 +186,7 @@ rt_dump(pid_t pid) lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid, &rtctl, sizeof(rtctl)); + rtctl.first = 0; } if (LIST_EMPTY(&fn->downstream)) { rtctl.in_use = 0; @@ -338,9 +337,6 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, if (fec_nh_find(fn, af, nexthop, priority) != NULL) return; - log_debug("lde add fec %s nexthop %s", - log_fec(&fn->fec), log_addr(af, nexthop)); - if (fn->fec.type == FEC_TYPE_PWID) fn->data = data; @@ -396,9 +392,6 @@ lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop, /* route lost */ return; - log_debug("lde remove fec %s nexthop %s", - log_fec(&fn->fec), log_addr(af, nexthop)); - lde_send_delete_klabel(fn, fnh); fec_nh_del(fnh); if (LIST_EMPTY(&fn->nexthops)) { @@ -738,8 +731,8 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) /* gabage collector timer: timer to remove dead entries from the LIB */ /* ARGSUSED */ -void -lde_gc_timer(int fd, short event, void *arg) +int +lde_gc_timer(struct thread *thread) { struct fec *fec, *safe; struct fec_node *fn; @@ -762,23 +755,20 @@ lde_gc_timer(int fd, short event, void *arg) log_debug("%s: %u entries removed", __func__, count); lde_gc_start_timer(); + + return (0); } void lde_gc_start_timer(void) { - struct timeval tv; - - timerclear(&tv); - tv.tv_sec = LDE_GC_INTERVAL; - if (evtimer_add(&gc_timer, &tv) == -1) - fatal(__func__); + THREAD_TIMER_OFF(gc_timer); + gc_timer = thread_add_timer(master, lde_gc_timer, NULL, + LDE_GC_INTERVAL); } void lde_gc_stop_timer(void) { - if (evtimer_pending(&gc_timer, NULL) && - evtimer_del(&gc_timer) == -1) - fatal(__func__); + THREAD_TIMER_OFF(gc_timer); } diff --git a/ldpd/ldp.h b/ldpd/ldp.h index 77034b30a5..c421cddc38 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -23,8 +23,6 @@ #ifndef _LDP_H_ #define _LDP_H_ -#include - /* misc */ #define LDP_VERSION 1 #define LDP_PORT 646 @@ -106,7 +104,7 @@ struct ldp_hdr { uint16_t length; uint32_t lsr_id; uint16_t lspace_id; -} __packed; +} __attribute__ ((packed)); #define LDP_HDR_SIZE 10 /* actual size of the LDP header */ #define LDP_HDR_PDU_LEN 6 /* minimum "PDU Length" */ @@ -125,7 +123,7 @@ struct ldp_msg { uint32_t id; /* Mandatory Parameters */ /* Optional Parameters */ -} __packed; +} __attribute__ ((packed)); #define LDP_MSG_SIZE 8 /* minimum size of LDP message */ #define LDP_MSG_LEN 4 /* minimum "Message Length" */ @@ -212,7 +210,7 @@ struct sess_prms_tlv { uint16_t max_pdu_len; uint32_t lsr_id; uint16_t lspace_id; -} __packed; +} __attribute__ ((packed)); #define SESS_PRMS_SIZE 18 #define SESS_PRMS_LEN 14 @@ -223,7 +221,7 @@ struct status_tlv { uint32_t status_code; uint32_t msg_id; uint16_t msg_type; -} __packed; +} __attribute__ ((packed)); #define STATUS_SIZE 14 #define STATUS_TLV_LEN 10 @@ -237,7 +235,7 @@ struct address_list_tlv { uint16_t length; uint16_t family; /* address entries */ -} __packed; +} __attribute__ ((packed)); #define ADDR_LIST_SIZE 6 diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c new file mode 100644 index 0000000000..15dd06a0f3 --- /dev/null +++ b/ldpd/ldp_debug.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "command.h" +#include "vty.h" + +#include "ldpd.h" +#include "ldp_debug.h" +#include "ldp_vty.h" + +struct ldp_debug conf_ldp_debug; +struct ldp_debug ldp_debug; + +/* Debug node. */ +struct cmd_node ldp_debug_node = +{ + DEBUG_NODE, + "", + 1 +}; + +int +ldp_vty_debug(struct vty *vty, struct vty_arg *args[]) +{ + const char *type_str, *dir_str; + int disable, all; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + type_str = vty_get_arg_value(args, "type"); + + if (strcmp(type_str, "discovery") == 0) { + dir_str = vty_get_arg_value(args, "dir"); + if (dir_str == NULL) + return (CMD_WARNING); + + if (dir_str[0] == 'r') { + if (disable) + DEBUG_OFF(hello, HELLO_RECV); + else + DEBUG_ON(hello, HELLO_RECV); + } else { + if (disable) + DEBUG_OFF(hello, HELLO_SEND); + else + DEBUG_ON(hello, HELLO_SEND); + } + } else if (strcmp(type_str, "errors") == 0) { + if (disable) + DEBUG_OFF(errors, ERRORS); + else + DEBUG_ON(errors, ERRORS); + } else if (strcmp(type_str, "event") == 0) { + if (disable) + DEBUG_OFF(event, EVENT); + else + DEBUG_ON(event, EVENT); + } else if (strcmp(type_str, "messages") == 0) { + all = (vty_get_arg_value(args, "all")) ? 1 : 0; + dir_str = vty_get_arg_value(args, "dir"); + if (dir_str == NULL) + return (CMD_WARNING); + + if (dir_str[0] == 'r') { + if (disable) { + DEBUG_OFF(msg, MSG_RECV); + DEBUG_OFF(msg, MSG_RECV_ALL); + } else { + DEBUG_ON(msg, MSG_RECV); + if (all) + DEBUG_ON(msg, MSG_RECV_ALL); + } + } else { + if (disable) { + DEBUG_OFF(msg, MSG_SEND); + DEBUG_OFF(msg, MSG_SEND_ALL); + } else { + DEBUG_ON(msg, MSG_SEND); + if (all) + DEBUG_ON(msg, MSG_SEND_ALL); + } + } + } else if (strcmp(type_str, "zebra") == 0) { + if (disable) + DEBUG_OFF(zebra, ZEBRA); + else + DEBUG_ON(zebra, ZEBRA); + } + + main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, + sizeof(ldp_debug)); + + return (CMD_SUCCESS); +} + +int +ldp_vty_show_debugging(struct vty *vty, struct vty_arg *args[]) +{ + vty_out(vty, "LDP debugging status:%s", VTY_NEWLINE); + + if (LDP_DEBUG(hello, HELLO_RECV)) + vty_out(vty, " LDP discovery debugging is on (inbound)%s", + VTY_NEWLINE); + if (LDP_DEBUG(hello, HELLO_SEND)) + vty_out(vty, " LDP discovery debugging is on (outbound)%s", + VTY_NEWLINE); + if (LDP_DEBUG(errors, ERRORS)) + vty_out(vty, " LDP errors debugging is on%s", VTY_NEWLINE); + if (LDP_DEBUG(event, EVENT)) + vty_out(vty, " LDP events debugging is on%s", VTY_NEWLINE); + if (LDP_DEBUG(msg, MSG_RECV_ALL)) + vty_out(vty, " LDP detailed messages debugging is on " + "(inbound)%s", VTY_NEWLINE); + else if (LDP_DEBUG(msg, MSG_RECV)) + vty_out(vty, " LDP messages debugging is on (inbound)%s", + VTY_NEWLINE); + if (LDP_DEBUG(msg, MSG_SEND_ALL)) + vty_out(vty, " LDP detailed messages debugging is on " + "(outbound)%s", VTY_NEWLINE); + else if (LDP_DEBUG(msg, MSG_SEND)) + vty_out(vty, " LDP messages debugging is on (outbound)%s", + VTY_NEWLINE); + if (LDP_DEBUG(zebra, ZEBRA)) + vty_out(vty, " LDP zebra debugging is on%s", VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + + return (CMD_SUCCESS); +} + +int +ldp_debug_config_write(struct vty *vty) +{ + int write = 0; + + if (CONF_LDP_DEBUG(hello, HELLO_RECV)) { + vty_out(vty, "debug mpls ldp discovery hello recv%s", + VTY_NEWLINE); + write = 1; + } + + if (CONF_LDP_DEBUG(hello, HELLO_SEND)) { + vty_out(vty, "debug mpls ldp discovery hello sent%s", + VTY_NEWLINE); + write = 1; + } + + if (CONF_LDP_DEBUG(errors, ERRORS)) { + vty_out(vty, "debug mpls ldp errors%s", VTY_NEWLINE); + write = 1; + } + + if (CONF_LDP_DEBUG(event, EVENT)) { + vty_out(vty, "debug mpls ldp event%s", VTY_NEWLINE); + write = 1; + } + + if (CONF_LDP_DEBUG(msg, MSG_RECV_ALL)) { + vty_out(vty, "debug mpls ldp messages recv all%s", VTY_NEWLINE); + write = 1; + } else if (CONF_LDP_DEBUG(msg, MSG_RECV)) { + vty_out(vty, "debug mpls ldp messages recv%s", VTY_NEWLINE); + write = 1; + } + + if (CONF_LDP_DEBUG(msg, MSG_SEND_ALL)) { + vty_out(vty, "debug mpls ldp messages sent all%s", VTY_NEWLINE); + write = 1; + } else if (CONF_LDP_DEBUG(msg, MSG_SEND)) { + vty_out(vty, "debug mpls ldp messages sent%s", VTY_NEWLINE); + write = 1; + } + + if (CONF_LDP_DEBUG(zebra, ZEBRA)) { + vty_out(vty, "debug mpls ldp zebra%s", VTY_NEWLINE); + write = 1; + } + + return (write); +} diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h new file mode 100644 index 0000000000..aa0cd47e7b --- /dev/null +++ b/ldpd/ldp_debug.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LDP_DEBUG_H_ +#define _LDP_DEBUG_H_ + +struct ldp_debug { + int hello; +#define LDP_DEBUG_HELLO_RECV 0x01 +#define LDP_DEBUG_HELLO_SEND 0x02 + + int errors; +#define LDP_DEBUG_ERRORS 0x01 + + int event; +#define LDP_DEBUG_EVENT 0x01 + + int msg; +#define LDP_DEBUG_MSG_RECV 0x01 +#define LDP_DEBUG_MSG_RECV_ALL 0x02 +#define LDP_DEBUG_MSG_SEND 0x04 +#define LDP_DEBUG_MSG_SEND_ALL 0x08 + + int zebra; +#define LDP_DEBUG_ZEBRA 0x01 +}; +extern struct ldp_debug conf_ldp_debug; +extern struct ldp_debug ldp_debug; + +#define CONF_DEBUG_ON(a, b) (conf_ldp_debug.a |= (LDP_DEBUG_ ## b)) +#define CONF_DEBUG_OFF(a, b) (conf_ldp_debug.a &= ~(LDP_DEBUG_ ## b)) + +#define TERM_DEBUG_ON(a, b) (ldp_debug.a |= (LDP_DEBUG_ ## b)) +#define TERM_DEBUG_OFF(a, b) (ldp_debug.a &= ~(LDP_DEBUG_ ## b)) + +#define DEBUG_ON(a, b) \ + do { \ + if (vty->node == CONFIG_NODE) { \ + CONF_DEBUG_ON(a, b); \ + TERM_DEBUG_ON(a, b); \ + } else \ + TERM_DEBUG_ON(a, b); \ + } while (0) +#define DEBUG_OFF(a, b) \ + do { \ + CONF_DEBUG_OFF(a, b); \ + TERM_DEBUG_OFF(a, b); \ + } while (0) + +#define LDP_DEBUG(a, b) (ldp_debug.a & LDP_DEBUG_ ## b) +#define CONF_LDP_DEBUG(a, b) (conf_ldp_debug.a & LDP_DEBUG_ ## b) + +#define debug_hello_recv(emsg, ...) \ +do { \ + if (LDP_DEBUG(hello, HELLO_RECV)) \ + log_debug("discovery[recv]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_hello_send(emsg, ...) \ +do { \ + if (LDP_DEBUG(hello, HELLO_SEND)) \ + log_debug("discovery[send]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_err(emsg, ...) \ +do { \ + if (LDP_DEBUG(errors, ERRORS)) \ + log_debug("error: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_evt(emsg, ...) \ +do { \ + if (LDP_DEBUG(event, EVENT)) \ + log_debug("event: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_msg_recv(emsg, ...) \ +do { \ + if (LDP_DEBUG(msg, MSG_RECV)) \ + log_debug("msg[in]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_msg_send(emsg, ...) \ +do { \ + if (LDP_DEBUG(msg, MSG_SEND)) \ + log_debug("msg[out]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_kalive_recv(emsg, ...) \ +do { \ + if (LDP_DEBUG(msg, MSG_RECV_ALL)) \ + log_debug("kalive[in]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_kalive_send(emsg, ...) \ +do { \ + if (LDP_DEBUG(msg, MSG_SEND_ALL)) \ + log_debug("kalive[out]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_zebra_in(emsg, ...) \ +do { \ + if (LDP_DEBUG(zebra, ZEBRA)) \ + log_debug("zebra[in]: " emsg, __VA_ARGS__); \ +} while (0) + +#define debug_zebra_out(emsg, ...) \ +do { \ + if (LDP_DEBUG(zebra, ZEBRA)) \ + log_debug("zebra[out]: " emsg, __VA_ARGS__); \ +} while (0) + +#endif /* _LDP_DEBUG_H_ */ diff --git a/ldpd/ldp_vty.h b/ldpd/ldp_vty.h new file mode 100644 index 0000000000..735554badf --- /dev/null +++ b/ldpd/ldp_vty.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LDP_VTY_H_ +#define _LDP_VTY_H_ + +#include "vty.h" + +extern struct cmd_node ldp_node; +extern struct cmd_node ldp_ipv4_node; +extern struct cmd_node ldp_ipv6_node; +extern struct cmd_node ldp_ipv4_iface_node; +extern struct cmd_node ldp_ipv6_iface_node; +extern struct cmd_node ldp_l2vpn_node; +extern struct cmd_node ldp_pseudowire_node; +extern struct cmd_node ldp_debug_node; + +union ldpd_addr; +int ldp_get_address(const char *, int *, union ldpd_addr *); +int ldp_config_write(struct vty *); +int ldp_l2vpn_config_write(struct vty *); +int ldp_debug_config_write(struct vty *); +int ldp_vty_mpls_ldp (struct vty *, struct vty_arg *[]); +int ldp_vty_address_family (struct vty *, struct vty_arg *[]); +int ldp_vty_disc_holdtime(struct vty *, struct vty_arg *[]); +int ldp_vty_disc_interval(struct vty *, struct vty_arg *[]); +int ldp_vty_targeted_hello_accept(struct vty *, struct vty_arg *[]); +int ldp_vty_session_holdtime(struct vty *, struct vty_arg *[]); +int ldp_vty_interface(struct vty *, struct vty_arg *[]); +int ldp_vty_trans_addr(struct vty *, struct vty_arg *[]); +int ldp_vty_neighbor_targeted(struct vty *, struct vty_arg *[]); +int ldp_vty_explicit_null(struct vty *, struct vty_arg *[]); +int ldp_vty_ttl_security(struct vty *, struct vty_arg *[]); +int ldp_vty_router_id(struct vty *, struct vty_arg *[]); +int ldp_vty_ds_cisco_interop(struct vty *, struct vty_arg *[]); +int ldp_vty_trans_pref_ipv4(struct vty *, struct vty_arg *[]); +int ldp_vty_neighbor_password(struct vty *, struct vty_arg *[]); +int ldp_vty_neighbor_ttl_security(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_bridge(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_mtu(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pwtype(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_interface(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pseudowire(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pw_cword(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pw_nbr_addr(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pw_nbr_id(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pw_pwid(struct vty *, struct vty_arg *[]); +int ldp_vty_l2vpn_pw_pwstatus(struct vty *, struct vty_arg *[]); +int ldp_vty_show_binding(struct vty *, struct vty_arg *[]); +int ldp_vty_show_discovery(struct vty *, struct vty_arg *[]); +int ldp_vty_show_interface(struct vty *, struct vty_arg *[]); +int ldp_vty_show_neighbor(struct vty *, struct vty_arg *[]); +int ldp_vty_show_atom_binding(struct vty *, struct vty_arg *[]); +int ldp_vty_show_atom_vc(struct vty *, struct vty_arg *[]); +int ldp_vty_clear_nbr(struct vty *, struct vty_arg *[]); +int ldp_vty_debug(struct vty *, struct vty_arg *[]); +int ldp_vty_show_debugging(struct vty *, struct vty_arg *[]); + +void ldp_vty_init(void); +void ldp_vty_if_init(void); + +#endif /* _LDP_VTY_H_ */ diff --git a/ldpd/ldp_vty.xml b/ldpd/ldp_vty.xml new file mode 100644 index 0000000000..8742f9c1be --- /dev/null +++ b/ldpd/ldp_vty.xml @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c new file mode 100644 index 0000000000..7160835726 --- /dev/null +++ b/ldpd/ldp_vty_cmds.c @@ -0,0 +1,1738 @@ +/* Auto-generated from ldp_vty.xml. */ +/* Do not edit! */ + +#include + +#include "command.h" +#include "vty.h" +#include "ldp_vty.h" + +DEFUN (ldp_mpls_ldp, + ldp_mpls_ldp_cmd, + "mpls ldp", + "Global MPLS configuration subcommands\n" + "Label Distribution Protocol\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_mpls_ldp (vty, args); +} + +DEFUN (ldp_l2vpn_word_type_vpls, + ldp_l2vpn_word_type_vpls_cmd, + "l2vpn WORD type vpls", + "Configure l2vpn commands\n" + "L2VPN name\n" + "L2VPN type\n" + "Virtual Private LAN Service\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "name", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn (vty, args); +} + +DEFUN (ldp_no_mpls_ldp, + ldp_no_mpls_ldp_cmd, + "no mpls ldp", + "Negate a command or set its defaults\n" + "Global MPLS configuration subcommands\n" + "Label Distribution Protocol\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + NULL + }; + return ldp_vty_mpls_ldp (vty, args); +} + +DEFUN (ldp_no_l2vpn_word_type_vpls, + ldp_no_l2vpn_word_type_vpls_cmd, + "no l2vpn WORD type vpls", + "Negate a command or set its defaults\n" + "Configure l2vpn commands\n" + "L2VPN name\n" + "L2VPN type\n" + "Virtual Private LAN Service\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "name", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn (vty, args); +} + +DEFUN (ldp_address_family_ipv4, + ldp_address_family_ipv4_cmd, + "address-family ipv4", + "Configure Address Family and its parameters\n" + "IPv4\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "address-family", .value = "ipv4" }, + NULL + }; + return ldp_vty_address_family (vty, args); +} + +DEFUN (ldp_address_family_ipv6, + ldp_address_family_ipv6_cmd, + "address-family ipv6", + "Configure Address Family and its parameters\n" + "IPv6\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "address-family", .value = "ipv6" }, + NULL + }; + return ldp_vty_address_family (vty, args); +} + +DEFUN (ldp_discovery_hello_holdtime_disc_time, + ldp_discovery_hello_holdtime_disc_time_cmd, + "discovery hello holdtime <1-65535>", + "Configure discovery parameters\n" + "LDP Link Hellos\n" + "Hello holdtime\n" + "Time (seconds) - 65535 implies infinite\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "hello_type", .value = "hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_holdtime (vty, args); +} + +DEFUN (ldp_discovery_hello_interval_disc_time, + ldp_discovery_hello_interval_disc_time_cmd, + "discovery hello interval <1-65535>", + "Configure discovery parameters\n" + "LDP Link Hellos\n" + "Hello interval\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "hello_type", .value = "hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_interval (vty, args); +} + +DEFUN (ldp_discovery_targeted_hello_holdtime_disc_time, + ldp_discovery_targeted_hello_holdtime_disc_time_cmd, + "discovery targeted-hello holdtime <1-65535>", + "Configure discovery parameters\n" + "LDP Targeted Hellos\n" + "Targeted hello holdtime\n" + "Time (seconds) - 65535 implies infinite\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "hello_type", .value = "targeted-hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_holdtime (vty, args); +} + +DEFUN (ldp_discovery_targeted_hello_interval_disc_time, + ldp_discovery_targeted_hello_interval_disc_time_cmd, + "discovery targeted-hello interval <1-65535>", + "Configure discovery parameters\n" + "LDP Targeted Hellos\n" + "Targeted hello interval\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "hello_type", .value = "targeted-hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_interval (vty, args); +} + +DEFUN (ldp_dual_stack_transport_connection_prefer_ipv4, + ldp_dual_stack_transport_connection_prefer_ipv4_cmd, + "dual-stack transport-connection prefer ipv4", + "Configure dual stack parameters\n" + "Configure TCP transport parameters\n" + "Configure prefered address family for TCP transport connection with neighbor\n" + "IPv4\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_trans_pref_ipv4 (vty, args); +} + +DEFUN (ldp_dual_stack_cisco_interop, + ldp_dual_stack_cisco_interop_cmd, + "dual-stack cisco-interop", + "Configure dual stack parameters\n" + "Use Cisco non-compliant format to send and interpret the Dual-Stack capability TLV\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_ds_cisco_interop (vty, args); +} + +DEFUN (ldp_neighbor_ipv4_password_word, + ldp_neighbor_ipv4_password_word_cmd, + "neighbor A.B.C.D password WORD", + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "Configure password for MD5 authentication\n" + "The password\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + &(struct vty_arg) { .name = "password", .value = argv[1] }, + NULL + }; + return ldp_vty_neighbor_password (vty, args); +} + +DEFUN (ldp_neighbor_ipv4_session_holdtime_session_time, + ldp_neighbor_ipv4_session_holdtime_session_time_cmd, + "neighbor A.B.C.D session holdtime <15-65535>", + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "Configure session parameters\n" + "Configure session holdtime\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + &(struct vty_arg) { .name = "seconds", .value = argv[1] }, + NULL + }; + return ldp_vty_session_holdtime (vty, args); +} + +DEFUN (ldp_neighbor_ipv4_ttl_security_disable, + ldp_neighbor_ipv4_ttl_security_disable_cmd, + "neighbor A.B.C.D ttl-security disable", + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "LDP ttl security check\n" + "Disable ttl security\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + NULL + }; + return ldp_vty_neighbor_ttl_security (vty, args); +} + +DEFUN (ldp_neighbor_ipv4_ttl_security_hops_hops, + ldp_neighbor_ipv4_ttl_security_hops_hops_cmd, + "neighbor A.B.C.D ttl-security hops <1-254>", + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "LDP ttl security check\n" + "IP hops\n" + "maximum number of hops\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + &(struct vty_arg) { .name = "hops", .value = argv[1] }, + NULL + }; + return ldp_vty_neighbor_ttl_security (vty, args); +} + +DEFUN (ldp_router_id_ipv4, + ldp_router_id_ipv4_cmd, + "router-id A.B.C.D", + "Configure router Id\n" + "LSR Id (in form of an IPv4 address)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_router_id (vty, args); +} + +DEFUN (ldp_no_address_family_ipv4, + ldp_no_address_family_ipv4_cmd, + "no address-family ipv4", + "Negate a command or set its defaults\n" + "Configure Address Family and its parameters\n" + "IPv4\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "address-family", .value = "ipv4" }, + NULL + }; + return ldp_vty_address_family (vty, args); +} + +DEFUN (ldp_no_address_family_ipv6, + ldp_no_address_family_ipv6_cmd, + "no address-family ipv6", + "Negate a command or set its defaults\n" + "Configure Address Family and its parameters\n" + "IPv6\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "address-family", .value = "ipv6" }, + NULL + }; + return ldp_vty_address_family (vty, args); +} + +DEFUN (ldp_no_discovery_hello_holdtime_disc_time, + ldp_no_discovery_hello_holdtime_disc_time_cmd, + "no discovery hello holdtime <1-65535>", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "LDP Link Hellos\n" + "Hello holdtime\n" + "Time (seconds) - 65535 implies infinite\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "hello_type", .value = "hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_holdtime (vty, args); +} + +DEFUN (ldp_no_discovery_hello_interval_disc_time, + ldp_no_discovery_hello_interval_disc_time_cmd, + "no discovery hello interval <1-65535>", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "LDP Link Hellos\n" + "Hello interval\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "hello_type", .value = "hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_interval (vty, args); +} + +DEFUN (ldp_no_discovery_targeted_hello_holdtime_disc_time, + ldp_no_discovery_targeted_hello_holdtime_disc_time_cmd, + "no discovery targeted-hello holdtime <1-65535>", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "LDP Targeted Hellos\n" + "Targeted hello holdtime\n" + "Time (seconds) - 65535 implies infinite\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "hello_type", .value = "targeted-hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_holdtime (vty, args); +} + +DEFUN (ldp_no_discovery_targeted_hello_interval_disc_time, + ldp_no_discovery_targeted_hello_interval_disc_time_cmd, + "no discovery targeted-hello interval <1-65535>", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "LDP Targeted Hellos\n" + "Targeted hello interval\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "hello_type", .value = "targeted-hello" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_disc_interval (vty, args); +} + +DEFUN (ldp_no_dual_stack_transport_connection_prefer_ipv4, + ldp_no_dual_stack_transport_connection_prefer_ipv4_cmd, + "no dual-stack transport-connection prefer ipv4", + "Negate a command or set its defaults\n" + "Configure dual stack parameters\n" + "Configure TCP transport parameters\n" + "Configure prefered address family for TCP transport connection with neighbor\n" + "IPv4\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + NULL + }; + return ldp_vty_trans_pref_ipv4 (vty, args); +} + +DEFUN (ldp_no_dual_stack_cisco_interop, + ldp_no_dual_stack_cisco_interop_cmd, + "no dual-stack cisco-interop", + "Negate a command or set its defaults\n" + "Configure dual stack parameters\n" + "Use Cisco non-compliant format to send and interpret the Dual-Stack capability TLV\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + NULL + }; + return ldp_vty_ds_cisco_interop (vty, args); +} + +DEFUN (ldp_no_neighbor_ipv4_password_word, + ldp_no_neighbor_ipv4_password_word_cmd, + "no neighbor A.B.C.D password WORD", + "Negate a command or set its defaults\n" + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "Configure password for MD5 authentication\n" + "The password\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + &(struct vty_arg) { .name = "password", .value = argv[1] }, + NULL + }; + return ldp_vty_neighbor_password (vty, args); +} + +DEFUN (ldp_no_neighbor_ipv4_session_holdtime_session_time, + ldp_no_neighbor_ipv4_session_holdtime_session_time_cmd, + "no neighbor A.B.C.D session holdtime <15-65535>", + "Negate a command or set its defaults\n" + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "Configure session parameters\n" + "Configure session holdtime\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + &(struct vty_arg) { .name = "seconds", .value = argv[1] }, + NULL + }; + return ldp_vty_session_holdtime (vty, args); +} + +DEFUN (ldp_no_neighbor_ipv4_ttl_security_disable, + ldp_no_neighbor_ipv4_ttl_security_disable_cmd, + "no neighbor A.B.C.D ttl-security disable", + "Negate a command or set its defaults\n" + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "LDP ttl security check\n" + "Disable ttl security\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + NULL + }; + return ldp_vty_neighbor_ttl_security (vty, args); +} + +DEFUN (ldp_no_neighbor_ipv4_ttl_security_hops_hops, + ldp_no_neighbor_ipv4_ttl_security_hops_hops_cmd, + "no neighbor A.B.C.D ttl-security hops <1-254>", + "Negate a command or set its defaults\n" + "Configure neighbor parameters\n" + "LDP Id of neighbor\n" + "LDP ttl security check\n" + "IP hops\n" + "maximum number of hops\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "lsr_id", .value = argv[0] }, + &(struct vty_arg) { .name = "hops", .value = argv[1] }, + NULL + }; + return ldp_vty_neighbor_ttl_security (vty, args); +} + +DEFUN (ldp_no_router_id_ipv4, + ldp_no_router_id_ipv4_cmd, + "no router-id A.B.C.D", + "Negate a command or set its defaults\n" + "Configure router Id\n" + "LSR Id (in form of an IPv4 address)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_router_id (vty, args); +} + +DEFUN (ldp_discovery_targeted_hello_accept, + ldp_discovery_targeted_hello_accept_cmd, + "discovery targeted-hello accept", + "Configure discovery parameters\n" + "LDP Targeted Hellos\n" + "Accept and respond to targeted hellos\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "hello_type", .value = "targeted-hello" }, + NULL + }; + return ldp_vty_targeted_hello_accept (vty, args); +} + +DEFUN (ldp_label_local_advertise_explicit_null, + ldp_label_local_advertise_explicit_null_cmd, + "label local advertise explicit-null", + "Configure label control and policies\n" + "Configure local label control and policies\n" + "Configure outbound label advertisement control\n" + "Configure explicit-null advertisement\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_explicit_null (vty, args); +} + +DEFUN (ldp_ttl_security_disable, + ldp_ttl_security_disable_cmd, + "ttl-security disable", + "LDP ttl security check\n" + "Disable ttl security\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_ttl_security (vty, args); +} + +DEFUN (ldp_session_holdtime_session_time, + ldp_session_holdtime_session_time_cmd, + "session holdtime <15-65535>", + "Configure session parameters\n" + "Configure session holdtime\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_session_holdtime (vty, args); +} + +DEFUN (ldp_interface_ifname, + ldp_interface_ifname_cmd, + "interface IFNAME", + "Enable LDP on an interface and enter interface submode\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_interface (vty, args); +} + +DEFUN (ldp_discovery_transport_address_ipv4, + ldp_discovery_transport_address_ipv4_cmd, + "discovery transport-address A.B.C.D", + "Configure discovery parameters\n" + "Specify transport address for TCP connection\n" + "IP address to be used as transport address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_trans_addr (vty, args); +} + +DEFUN (ldp_neighbor_ipv4_targeted, + ldp_neighbor_ipv4_targeted_cmd, + "neighbor A.B.C.D targeted", + "Configure neighbor parameters\n" + "IP address of neighbor\n" + "Establish targeted session\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_neighbor_targeted (vty, args); +} + +DEFUN (ldp_no_discovery_targeted_hello_accept, + ldp_no_discovery_targeted_hello_accept_cmd, + "no discovery targeted-hello accept", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "LDP Targeted Hellos\n" + "Accept and respond to targeted hellos\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "hello_type", .value = "targeted-hello" }, + NULL + }; + return ldp_vty_targeted_hello_accept (vty, args); +} + +DEFUN (ldp_no_label_local_advertise_explicit_null, + ldp_no_label_local_advertise_explicit_null_cmd, + "no label local advertise explicit-null", + "Negate a command or set its defaults\n" + "Configure label control and policies\n" + "Configure local label control and policies\n" + "Configure outbound label advertisement control\n" + "Configure explicit-null advertisement\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + NULL + }; + return ldp_vty_explicit_null (vty, args); +} + +DEFUN (ldp_no_ttl_security_disable, + ldp_no_ttl_security_disable_cmd, + "no ttl-security disable", + "Negate a command or set its defaults\n" + "LDP ttl security check\n" + "Disable ttl security\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + NULL + }; + return ldp_vty_ttl_security (vty, args); +} + +DEFUN (ldp_no_session_holdtime_session_time, + ldp_no_session_holdtime_session_time_cmd, + "no session holdtime <15-65535>", + "Negate a command or set its defaults\n" + "Configure session parameters\n" + "Configure session holdtime\n" + "Time (seconds)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "seconds", .value = argv[0] }, + NULL + }; + return ldp_vty_session_holdtime (vty, args); +} + +DEFUN (ldp_no_interface_ifname, + ldp_no_interface_ifname_cmd, + "no interface IFNAME", + "Negate a command or set its defaults\n" + "Enable LDP on an interface and enter interface submode\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_interface (vty, args); +} + +DEFUN (ldp_no_discovery_transport_address_ipv4, + ldp_no_discovery_transport_address_ipv4_cmd, + "no discovery transport-address A.B.C.D", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "Specify transport address for TCP connection\n" + "IP address to be used as transport address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_trans_addr (vty, args); +} + +DEFUN (ldp_no_neighbor_ipv4_targeted, + ldp_no_neighbor_ipv4_targeted_cmd, + "no neighbor A.B.C.D targeted", + "Negate a command or set its defaults\n" + "Configure neighbor parameters\n" + "IP address of neighbor\n" + "Establish targeted session\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_neighbor_targeted (vty, args); +} + +DEFUN (ldp_discovery_transport_address_ipv6, + ldp_discovery_transport_address_ipv6_cmd, + "discovery transport-address X:X::X:X", + "Configure discovery parameters\n" + "Specify transport address for TCP connection\n" + "IPv6 address to be used as transport address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_trans_addr (vty, args); +} + +DEFUN (ldp_neighbor_ipv6_targeted, + ldp_neighbor_ipv6_targeted_cmd, + "neighbor X:X::X:X targeted", + "Configure neighbor parameters\n" + "IPv6 address of neighbor\n" + "Establish targeted session\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_neighbor_targeted (vty, args); +} + +DEFUN (ldp_no_discovery_transport_address_ipv6, + ldp_no_discovery_transport_address_ipv6_cmd, + "no discovery transport-address X:X::X:X", + "Negate a command or set its defaults\n" + "Configure discovery parameters\n" + "Specify transport address for TCP connection\n" + "IPv6 address to be used as transport address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_trans_addr (vty, args); +} + +DEFUN (ldp_no_neighbor_ipv6_targeted, + ldp_no_neighbor_ipv6_targeted_cmd, + "no neighbor X:X::X:X targeted", + "Negate a command or set its defaults\n" + "Configure neighbor parameters\n" + "IPv6 address of neighbor\n" + "Establish targeted session\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_neighbor_targeted (vty, args); +} + +DEFUN (ldp_bridge_ifname, + ldp_bridge_ifname_cmd, + "bridge IFNAME", + "Bridge interface\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_bridge (vty, args); +} + +DEFUN (ldp_mtu_mtu, + ldp_mtu_mtu_cmd, + "mtu <1500-9180>", + "set Maximum Transmission Unit\n" + "Maximum Transmission Unit value\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "mtu", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_mtu (vty, args); +} + +DEFUN (ldp_member_interface_ifname, + ldp_member_interface_ifname_cmd, + "member interface IFNAME", + "L2VPN member configuration\n" + "Local interface\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_interface (vty, args); +} + +DEFUN (ldp_member_pseudowire_ifname, + ldp_member_pseudowire_ifname_cmd, + "member pseudowire IFNAME", + "L2VPN member configuration\n" + "Pseudowire interface\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pseudowire (vty, args); +} + +DEFUN (ldp_vc_type_pwtype, + ldp_vc_type_pwtype_cmd, + "vc type (ethernet|ethernet-tagged)", + "Virtual Circuit options\n" + "Virtual Circuit type to use\n" + "Ethernet (type 5)\n" + "Ethernet-tagged (type 4)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pwtype (vty, args); +} + +DEFUN (ldp_no_bridge_ifname, + ldp_no_bridge_ifname_cmd, + "no bridge IFNAME", + "Negate a command or set its defaults\n" + "Bridge interface\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_bridge (vty, args); +} + +DEFUN (ldp_no_mtu_mtu, + ldp_no_mtu_mtu_cmd, + "no mtu <1500-9180>", + "Negate a command or set its defaults\n" + "set Maximum Transmission Unit\n" + "Maximum Transmission Unit value\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "mtu", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_mtu (vty, args); +} + +DEFUN (ldp_no_member_interface_ifname, + ldp_no_member_interface_ifname_cmd, + "no member interface IFNAME", + "Negate a command or set its defaults\n" + "L2VPN member configuration\n" + "Local interface\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_interface (vty, args); +} + +DEFUN (ldp_no_member_pseudowire_ifname, + ldp_no_member_pseudowire_ifname_cmd, + "no member pseudowire IFNAME", + "Negate a command or set its defaults\n" + "L2VPN member configuration\n" + "Pseudowire interface\n" + "Interface's name\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "ifname", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pseudowire (vty, args); +} + +DEFUN (ldp_no_vc_type_pwtype, + ldp_no_vc_type_pwtype_cmd, + "no vc type (ethernet|ethernet-tagged)", + "Negate a command or set its defaults\n" + "Virtual Circuit options\n" + "Virtual Circuit type to use\n" + "Ethernet (type 5)\n" + "Ethernet-tagged (type 4)\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pwtype (vty, args); +} + +DEFUN (ldp_control_word_cword, + ldp_control_word_cword_cmd, + "control-word (exclude|include)", + "Control-word options\n" + "Exclude control-word in pseudowire packets\n" + "Include control-word in pseudowire packets\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "preference", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_cword (vty, args); +} + +DEFUN (ldp_neighbor_address_addr, + ldp_neighbor_address_addr_cmd, + "neighbor address (A.B.C.D|X:X::X:X)", + "Remote endpoint configuration\n" + "Specify the IPv4 or IPv6 address of the remote endpoint\n" + "IPv4 address\n" + "IPv6 address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_nbr_addr (vty, args); +} + +DEFUN (ldp_neighbor_lsr_id_ipv4, + ldp_neighbor_lsr_id_ipv4_cmd, + "neighbor lsr-id A.B.C.D", + "Remote endpoint configuration\n" + "Specify the LSR-ID of the remote endpoint\n" + "IPv4 address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "lsr-id", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_nbr_id (vty, args); +} + +DEFUN (ldp_pw_id_pwid, + ldp_pw_id_pwid_cmd, + "pw-id <1-4294967295>", + "Set the Virtual Circuit ID\n" + "Virtual Circuit ID value\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "pwid", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_pwid (vty, args); +} + +DEFUN (ldp_pw_status_disable, + ldp_pw_status_disable_cmd, + "pw-status disable", + "Configure PW status\n" + "Disable PW status\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_l2vpn_pw_pwstatus (vty, args); +} + +DEFUN (ldp_no_control_word_cword, + ldp_no_control_word_cword_cmd, + "no control-word (exclude|include)", + "Negate a command or set its defaults\n" + "Control-word options\n" + "Exclude control-word in pseudowire packets\n" + "Include control-word in pseudowire packets\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "preference", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_cword (vty, args); +} + +DEFUN (ldp_no_neighbor_address_addr, + ldp_no_neighbor_address_addr_cmd, + "no neighbor address (A.B.C.D|X:X::X:X)", + "Negate a command or set its defaults\n" + "Remote endpoint configuration\n" + "Specify the IPv4 or IPv6 address of the remote endpoint\n" + "IPv4 address\n" + "IPv6 address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_nbr_addr (vty, args); +} + +DEFUN (ldp_no_neighbor_lsr_id_ipv4, + ldp_no_neighbor_lsr_id_ipv4_cmd, + "no neighbor lsr-id A.B.C.D", + "Negate a command or set its defaults\n" + "Remote endpoint configuration\n" + "Specify the LSR-ID of the remote endpoint\n" + "IPv4 address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "lsr-id", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_nbr_id (vty, args); +} + +DEFUN (ldp_no_pw_id_pwid, + ldp_no_pw_id_pwid_cmd, + "no pw-id <1-4294967295>", + "Negate a command or set its defaults\n" + "Set the Virtual Circuit ID\n" + "Virtual Circuit ID value\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "pwid", .value = argv[0] }, + NULL + }; + return ldp_vty_l2vpn_pw_pwid (vty, args); +} + +DEFUN (ldp_no_pw_status_disable, + ldp_no_pw_status_disable_cmd, + "no pw-status disable", + "Negate a command or set its defaults\n" + "Configure PW status\n" + "Disable PW status\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + NULL + }; + return ldp_vty_l2vpn_pw_pwstatus (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_neighbor, + ldp_show_mpls_ldp_neighbor_cmd, + "show mpls ldp neighbor", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Neighbor information\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_neighbor (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_binding, + ldp_show_mpls_ldp_binding_cmd, + "show mpls ldp binding", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Label Information Base (LIB) information\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_binding (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_discovery, + ldp_show_mpls_ldp_discovery_cmd, + "show mpls ldp discovery", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Discovery Hello Information\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_discovery (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_interface, + ldp_show_mpls_ldp_interface_cmd, + "show mpls ldp interface", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "interface information\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_interface (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_address_family_binding, + ldp_show_mpls_ldp_address_family_binding_cmd, + "show mpls ldp (ipv4|ipv6) binding", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "IPv4 Address Family\n" + "IPv6 Address Family\n" + "Label Information Base (LIB) information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "address-family", .value = argv[0] }, + NULL + }; + return ldp_vty_show_binding (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_address_family_discovery, + ldp_show_mpls_ldp_address_family_discovery_cmd, + "show mpls ldp (ipv4|ipv6) discovery", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "IPv4 Address Family\n" + "IPv6 Address Family\n" + "Discovery Hello Information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "address-family", .value = argv[0] }, + NULL + }; + return ldp_vty_show_discovery (vty, args); +} + +DEFUN (ldp_show_mpls_ldp_address_family_interface, + ldp_show_mpls_ldp_address_family_interface_cmd, + "show mpls ldp (ipv4|ipv6) interface", + "Show running system information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "IPv4 Address Family\n" + "IPv6 Address Family\n" + "interface information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "address-family", .value = argv[0] }, + NULL + }; + return ldp_vty_show_interface (vty, args); +} + +DEFUN (ldp_show_l2vpn_atom_binding, + ldp_show_l2vpn_atom_binding_cmd, + "show l2vpn atom binding", + "Show running system information\n" + "Show information about Layer2 VPN\n" + "Show Any Transport over MPLS information\n" + "Show AToM label binding information\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_atom_binding (vty, args); +} + +DEFUN (ldp_show_l2vpn_atom_vc, + ldp_show_l2vpn_atom_vc_cmd, + "show l2vpn atom vc", + "Show running system information\n" + "Show information about Layer2 VPN\n" + "Show Any Transport over MPLS information\n" + "Show AToM virtual circuit information\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_atom_vc (vty, args); +} + +DEFUN (ldp_show_debugging_mpls_ldp, + ldp_show_debugging_mpls_ldp_cmd, + "show debugging mpls ldp", + "Show running system information\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_show_debugging (vty, args); +} + +DEFUN (ldp_clear_mpls_ldp_neighbor, + ldp_clear_mpls_ldp_neighbor_cmd, + "clear mpls ldp neighbor", + "Reset functions\n" + "Reset MPLS statistical information\n" + "Clear LDP state\n" + "Clear LDP neighbor sessions\n") +{ + struct vty_arg *args[] = { NULL }; + return ldp_vty_clear_nbr (vty, args); +} + +DEFUN (ldp_clear_mpls_ldp_neighbor_addr, + ldp_clear_mpls_ldp_neighbor_addr_cmd, + "clear mpls ldp neighbor (A.B.C.D|X:X::X:X)", + "Reset functions\n" + "Reset MPLS statistical information\n" + "Clear LDP state\n" + "Clear LDP neighbor sessions\n" + "IPv4 address\n" + "IPv6 address\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "addr", .value = argv[0] }, + NULL + }; + return ldp_vty_clear_nbr (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_discovery_hello_dir, + ldp_debug_mpls_ldp_discovery_hello_dir_cmd, + "debug mpls ldp discovery hello (recv|sent)", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Discovery messages\n" + "Discovery hello message\n" + "Received messages\n" + "Sent messages\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "discovery" }, + &(struct vty_arg) { .name = "dir", .value = argv[0] }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_errors, + ldp_debug_mpls_ldp_errors_cmd, + "debug mpls ldp errors", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Errors\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "errors" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_event, + ldp_debug_mpls_ldp_event_cmd, + "debug mpls ldp event", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "LDP event information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "event" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_messages_recv, + ldp_debug_mpls_ldp_messages_recv_cmd, + "debug mpls ldp messages recv", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Received messages, excluding periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "recv" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_messages_recv_all, + ldp_debug_mpls_ldp_messages_recv_all_cmd, + "debug mpls ldp messages recv all", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Received messages, excluding periodic Keep Alives\n" + "Received messages, including periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "recv" }, + &(struct vty_arg) { .name = "all", .value = "all" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_messages_sent, + ldp_debug_mpls_ldp_messages_sent_cmd, + "debug mpls ldp messages sent", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Sent messages, excluding periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "sent" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_messages_sent_all, + ldp_debug_mpls_ldp_messages_sent_all_cmd, + "debug mpls ldp messages sent all", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Sent messages, excluding periodic Keep Alives\n" + "Sent messages, including periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "sent" }, + &(struct vty_arg) { .name = "all", .value = "all" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_debug_mpls_ldp_zebra, + ldp_debug_mpls_ldp_zebra_cmd, + "debug mpls ldp zebra", + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "LDP zebra information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "type", .value = "zebra" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_discovery_hello_dir, + ldp_no_debug_mpls_ldp_discovery_hello_dir_cmd, + "no debug mpls ldp discovery hello (recv|sent)", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Discovery messages\n" + "Discovery hello message\n" + "Received messages\n" + "Sent messages\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "discovery" }, + &(struct vty_arg) { .name = "dir", .value = argv[0] }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_errors, + ldp_no_debug_mpls_ldp_errors_cmd, + "no debug mpls ldp errors", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Errors\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "errors" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_event, + ldp_no_debug_mpls_ldp_event_cmd, + "no debug mpls ldp event", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "LDP event information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "event" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_messages_recv, + ldp_no_debug_mpls_ldp_messages_recv_cmd, + "no debug mpls ldp messages recv", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Received messages, excluding periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "recv" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_messages_recv_all, + ldp_no_debug_mpls_ldp_messages_recv_all_cmd, + "no debug mpls ldp messages recv all", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Received messages, excluding periodic Keep Alives\n" + "Received messages, including periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "recv" }, + &(struct vty_arg) { .name = "all", .value = "all" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_messages_sent, + ldp_no_debug_mpls_ldp_messages_sent_cmd, + "no debug mpls ldp messages sent", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Sent messages, excluding periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "sent" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_messages_sent_all, + ldp_no_debug_mpls_ldp_messages_sent_all_cmd, + "no debug mpls ldp messages sent all", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "Messages\n" + "Sent messages, excluding periodic Keep Alives\n" + "Sent messages, including periodic Keep Alives\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "messages" }, + &(struct vty_arg) { .name = "dir", .value = "sent" }, + &(struct vty_arg) { .name = "all", .value = "all" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +DEFUN (ldp_no_debug_mpls_ldp_zebra, + ldp_no_debug_mpls_ldp_zebra_cmd, + "no debug mpls ldp zebra", + "Negate a command or set its defaults\n" + "Debugging functions\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "LDP zebra information\n") +{ + struct vty_arg *args[] = + { + &(struct vty_arg) { .name = "no", .value = "no" }, + &(struct vty_arg) { .name = "type", .value = "zebra" }, + NULL + }; + return ldp_vty_debug (vty, args); +} + +void +ldp_vty_init (void) +{ + install_element (CONFIG_NODE, &ldp_mpls_ldp_cmd); + install_element (CONFIG_NODE, &ldp_l2vpn_word_type_vpls_cmd); + install_element (CONFIG_NODE, &ldp_no_mpls_ldp_cmd); + install_element (CONFIG_NODE, &ldp_no_l2vpn_word_type_vpls_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_discovery_hello_dir_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_errors_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_event_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_messages_recv_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_messages_recv_all_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_messages_sent_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_messages_sent_all_cmd); + install_element (CONFIG_NODE, &ldp_debug_mpls_ldp_zebra_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_discovery_hello_dir_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_errors_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_event_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_messages_recv_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_messages_recv_all_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_messages_sent_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_messages_sent_all_cmd); + install_element (CONFIG_NODE, &ldp_no_debug_mpls_ldp_zebra_cmd); + install_node (&ldp_node, ldp_config_write); + install_default (LDP_NODE); + install_element (LDP_NODE, &ldp_address_family_ipv4_cmd); + install_element (LDP_NODE, &ldp_address_family_ipv6_cmd); + install_element (LDP_NODE, &ldp_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_NODE, &ldp_discovery_hello_interval_disc_time_cmd); + install_element (LDP_NODE, &ldp_discovery_targeted_hello_holdtime_disc_time_cmd); + install_element (LDP_NODE, &ldp_discovery_targeted_hello_interval_disc_time_cmd); + install_element (LDP_NODE, &ldp_dual_stack_transport_connection_prefer_ipv4_cmd); + install_element (LDP_NODE, &ldp_dual_stack_cisco_interop_cmd); + install_element (LDP_NODE, &ldp_neighbor_ipv4_password_word_cmd); + install_element (LDP_NODE, &ldp_neighbor_ipv4_session_holdtime_session_time_cmd); + install_element (LDP_NODE, &ldp_neighbor_ipv4_ttl_security_disable_cmd); + install_element (LDP_NODE, &ldp_neighbor_ipv4_ttl_security_hops_hops_cmd); + install_element (LDP_NODE, &ldp_router_id_ipv4_cmd); + install_element (LDP_NODE, &ldp_no_address_family_ipv4_cmd); + install_element (LDP_NODE, &ldp_no_address_family_ipv6_cmd); + install_element (LDP_NODE, &ldp_no_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_NODE, &ldp_no_discovery_hello_interval_disc_time_cmd); + install_element (LDP_NODE, &ldp_no_discovery_targeted_hello_holdtime_disc_time_cmd); + install_element (LDP_NODE, &ldp_no_discovery_targeted_hello_interval_disc_time_cmd); + install_element (LDP_NODE, &ldp_no_dual_stack_transport_connection_prefer_ipv4_cmd); + install_element (LDP_NODE, &ldp_no_dual_stack_cisco_interop_cmd); + install_element (LDP_NODE, &ldp_no_neighbor_ipv4_password_word_cmd); + install_element (LDP_NODE, &ldp_no_neighbor_ipv4_session_holdtime_session_time_cmd); + install_element (LDP_NODE, &ldp_no_neighbor_ipv4_ttl_security_disable_cmd); + install_element (LDP_NODE, &ldp_no_neighbor_ipv4_ttl_security_hops_hops_cmd); + install_element (LDP_NODE, &ldp_no_router_id_ipv4_cmd); + install_node (&ldp_ipv4_node, NULL); + install_default (LDP_IPV4_NODE); + install_element (LDP_IPV4_NODE, &ldp_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_discovery_hello_interval_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_discovery_targeted_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_discovery_targeted_hello_interval_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_discovery_targeted_hello_accept_cmd); + install_element (LDP_IPV4_NODE, &ldp_label_local_advertise_explicit_null_cmd); + install_element (LDP_IPV4_NODE, &ldp_ttl_security_disable_cmd); + install_element (LDP_IPV4_NODE, &ldp_session_holdtime_session_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_interface_ifname_cmd); + install_element (LDP_IPV4_NODE, &ldp_discovery_transport_address_ipv4_cmd); + install_element (LDP_IPV4_NODE, &ldp_neighbor_ipv4_targeted_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_discovery_hello_interval_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_discovery_targeted_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_discovery_targeted_hello_interval_disc_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_discovery_targeted_hello_accept_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_label_local_advertise_explicit_null_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_ttl_security_disable_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_session_holdtime_session_time_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_interface_ifname_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_discovery_transport_address_ipv4_cmd); + install_element (LDP_IPV4_NODE, &ldp_no_neighbor_ipv4_targeted_cmd); + install_node (&ldp_ipv6_node, NULL); + install_default (LDP_IPV6_NODE); + install_element (LDP_IPV6_NODE, &ldp_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_discovery_hello_interval_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_discovery_targeted_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_discovery_targeted_hello_interval_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_discovery_targeted_hello_accept_cmd); + install_element (LDP_IPV6_NODE, &ldp_label_local_advertise_explicit_null_cmd); + install_element (LDP_IPV6_NODE, &ldp_ttl_security_disable_cmd); + install_element (LDP_IPV6_NODE, &ldp_session_holdtime_session_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_interface_ifname_cmd); + install_element (LDP_IPV6_NODE, &ldp_discovery_transport_address_ipv6_cmd); + install_element (LDP_IPV6_NODE, &ldp_neighbor_ipv6_targeted_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_discovery_hello_interval_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_discovery_targeted_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_discovery_targeted_hello_interval_disc_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_discovery_targeted_hello_accept_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_label_local_advertise_explicit_null_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_ttl_security_disable_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_session_holdtime_session_time_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_interface_ifname_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_discovery_transport_address_ipv6_cmd); + install_element (LDP_IPV6_NODE, &ldp_no_neighbor_ipv6_targeted_cmd); + install_node (&ldp_ipv4_iface_node, NULL); + install_default (LDP_IPV4_IFACE_NODE); + install_element (LDP_IPV4_IFACE_NODE, &ldp_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV4_IFACE_NODE, &ldp_discovery_hello_interval_disc_time_cmd); + install_element (LDP_IPV4_IFACE_NODE, &ldp_no_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV4_IFACE_NODE, &ldp_no_discovery_hello_interval_disc_time_cmd); + install_node (&ldp_ipv6_iface_node, NULL); + install_default (LDP_IPV6_IFACE_NODE); + install_element (LDP_IPV6_IFACE_NODE, &ldp_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV6_IFACE_NODE, &ldp_discovery_hello_interval_disc_time_cmd); + install_element (LDP_IPV6_IFACE_NODE, &ldp_no_discovery_hello_holdtime_disc_time_cmd); + install_element (LDP_IPV6_IFACE_NODE, &ldp_no_discovery_hello_interval_disc_time_cmd); + install_node (&ldp_l2vpn_node, ldp_l2vpn_config_write); + install_default (LDP_L2VPN_NODE); + install_element (LDP_L2VPN_NODE, &ldp_bridge_ifname_cmd); + install_element (LDP_L2VPN_NODE, &ldp_mtu_mtu_cmd); + install_element (LDP_L2VPN_NODE, &ldp_member_interface_ifname_cmd); + install_element (LDP_L2VPN_NODE, &ldp_member_pseudowire_ifname_cmd); + install_element (LDP_L2VPN_NODE, &ldp_vc_type_pwtype_cmd); + install_element (LDP_L2VPN_NODE, &ldp_no_bridge_ifname_cmd); + install_element (LDP_L2VPN_NODE, &ldp_no_mtu_mtu_cmd); + install_element (LDP_L2VPN_NODE, &ldp_no_member_interface_ifname_cmd); + install_element (LDP_L2VPN_NODE, &ldp_no_member_pseudowire_ifname_cmd); + install_element (LDP_L2VPN_NODE, &ldp_no_vc_type_pwtype_cmd); + install_node (&ldp_pseudowire_node, NULL); + install_default (LDP_PSEUDOWIRE_NODE); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_control_word_cword_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_neighbor_address_addr_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_neighbor_lsr_id_ipv4_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_pw_id_pwid_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_pw_status_disable_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_control_word_cword_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_neighbor_address_addr_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_neighbor_lsr_id_ipv4_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_pw_id_pwid_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_pw_status_disable_cmd); + install_node (&ldp_debug_node, ldp_debug_config_write); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_neighbor_cmd); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_binding_cmd); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_discovery_cmd); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_interface_cmd); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_address_family_binding_cmd); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_address_family_discovery_cmd); + install_element (ENABLE_NODE, &ldp_show_mpls_ldp_address_family_interface_cmd); + install_element (ENABLE_NODE, &ldp_show_l2vpn_atom_binding_cmd); + install_element (ENABLE_NODE, &ldp_show_l2vpn_atom_vc_cmd); + install_element (ENABLE_NODE, &ldp_show_debugging_mpls_ldp_cmd); + install_element (ENABLE_NODE, &ldp_clear_mpls_ldp_neighbor_cmd); + install_element (ENABLE_NODE, &ldp_clear_mpls_ldp_neighbor_addr_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_discovery_hello_dir_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_errors_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_event_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_messages_recv_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_messages_recv_all_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_messages_sent_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_messages_sent_all_cmd); + install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_zebra_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_discovery_hello_dir_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_errors_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_event_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_messages_recv_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_messages_recv_all_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_messages_sent_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_messages_sent_all_cmd); + install_element (ENABLE_NODE, &ldp_no_debug_mpls_ldp_zebra_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_neighbor_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_binding_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_discovery_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_interface_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_address_family_binding_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_address_family_discovery_cmd); + install_element (VIEW_NODE, &ldp_show_mpls_ldp_address_family_interface_cmd); + install_element (VIEW_NODE, &ldp_show_l2vpn_atom_binding_cmd); + install_element (VIEW_NODE, &ldp_show_l2vpn_atom_vc_cmd); + install_element (VIEW_NODE, &ldp_show_debugging_mpls_ldp_cmd); + install_element (VIEW_NODE, &ldp_clear_mpls_ldp_neighbor_cmd); + install_element (VIEW_NODE, &ldp_clear_mpls_ldp_neighbor_addr_cmd); +} \ No newline at end of file diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c new file mode 100644 index 0000000000..a3e1b9a250 --- /dev/null +++ b/ldpd/ldp_vty_conf.c @@ -0,0 +1,1637 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" + +#include "command.h" +#include "vrf.h" +#include "if.h" +#include "vty.h" +#include "ldp_vty.h" + +static int interface_config_write(struct vty *); +static void ldp_af_iface_config_write(struct vty *, int); +static void ldp_af_config_write(struct vty *, int, struct ldpd_conf *, + struct ldpd_af_conf *); +static void ldp_l2vpn_pw_config_write(struct vty *, struct l2vpn_pw *); +static int ldp_vty_get_af(struct vty *); +static int ldp_iface_is_configured(struct ldpd_conf *, const char *); +static int ldp_vty_nbr_session_holdtime(struct vty *, struct vty_arg *[]); +static int ldp_vty_af_session_holdtime(struct vty *, struct vty_arg *[]); + +static char vty_ifname[IF_NAMESIZE]; +static char vty_l2vpn_name[L2VPN_NAME_LEN]; +static char vty_pw_ifname[IF_NAMESIZE]; + +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", + 1 +}; + +struct cmd_node ldp_node = +{ + LDP_NODE, + "%s(config-ldp)# ", + 1, +}; + +struct cmd_node ldp_ipv4_node = +{ + LDP_IPV4_NODE, + "%s(config-ldp-af)# ", + 1, +}; + +struct cmd_node ldp_ipv6_node = +{ + LDP_IPV6_NODE, + "%s(config-ldp-af)# ", + 1, +}; + +struct cmd_node ldp_ipv4_iface_node = +{ + LDP_IPV4_IFACE_NODE, + "%s(config-ldp-af-if)# ", + 1, +}; + +struct cmd_node ldp_ipv6_iface_node = +{ + LDP_IPV6_IFACE_NODE, + "%s(config-ldp-af-if)# ", + 1, +}; + +struct cmd_node ldp_l2vpn_node = +{ + LDP_L2VPN_NODE, + "%s(config-l2vpn)# ", + 1, +}; + +struct cmd_node ldp_pseudowire_node = +{ + LDP_PSEUDOWIRE_NODE, + "%s(config-l2vpn-pw)# ", + 1, +}; + +int +ldp_get_address(const char *str, int *af, union ldpd_addr *addr) +{ + memset(addr, 0, sizeof(*addr)); + + if (inet_pton(AF_INET, str, &addr->v4) == 1) { + *af = AF_INET; + return (0); + } + + if (inet_pton(AF_INET6, str, &addr->v6) == 1) { + *af = AF_INET6; + return (0); + } + + return (-1); +} + +static int +interface_config_write(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + int write = 0; + + for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), node, ifp)) { + vty_out(vty, "!%s", VTY_NEWLINE); + vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE); + if (ifp->desc) + vty_out(vty, " description %s%s", ifp->desc, + VTY_NEWLINE); + + write++; + } + + return (write); +} + +static void +ldp_af_iface_config_write(struct vty *vty, int af) +{ + struct iface *iface; + struct iface_af *ia; + + LIST_FOREACH(iface, &ldpd_conf->iface_list, entry) { + ia = iface_af_get(iface, af); + if (!ia->enabled) + continue; + + vty_out(vty, " !%s", VTY_NEWLINE); + vty_out(vty, " interface %s%s", iface->name, VTY_NEWLINE); + + if (ia->hello_holdtime != LINK_DFLT_HOLDTIME && + ia->hello_holdtime != 0) + vty_out(vty, " discovery hello holdtime %u%s", + ia->hello_holdtime, VTY_NEWLINE); + if (ia->hello_interval != DEFAULT_HELLO_INTERVAL && + ia->hello_interval != 0) + vty_out(vty, " discovery hello interval %u%s", + ia->hello_interval, VTY_NEWLINE); + } +} + +static void +ldp_af_config_write(struct vty *vty, int af, struct ldpd_conf *conf, + struct ldpd_af_conf *af_conf) +{ + struct tnbr *tnbr; + + if (!(af_conf->flags & F_LDPD_AF_ENABLED)) + return; + + vty_out(vty, " !%s", VTY_NEWLINE); + vty_out(vty, " address-family %s%s", af_name(af), VTY_NEWLINE); + + if (af_conf->lhello_holdtime != LINK_DFLT_HOLDTIME && + af_conf->lhello_holdtime != 0 ) + vty_out(vty, " discovery hello holdtime %u%s", + af_conf->lhello_holdtime, VTY_NEWLINE); + if (af_conf->lhello_interval != DEFAULT_HELLO_INTERVAL && + af_conf->lhello_interval != 0) + vty_out(vty, " discovery hello interval %u%s", + af_conf->lhello_interval, VTY_NEWLINE); + + if (af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) + vty_out(vty, " discovery targeted-hello accept%s", + VTY_NEWLINE); + + if (af_conf->thello_holdtime != TARGETED_DFLT_HOLDTIME && + af_conf->thello_holdtime != 0) + vty_out(vty, " discovery targeted-hello holdtime %u%s", + af_conf->thello_holdtime, VTY_NEWLINE); + if (af_conf->thello_interval != DEFAULT_HELLO_INTERVAL && + af_conf->thello_interval != 0) + vty_out(vty, " discovery targeted-hello interval %u%s", + af_conf->thello_interval, VTY_NEWLINE); + + if (ldp_addrisset(af, &af_conf->trans_addr)) + vty_out(vty, " discovery transport-address %s%s", + log_addr(af, &af_conf->trans_addr), VTY_NEWLINE); + else + vty_out(vty, " ! Incomplete config, specify a discovery " + "transport-address%s", VTY_NEWLINE); + + if (af_conf->flags & F_LDPD_AF_EXPNULL) + vty_out(vty, " label local advertise explicit-null%s", + VTY_NEWLINE); + + if (af_conf->flags & F_LDPD_AF_NO_GTSM) + vty_out(vty, " ttl-security disable%s", VTY_NEWLINE); + + if (af_conf->keepalive != DEFAULT_KEEPALIVE) + vty_out(vty, " session holdtime %u%s", af_conf->keepalive, + VTY_NEWLINE); + + LIST_FOREACH(tnbr, &ldpd_conf->tnbr_list, entry) { + if (tnbr->af == af) { + vty_out(vty, " !%s", VTY_NEWLINE); + vty_out(vty, " neighbor %s targeted%s", + log_addr(tnbr->af, &tnbr->addr), VTY_NEWLINE); + } + } + + ldp_af_iface_config_write(vty, af); + + vty_out(vty, " !%s", VTY_NEWLINE); +} + +int +ldp_config_write(struct vty *vty) +{ + struct nbr_params *nbrp; + + if (!(ldpd_conf->flags & F_LDPD_ENABLED)) + return (0); + + vty_out(vty, "mpls ldp%s", VTY_NEWLINE); + + if (ldpd_conf->rtr_id.s_addr != 0) + vty_out(vty, " router-id %s%s", + inet_ntoa(ldpd_conf->rtr_id), VTY_NEWLINE); + + if (ldpd_conf->lhello_holdtime != LINK_DFLT_HOLDTIME && + ldpd_conf->lhello_holdtime != 0) + vty_out(vty, " discovery hello holdtime %u%s", + ldpd_conf->lhello_holdtime, VTY_NEWLINE); + if (ldpd_conf->lhello_interval != DEFAULT_HELLO_INTERVAL && + ldpd_conf->lhello_interval != 0) + vty_out(vty, " discovery hello interval %u%s", + ldpd_conf->lhello_interval, VTY_NEWLINE); + + if (ldpd_conf->thello_holdtime != TARGETED_DFLT_HOLDTIME && + ldpd_conf->thello_holdtime != 0) + vty_out(vty, " discovery targeted-hello holdtime %u%s", + ldpd_conf->thello_holdtime, VTY_NEWLINE); + if (ldpd_conf->thello_interval != DEFAULT_HELLO_INTERVAL && + ldpd_conf->thello_interval != 0) + vty_out(vty, " discovery targeted-hello interval %u%s", + ldpd_conf->thello_interval, VTY_NEWLINE); + + if (ldpd_conf->trans_pref == DUAL_STACK_LDPOV4) + vty_out(vty, " dual-stack transport-connection prefer ipv4%s", + VTY_NEWLINE); + + if (ldpd_conf->flags & F_LDPD_DS_CISCO_INTEROP) + vty_out(vty, " dual-stack cisco-interop%s", VTY_NEWLINE); + + LIST_FOREACH(nbrp, &ldpd_conf->nbrp_list, entry) { + if (nbrp->flags & F_NBRP_KEEPALIVE) + vty_out(vty, " neighbor %s session holdtime %u%s", + inet_ntoa(nbrp->lsr_id), nbrp->keepalive, + VTY_NEWLINE); + + if (nbrp->flags & F_NBRP_GTSM) { + if (nbrp->gtsm_enabled) + vty_out(vty, " neighbor %s ttl-security hops " + "%u%s", inet_ntoa(nbrp->lsr_id), + nbrp->gtsm_hops, VTY_NEWLINE); + else + vty_out(vty, " neighbor %s ttl-security " + "disable%s", inet_ntoa(nbrp->lsr_id), + VTY_NEWLINE); + } + + if (nbrp->auth.method == AUTH_MD5SIG) + vty_out(vty, " neighbor %s password %s%s", + inet_ntoa(nbrp->lsr_id), nbrp->auth.md5key, + VTY_NEWLINE); + } + + ldp_af_config_write(vty, AF_INET, ldpd_conf, &ldpd_conf->ipv4); + ldp_af_config_write(vty, AF_INET6, ldpd_conf, &ldpd_conf->ipv6); + vty_out(vty, " !%s", VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + return (1); +} + +static void +ldp_l2vpn_pw_config_write(struct vty *vty, struct l2vpn_pw *pw) +{ + int missing_lsrid = 0; + int missing_pwid = 0; + + vty_out(vty, " !%s", VTY_NEWLINE); + vty_out(vty, " member pseudowire %s%s", pw->ifname, VTY_NEWLINE); + + if (pw->lsr_id.s_addr != INADDR_ANY) + vty_out(vty, " neighbor lsr-id %s%s", inet_ntoa(pw->lsr_id), + VTY_NEWLINE); + else + missing_lsrid = 1; + + if (pw->flags & F_PW_STATIC_NBR_ADDR) + vty_out(vty, " neighbor address %s%s", log_addr(pw->af, + &pw->addr), VTY_NEWLINE); + + if (pw->pwid != 0) + vty_out(vty, " pw-id %u%s", pw->pwid, VTY_NEWLINE); + else + missing_pwid = 1; + + if (!(pw->flags & F_PW_CWORD_CONF)) + vty_out(vty, " control-word exclude%s", VTY_NEWLINE); + + if (!(pw->flags & F_PW_STATUSTLV_CONF)) + vty_out(vty, " pw-status disable%s", VTY_NEWLINE); + + if (missing_lsrid) + vty_out(vty, " ! Incomplete config, specify a neighbor " + "lsr-id%s", VTY_NEWLINE); + if (missing_pwid) + vty_out(vty, " ! Incomplete config, specify a pw-id%s", + VTY_NEWLINE); +} + +int +ldp_l2vpn_config_write(struct vty *vty) +{ + struct l2vpn *l2vpn; + struct l2vpn_if *lif; + struct l2vpn_pw *pw; + + LIST_FOREACH(l2vpn, &ldpd_conf->l2vpn_list, entry) { + vty_out(vty, "l2vpn %s type vpls%s", l2vpn->name, VTY_NEWLINE); + + if (l2vpn->pw_type != DEFAULT_PW_TYPE) + vty_out(vty, " vc type ethernet-tagged%s", VTY_NEWLINE); + + if (l2vpn->mtu != DEFAULT_L2VPN_MTU) + vty_out(vty, " mtu %u%s", l2vpn->mtu, VTY_NEWLINE); + + if (l2vpn->br_ifname[0] != '\0') + vty_out(vty, " bridge %s%s", l2vpn->br_ifname, + VTY_NEWLINE); + + LIST_FOREACH(lif, &l2vpn->if_list, entry) + vty_out(vty, " member interface %s%s", lif->ifname, + VTY_NEWLINE); + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + ldp_l2vpn_pw_config_write(vty, pw); + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) + ldp_l2vpn_pw_config_write(vty, pw); + + vty_out(vty, " !%s", VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + } + + return (0); +} + +static int +ldp_vty_get_af(struct vty *vty) +{ + switch (vty->node) { + case LDP_IPV4_NODE: + case LDP_IPV4_IFACE_NODE: + return (AF_INET); + case LDP_IPV6_NODE: + case LDP_IPV6_IFACE_NODE: + return (AF_INET6); + default: + fatalx("ldp_vty_get_af: unexpected node"); + } +} + +static int +ldp_iface_is_configured(struct ldpd_conf *xconf, const char *ifname) +{ + struct l2vpn *l2vpn; + + if (if_lookup_name(xconf, ifname)) + return (1); + + LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) { + if (l2vpn_if_find_name(l2vpn, ifname)) + return (1); + if (l2vpn_pw_find_name(l2vpn, ifname)) + return (1); + } + + return (0); +} + +int +ldp_vty_mpls_ldp(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + int disable; + + vty_conf = ldp_dup_config(ldpd_conf); + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + if (disable) + vty_conf->flags &= ~F_LDPD_ENABLED; + else { + vty->node = LDP_NODE; + vty_conf->flags |= F_LDPD_ENABLED; + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_address_family(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + int af; + const char *af_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + af_str = vty_get_arg_value(args, "address-family"); + + vty_conf = ldp_dup_config(ldpd_conf); + if (strcmp(af_str, "ipv4") == 0) { + af = AF_INET; + af_conf = &vty_conf->ipv4; + } else if (strcmp(af_str, "ipv6") == 0) { + af = AF_INET6; + af_conf = &vty_conf->ipv6; + } else + return (CMD_WARNING); + + if (disable) { + af_conf->flags &= ~F_LDPD_AF_ENABLED; + ldp_reload(vty_conf); + return (CMD_SUCCESS); + } + + switch (af) { + case AF_INET: + vty->node = LDP_IPV4_NODE; + break; + case AF_INET6: + vty->node = LDP_IPV6_NODE; + break; + default: + fatalx("ldp_vty_address_family: unknown af"); + } + af_conf->flags |= F_LDPD_AF_ENABLED; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_disc_holdtime(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + struct iface *iface; + struct iface_af *ia; + int af; + char *ep; + long int secs; + enum hello_type hello_type; + const char *seconds_str; + const char *hello_type_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + seconds_str = vty_get_arg_value(args, "seconds"); + hello_type_str = vty_get_arg_value(args, "hello_type"); + + secs = strtol(seconds_str, &ep, 10); + if (*ep != '\0' || secs < MIN_HOLDTIME || secs > MAX_HOLDTIME) { + vty_out(vty, "%% Invalid holdtime%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + if (hello_type_str[0] == 'h') + hello_type = HELLO_LINK; + else + hello_type = HELLO_TARGETED; + + vty_conf = ldp_dup_config(ldpd_conf); + + switch (vty->node) { + case LDP_NODE: + if (disable) { + switch (hello_type) { + case HELLO_LINK: + vty_conf->lhello_holdtime = LINK_DFLT_HOLDTIME; + break; + case HELLO_TARGETED: + vty_conf->thello_holdtime = + TARGETED_DFLT_HOLDTIME; + break; + } + } else { + switch (hello_type) { + case HELLO_LINK: + vty_conf->lhello_holdtime = secs; + break; + case HELLO_TARGETED: + vty_conf->thello_holdtime = secs; + break; + } + } + break; + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) { + switch (hello_type) { + case HELLO_LINK: + af_conf->lhello_holdtime = 0; + break; + case HELLO_TARGETED: + af_conf->thello_holdtime = 0; + break; + } + } else { + switch (hello_type) { + case HELLO_LINK: + af_conf->lhello_holdtime = secs; + break; + case HELLO_TARGETED: + af_conf->thello_holdtime = secs; + break; + } + } + break; + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + af = ldp_vty_get_af(vty); + iface = if_lookup_name(vty_conf, vty_ifname); + ia = iface_af_get(iface, af); + + if (disable) + ia->hello_holdtime = 0; + else + ia->hello_holdtime = secs; + break; + default: + fatalx("ldp_vty_disc_holdtime: unexpected node"); + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_disc_interval(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + struct iface *iface; + struct iface_af *ia; + int af; + char *ep; + long int secs; + enum hello_type hello_type; + const char *seconds_str; + const char *hello_type_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + seconds_str = vty_get_arg_value(args, "seconds"); + hello_type_str = vty_get_arg_value(args, "hello_type"); + + secs = strtol(seconds_str, &ep, 10); + if (*ep != '\0' || secs < MIN_HELLO_INTERVAL || + secs > MAX_HELLO_INTERVAL) { + vty_out(vty, "%% Invalid interval%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + if (hello_type_str[0] == 'h') + hello_type = HELLO_LINK; + else + hello_type = HELLO_TARGETED; + + vty_conf = ldp_dup_config(ldpd_conf); + + switch (vty->node) { + case LDP_NODE: + if (disable) { + switch (hello_type) { + case HELLO_LINK: + vty_conf->lhello_interval = LINK_DFLT_HOLDTIME; + break; + case HELLO_TARGETED: + vty_conf->thello_interval = + TARGETED_DFLT_HOLDTIME; + break; + } + } else { + switch (hello_type) { + case HELLO_LINK: + vty_conf->lhello_interval = secs; + break; + case HELLO_TARGETED: + vty_conf->thello_interval = secs; + break; + } + } + break; + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) { + switch (hello_type) { + case HELLO_LINK: + af_conf->lhello_interval = 0; + break; + case HELLO_TARGETED: + af_conf->thello_interval = 0; + break; + } + } else { + switch (hello_type) { + case HELLO_LINK: + af_conf->lhello_interval = secs; + break; + case HELLO_TARGETED: + af_conf->thello_interval = secs; + break; + } + } + break; + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + af = ldp_vty_get_af(vty); + iface = if_lookup_name(vty_conf, vty_ifname); + ia = iface_af_get(iface, af); + + if (disable) + ia->hello_interval = 0; + else + ia->hello_interval = secs; + break; + default: + fatalx("ldp_vty_disc_interval: unexpected node"); + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_targeted_hello_accept(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + int af; + int disable; + + vty_conf = ldp_dup_config(ldpd_conf); + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) + af_conf->flags &= ~F_LDPD_AF_THELLO_ACCEPT; + else + af_conf->flags |= F_LDPD_AF_THELLO_ACCEPT; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +static int +ldp_vty_nbr_session_holdtime(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + char *ep; + long int secs; + struct in_addr lsr_id; + struct nbr_params *nbrp; + const char *seconds_str; + const char *lsr_id_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + seconds_str = vty_get_arg_value(args, "seconds"); + lsr_id_str = vty_get_arg_value(args, "lsr_id"); + + if (inet_pton(AF_INET, lsr_id_str, &lsr_id) != 1 || + bad_addr_v4(lsr_id)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + nbrp = nbr_params_find(vty_conf, lsr_id); + + secs = strtol(seconds_str, &ep, 10); + if (*ep != '\0' || secs < MIN_KEEPALIVE || secs > MAX_KEEPALIVE) { + vty_out(vty, "%% Invalid holdtime%s", VTY_NEWLINE); + goto cancel; + } + + if (disable) { + if (nbrp == NULL) + goto cancel; + + nbrp->keepalive = 0; + nbrp->flags &= ~F_NBRP_KEEPALIVE; + } else { + if (nbrp == NULL) { + nbrp = nbr_params_new(lsr_id); + LIST_INSERT_HEAD(&vty_conf->nbrp_list, nbrp, entry); + } else if (nbrp->keepalive == secs) + goto cancel; + + nbrp->keepalive = secs; + nbrp->flags |= F_NBRP_KEEPALIVE; + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +static int +ldp_vty_af_session_holdtime(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + int af; + char *ep; + long int secs; + const char *seconds_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + seconds_str = vty_get_arg_value(args, "seconds"); + + secs = strtol(seconds_str, &ep, 10); + if (*ep != '\0' || secs < MIN_KEEPALIVE || secs > MAX_KEEPALIVE) { + vty_out(vty, "%% Invalid holdtime%s", VTY_NEWLINE); + return (CMD_SUCCESS); + } + + vty_conf = ldp_dup_config(ldpd_conf); + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) + af_conf->keepalive = DEFAULT_KEEPALIVE; + else + af_conf->keepalive = secs; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_session_holdtime(struct vty *vty, struct vty_arg *args[]) +{ + switch (vty->node) { + case LDP_NODE: + return (ldp_vty_nbr_session_holdtime(vty, args)); + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + return (ldp_vty_af_session_holdtime(vty, args)); + default: + fatalx("ldp_vty_session_holdtime: unexpected node"); + } +} + +int +ldp_vty_interface(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + int af; + struct iface *iface; + struct iface_af *ia; + struct interface *ifp; + struct kif kif; + const char *ifname; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + ifname = vty_get_arg_value(args, "ifname"); + + vty_conf = ldp_dup_config(ldpd_conf); + af = ldp_vty_get_af(vty); + iface = if_lookup_name(vty_conf, ifname); + + if (disable) { + if (iface == NULL) + goto cancel; + + ia = iface_af_get(iface, af); + if (ia->enabled == 0) + goto cancel; + + ia->enabled = 0; + ldp_reload(vty_conf); + return (CMD_SUCCESS); + } + + switch (af) { + case AF_INET: + vty->node = LDP_IPV4_IFACE_NODE; + break; + case AF_INET6: + vty->node = LDP_IPV6_IFACE_NODE; + break; + default: + break; + } + strlcpy(vty_ifname, ifname, sizeof(vty_ifname)); + + if (iface == NULL) { + if (ldp_iface_is_configured(vty_conf, ifname)) { + vty_out(vty, "%% Interface is already in use%s", + VTY_NEWLINE); + goto cancel; + } + + ifp = if_lookup_by_name(ifname); + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + if (ifp) { + kif.ifindex = ifp->ifindex; + kif.flags = ifp->flags; + } + iface = if_new(&kif); + + ia = iface_af_get(iface, af); + ia->enabled = 1; + LIST_INSERT_HEAD(&vty_conf->iface_list, iface, entry); + } else { + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + + ia = iface_af_get(iface, af); + if (ia->enabled) + goto cancel; + ia->enabled = 1; + } + + ldp_reload(vty_conf); + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_trans_addr(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + int af; + const char *addr_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + addr_str = vty_get_arg_value(args, "addr"); + + vty_conf = ldp_dup_config(ldpd_conf); + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) + memset(&af_conf->trans_addr, 0, sizeof(af_conf->trans_addr)); + else { + if (inet_pton(af, addr_str, &af_conf->trans_addr) != 1 || + bad_addr(af, &af_conf->trans_addr)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + goto cancel; + } + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_neighbor_targeted(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + int af; + union ldpd_addr addr; + struct tnbr *tnbr; + const char *addr_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + addr_str = vty_get_arg_value(args, "addr"); + + af = ldp_vty_get_af(vty); + + if (inet_pton(af, addr_str, &addr) != 1 || + bad_addr(af, &addr)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&addr.v6)) { + vty_out(vty, "%% Address can not be link-local%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + tnbr = tnbr_find(vty_conf, af, &addr); + + if (disable) { + if (tnbr == NULL) + goto cancel; + + LIST_REMOVE(tnbr, entry); + free(tnbr); + ldp_reload(vty_conf); + return (CMD_SUCCESS); + } + + if (tnbr) + goto cancel; + + tnbr = tnbr_new(af, &addr); + tnbr->flags |= F_TNBR_CONFIGURED; + LIST_INSERT_HEAD(&vty_conf->tnbr_list, tnbr, entry); + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_explicit_null(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + int af; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + vty_conf = ldp_dup_config(ldpd_conf); + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) + af_conf->flags &= ~F_LDPD_AF_EXPNULL; + else + af_conf->flags |= F_LDPD_AF_EXPNULL; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_ttl_security(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct ldpd_af_conf *af_conf; + int af; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + vty_conf = ldp_dup_config(ldpd_conf); + af = ldp_vty_get_af(vty); + af_conf = ldp_af_conf_get(vty_conf, af); + + if (disable) + af_conf->flags &= ~F_LDPD_AF_NO_GTSM; + else + af_conf->flags |= F_LDPD_AF_NO_GTSM; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_router_id(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + const char *addr_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + addr_str = vty_get_arg_value(args, "addr"); + + vty_conf = ldp_dup_config(ldpd_conf); + + if (disable) + vty_conf->rtr_id.s_addr = INADDR_ANY; + else { + if (inet_pton(AF_INET, addr_str, &vty_conf->rtr_id) != 1 || + bad_addr_v4(vty_conf->rtr_id)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + goto cancel; + } + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_ds_cisco_interop(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + vty_conf = ldp_dup_config(ldpd_conf); + + if (disable) + vty_conf->flags &= ~F_LDPD_DS_CISCO_INTEROP; + else + vty_conf->flags |= F_LDPD_DS_CISCO_INTEROP; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_trans_pref_ipv4(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + vty_conf = ldp_dup_config(ldpd_conf); + + if (disable) + vty_conf->trans_pref = DUAL_STACK_LDPOV6; + else + vty_conf->trans_pref = DUAL_STACK_LDPOV4; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_neighbor_password(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct in_addr lsr_id; + size_t password_len; + struct nbr_params *nbrp; + const char *lsr_id_str; + const char *password_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + lsr_id_str = vty_get_arg_value(args, "lsr_id"); + password_str = vty_get_arg_value(args, "password"); + + if (inet_pton(AF_INET, lsr_id_str, &lsr_id) != 1 || + bad_addr_v4(lsr_id)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + nbrp = nbr_params_find(vty_conf, lsr_id); + + if (disable) { + if (nbrp == NULL) + goto cancel; + + memset(&nbrp->auth, 0, sizeof(nbrp->auth)); + nbrp->auth.method = AUTH_NONE; + } else { + if (nbrp == NULL) { + nbrp = nbr_params_new(lsr_id); + LIST_INSERT_HEAD(&vty_conf->nbrp_list, nbrp, entry); + } else if (nbrp->auth.method == AUTH_MD5SIG && + strcmp(nbrp->auth.md5key, password_str) == 0) + goto cancel; + + password_len = strlcpy(nbrp->auth.md5key, password_str, + sizeof(nbrp->auth.md5key)); + if (password_len >= sizeof(nbrp->auth.md5key)) + vty_out(vty, "%% password has been truncated to %zu " + "characters.", sizeof(nbrp->auth.md5key) - 1); + nbrp->auth.md5key_len = password_len; + nbrp->auth.method = AUTH_MD5SIG; + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_neighbor_ttl_security(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct in_addr lsr_id; + struct nbr_params *nbrp; + long int hops = 0; + char *ep; + const char *lsr_id_str; + const char *hops_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + lsr_id_str = vty_get_arg_value(args, "lsr_id"); + hops_str = vty_get_arg_value(args, "hops"); + + if (inet_pton(AF_INET, lsr_id_str, &lsr_id) != 1 || + bad_addr_v4(lsr_id)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + if (hops_str) { + hops = strtol(hops_str, &ep, 10); + if (*ep != '\0' || hops < 1 || hops > 254) { + vty_out(vty, "%% Invalid hop count%s", VTY_NEWLINE); + return (CMD_SUCCESS); + } + } + + vty_conf = ldp_dup_config(ldpd_conf); + nbrp = nbr_params_find(vty_conf, lsr_id); + + if (disable) { + if (nbrp == NULL) + goto cancel; + + nbrp->flags &= ~(F_NBRP_GTSM|F_NBRP_GTSM_HOPS); + nbrp->gtsm_enabled = 0; + nbrp->gtsm_hops = 0; + } else { + if (nbrp == NULL) { + nbrp = nbr_params_new(lsr_id); + LIST_INSERT_HEAD(&vty_conf->nbrp_list, nbrp, entry); + } + + nbrp->flags |= F_NBRP_GTSM; + nbrp->flags &= ~F_NBRP_GTSM_HOPS; + if (hops_str) { + nbrp->gtsm_enabled = 1; + nbrp->gtsm_hops = hops; + nbrp->flags |= F_NBRP_GTSM_HOPS; + } else + nbrp->gtsm_enabled = 0; + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + const char *name_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + name_str = vty_get_arg_value(args, "name"); + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, name_str); + + if (disable) { + if (l2vpn == NULL) + goto cancel; + + LIST_REMOVE(l2vpn, entry); + l2vpn_del(l2vpn); + ldp_reload(vty_conf); + return (CMD_SUCCESS); + } + + vty->node = LDP_L2VPN_NODE; + strlcpy(vty_l2vpn_name, name_str, sizeof(vty_l2vpn_name)); + if (l2vpn) + goto cancel; + + l2vpn = l2vpn_new(name_str); + l2vpn->type = L2VPN_TYPE_VPLS; + LIST_INSERT_HEAD(&vty_conf->l2vpn_list, l2vpn, entry); + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_bridge(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + const char *ifname; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + ifname = vty_get_arg_value(args, "ifname"); + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + + if (disable) + memset(l2vpn->br_ifname, 0, sizeof(l2vpn->br_ifname)); + else + strlcpy(l2vpn->br_ifname, ifname, sizeof(l2vpn->br_ifname)); + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_mtu(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + char *ep; + int mtu; + const char *mtu_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + mtu_str = vty_get_arg_value(args, "mtu"); + + mtu = strtol(mtu_str, &ep, 10); + if (*ep != '\0' || mtu < MIN_L2VPN_MTU || mtu > MAX_L2VPN_MTU) { + vty_out(vty, "%% Invalid MTU%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + + if (disable) + l2vpn->mtu = DEFAULT_L2VPN_MTU; + else + l2vpn->mtu = mtu; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pwtype(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + int pw_type; + const char *type_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + type_str = vty_get_arg_value(args, "type"); + + if (strcmp(type_str, "ethernet") == 0) + pw_type = PW_TYPE_ETHERNET; + else + pw_type = PW_TYPE_ETHERNET_TAGGED; + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + + if (disable) + l2vpn->pw_type = DEFAULT_PW_TYPE; + else + l2vpn->pw_type = pw_type; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_interface(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_if *lif; + struct interface *ifp; + struct kif kif; + const char *ifname; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + ifname = vty_get_arg_value(args, "ifname"); + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + lif = l2vpn_if_find_name(l2vpn, ifname); + + if (disable) { + if (lif == NULL) + goto cancel; + + LIST_REMOVE(lif, entry); + free(lif); + ldp_reload(vty_conf); + return (CMD_SUCCESS); + } + + if (lif) + goto cancel; + + if (ldp_iface_is_configured(vty_conf, ifname)) { + vty_out(vty, "%% Interface is already in use%s", VTY_NEWLINE); + goto cancel; + } + + ifp = if_lookup_by_name(ifname); + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + if (ifp) { + kif.ifindex = ifp->ifindex; + kif.flags = ifp->flags; + } + + lif = l2vpn_if_new(l2vpn, &kif); + LIST_INSERT_HEAD(&l2vpn->if_list, lif, entry); + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + struct interface *ifp; + struct kif kif; + const char *ifname; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + ifname = vty_get_arg_value(args, "ifname"); + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + pw = l2vpn_pw_find_name(l2vpn, ifname); + + if (disable) { + if (pw == NULL) + goto cancel; + + LIST_REMOVE(pw, entry); + free(pw); + ldp_reload(vty_conf); + return (CMD_SUCCESS); + } + + if (pw) { + vty->node = LDP_PSEUDOWIRE_NODE; + strlcpy(vty_pw_ifname, ifname, sizeof(vty_pw_ifname)); + goto cancel; + } + + if (ldp_iface_is_configured(vty_conf, ifname)) { + vty_out(vty, "%% Interface is already in use%s", VTY_NEWLINE); + goto cancel; + } + + ifp = if_lookup_by_name(ifname); + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + if (ifp) { + kif.ifindex = ifp->ifindex; + kif.flags = ifp->flags; + } + + pw = l2vpn_pw_new(l2vpn, &kif); + pw->flags = F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF; + LIST_INSERT_HEAD(&l2vpn->pw_inactive_list, pw, entry); + + ldp_reload(vty_conf); + + vty->node = LDP_PSEUDOWIRE_NODE; + strlcpy(vty_pw_ifname, ifname, sizeof(vty_pw_ifname)); + return (CMD_SUCCESS); + +cancel: + ldp_clear_config(vty_conf); + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pw_cword(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + const char *preference_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + preference_str = vty_get_arg_value(args, "preference"); + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + + if (disable) + pw->flags |= F_PW_CWORD_CONF; + else { + if (preference_str[0] == 'e') + pw->flags &= ~F_PW_CWORD_CONF; + else + pw->flags |= F_PW_CWORD_CONF; + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pw_nbr_addr(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + int af; + union ldpd_addr addr; + const char *addr_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + addr_str = vty_get_arg_value(args, "addr"); + + if (ldp_get_address(addr_str, &af, &addr) == -1 || + bad_addr(af, &addr)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + + if (disable) { + pw->af = AF_UNSPEC; + memset(&pw->addr, 0, sizeof(pw->addr)); + pw->flags &= ~F_PW_STATIC_NBR_ADDR; + } else { + pw->af = af; + pw->addr = addr; + pw->flags |= F_PW_STATIC_NBR_ADDR; + } + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pw_nbr_id(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + struct in_addr lsr_id; + const char *lsr_id_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + lsr_id_str = vty_get_arg_value(args, "lsr-id"); + + if (inet_pton(AF_INET, lsr_id_str, &lsr_id) != 1 || + bad_addr_v4(lsr_id)) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + + if (disable) + pw->lsr_id.s_addr = INADDR_ANY; + else + pw->lsr_id = lsr_id; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pw_pwid(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + char *ep; + uint32_t pwid; + const char *pwid_str; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + pwid_str = vty_get_arg_value(args, "pwid"); + + pwid = strtol(pwid_str, &ep, 10); + if (*ep != '\0' || pwid < MIN_PWID_ID || pwid > MAX_PWID_ID) { + vty_out(vty, "%% Invalid pw-id%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + + if (disable) + pw->pwid = 0; + else + pw->pwid = pwid; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +int +ldp_vty_l2vpn_pw_pwstatus(struct vty *vty, struct vty_arg *args[]) +{ + struct ldpd_conf *vty_conf; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + int disable; + + disable = (vty_get_arg_value(args, "no")) ? 1 : 0; + + vty_conf = ldp_dup_config(ldpd_conf); + l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + + if (disable) + pw->flags |= F_PW_STATUSTLV_CONF; + else + pw->flags &= ~F_PW_STATUSTLV_CONF; + + ldp_reload(vty_conf); + + return (CMD_SUCCESS); +} + +void +ldp_vty_if_init(void) +{ + /* Install interface node. */ + install_node (&interface_node, interface_config_write); + + install_element(CONFIG_NODE, &interface_cmd); + install_element(CONFIG_NODE, &no_interface_cmd); + install_default(INTERFACE_NODE); + + /* "description" commands. */ + install_element(INTERFACE_NODE, &interface_desc_cmd); + install_element(INTERFACE_NODE, &no_interface_desc_cmd); +} diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c new file mode 100644 index 0000000000..a57cf3c3f6 --- /dev/null +++ b/ldpd/ldp_vty_exec.c @@ -0,0 +1,667 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" +#include "ldp_vty.h" + +#include "command.h" +#include "vty.h" +#include "mpls.h" + +enum show_command { + SHOW_DISC, + SHOW_IFACE, + SHOW_NBR, + SHOW_LIB, + SHOW_L2VPN_PW, + SHOW_L2VPN_BINDING +}; + +struct show_filter { + int family; + union ldpd_addr addr; + uint8_t prefixlen; +}; + +#define LDPBUFSIZ 65535 + +static int show_interface_msg(struct vty *, struct imsg *, + struct show_filter *); +static void show_discovery_adj(struct vty *, char *, + struct ctl_adj *); +static int show_discovery_msg(struct vty *, struct imsg *, + struct show_filter *); +static void show_nbr_adj(struct vty *, char *, struct ctl_adj *); +static int show_nbr_msg(struct vty *, struct imsg *, + struct show_filter *); +static int show_lib_msg(struct vty *, struct imsg *, + struct show_filter *); +static int show_l2vpn_binding_msg(struct vty *, struct imsg *); +static int show_l2vpn_pw_msg(struct vty *, struct imsg *); +static int ldp_vty_connect(struct imsgbuf *); +static int ldp_vty_dispatch(struct vty *, struct imsgbuf *, + enum show_command, struct show_filter *); +static int ldp_vty_get_af(const char *, int *); + +static int +show_interface_msg(struct vty *vty, struct imsg *imsg, + struct show_filter *filter) +{ + struct ctl_iface *iface; + char timers[BUFSIZ]; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_INTERFACE: + iface = imsg->data; + + if (filter->family != AF_UNSPEC && filter->family != iface->af) + break; + + snprintf(timers, sizeof(timers), "%u/%u", + iface->hello_interval, iface->hello_holdtime); + + vty_out(vty, "%-4s %-11s %-6s %-8s %-12s %3u%s", + af_name(iface->af), iface->name, + if_state_name(iface->state), iface->uptime == 0 ? + "00:00:00" : log_time(iface->uptime), timers, + iface->adj_cnt, VTY_NEWLINE); + break; + case IMSG_CTL_END: + vty_out(vty, "%s", VTY_NEWLINE); + return (1); + default: + break; + } + + return (0); +} + +static void +show_discovery_adj(struct vty *vty, char *buffer, struct ctl_adj *adj) +{ + size_t buflen = strlen(buffer); + + snprintf(buffer + buflen, LDPBUFSIZ - buflen, + " LDP Id: %s:0, Transport address: %s%s", + inet_ntoa(adj->id), log_addr(adj->af, + &adj->trans_addr), VTY_NEWLINE); + buflen = strlen(buffer); + snprintf(buffer + buflen, LDPBUFSIZ - buflen, + " Hold time: %u sec%s", adj->holdtime, VTY_NEWLINE); +} + +static int +show_discovery_msg(struct vty *vty, struct imsg *imsg, + struct show_filter *filter) +{ + struct ctl_adj *adj; + struct ctl_disc_if *iface; + struct ctl_disc_tnbr *tnbr; + struct in_addr rtr_id; + union ldpd_addr *trans_addr; + size_t buflen; + static char ifaces_buffer[LDPBUFSIZ]; + static char tnbrs_buffer[LDPBUFSIZ]; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_DISCOVERY: + ifaces_buffer[0] = '\0'; + tnbrs_buffer[0] = '\0'; + break; + case IMSG_CTL_SHOW_DISC_IFACE: + iface = imsg->data; + + if (filter->family != AF_UNSPEC && + ((filter->family == AF_INET && !iface->active_v4) || + (filter->family == AF_INET6 && !iface->active_v6))) + break; + + buflen = strlen(ifaces_buffer); + snprintf(ifaces_buffer + buflen, LDPBUFSIZ - buflen, + " %s: %s%s", iface->name, (iface->no_adj) ? + "xmit" : "xmit/recv", VTY_NEWLINE); + break; + case IMSG_CTL_SHOW_DISC_TNBR: + tnbr = imsg->data; + + if (filter->family != AF_UNSPEC && filter->family != tnbr->af) + break; + + trans_addr = &(ldp_af_conf_get(ldpd_conf, + tnbr->af))->trans_addr; + buflen = strlen(tnbrs_buffer); + snprintf(tnbrs_buffer + buflen, LDPBUFSIZ - buflen, + " %s -> %s: %s%s", log_addr(tnbr->af, trans_addr), + log_addr(tnbr->af, &tnbr->addr), (tnbr->no_adj) ? "xmit" : + "xmit/recv", VTY_NEWLINE); + break; + case IMSG_CTL_SHOW_DISC_ADJ: + adj = imsg->data; + + if (filter->family != AF_UNSPEC && filter->family != adj->af) + break; + + switch(adj->type) { + case HELLO_LINK: + show_discovery_adj(vty, ifaces_buffer, adj); + break; + case HELLO_TARGETED: + show_discovery_adj(vty, tnbrs_buffer, adj); + break; + } + break; + case IMSG_CTL_END: + rtr_id.s_addr = ldp_rtr_id_get(ldpd_conf); + vty_out(vty, "Local LDP Identifier: %s:0%s", inet_ntoa(rtr_id), + VTY_NEWLINE); + vty_out(vty, "Discovery Sources:%s", VTY_NEWLINE); + vty_out(vty, " Interfaces:%s", VTY_NEWLINE); + vty_out(vty, "%s", ifaces_buffer); + vty_out(vty, " Targeted Hellos:%s", VTY_NEWLINE); + vty_out(vty, "%s", tnbrs_buffer); + vty_out(vty, "%s", VTY_NEWLINE); + return (1); + default: + break; + } + + return (0); +} + +static void +show_nbr_adj(struct vty *vty, char *buffer, struct ctl_adj *adj) +{ + size_t buflen = strlen(buffer); + + switch (adj->type) { + case HELLO_LINK: + snprintf(buffer + buflen, LDPBUFSIZ - buflen, + " Interface: %s%s", adj->ifname, VTY_NEWLINE); + break; + case HELLO_TARGETED: + snprintf(buffer + buflen, LDPBUFSIZ - buflen, + " Targeted Hello: %s%s", log_addr(adj->af, + &adj->src_addr), VTY_NEWLINE); + break; + } +} + +static int +show_nbr_msg(struct vty *vty, struct imsg *imsg, struct show_filter *filter) +{ + struct ctl_adj *adj; + struct ctl_nbr *nbr; + static char v4adjs_buffer[LDPBUFSIZ]; + static char v6adjs_buffer[LDPBUFSIZ]; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_NBR: + nbr = imsg->data; + + v4adjs_buffer[0] = '\0'; + v6adjs_buffer[0] = '\0'; + vty_out(vty, "Peer LDP Identifier: %s:0%s", inet_ntoa(nbr->id), + VTY_NEWLINE); + vty_out(vty, " TCP connection: %s:%u - %s:%u%s", + log_addr(nbr->af, &nbr->laddr), ntohs(nbr->lport), + log_addr(nbr->af, &nbr->raddr), ntohs(nbr->rport), + VTY_NEWLINE); + vty_out(vty, " Session Holdtime: %u sec%s", nbr->holdtime, + VTY_NEWLINE); + vty_out(vty, " State: %s; Downstream-Unsolicited%s", + nbr_state_name(nbr->nbr_state), VTY_NEWLINE); + vty_out(vty, " Up time: %s%s", log_time(nbr->uptime), + VTY_NEWLINE); + break; + case IMSG_CTL_SHOW_NBR_DISC: + adj = imsg->data; + + switch (adj->af) { + case AF_INET: + show_nbr_adj(vty, v4adjs_buffer, adj); + break; + case AF_INET6: + show_nbr_adj(vty, v6adjs_buffer, adj); + break; + default: + fatalx("show_nbr_msg: unknown af"); + } + break; + case IMSG_CTL_SHOW_NBR_END: + vty_out(vty, " LDP Discovery Sources:%s", VTY_NEWLINE); + if (v4adjs_buffer[0] != '\0') { + vty_out(vty, " IPv4:%s", VTY_NEWLINE); + vty_out(vty, "%s", v4adjs_buffer); + } + if (v6adjs_buffer[0] != '\0') { + vty_out(vty, " IPv6:%s", VTY_NEWLINE); + vty_out(vty, "%s", v6adjs_buffer); + } + vty_out(vty, "%s", VTY_NEWLINE); + break; + case IMSG_CTL_END: + return (1); + default: + break; + } + + return (0); +} + +static int +show_lib_msg(struct vty *vty, struct imsg *imsg, struct show_filter *filter) +{ + struct ctl_rt *rt; + char dstnet[BUFSIZ]; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_LIB: + rt = imsg->data; + + if (filter->family != AF_UNSPEC && filter->family != rt->af) + break; + + snprintf(dstnet, sizeof(dstnet), "%s/%d", + log_addr(rt->af, &rt->prefix), rt->prefixlen); + + if (rt->first) { + vty_out(vty, "%s%s", dstnet, VTY_NEWLINE); + vty_out(vty, "%-8sLocal binding: label: %s%s", "", + log_label(rt->local_label), VTY_NEWLINE); + + if (rt->remote_label != NO_LABEL) { + vty_out(vty, "%-8sRemote bindings:%s", "", + VTY_NEWLINE); + vty_out(vty, "%-12sPeer Label%s", + "", VTY_NEWLINE); + vty_out(vty, "%-12s----------------- " + "---------%s", "", VTY_NEWLINE); + } else + vty_out(vty, "%-8sNo remote bindings%s", "", + VTY_NEWLINE); + } + if (rt->remote_label != NO_LABEL) + vty_out(vty, "%12s%-20s%s%s", "", inet_ntoa(rt->nexthop), + log_label(rt->remote_label), VTY_NEWLINE); + break; + case IMSG_CTL_END: + vty_out(vty, "%s", VTY_NEWLINE); + return (1); + default: + break; + } + + return (0); +} + +static int +show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg) +{ + struct ctl_pw *pw; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_L2VPN_BINDING: + pw = imsg->data; + + vty_out(vty, " Destination Address: %s, VC ID: %u%s", + inet_ntoa(pw->lsr_id), pw->pwid, VTY_NEWLINE); + + /* local binding */ + if (pw->local_label != NO_LABEL) { + vty_out(vty, " Local Label: %u%s", pw->local_label, + VTY_NEWLINE); + vty_out(vty, "%-8sCbit: %u, VC Type: %s, " + "GroupID: %u%s", "", pw->local_cword, + pw_type_name(pw->type), pw->local_gid, + VTY_NEWLINE); + vty_out(vty, "%-8sMTU: %u%s", "", pw->local_ifmtu, + VTY_NEWLINE); + } else + vty_out(vty, " Local Label: unassigned%s", + VTY_NEWLINE); + + /* remote binding */ + if (pw->remote_label != NO_LABEL) { + vty_out(vty, " Remote Label: %u%s", + pw->remote_label, VTY_NEWLINE); + vty_out(vty, "%-8sCbit: %u, VC Type: %s, " + "GroupID: %u%s", "", pw->remote_cword, + pw_type_name(pw->type), pw->remote_gid, + VTY_NEWLINE); + vty_out(vty, "%-8sMTU: %u%s", "", pw->remote_ifmtu, + VTY_NEWLINE); + } else + vty_out(vty, " Remote Label: unassigned%s", + VTY_NEWLINE); + break; + case IMSG_CTL_END: + vty_out(vty, "%s", VTY_NEWLINE); + return (1); + default: + break; + } + + return (0); +} + +static int +show_l2vpn_pw_msg(struct vty *vty, struct imsg *imsg) +{ + struct ctl_pw *pw; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_L2VPN_PW: + pw = imsg->data; + + vty_out(vty, "%-9s %-15s %-10u %-16s %-10s%s", pw->ifname, + inet_ntoa(pw->lsr_id), pw->pwid, pw->l2vpn_name, + (pw->status ? "UP" : "DOWN"), VTY_NEWLINE); + break; + case IMSG_CTL_END: + vty_out(vty, "%s", VTY_NEWLINE); + return (1); + default: + break; + } + + return (0); +} + +static int +ldp_vty_connect(struct imsgbuf *ibuf) +{ + struct sockaddr_un s_un; + int ctl_sock; + + /* connect to ldpd control socket */ + if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + log_warn("%s: socket", __func__); + return (-1); + } + + memset(&s_un, 0, sizeof(s_un)); + s_un.sun_family = AF_UNIX; + strlcpy(s_un.sun_path, LDPD_SOCKET, sizeof(s_un.sun_path)); + if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { + log_warn("%s: connect: %s", __func__, LDPD_SOCKET); + close(ctl_sock); + return (-1); + } + + imsg_init(ibuf, ctl_sock); + + return (0); +} + +static int +ldp_vty_dispatch(struct vty *vty, struct imsgbuf *ibuf, enum show_command cmd, + struct show_filter *filter) +{ + struct imsg imsg; + int n, done = 0; + + while (ibuf->w.queued) + if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) { + log_warn("write error"); + close(ibuf->fd); + return (CMD_WARNING); + } + + while (!done) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) { + log_warnx("imsg_read error"); + close(ibuf->fd); + return (CMD_WARNING); + } + if (n == 0) { + log_warnx("pipe closed"); + close(ibuf->fd); + return (CMD_WARNING); + } + + while (!done) { + if ((n = imsg_get(ibuf, &imsg)) == -1) { + log_warnx("imsg_get error"); + close(ibuf->fd); + return (CMD_WARNING); + } + if (n == 0) + break; + switch (cmd) { + case SHOW_IFACE: + done = show_interface_msg(vty, &imsg, filter); + break; + case SHOW_DISC: + done = show_discovery_msg(vty, &imsg, filter); + break; + case SHOW_NBR: + done = show_nbr_msg(vty, &imsg, filter); + break; + case SHOW_LIB: + done = show_lib_msg(vty, &imsg, filter); + break; + case SHOW_L2VPN_PW: + done = show_l2vpn_pw_msg(vty, &imsg); + break; + case SHOW_L2VPN_BINDING: + done = show_l2vpn_binding_msg(vty, &imsg); + break; + default: + break; + } + imsg_free(&imsg); + } + } + + close(ibuf->fd); + + return (CMD_SUCCESS); +} + +static int +ldp_vty_get_af(const char *str, int *af) +{ + if (str == NULL) { + *af = AF_UNSPEC; + return (0); + } else if (strcmp(str, "ipv4") == 0) { + *af = AF_INET; + return (0); + } else if (strcmp(str, "ipv6") == 0) { + *af = AF_INET6; + return (0); + } + + return (-1); +} + +int +ldp_vty_show_binding(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + struct show_filter filter; + const char *af_str; + int af; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_SHOW_LIB, 0, 0, -1, NULL, 0); + + af_str = vty_get_arg_value(args, "address-family"); + if (ldp_vty_get_af(af_str, &af) < 0) + return (CMD_ERR_NO_MATCH); + + memset(&filter, 0, sizeof(filter)); + filter.family = af; + + return (ldp_vty_dispatch(vty, &ibuf, SHOW_LIB, &filter)); +} + +int +ldp_vty_show_discovery(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + struct show_filter filter; + const char *af_str; + int af; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0); + + af_str = vty_get_arg_value(args, "address-family"); + if (ldp_vty_get_af(af_str, &af) < 0) + return (CMD_ERR_NO_MATCH); + + memset(&filter, 0, sizeof(filter)); + filter.family = af; + + return (ldp_vty_dispatch(vty, &ibuf, SHOW_DISC, &filter)); +} + +int +ldp_vty_show_interface(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + struct show_filter filter; + unsigned int ifidx = 0; + const char *af_str; + int af; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, &ifidx, + sizeof(ifidx)); + + af_str = vty_get_arg_value(args, "address-family"); + if (ldp_vty_get_af(af_str, &af) < 0) + return (CMD_ERR_NO_MATCH); + + memset(&filter, 0, sizeof(filter)); + filter.family = af; + + /* header */ + vty_out(vty, "%-4s %-11s %-6s %-8s %-12s %3s%s", "AF", + "Interface", "State", "Uptime", "Hello Timers", "ac", VTY_NEWLINE); + + return (ldp_vty_dispatch(vty, &ibuf, SHOW_IFACE, &filter)); +} + +int +ldp_vty_show_neighbor(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + struct show_filter filter; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0); + + /* not used */ + memset(&filter, 0, sizeof(filter)); + + return (ldp_vty_dispatch(vty, &ibuf, SHOW_NBR, &filter)); +} + +int +ldp_vty_show_atom_binding(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + struct show_filter filter; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_SHOW_L2VPN_BINDING, 0, 0, -1, NULL, 0); + + /* not used */ + memset(&filter, 0, sizeof(filter)); + + return (ldp_vty_dispatch(vty, &ibuf, SHOW_L2VPN_BINDING, &filter)); +} + +int +ldp_vty_show_atom_vc(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + struct show_filter filter; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_SHOW_L2VPN_PW, 0, 0, -1, NULL, 0); + + /* not used */ + memset(&filter, 0, sizeof(filter)); + + /* header */ + vty_out(vty, "%-9s %-15s %-10s %-16s %-10s%s", + "Interface", "Peer ID", "VC ID", "Name", "Status", VTY_NEWLINE); + vty_out(vty, "%-9s %-15s %-10s %-16s %-10s%s", + "---------", "---------------", "----------", + "----------------", "----------", VTY_NEWLINE); + + return (ldp_vty_dispatch(vty, &ibuf, SHOW_L2VPN_PW, &filter)); +} + +int +ldp_vty_clear_nbr(struct vty *vty, struct vty_arg *args[]) +{ + struct imsgbuf ibuf; + const char *addr_str; + struct ctl_nbr nbr; + + addr_str = vty_get_arg_value(args, "addr"); + + memset(&nbr, 0, sizeof(nbr)); + if (addr_str && + (ldp_get_address(addr_str, &nbr.af, &nbr.raddr) == -1 || + bad_addr(nbr.af, &nbr.raddr))) { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return (CMD_WARNING); + } + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + imsg_compose(&ibuf, IMSG_CTL_CLEAR_NBR, 0, 0, -1, &nbr, sizeof(nbr)); + + while (ibuf.w.queued) + if (msgbuf_write(&ibuf.w) <= 0 && errno != EAGAIN) { + log_warn("write error"); + close(ibuf.fd); + return (CMD_WARNING); + } + + close(ibuf.fd); + + return (CMD_SUCCESS); +} diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c new file mode 100644 index 0000000000..41ff47fba3 --- /dev/null +++ b/ldpd/ldp_zebra.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2016 by Open Source Routing. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "prefix.h" +#include "stream.h" +#include "memory.h" +#include "zclient.h" +#include "command.h" +#include "network.h" +#include "linklist.h" + +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "log.h" +#include "ldp_debug.h" + +static void ifp2kif(struct interface *, struct kif *); +static void ifc2kaddr(struct interface *, struct connected *, + struct kaddr *); +static int ldp_router_id_update(int, struct zclient *, zebra_size_t, + vrf_id_t); +static int ldp_interface_add(int, struct zclient *, zebra_size_t, + vrf_id_t); +static int ldp_interface_delete(int, struct zclient *, zebra_size_t, + vrf_id_t); +static int ldp_interface_status_change(int command, struct zclient *, + zebra_size_t, vrf_id_t); +static int ldp_interface_address_add(int, struct zclient *, zebra_size_t, + vrf_id_t); +static int ldp_interface_address_delete(int, struct zclient *, + zebra_size_t, vrf_id_t); +static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t, + vrf_id_t); +static void ldp_zebra_connected(struct zclient *); + +static struct zclient *zclient; + +static void +ifp2kif(struct interface *ifp, struct kif *kif) +{ + memset(kif, 0, sizeof(*kif)); + strlcpy(kif->ifname, ifp->name, sizeof(kif->ifname)); + kif->ifindex = ifp->ifindex; + kif->flags = ifp->flags; +} + +static void +ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka) +{ + memset(ka, 0, sizeof(*ka)); + ka->ifindex = ifp->ifindex; + ka->af = ifc->address->family; + ka->prefixlen = ifc->address->prefixlen; + + switch (ka->af) { + case AF_INET: + ka->addr.v4 = ifc->address->u.prefix4; + if (ifc->destination) + ka->dstbrd.v4 = ifc->destination->u.prefix4; + break; + case AF_INET6: + ka->addr.v6 = ifc->address->u.prefix6; + if (ifc->destination) + ka->dstbrd.v6 = ifc->destination->u.prefix6; + break; + default: + break; + } +} + +int +kr_change(struct kroute *kr) +{ + /* TODO */ + return (0); +} + +int +kr_delete(struct kroute *kr) +{ + /* TODO */ + return (0); +} + +int +kmpw_set(struct kpw *kpw) +{ + /* TODO */ + return (0); +} + +int +kmpw_unset(struct kpw *kpw) +{ + /* TODO */ + return (0); +} + +void +kif_redistribute(const char *ifname) +{ + struct listnode *node, *cnode; + struct interface *ifp; + struct connected *ifc; + struct kif kif; + struct kaddr ka; + + for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp)) { + if (ifname && strcmp(ifname, ifp->name) != 0) + continue; + + ifp2kif(ifp, &kif); + main_imsg_compose_ldpe(IMSG_IFSTATUS, 0, &kif, sizeof(kif)); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { + ifc2kaddr(ifp, ifc, &ka); + main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, + sizeof(ka)); + } + } +} + +static int +ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) +{ + struct prefix router_id; + + zebra_router_id_update_read(zclient->ibuf, &router_id); + + if (bad_addr_v4(router_id.u.prefix4)) + return (0); + + debug_zebra_in("router-id update %s", inet_ntoa(router_id.u.prefix4)); + + global.rtr_id.s_addr = router_id.u.prefix4.s_addr; + main_imsg_compose_ldpe(IMSG_RTRID_UPDATE, 0, &global.rtr_id, + sizeof(global.rtr_id)); + + return (0); +} + +static int +ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) +{ + struct interface *ifp; + struct kif kif; + + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + debug_zebra_in("interface add %s index %d mtu %d", ifp->name, + ifp->ifindex, ifp->mtu); + + ifp2kif(ifp, &kif); + main_imsg_compose_ldpe(IMSG_IFSTATUS, 0, &kif, sizeof(kif)); + + return (0); +} + +static int +ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* zebra_interface_state_read() updates interface structure in iflist */ + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (ifp == NULL) + return (0); + + debug_zebra_in("interface delete %s index %d mtu %d", ifp->name, + ifp->ifindex, ifp->mtu); + + /* To support pseudo interface do not free interface structure. */ + /* if_delete(ifp); */ + ifp->ifindex = IFINDEX_INTERNAL; + + return (0); +} + +static int +ldp_interface_status_change(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct listnode *node; + struct connected *ifc; + struct kif kif; + struct kaddr ka; + int link_new; + + /* + * zebra_interface_state_read() updates interface structure in + * iflist. + */ + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (ifp == NULL) + return (0); + + debug_zebra_in("interface %s state update", ifp->name); + + ifp2kif(ifp, &kif); + main_imsg_compose_ldpe(IMSG_IFSTATUS, 0, &kif, sizeof(kif)); + + link_new = (ifp->flags & IFF_UP) && (ifp->flags & IFF_RUNNING); + if (link_new) { + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + ifc2kaddr(ifp, ifc, &ka); + main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, + sizeof(ka)); + } + } else { + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + ifc2kaddr(ifp, ifc, &ka); + main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, + sizeof(ka)); + } + } + + return (0); +} + +static int +ldp_interface_address_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *ifc; + struct interface *ifp; + struct kaddr ka; + + ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + if (ifc == NULL) + return (0); + + ifp = ifc->ifp; + ifc2kaddr(ifp, ifc, &ka); + + /* Filter invalid addresses. */ + if (bad_addr(ka.af, &ka.addr)) + return (0); + + debug_zebra_in("address add %s/%u", log_addr(ka.af, &ka.addr), + ka.prefixlen); + + /* notify ldpe about new address */ + main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka)); + + return (0); +} + +static int +ldp_interface_address_delete(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *ifc; + struct interface *ifp; + struct kaddr ka; + + ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + if (ifc == NULL) + return (0); + + ifp = ifc->ifp; + ifc2kaddr(ifp, ifc, &ka); + connected_free(ifc); + + /* Filter invalid addresses. */ + if (bad_addr(ka.af, &ka.addr)) + return (0); + + debug_zebra_in("address delete %s/%u", log_addr(ka.af, &ka.addr), + ka.prefixlen); + + /* notify ldpe about removed address */ + main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, sizeof(ka)); + + return (0); +} + +static int +ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) +{ + struct stream *s; + u_char type; + u_char message_flags; + struct kroute kr; + int nhnum, nhlen; + size_t nhmark; + + memset(&kr, 0, sizeof(kr)); + s = zclient->ibuf; + + type = stream_getc(s); + if (type == ZEBRA_ROUTE_CONNECT) + kr.flags |= F_CONNECTED; + stream_getc(s); /* flags, unused */ + stream_getw(s); /* instance, unused */ + message_flags = stream_getc(s); + if (!CHECK_FLAG(message_flags, ZAPI_MESSAGE_NEXTHOP)) + return (0); + + switch (command) { + case ZEBRA_IPV4_ROUTE_ADD: + case ZEBRA_REDISTRIBUTE_IPV4_ADD: + case ZEBRA_IPV4_ROUTE_DELETE: + case ZEBRA_REDISTRIBUTE_IPV4_DEL: + kr.af = AF_INET; + nhlen = sizeof(struct in_addr); + break; + case ZEBRA_IPV6_ROUTE_ADD: + case ZEBRA_REDISTRIBUTE_IPV6_ADD: + case ZEBRA_IPV6_ROUTE_DELETE: + case ZEBRA_REDISTRIBUTE_IPV6_DEL: + kr.af = AF_INET6; + nhlen = sizeof(struct in6_addr); + break; + default: + fatalx("ldp_zebra_read_route: unknown command"); + } + kr.prefixlen = stream_getc(s); + stream_get(&kr.prefix, s, PSIZE(kr.prefixlen)); + + if (bad_addr(kr.af, &kr.prefix) || + (kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6))) + return (0); + + nhnum = stream_getc(s); + nhmark = stream_get_getp(s); + stream_set_getp(s, nhmark + nhnum * (nhlen + 5)); + + if (CHECK_FLAG(message_flags, ZAPI_MESSAGE_DISTANCE)) + kr.priority = stream_getc(s); + if (CHECK_FLAG(message_flags, ZAPI_MESSAGE_METRIC)) + stream_getl(s); /* metric, not used */ + + stream_set_getp(s, nhmark); + + /* loop through all the nexthops */ + for (; nhnum > 0; nhnum--) { + switch (kr.af) { + case AF_INET: + kr.nexthop.v4.s_addr = stream_get_ipv4(s); + break; + case AF_INET6: + stream_get(&kr.nexthop.v6, s, sizeof(kr.nexthop.v6)); + break; + default: + break; + } + stream_getc(s); /* ifindex_num, unused. */ + kr.ifindex = stream_getl(s); + + switch (command) { + case ZEBRA_IPV4_ROUTE_ADD: + case ZEBRA_REDISTRIBUTE_IPV4_ADD: + case ZEBRA_IPV6_ROUTE_ADD: + case ZEBRA_REDISTRIBUTE_IPV6_ADD: + debug_zebra_in("route add %s/%d nexthop %s (%s)", + log_addr(kr.af, &kr.prefix), kr.prefixlen, + log_addr(kr.af, &kr.nexthop), + zebra_route_string(type)); + main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr, + sizeof(kr)); + break; + case ZEBRA_IPV4_ROUTE_DELETE: + case ZEBRA_REDISTRIBUTE_IPV4_DEL: + case ZEBRA_IPV6_ROUTE_DELETE: + case ZEBRA_REDISTRIBUTE_IPV6_DEL: + debug_zebra_in("route delete %s/%d nexthop %s (%s)", + log_addr(kr.af, &kr.prefix), kr.prefixlen, + log_addr(kr.af, &kr.nexthop), + zebra_route_string(type)); + main_imsg_compose_lde(IMSG_NETWORK_DEL, 0, &kr, + sizeof(kr)); + break; + default: + fatalx("ldp_zebra_read_route: unknown command"); + } + } + + return (0); +} + +static void +ldp_zebra_connected(struct zclient *zclient) +{ + int i; + + zclient_send_reg_requests(zclient, VRF_DEFAULT); + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + switch (i) { + case ZEBRA_ROUTE_KERNEL: + case ZEBRA_ROUTE_CONNECT: + case ZEBRA_ROUTE_STATIC: + case ZEBRA_ROUTE_ISIS: + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, + AFI_IP, i, 0, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, + AFI_IP6, i, 0, VRF_DEFAULT); + break; + case ZEBRA_ROUTE_RIP: + case ZEBRA_ROUTE_OSPF: + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, + AFI_IP, i, 0, VRF_DEFAULT); + break; + case ZEBRA_ROUTE_RIPNG: + case ZEBRA_ROUTE_OSPF6: + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, + AFI_IP6, i, 0, VRF_DEFAULT); + break; + case ZEBRA_ROUTE_BGP: + /* LDP should follow the IGP and ignore BGP routes */ + default: + break; + } + } +} + +void +ldp_zebra_init(struct thread_master *master) +{ + /* Set default values. */ + zclient = zclient_new(master); + zclient_init(zclient, ZEBRA_ROUTE_LDP, 0); + + /* set callbacks */ + zclient->zebra_connected = ldp_zebra_connected; + zclient->router_id_update = ldp_router_id_update; + zclient->interface_add = ldp_interface_add; + zclient->interface_delete = ldp_interface_delete; + zclient->interface_up = ldp_interface_status_change; + zclient->interface_down = ldp_interface_status_change; + zclient->interface_address_add = ldp_interface_address_add; + zclient->interface_address_delete = ldp_interface_address_delete; + zclient->ipv4_route_add = ldp_zebra_read_route; + zclient->ipv4_route_delete = ldp_zebra_read_route; + zclient->redistribute_route_ipv4_add = ldp_zebra_read_route; + zclient->redistribute_route_ipv4_del = ldp_zebra_read_route; + zclient->ipv6_route_add = ldp_zebra_read_route; + zclient->ipv6_route_delete = ldp_zebra_read_route; + zclient->redistribute_route_ipv6_add = ldp_zebra_read_route; + zclient->redistribute_route_ipv6_del = ldp_zebra_read_route; +} diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 4e14491989..1eaa7df4a5 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -19,36 +19,40 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include "ldpd.h" #include "ldpe.h" #include "lde.h" #include "log.h" +#include "ldp_vty.h" +#include "ldp_debug.h" -static void main_sig_handler(int, short, void *); -static __dead void usage(void); -static __dead void ldpd_shutdown(void); -static pid_t start_child(enum ldpd_process, char *, int, int, int); -static void main_dispatch_ldpe(int, short, void *); -static void main_dispatch_lde(int, short, void *); -static int main_imsg_compose_both(enum imsg_type, void *, - uint16_t); +#include +#include +#include "getopt.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "vrf.h" + +static void ldpd_shutdown(void); +static pid_t start_child(enum ldpd_process, char *, int, + const char *, const char *); +static int main_dispatch_ldpe(struct thread *); +static int main_dispatch_lde(struct thread *); static int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *); static void main_imsg_send_net_sockets(int); static void main_imsg_send_net_socket(int, enum socket_type); static int main_imsg_send_config(struct ldpd_conf *); -static int ldp_reload(void); +static void ldp_config_normalize(struct ldpd_conf *); +static void ldp_config_reset_main(struct ldpd_conf *); +static void ldp_config_reset_af(struct ldpd_conf *, int); static void merge_global(struct ldpd_conf *, struct ldpd_conf *); static void merge_af(int, struct ldpd_af_conf *, struct ldpd_af_conf *); @@ -63,84 +67,216 @@ static void merge_l2vpn(struct ldpd_conf *, struct l2vpn *, struct ldpd_global global; struct ldpd_conf *ldpd_conf; -static char *conffile; static struct imsgev *iev_ldpe; static struct imsgev *iev_lde; static pid_t ldpe_pid; static pid_t lde_pid; -/* ARGSUSED */ -static void -main_sig_handler(int sig, short event, void *arg) +#define LDP_DEFAULT_CONFIG "ldpd.conf" +#define LDP_VTY_PORT 2612 + +/* Master of threads. */ +struct thread_master *master; + +/* Process ID saved for use by init system */ +static const char *pid_file = PATH_LDPD_PID; + +/* Configuration filename and directory. */ +static char config_default[] = SYSCONFDIR LDP_DEFAULT_CONFIG; + +/* ldpd privileges */ +static zebra_capabilities_t _caps_p [] = { - /* signal handler rules don't apply, libevent decouples for us */ - switch (sig) { - case SIGTERM: - case SIGINT: - ldpd_shutdown(); - /* NOTREACHED */ - case SIGHUP: - if (ldp_reload() == -1) - log_warnx("configuration reload failed"); - else - log_debug("configuration reloaded"); - break; - default: - fatalx("unexpected signal"); - /* NOTREACHED */ + ZCAP_BIND, + ZCAP_NET_ADMIN +}; + +struct zebra_privs_t ldpd_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0 +}; + +/* LDPd options. */ +static struct option longopts[] = +{ + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "pid_file", required_argument, NULL, 'i'}, + { "socket", required_argument, NULL, 'z'}, + { "dryrun", no_argument, NULL, 'C'}, + { "help", no_argument, NULL, 'h'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +/* Help information display. */ +static void __attribute__ ((noreturn)) +usage(char *progname, int status) +{ + if (status != 0) + fprintf(stderr, "Try `%s --help' for more information.\n", + progname); + else { + printf("Usage : %s [OPTION...]\n\ +Daemon which manages LDP.\n\n\ +-d, --daemon Runs in daemon mode\n\ +-f, --config_file Set configuration file name\n\ +-i, --pid_file Set process identifier file name\n\ +-z, --socket Set path of zebra socket\n\ +-A, --vty_addr Set vty's bind address\n\ +-P, --vty_port Set vty's port number\n\ +-u, --user User to run as\n\ +-g, --group Group to run as\n\ +-v, --version Print program version\n\ +-C, --dryrun Check configuration for validity and exit\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } + + exit(status); } -static __dead void -usage(void) +/* SIGHUP handler. */ +static void +sighup(void) { - extern char *__progname; - - fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", - __progname); - exit(1); + log_info("SIGHUP received"); } +/* SIGINT / SIGTERM handler. */ +static void +sigint(void) +{ + log_info("SIGINT received"); + ldpd_shutdown(); +} + +/* SIGUSR1 handler. */ +static void +sigusr1(void) +{ + zlog_rotate(NULL); +} + +static struct quagga_signal_t ldp_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + } +}; + int main(int argc, char *argv[]) { - struct event ev_sigint, ev_sigterm, ev_sighup; char *saved_argv0; - int ch; - int debug = 0, lflag = 0, eflag = 0; + int lflag = 0, eflag = 0; int pipe_parent2ldpe[2]; int pipe_parent2lde[2]; + char *p; + char *vty_addr = NULL; + int vty_port = LDP_VTY_PORT; + int daemon_mode = 0; + const char *user = NULL; + const char *group = NULL; + char *config_file = NULL; + char *progname; + struct thread thread; + int dryrun = 0; - conffile = CONF_FILE; ldpd_process = PROC_MAIN; - log_init(1); /* log to stderr until daemonized */ - log_verbose(1); + /* Set umask before anything for security */ + umask(0027); + + /* get program name */ + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); saved_argv0 = argv[0]; if (saved_argv0 == NULL) - saved_argv0 = "ldpd"; + saved_argv0 = (char *)"ldpd"; - while ((ch = getopt(argc, argv, "dD:f:nvLE")) != -1) { - switch (ch) { - case 'd': - debug = 1; + while (1) { + int opt; + + opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:vCLE", + longopts, 0); + + if (opt == EOF) break; - case 'D': - if (cmdline_symset(optarg) < 0) - log_warnx("could not parse macro definition %s", - optarg); + + switch (opt) { + case 0: + break; + case 'd': + daemon_mode = 1; break; case 'f': - conffile = optarg; + config_file = optarg; break; - case 'n': - global.cmd_opts |= LDPD_OPT_NOACTION; + case 'A': + vty_addr = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zclient_serv_path_set(optarg); + break; + case 'P': + /* + * Deal with atoi() returning 0 on failure, and ldpd + * not listening on ldpd port. + */ + if (strcmp(optarg, "0") == 0) { + vty_port = 0; + break; + } + vty_port = atoi(optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = LDP_VTY_PORT; + break; + case 'u': + user = optarg; + break; + case 'g': + group = optarg; break; case 'v': - if (global.cmd_opts & LDPD_OPT_VERBOSE) - global.cmd_opts |= LDPD_OPT_VERBOSE2; - global.cmd_opts |= LDPD_OPT_VERBOSE; + print_version(progname); + exit(0); + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage(progname, 0); break; case 'L': lflag = 1; @@ -149,125 +285,132 @@ main(int argc, char *argv[]) eflag = 1; break; default: - usage(); - /* NOTREACHED */ + usage(progname, 1); + break; } } argc -= optind; argv += optind; if (argc > 0 || (lflag && eflag)) - usage(); + usage(progname, 1); - if (lflag) - lde(debug, global.cmd_opts & LDPD_OPT_VERBOSE); - else if (eflag) - ldpe(debug, global.cmd_opts & LDPD_OPT_VERBOSE); - - /* fetch interfaces early */ - kif_init(); - - /* parse config file */ - if ((ldpd_conf = parse_config(conffile)) == NULL ) { - kif_clear(); + /* check for root privileges */ + if (geteuid() != 0) { + errno = EPERM; + perror(progname); exit(1); } - if (global.cmd_opts & LDPD_OPT_NOACTION) { - if (global.cmd_opts & LDPD_OPT_VERBOSE) - print_config(ldpd_conf); - else - fprintf(stderr, "configuration OK\n"); - kif_clear(); + zlog_default = openzlog(progname, ZLOG_LDP, 0, + LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + + if (lflag) + lde(user, group); + else if (eflag) + ldpe(user, group); + + master = thread_master_create(); + + cmd_init(1); + vty_init(master); + vrf_init(); + ldp_vty_init(); + ldp_vty_if_init(); + + /* Get configuration file. */ + ldpd_conf = config_new_empty(); + ldp_config_reset_main(ldpd_conf); + vty_read_config(config_file, config_default); + + /* Start execution only if not in dry-run mode */ + if (dryrun) exit(0); + + if (daemon_mode && daemon(0, 0) < 0) { + log_warn("LDPd daemon failed"); + exit(1); } - /* check for root privileges */ - if (geteuid()) - errx(1, "need root privileges"); - - /* check for ldpd user */ - if (getpwnam(LDPD_USER) == NULL) - errx(1, "unknown user %s", LDPD_USER); - - log_init(debug); - log_verbose(global.cmd_opts & (LDPD_OPT_VERBOSE | LDPD_OPT_VERBOSE2)); - - if (!debug) - daemon(1, 0); - - log_info("startup"); - - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, - PF_UNSPEC, pipe_parent2ldpe) == -1) + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2ldpe) == -1) fatal("socketpair"); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, - PF_UNSPEC, pipe_parent2lde) == -1) + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2lde) == -1) fatal("socketpair"); + sock_set_nonblock(pipe_parent2ldpe[0]); + sock_set_cloexec(pipe_parent2ldpe[0]); + sock_set_nonblock(pipe_parent2ldpe[1]); + sock_set_cloexec(pipe_parent2ldpe[1]); + sock_set_nonblock(pipe_parent2lde[0]); + sock_set_cloexec(pipe_parent2lde[0]); + sock_set_nonblock(pipe_parent2lde[1]); + sock_set_cloexec(pipe_parent2lde[1]); /* start children */ lde_pid = start_child(PROC_LDE_ENGINE, saved_argv0, - pipe_parent2lde[1], debug, global.cmd_opts & LDPD_OPT_VERBOSE); + pipe_parent2lde[1], user, group); ldpe_pid = start_child(PROC_LDP_ENGINE, saved_argv0, - pipe_parent2ldpe[1], debug, global.cmd_opts & LDPD_OPT_VERBOSE); + pipe_parent2ldpe[1], user, group); - event_init(); + /* drop privileges */ + if (user) + ldpd_privs.user = user; + if (group) + ldpd_privs.group = group; + zprivs_init(&ldpd_privs); /* setup signal handler */ - signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); - signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); - signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); - signal_add(&ev_sigint, NULL); - signal_add(&ev_sigterm, NULL); - signal_add(&ev_sighup, NULL); - signal(SIGPIPE, SIG_IGN); + signal_init(master, array_size(ldp_signals), ldp_signals); + + /* library inits */ + ldp_zebra_init(master); /* setup pipes to children */ if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL || (iev_lde = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]); - iev_ldpe->handler = main_dispatch_ldpe; + iev_ldpe->handler_read = main_dispatch_ldpe; + iev_ldpe->ev_read = thread_add_read(master, iev_ldpe->handler_read, + iev_ldpe, iev_ldpe->ibuf.fd); + iev_ldpe->handler_write = ldp_write_handler; + iev_ldpe->ev_write = NULL; + imsg_init(&iev_lde->ibuf, pipe_parent2lde[0]); - iev_lde->handler = main_dispatch_lde; - - /* setup event handler */ - iev_ldpe->events = EV_READ; - event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd, iev_ldpe->events, - iev_ldpe->handler, iev_ldpe); - event_add(&iev_ldpe->ev, NULL); - - iev_lde->events = EV_READ; - event_set(&iev_lde->ev, iev_lde->ibuf.fd, iev_lde->events, - iev_lde->handler, iev_lde); - event_add(&iev_lde->ev, NULL); + iev_lde->handler_read = main_dispatch_lde; + iev_lde->ev_read = thread_add_read(master, iev_lde->handler_read, + iev_lde, iev_lde->ibuf.fd); + iev_lde->handler_write = ldp_write_handler; + iev_lde->ev_write = NULL; if (main_imsg_send_ipc_sockets(&iev_ldpe->ibuf, &iev_lde->ibuf)) fatal("could not establish imsg links"); + main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, + sizeof(ldp_debug)); main_imsg_send_config(ldpd_conf); - /* notify ldpe about existing interfaces and addresses */ - kif_redistribute(NULL); - - if (kr_init(!(ldpd_conf->flags & F_LDPD_NO_FIB_UPDATE)) == -1) - fatalx("kr_init failed"); - if (ldpd_conf->ipv4.flags & F_LDPD_AF_ENABLED) main_imsg_send_net_sockets(AF_INET); if (ldpd_conf->ipv6.flags & F_LDPD_AF_ENABLED) main_imsg_send_net_sockets(AF_INET6); - /* remove unneded stuff from config */ - /* ... */ + /* Process id file create. */ + pid_output(pid_file); - event_dispatch(); + /* Create VTY socket */ + vty_serv_sock(vty_addr, vty_port, LDP_VTYSH_PATH); + + /* Print banner. */ + log_notice("LDPd %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); - ldpd_shutdown(); /* NOTREACHED */ return (0); } -static __dead void +static void ldpd_shutdown(void) { pid_t pid; @@ -279,7 +422,6 @@ ldpd_shutdown(void) msgbuf_clear(&iev_lde->ibuf.w); close(iev_lde->ibuf.fd); - kr_shutdown(); config_clear(ldpd_conf); log_debug("waiting for children to terminate"); @@ -302,9 +444,10 @@ ldpd_shutdown(void) } static pid_t -start_child(enum ldpd_process p, char *argv0, int fd, int debug, int verbose) +start_child(enum ldpd_process p, char *argv0, int fd, const char *user, + const char *group) { - char *argv[5]; + char *argv[7]; int argc = 0; pid_t pid; @@ -326,16 +469,20 @@ start_child(enum ldpd_process p, char *argv0, int fd, int debug, int verbose) case PROC_MAIN: fatalx("Can not start main process"); case PROC_LDE_ENGINE: - argv[argc++] = "-L"; + argv[argc++] = (char *)"-L"; break; case PROC_LDP_ENGINE: - argv[argc++] = "-E"; + argv[argc++] = (char *)"-E"; break; } - if (debug) - argv[argc++] = "-d"; - if (verbose) - argv[argc++] = "-v"; + if (user) { + argv[argc++] = (char *)"-u"; + argv[argc++] = (char *)user; + } + if (group) { + argv[argc++] = (char *)"-g"; + argv[argc++] = (char *)group; + } argv[argc++] = NULL; execvp(argv0, argv); @@ -344,28 +491,22 @@ start_child(enum ldpd_process p, char *argv0, int fd, int debug, int verbose) /* imsg handling */ /* ARGSUSED */ -static void -main_dispatch_ldpe(int fd, short event, void *bula) +static int +main_dispatch_ldpe(struct thread *thread) { - struct imsgev *iev = bula; + struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; int af; ssize_t n; - int shut = 0, verbose; + int shut = 0; - if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatal("imsg_read error"); - if (n == 0) /* connection closed */ - shut = 1; - } - if (event & EV_WRITE) { - if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) - fatal("msgbuf_write"); - if (n == 0) - shut = 1; - } + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -375,39 +516,13 @@ main_dispatch_ldpe(int fd, short event, void *bula) break; switch (imsg.hdr.type) { + case IMSG_LOG: + logit(imsg.hdr.pid, "%s", (const char *)imsg.data); + break; case IMSG_REQUEST_SOCKETS: af = imsg.hdr.pid; main_imsg_send_net_sockets(af); break; - case IMSG_CTL_RELOAD: - if (ldp_reload() == -1) - log_warnx("configuration reload failed"); - else - log_debug("configuration reloaded"); - break; - case IMSG_CTL_FIB_COUPLE: - kr_fib_couple(); - break; - case IMSG_CTL_FIB_DECOUPLE: - kr_fib_decouple(); - break; - case IMSG_CTL_KROUTE: - case IMSG_CTL_KROUTE_ADDR: - kr_show_route(&imsg); - break; - case IMSG_CTL_IFINFO: - if (imsg.hdr.len == IMSG_HEADER_SIZE) - kr_ifinfo(NULL, imsg.hdr.pid); - else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ) - kr_ifinfo(imsg.data, imsg.hdr.pid); - else - log_warnx("IFINFO request with wrong len"); - break; - case IMSG_CTL_LOG_VERBOSE: - /* already checked by ldpe */ - memcpy(&verbose, imsg.data, sizeof(verbose)); - log_verbose(verbose); - break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); @@ -418,34 +533,35 @@ main_dispatch_ldpe(int fd, short event, void *bula) if (!shut) imsg_event_add(iev); else { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + ldpe_pid = 0; + if (lde_pid == 0) + ldpd_shutdown(); + else + kill(lde_pid, SIGTERM); } + + return (0); } /* ARGSUSED */ -static void -main_dispatch_lde(int fd, short event, void *bula) +static int +main_dispatch_lde(struct thread *thread) { - struct imsgev *iev = bula; + struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; ssize_t n; int shut = 0; - if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatal("imsg_read error"); - if (n == 0) /* connection closed */ - shut = 1; - } - if (event & EV_WRITE) { - if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) - fatal("msgbuf_write"); - if (n == 0) - shut = 1; - } + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -455,6 +571,9 @@ main_dispatch_lde(int fd, short event, void *bula) break; switch (imsg.hdr.type) { + case IMSG_LOG: + logit(imsg.hdr.pid, "%s", (const char *)imsg.data); + break; case IMSG_KLABEL_CHANGE: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct kroute)) @@ -495,10 +614,41 @@ main_dispatch_lde(int fd, short event, void *bula) if (!shut) imsg_event_add(iev); else { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + lde_pid = 0; + if (ldpe_pid == 0) + ldpd_shutdown(); + else + kill(ldpe_pid, SIGTERM); } + + return (0); +} + +/* ARGSUSED */ +int +ldp_write_handler(struct thread *thread) +{ + struct imsgev *iev = THREAD_ARG(thread); + struct imsgbuf *ibuf = &iev->ibuf; + ssize_t n; + + iev->ev_write = NULL; + + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) { + /* this pipe is dead, so remove the event handlers */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + return (0); + } + + imsg_event_add(iev); + + return (0); } void @@ -515,9 +665,11 @@ main_imsg_compose_lde(int type, pid_t pid, void *data, uint16_t datalen) imsg_compose_event(iev_lde, type, 0, pid, -1, data, datalen); } -static int +int main_imsg_compose_both(enum imsg_type type, void *buf, uint16_t len) { + if (iev_ldpe == NULL || iev_lde == NULL) + return (0); if (imsg_compose_event(iev_ldpe, type, 0, 0, -1, buf, len) == -1) return (-1); if (imsg_compose_event(iev_lde, type, 0, 0, -1, buf, len) == -1) @@ -528,13 +680,12 @@ main_imsg_compose_both(enum imsg_type type, void *buf, uint16_t len) void imsg_event_add(struct imsgev *iev) { - iev->events = EV_READ; - if (iev->ibuf.w.queued) - iev->events |= EV_WRITE; + THREAD_READ_ON(master, iev->ev_read, iev->handler_read, iev, + iev->ibuf.fd); - event_del(&iev->ev); - event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); - event_add(&iev->ev, NULL); + if (iev->ibuf.w.queued) + THREAD_WRITE_ON(master, iev->ev_write, iev->handler_write, iev, + iev->ibuf.fd); } int @@ -560,22 +711,24 @@ void evbuf_event_add(struct evbuf *eb) { if (eb->wbuf.queued) - event_add(&eb->ev, NULL); + THREAD_WRITE_ON(master, eb->ev, eb->handler, eb->arg, + eb->wbuf.fd); } void -evbuf_init(struct evbuf *eb, int fd, void (*handler)(int, short, void *), +evbuf_init(struct evbuf *eb, int fd, int (*handler)(struct thread *), void *arg) { msgbuf_init(&eb->wbuf); eb->wbuf.fd = fd; - event_set(&eb->ev, eb->wbuf.fd, EV_WRITE, handler, arg); + eb->handler = handler; + eb->arg = arg; } void evbuf_clear(struct evbuf *eb) { - event_del(&eb->ev); + THREAD_WRITE_OFF(eb->ev); msgbuf_clear(&eb->wbuf); eb->wbuf.fd = -1; } @@ -585,9 +738,10 @@ main_imsg_send_ipc_sockets(struct imsgbuf *ldpe_buf, struct imsgbuf *lde_buf) { int pipe_ldpe2lde[2]; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - PF_UNSPEC, pipe_ldpe2lde) == -1) + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_ldpe2lde) == -1) return (-1); + sock_set_nonblock(pipe_ldpe2lde[0]); + sock_set_nonblock(pipe_ldpe2lde[1]); if (imsg_compose(ldpe_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[0], NULL, 0) == -1) @@ -602,6 +756,9 @@ main_imsg_send_ipc_sockets(struct imsgbuf *ldpe_buf, struct imsgbuf *lde_buf) static void main_imsg_send_net_sockets(int af) { + if (!ldp_addrisset(af, &(ldp_af_conf_get(ldpd_conf, af))->trans_addr)) + return; + main_imsg_send_net_socket(af, LDP_SOCKET_DISC); main_imsg_send_net_socket(af, LDP_SOCKET_EDISC); main_imsg_send_net_socket(af, LDP_SOCKET_SESSION); @@ -657,6 +814,15 @@ ldp_is_dual_stack(struct ldpd_conf *xconf) (xconf->ipv6.flags & F_LDPD_AF_ENABLED)); } +in_addr_t +ldp_rtr_id_get(struct ldpd_conf *xconf) +{ + if (xconf->rtr_id.s_addr != INADDR_ANY) + return (xconf->rtr_id.s_addr); + else + return (global.rtr_id.s_addr); +} + static int main_imsg_send_config(struct ldpd_conf *xconf) { @@ -704,6 +870,11 @@ main_imsg_send_config(struct ldpd_conf *xconf) sizeof(*pw)) == -1) return (-1); } + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) { + if (main_imsg_compose_both(IMSG_RECONF_L2VPN_IPW, pw, + sizeof(*pw)) == -1) + return (-1); + } } if (main_imsg_compose_both(IMSG_RECONF_END, NULL, 0) == -1) @@ -712,13 +883,10 @@ main_imsg_send_config(struct ldpd_conf *xconf) return (0); } -static int -ldp_reload(void) +int +ldp_reload(struct ldpd_conf *xconf) { - struct ldpd_conf *xconf; - - if ((xconf = parse_config(conffile)) == NULL) - return (-1); + ldp_config_normalize(xconf); if (main_imsg_send_config(xconf) == -1) return (-1); @@ -728,6 +896,205 @@ ldp_reload(void) return (0); } +static void +ldp_config_normalize(struct ldpd_conf *xconf) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + + if (!(xconf->flags & F_LDPD_ENABLED)) + ldp_config_reset_main(xconf); + else { + if (!(xconf->ipv4.flags & F_LDPD_AF_ENABLED)) + ldp_config_reset_af(xconf, AF_INET); + if (!(xconf->ipv6.flags & F_LDPD_AF_ENABLED)) + ldp_config_reset_af(xconf, AF_INET6); + } + + LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) { + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + if (pw->flags & F_PW_STATIC_NBR_ADDR) + continue; + + pw->af = AF_INET; + pw->addr.v4 = pw->lsr_id; + } + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) { + if (pw->flags & F_PW_STATIC_NBR_ADDR) + continue; + + pw->af = AF_INET; + pw->addr.v4 = pw->lsr_id; + } + } +} + +static void +ldp_config_reset_main(struct ldpd_conf *conf) +{ + struct iface *iface; + struct nbr_params *nbrp; + + while ((iface = LIST_FIRST(&conf->iface_list)) != NULL) { + LIST_REMOVE(iface, entry); + free(iface); + } + + while ((nbrp = LIST_FIRST(&conf->nbrp_list)) != NULL) { + LIST_REMOVE(nbrp, entry); + free(nbrp); + } + + conf->rtr_id.s_addr = INADDR_ANY; + ldp_config_reset_af(conf, AF_INET); + ldp_config_reset_af(conf, AF_INET6); + conf->lhello_holdtime = LINK_DFLT_HOLDTIME; + conf->lhello_interval = DEFAULT_HELLO_INTERVAL; + conf->thello_holdtime = TARGETED_DFLT_HOLDTIME; + conf->thello_interval = DEFAULT_HELLO_INTERVAL; + conf->trans_pref = DUAL_STACK_LDPOV6; + conf->flags = 0; +} + +static void +ldp_config_reset_af(struct ldpd_conf *conf, int af) +{ + struct ldpd_af_conf *af_conf; + struct iface *iface; + struct iface_af *ia; + struct tnbr *tnbr, *ttmp; + + LIST_FOREACH(iface, &conf->iface_list, entry) { + ia = iface_af_get(iface, af); + ia->enabled = 0; + } + + LIST_FOREACH_SAFE(tnbr, &conf->tnbr_list, entry, ttmp) { + if (tnbr->af != af) + continue; + + LIST_REMOVE(tnbr, entry); + free(tnbr); + } + + af_conf = ldp_af_conf_get(conf, af); + af_conf->keepalive = 180; + af_conf->lhello_holdtime = 0; + af_conf->lhello_interval = 0; + af_conf->thello_holdtime = 0; + af_conf->thello_interval = 0; + memset(&af_conf->trans_addr, 0, sizeof(af_conf->trans_addr)); + af_conf->flags = 0; +} + +struct ldpd_conf * +ldp_dup_config(struct ldpd_conf *conf) +{ + struct ldpd_conf *xconf; + struct iface *iface, *xi; + struct tnbr *tnbr, *xt; + struct nbr_params *nbrp, *xn; + struct l2vpn *l2vpn, *xl; + struct l2vpn_if *lif, *xf; + struct l2vpn_pw *pw, *xp; + + xconf = malloc(sizeof(*xconf)); + *xconf = *conf; + LIST_INIT(&xconf->iface_list); + LIST_INIT(&xconf->tnbr_list); + LIST_INIT(&xconf->nbrp_list); + LIST_INIT(&xconf->l2vpn_list); + + LIST_FOREACH(iface, &conf->iface_list, entry) { + xi = calloc(1, sizeof(*xi)); + if (xi == NULL) + fatal(__func__); + *xi = *iface; + xi->ipv4.iface = xi; + xi->ipv6.iface = xi; + LIST_INSERT_HEAD(&xconf->iface_list, xi, entry); + } + LIST_FOREACH(tnbr, &conf->tnbr_list, entry) { + xt = calloc(1, sizeof(*xt)); + if (xt == NULL) + fatal(__func__); + *xt = *tnbr; + LIST_INSERT_HEAD(&xconf->tnbr_list, xt, entry); + } + LIST_FOREACH(nbrp, &conf->nbrp_list, entry) { + xn = calloc(1, sizeof(*xn)); + if (xn == NULL) + fatal(__func__); + *xn = *nbrp; + LIST_INSERT_HEAD(&xconf->nbrp_list, xn, entry); + } + LIST_FOREACH(l2vpn, &conf->l2vpn_list, entry) { + xl = calloc(1, sizeof(*xl)); + if (xl == NULL) + fatal(__func__); + *xl = *l2vpn; + LIST_INIT(&xl->if_list); + LIST_INIT(&xl->pw_list); + LIST_INIT(&xl->pw_inactive_list); + LIST_INSERT_HEAD(&xconf->l2vpn_list, xl, entry); + + LIST_FOREACH(lif, &l2vpn->if_list, entry) { + xf = calloc(1, sizeof(*xf)); + if (xf == NULL) + fatal(__func__); + *xf = *lif; + xf->l2vpn = xl; + LIST_INSERT_HEAD(&xl->if_list, xf, entry); + } + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + xp = calloc(1, sizeof(*xp)); + if (xp == NULL) + fatal(__func__); + *xp = *pw; + xp->l2vpn = xl; + LIST_INSERT_HEAD(&xl->pw_list, xp, entry); + } + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) { + xp = calloc(1, sizeof(*xp)); + if (xp == NULL) + fatal(__func__); + *xp = *pw; + xp->l2vpn = xl; + LIST_INSERT_HEAD(&xl->pw_inactive_list, xp, entry); + } + } + + return (xconf); +} + +void +ldp_clear_config(struct ldpd_conf *xconf) +{ + struct iface *iface; + struct tnbr *tnbr; + struct nbr_params *nbrp; + struct l2vpn *l2vpn; + + while ((iface = LIST_FIRST(&xconf->iface_list)) != NULL) { + LIST_REMOVE(iface, entry); + free(iface); + } + while ((tnbr = LIST_FIRST(&xconf->tnbr_list)) != NULL) { + LIST_REMOVE(tnbr, entry); + free(tnbr); + } + while ((nbrp = LIST_FIRST(&xconf->nbrp_list)) != NULL) { + LIST_REMOVE(nbrp, entry); + free(nbrp); + } + while ((l2vpn = LIST_FIRST(&xconf->l2vpn_list)) != NULL) { + LIST_REMOVE(l2vpn, entry); + l2vpn_del(l2vpn); + } + + free(xconf); +} + void merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) { @@ -758,6 +1125,11 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) conf->rtr_id = xconf->rtr_id; } + conf->lhello_holdtime = xconf->lhello_holdtime; + conf->lhello_interval = xconf->lhello_interval; + conf->thello_holdtime = xconf->thello_holdtime; + conf->thello_interval = xconf->thello_interval; + if (conf->trans_pref != xconf->trans_pref) { if (ldpd_process == PROC_LDP_ENGINE) ldpe_reset_ds_nbrs(); @@ -784,6 +1156,9 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) if (ldpd_process == PROC_LDP_ENGINE) ldpe_stop_init_backoff(af); } + + af_conf->lhello_holdtime = xa->lhello_holdtime; + af_conf->lhello_interval = xa->lhello_interval; af_conf->thello_holdtime = xa->thello_holdtime; af_conf->thello_interval = xa->thello_interval; @@ -815,10 +1190,6 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) lde_change_egress_label(af, af_conf->flags & F_LDPD_AF_EXPNULL); break; - case PROC_MAIN: - kr_change_egress_label(af, af_conf->flags & - F_LDPD_AF_EXPNULL); - break; default: break; } @@ -829,7 +1200,7 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) update_sockets = 1; } - if (ldpd_process == PROC_MAIN && update_sockets) + if (ldpd_process == PROC_MAIN && iev_ldpe && update_sockets) imsg_compose_event(iev_ldpe, IMSG_CLOSE_SOCKETS, af, 0, -1, NULL, 0); } @@ -841,7 +1212,7 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) LIST_FOREACH_SAFE(iface, &conf->iface_list, entry, itmp) { /* find deleted interfaces */ - if ((xi = if_lookup(xconf, iface->ifindex)) == NULL) { + if ((xi = if_lookup_name(xconf, iface->name)) == NULL) { LIST_REMOVE(iface, entry); if (ldpd_process == PROC_LDP_ENGINE) if_exit(iface); @@ -850,7 +1221,7 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) } LIST_FOREACH_SAFE(xi, &xconf->iface_list, entry, itmp) { /* find new interfaces */ - if ((iface = if_lookup(conf, xi->ifindex)) == NULL) { + if ((iface = if_lookup_name(conf, xi->name)) == NULL) { LIST_REMOVE(xi, entry); LIST_INSERT_HEAD(&conf->iface_list, xi, entry); @@ -914,8 +1285,6 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) /* update existing tnbrs */ if (!(tnbr->flags & F_TNBR_CONFIGURED)) tnbr->flags |= F_TNBR_CONFIGURED; - tnbr->hello_holdtime = xt->hello_holdtime; - tnbr->hello_interval = xt->hello_interval; LIST_REMOVE(xt, entry); free(xt); } @@ -935,7 +1304,14 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); if (nbr) { session_shutdown(nbr, S_SHUTDOWN, 0, 0); +#ifdef __OpenBSD__ pfkey_remove(nbr); +#else + sock_set_md5sig( + (ldp_af_global_get(&global, + nbr->af))->ldp_session_socket, + nbr->af, &nbr->raddr, NULL); +#endif if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } @@ -954,8 +1330,16 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) nbr = nbr_find_ldpid(xn->lsr_id.s_addr); if (nbr) { session_shutdown(nbr, S_SHUTDOWN, 0, 0); +#ifdef __OpenBSD__ if (pfkey_establish(nbr, xn) == -1) fatalx("pfkey setup failed"); +#else + sock_set_md5sig( + (ldp_af_global_get(&global, + nbr->af))->ldp_session_socket, + nbr->af, &nbr->raddr, + xn->auth.md5key); +#endif if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } @@ -987,9 +1371,15 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); if (nbr && nbrp_changed) { session_shutdown(nbr, S_SHUTDOWN, 0, 0); +#ifdef __OpenBSD__ pfkey_remove(nbr); if (pfkey_establish(nbr, nbrp) == -1) fatalx("pfkey setup failed"); +#else + sock_set_md5sig((ldp_af_global_get(&global, + nbr->af))->ldp_session_socket, nbr->af, + &nbr->raddr, nbrp->auth.md5key); +#endif if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } @@ -1055,6 +1445,7 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) struct l2vpn_pw *pw, *ptmp, *xp; struct nbr *nbr; int reset_nbr, reinstall_pwfec, reinstall_tnbr; + LIST_HEAD(, l2vpn_pw) pw_aux_list; int previous_pw_type, previous_mtu; previous_pw_type = l2vpn->pw_type; @@ -1063,14 +1454,14 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) /* merge intefaces */ LIST_FOREACH_SAFE(lif, &l2vpn->if_list, entry, ftmp) { /* find deleted interfaces */ - if ((xf = l2vpn_if_find(xl, lif->ifindex)) == NULL) { + if ((xf = l2vpn_if_find_name(xl, lif->ifname)) == NULL) { LIST_REMOVE(lif, entry); free(lif); } } LIST_FOREACH_SAFE(xf, &xl->if_list, entry, ftmp) { /* find new interfaces */ - if ((lif = l2vpn_if_find(l2vpn, xf->ifindex)) == NULL) { + if ((lif = l2vpn_if_find_name(l2vpn, xf->ifname)) == NULL) { LIST_REMOVE(xf, entry); LIST_INSERT_HEAD(&l2vpn->if_list, xf, entry); xf->l2vpn = l2vpn; @@ -1081,10 +1472,11 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) free(xf); } - /* merge pseudowires */ + /* merge active pseudowires */ + LIST_INIT(&pw_aux_list); LIST_FOREACH_SAFE(pw, &l2vpn->pw_list, entry, ptmp) { - /* find deleted pseudowires */ - if ((xp = l2vpn_pw_find(xl, pw->ifindex)) == NULL) { + /* find deleted active pseudowires */ + if ((xp = l2vpn_pw_find_name(xl, pw->ifname)) == NULL) { switch (ldpd_process) { case PROC_LDE_ENGINE: l2vpn_pw_exit(pw); @@ -1101,8 +1493,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) } } LIST_FOREACH_SAFE(xp, &xl->pw_list, entry, ptmp) { - /* find new pseudowires */ - if ((pw = l2vpn_pw_find(l2vpn, xp->ifindex)) == NULL) { + /* find new active pseudowires */ + if ((pw = l2vpn_pw_find_name(l2vpn, xp->ifname)) == NULL) { LIST_REMOVE(xp, entry); LIST_INSERT_HEAD(&l2vpn->pw_list, xp, entry); xp->l2vpn = l2vpn; @@ -1120,7 +1512,7 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) continue; } - /* update existing pseudowire */ + /* update existing active pseudowire */ if (pw->af != xp->af || ldp_addrcmp(pw->af, &pw->addr, &xp->addr)) reinstall_tnbr = 1; @@ -1141,6 +1533,28 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) else reinstall_pwfec = 0; + /* check if the pseudowire should be disabled */ + if (xp->lsr_id.s_addr == INADDR_ANY || xp->pwid == 0) { + reinstall_tnbr = 0; + reset_nbr = 0; + reinstall_pwfec = 0; + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_exit(pw); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_pw_exit(pw); + break; + case PROC_MAIN: + break; + } + + /* remove from active list */ + LIST_REMOVE(pw, entry); + LIST_INSERT_HEAD(&pw_aux_list, pw, entry); + } + if (ldpd_process == PROC_LDP_ENGINE) { if (reinstall_tnbr) ldpe_l2vpn_pw_exit(pw); @@ -1167,6 +1581,10 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) pw->flags |= F_PW_STATUSTLV_CONF; else pw->flags &= ~F_PW_STATUSTLV_CONF; + if (xp->flags & F_PW_STATIC_NBR_ADDR) + pw->flags |= F_PW_STATIC_NBR_ADDR; + else + pw->flags &= ~F_PW_STATIC_NBR_ADDR; if (ldpd_process == PROC_LDP_ENGINE && reinstall_tnbr) ldpe_l2vpn_pw_init(pw); if (ldpd_process == PROC_LDE_ENGINE && @@ -1182,6 +1600,60 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) free(xp); } + /* merge inactive pseudowires */ + LIST_FOREACH_SAFE(pw, &l2vpn->pw_inactive_list, entry, ptmp) { + /* find deleted inactive pseudowires */ + if ((xp = l2vpn_pw_find_name(xl, pw->ifname)) == NULL) { + LIST_REMOVE(pw, entry); + free(pw); + } + } + LIST_FOREACH_SAFE(xp, &xl->pw_inactive_list, entry, ptmp) { + /* find new inactive pseudowires */ + if ((pw = l2vpn_pw_find_name(l2vpn, xp->ifname)) == NULL) { + LIST_REMOVE(xp, entry); + LIST_INSERT_HEAD(&l2vpn->pw_inactive_list, xp, entry); + xp->l2vpn = l2vpn; + continue; + } + + /* update existing inactive pseudowire */ + pw->lsr_id.s_addr = xp->lsr_id.s_addr; + pw->af = xp->af; + pw->addr = xp->addr; + pw->pwid = xp->pwid; + strlcpy(pw->ifname, xp->ifname, sizeof(pw->ifname)); + pw->ifindex = xp->ifindex; + pw->flags = xp->flags; + + /* check if the pseudowire should be activated */ + if (pw->lsr_id.s_addr != INADDR_ANY && pw->pwid != 0) { + /* remove from inactive list */ + LIST_REMOVE(pw, entry); + LIST_INSERT_HEAD(&l2vpn->pw_list, pw, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_init(pw); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_pw_init(pw); + break; + case PROC_MAIN: + break; + } + } + + LIST_REMOVE(xp, entry); + free(xp); + } + + /* insert pseudowires that were disabled in the inactive list */ + LIST_FOREACH_SAFE(pw, &pw_aux_list, entry, ptmp) { + LIST_REMOVE(pw, entry); + LIST_INSERT_HEAD(&l2vpn->pw_inactive_list, pw, entry); + } + l2vpn->pw_type = xl->pw_type; l2vpn->mtu = xl->mtu; strlcpy(l2vpn->br_ifname, xl->br_ifname, sizeof(l2vpn->br_ifname)); diff --git a/ldpd/ldpd.conf.sample b/ldpd/ldpd.conf.sample new file mode 100644 index 0000000000..49da35c284 --- /dev/null +++ b/ldpd/ldpd.conf.sample @@ -0,0 +1,46 @@ +! -*- ldp -*- +! +! LDPd sample configuration file +! +hostname ldpd +password zebra +log stdout +! +interface eth0 +! +interface eth1 +! +interface lo +! +mpls ldp + dual-stack cisco-interop + neighbor 10.0.1.5 password opensourcerouting + neighbor 172.16.0.1 password opensourcerouting + ! + address-family ipv4 + discovery transport-address 10.0.1.1 + label local advertise explicit-null + ! + interface eth0 + ! + interface eth1 + ! + ! + address-family ipv6 + discovery transport-address 2001:db8::1 + ! + interface eth1 + ! + ! +! +l2vpn ENG type vpls + bridge br0 + member interface eth2 + ! + member pseudowire mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! +! +line vty +! diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index d32d23c6c9..9601f25f70 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -22,19 +22,14 @@ #ifndef _LDPD_H_ #define _LDPD_H_ -#include -#include -#include -#include -#include -#include -#include -#include +#include "openbsd-queue.h" +#include "openbsd-tree.h" +#include "imsg.h" +#include "thread.h" #include "ldp.h" #define CONF_FILE "/etc/ldpd.conf" -#define LDPD_SOCKET "/var/run/ldpd.sock" #define LDPD_USER "_ldpd" #define LDPD_OPT_VERBOSE 0x00000001 @@ -57,15 +52,18 @@ #define F_REDISTRIBUTED 0x0040 struct evbuf { - struct msgbuf wbuf; - struct event ev; + struct msgbuf wbuf; + struct thread *ev; + int (*handler)(struct thread *); + void *arg; }; struct imsgev { struct imsgbuf ibuf; - void (*handler)(int, short, void *); - struct event ev; - short events; + int (*handler_write)(struct thread *); + struct thread *ev_write; + int (*handler_read)(struct thread *); + struct thread *ev_read; }; enum imsg_type { @@ -73,7 +71,12 @@ enum imsg_type { IMSG_CTL_RELOAD, IMSG_CTL_SHOW_INTERFACE, IMSG_CTL_SHOW_DISCOVERY, + IMSG_CTL_SHOW_DISC_IFACE, + IMSG_CTL_SHOW_DISC_TNBR, + IMSG_CTL_SHOW_DISC_ADJ, IMSG_CTL_SHOW_NBR, + IMSG_CTL_SHOW_NBR_DISC, + IMSG_CTL_SHOW_NBR_END, IMSG_CTL_SHOW_LIB, IMSG_CTL_SHOW_L2VPN_PW, IMSG_CTL_SHOW_L2VPN_BINDING, @@ -92,6 +95,7 @@ enum imsg_type { IMSG_IFSTATUS, IMSG_NEWADDR, IMSG_DELADDR, + IMSG_RTRID_UPDATE, IMSG_LABEL_MAPPING, IMSG_LABEL_MAPPING_FULL, IMSG_LABEL_REQUEST, @@ -126,7 +130,10 @@ enum imsg_type { IMSG_RECONF_L2VPN, IMSG_RECONF_L2VPN_IF, IMSG_RECONF_L2VPN_PW, - IMSG_RECONF_END + IMSG_RECONF_L2VPN_IPW, + IMSG_RECONF_END, + IMSG_DEBUG_UPDATE, + IMSG_LOG }; union ldpd_addr { @@ -249,7 +256,7 @@ struct iface_af { int state; LIST_HEAD(, adj) adj_list; time_t uptime; - struct event hello_timer; + struct thread *hello_timer; uint16_t hello_holdtime; uint16_t hello_interval; }; @@ -261,9 +268,7 @@ struct iface { struct if_addr_head addr_list; struct in6_addr linklocal; enum iface_type type; - uint8_t if_type; uint16_t flags; - uint8_t linkstate; struct iface_af ipv4; struct iface_af ipv6; }; @@ -271,13 +276,11 @@ struct iface { /* source of targeted hellos */ struct tnbr { LIST_ENTRY(tnbr) entry; - struct event hello_timer; + struct thread *hello_timer; struct adj *adj; int af; union ldpd_addr addr; int state; - uint16_t hello_holdtime; - uint16_t hello_interval; uint16_t pw_count; uint8_t flags; }; @@ -313,7 +316,6 @@ struct l2vpn_if { char ifname[IF_NAMESIZE]; unsigned int ifindex; uint16_t flags; - uint8_t link_state; }; struct l2vpn_pw { @@ -335,6 +337,7 @@ struct l2vpn_pw { #define F_PW_CWORD_CONF 0x04 /* control word configured */ #define F_PW_CWORD 0x08 /* control word negotiated */ #define F_PW_STATUS_UP 0x10 /* pseudowire is operational */ +#define F_PW_STATIC_NBR_ADDR 0x20 /* static neighbor address configured */ struct l2vpn { LIST_ENTRY(l2vpn) entry; @@ -346,6 +349,7 @@ struct l2vpn { unsigned int br_ifindex; LIST_HEAD(, l2vpn_if) if_list; LIST_HEAD(, l2vpn_pw) pw_list; + LIST_HEAD(, l2vpn_pw) pw_inactive_list; }; #define L2VPN_TYPE_VPWS 1 #define L2VPN_TYPE_VPLS 2 @@ -370,6 +374,8 @@ enum hello_type { struct ldpd_af_conf { uint16_t keepalive; + uint16_t lhello_holdtime; + uint16_t lhello_interval; uint16_t thello_holdtime; uint16_t thello_interval; union ldpd_addr trans_addr; @@ -388,15 +394,20 @@ struct ldpd_conf { LIST_HEAD(, tnbr) tnbr_list; LIST_HEAD(, nbr_params) nbrp_list; LIST_HEAD(, l2vpn) l2vpn_list; + uint16_t lhello_holdtime; + uint16_t lhello_interval; + uint16_t thello_holdtime; + uint16_t thello_interval; uint16_t trans_pref; int flags; }; #define F_LDPD_NO_FIB_UPDATE 0x0001 #define F_LDPD_DS_CISCO_INTEROP 0x0002 +#define F_LDPD_ENABLED 0x0004 struct ldpd_af_global { - struct event disc_ev; - struct event edisc_ev; + struct thread *disc_ev; + struct thread *edisc_ev; int ldp_disc_socket; int ldp_edisc_socket; int ldp_session_socket; @@ -405,6 +416,7 @@ struct ldpd_af_global { struct ldpd_global { int cmd_opts; time_t uptime; + struct in_addr rtr_id; struct ldpd_af_global ipv4; struct ldpd_af_global ipv6; uint32_t conf_seqnum; @@ -451,10 +463,7 @@ struct kif { char ifname[IF_NAMESIZE]; unsigned short ifindex; int flags; - uint8_t link_state; int mtu; - uint8_t if_type; - uint64_t baudrate; }; /* control data structures */ @@ -464,15 +473,26 @@ struct ctl_iface { unsigned int ifindex; int state; uint16_t flags; - uint8_t linkstate; enum iface_type type; - uint8_t if_type; uint16_t hello_holdtime; uint16_t hello_interval; time_t uptime; uint16_t adj_cnt; }; +struct ctl_disc_if { + char name[IF_NAMESIZE]; + int active_v4; + int active_v6; + int no_adj; +}; + +struct ctl_disc_tnbr { + int af; + union ldpd_addr addr; + int no_adj; +}; + struct ctl_adj { int af; struct in_addr id; @@ -487,7 +507,10 @@ struct ctl_nbr { int af; struct in_addr id; union ldpd_addr laddr; + in_port_t lport; union ldpd_addr raddr; + in_port_t rport; + uint16_t holdtime; time_t uptime; int nbr_state; }; @@ -501,19 +524,23 @@ struct ctl_rt { uint32_t remote_label; uint8_t flags; uint8_t in_use; + int first; }; struct ctl_pw { uint16_t type; + char l2vpn_name[L2VPN_NAME_LEN]; char ifname[IF_NAMESIZE]; uint32_t pwid; struct in_addr lsr_id; uint32_t local_label; uint32_t local_gid; uint16_t local_ifmtu; + uint8_t local_cword; uint32_t remote_label; uint32_t remote_gid; uint16_t remote_ifmtu; + uint8_t remote_cword; uint32_t status; }; @@ -525,19 +552,9 @@ struct ldpd_conf *parse_config(char *); int cmdline_symset(char *); /* kroute.c */ -int kif_init(void); -int kr_init(int); void kif_redistribute(const char *); int kr_change(struct kroute *); int kr_delete(struct kroute *); -void kr_shutdown(void); -void kr_fib_couple(void); -void kr_fib_decouple(void); -void kr_change_egress_label(int, int); -void kr_show_route(struct imsg *); -void kr_ifinfo(char *, pid_t); -struct kif *kif_findname(char *); -void kif_clear(void); int kmpw_set(struct kpw *); int kmpw_unset(struct kpw *); @@ -561,31 +578,46 @@ void recoverscope(struct sockaddr_in6 *); void addscope(struct sockaddr_in6 *, uint32_t); void clearscope(struct in6_addr *); struct sockaddr *addr2sa(int af, union ldpd_addr *, uint16_t); -void sa2addr(struct sockaddr *, int *, union ldpd_addr *); +void sa2addr(struct sockaddr *, int *, union ldpd_addr *, + in_port_t *); +socklen_t sockaddr_len(struct sockaddr *); /* ldpd.c */ +int ldp_write_handler(struct thread *); void main_imsg_compose_ldpe(int, pid_t, void *, uint16_t); void main_imsg_compose_lde(int, pid_t, void *, uint16_t); +int main_imsg_compose_both(enum imsg_type, void *, + uint16_t); void imsg_event_add(struct imsgev *); -int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t, - int, void *, uint16_t); +int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, + pid_t, int, void *, uint16_t); void evbuf_enqueue(struct evbuf *, struct ibuf *); void evbuf_event_add(struct evbuf *); -void evbuf_init(struct evbuf *, int, void (*)(int, short, void *), void *); +void evbuf_init(struct evbuf *, int, + int (*)(struct thread *), void *); void evbuf_clear(struct evbuf *); struct ldpd_af_conf *ldp_af_conf_get(struct ldpd_conf *, int); struct ldpd_af_global *ldp_af_global_get(struct ldpd_global *, int); int ldp_is_dual_stack(struct ldpd_conf *); +in_addr_t ldp_rtr_id_get(struct ldpd_conf *); +int ldp_reload(struct ldpd_conf *); +struct ldpd_conf *ldp_dup_config(struct ldpd_conf *); +void ldp_clear_config(struct ldpd_conf *); void merge_config(struct ldpd_conf *, struct ldpd_conf *); struct ldpd_conf *config_new_empty(void); void config_clear(struct ldpd_conf *); /* socket.c */ int ldp_create_socket(int, enum socket_type); +void sock_set_nonblock(int); +void sock_set_cloexec(int); void sock_set_recvbuf(int); int sock_set_reuse(int, int); int sock_set_bindany(int, int); +int sock_set_md5sig(int, int, union ldpd_addr *, const char *); int sock_set_ipv4_tos(int, int); +int sock_set_ipv4_pktinfo(int, int); +int sock_set_ipv4_recvdstaddr(int, int); int sock_set_ipv4_recvif(int, int); int sock_set_ipv4_minttl(int, int); int sock_set_ipv4_ucast_ttl(int fd, int); @@ -600,7 +632,19 @@ int sock_set_ipv6_mcast_hops(int, int); int sock_set_ipv6_mcast(struct iface *); int sock_set_ipv6_mcast_loop(int); -/* printconf.c */ -void print_config(struct ldpd_conf *); +/* quagga */ +extern struct thread_master *master; + +/* ldp_zebra.c */ +void ldp_zebra_init(struct thread_master *); + +/* compatibility */ +#ifndef __OpenBSD__ +#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) +#define __IPV6_ADDR_SCOPE_INTFACELOCAL 0x01 +#define IN6_IS_ADDR_MC_INTFACELOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && \ + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_INTFACELOCAL)) +#endif #endif /* _LDPD_H_ */ diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 1b4d2a67e7..aef33c8e37 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -19,70 +19,97 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "lde.h" #include "control.h" #include "log.h" +#include "ldp_debug.h" -static void ldpe_sig_handler(int, short, void *); -static __dead void ldpe_shutdown(void); -static void ldpe_dispatch_main(int, short, void *); -static void ldpe_dispatch_lde(int, short, void *); -static void ldpe_dispatch_pfkey(int, short, void *); +#include +#include "memory.h" +#include "privs.h" +#include "sigevent.h" + +static void ldpe_shutdown(void); +static int ldpe_dispatch_main(struct thread *); +static int ldpe_dispatch_lde(struct thread *); +#ifdef __OpenBSD__ +static int ldpe_dispatch_pfkey(struct thread *); +#endif static void ldpe_setup_sockets(int, int, int, int); static void ldpe_close_sockets(int); static void ldpe_iface_af_ctl(struct ctl_conn *, int, unsigned int); struct ldpd_conf *leconf; +#ifdef __OpenBSD__ struct ldpd_sysdep sysdep; +#endif static struct imsgev *iev_main; static struct imsgev *iev_lde; -static struct event pfkey_ev; +#ifdef __OpenBSD__ +static struct thread *pfkey_ev; +#endif -/* ARGSUSED */ -static void -ldpe_sig_handler(int sig, short event, void *bula) +/* Master of threads. */ +struct thread_master *master; + +/* ldpe privileges */ +static zebra_capabilities_t _caps_p [] = { - switch (sig) { - case SIGINT: - case SIGTERM: - ldpe_shutdown(); - /* NOTREACHED */ - default: - fatalx("unexpected signal"); - } + ZCAP_BIND, + ZCAP_NET_ADMIN +}; + +struct zebra_privs_t ldpe_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0 +}; + +/* SIGINT / SIGTERM handler. */ +static void +sigint(void) +{ + ldpe_shutdown(); } +static struct quagga_signal_t ldpe_signals[] = +{ + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + /* label distribution protocol engine */ void -ldpe(int debug, int verbose) +ldpe(const char *user, const char *group) { - struct passwd *pw; - struct event ev_sigint, ev_sigterm; + struct thread thread; leconf = config_new_empty(); - log_init(debug); - log_verbose(verbose); - +#ifdef HAVE_SETPROCTITLE setproctitle("ldp engine"); +#endif ldpd_process = PROC_LDP_ENGINE; - /* create ldpd control socket outside chroot */ - if (control_init() == -1) - fatalx("control socket setup failed"); - LIST_INIT(&global.addr_list); LIST_INIT(&global.adj_list); TAILQ_INIT(&global.pending_conns); @@ -90,50 +117,46 @@ ldpe(int debug, int verbose) fatal("inet_pton"); if (inet_pton(AF_INET6, AllRouters_v6, &global.mcast_addr_v6) != 1) fatal("inet_pton"); +#ifdef __OpenBSD__ global.pfkeysock = pfkey_init(); +#endif - if ((pw = getpwnam(LDPD_USER)) == NULL) - fatal("getpwnam"); + /* drop privileges */ + if (user) + ldpe_privs.user = user; + if (group) + ldpe_privs.group = group; + zprivs_init(&ldpe_privs); - if (chroot(pw->pw_dir) == -1) - fatal("chroot"); - if (chdir("/") == -1) - fatal("chdir(\"/\")"); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("can't drop privileges"); + if (control_init() == -1) + fatalx("control socket setup failed"); +#ifdef HAVE_PLEDGE if (pledge("stdio cpath inet mcast recvfd", NULL) == -1) fatal("pledge"); +#endif - event_init(); + master = thread_master_create(); accept_init(); /* setup signal handler */ - signal_set(&ev_sigint, SIGINT, ldpe_sig_handler, NULL); - signal_set(&ev_sigterm, SIGTERM, ldpe_sig_handler, NULL); - signal_add(&ev_sigint, NULL); - signal_add(&ev_sigterm, NULL); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); + signal_init(master, array_size(ldpe_signals), ldpe_signals); /* setup pipe and event handler to the parent process */ if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_main->ibuf, 3); - iev_main->handler = ldpe_dispatch_main; - iev_main->events = EV_READ; - event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, - iev_main->handler, iev_main); - event_add(&iev_main->ev, NULL); + iev_main->handler_read = ldpe_dispatch_main; + iev_main->ev_read = thread_add_read(master, iev_main->handler_read, + iev_main, iev_main->ibuf.fd); + iev_main->handler_write = ldp_write_handler; + iev_main->ev_write = NULL; - if (sysdep.no_pfkey == 0) { - event_set(&pfkey_ev, global.pfkeysock, EV_READ | EV_PERSIST, - ldpe_dispatch_pfkey, NULL); - event_add(&pfkey_ev, NULL); - } +#ifdef __OpenBSD__ + if (sysdep.no_pfkey == 0) + pfkey_ev = thread_add_read(master, ldpe_dispatch_pfkey, + NULL, global.pfkeysock); +#endif /* mark sockets as closed */ global.ipv4.ldp_disc_socket = -1; @@ -150,12 +173,12 @@ ldpe(int debug, int verbose) if ((pkt_ptr = calloc(1, IBUF_READ_SIZE)) == NULL) fatal(__func__); - event_dispatch(); - - ldpe_shutdown(); + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); } -static __dead void +static void ldpe_shutdown(void) { struct if_addr *if_addr; @@ -172,10 +195,12 @@ ldpe_shutdown(void) control_cleanup(); config_clear(leconf); +#ifdef __OpenBSD__ if (sysdep.no_pfkey == 0) { - event_del(&pfkey_ev); + THREAD_READ_OFF(pfkey_ev); close(global.pfkeysock); } +#endif ldpe_close_sockets(AF_INET); ldpe_close_sockets(AF_INET6); @@ -212,8 +237,8 @@ ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data, } /* ARGSUSED */ -static void -ldpe_dispatch_main(int fd, short event, void *bula) +static int +ldpe_dispatch_main(struct thread *thread) { static struct ldpd_conf *nconf; struct iface *niface; @@ -223,7 +248,8 @@ ldpe_dispatch_main(int fd, short event, void *bula) struct l2vpn_if *nlif; struct l2vpn_pw *npw; struct imsg imsg; - struct imsgev *iev = bula; + int fd = THREAD_FD(thread); + struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct iface *iface = NULL; struct kif *kif; @@ -233,21 +259,17 @@ ldpe_dispatch_main(int fd, short event, void *bula) static int edisc_socket = -1; static int session_socket = -1; struct nbr *nbr; +#ifdef __OpenBSD__ struct nbr_params *nbrp; +#endif int n, shut = 0; - if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatal("imsg_read error"); - if (n == 0) /* connection closed */ - shut = 1; - } - if (event & EV_WRITE) { - if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) - fatal("ldpe_dispatch_main: msgbuf_write"); - if (n == 0) - shut = 1; - } + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -262,12 +284,11 @@ ldpe_dispatch_main(int fd, short event, void *bula) fatalx("IFSTATUS imsg with wrong len"); kif = imsg.data; - iface = if_lookup(leconf, kif->ifindex); + iface = if_lookup_name(leconf, kif->ifname); if (!iface) break; - iface->flags = kif->flags; - iface->linkstate = kif->link_state; + if_update_info(iface, kif); if_update(iface, AF_UNSPEC); break; case IMSG_NEWADDR: @@ -299,11 +320,11 @@ ldpe_dispatch_main(int fd, short event, void *bula) if ((iev_lde = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_lde->ibuf, fd); - iev_lde->handler = ldpe_dispatch_lde; - iev_lde->events = EV_READ; - event_set(&iev_lde->ev, iev_lde->ibuf.fd, - iev_lde->events, iev_lde->handler, iev_lde); - event_add(&iev_lde->ev, NULL); + iev_lde->handler_read = ldpe_dispatch_lde; + iev_lde->ev_read = thread_add_read(master, + iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd); + iev_lde->handler_write = ldp_write_handler; + iev_lde->ev_write = NULL; break; case IMSG_CLOSE_SOCKETS: af = imsg.hdr.peerid; @@ -312,7 +333,9 @@ ldpe_dispatch_main(int fd, short event, void *bula) if (nbr->af != af) continue; session_shutdown(nbr, S_SHUTDOWN, 0, 0); +#ifdef __OpenBSD__ pfkey_remove(nbr); +#endif } ldpe_close_sockets(af); if_update_all(af); @@ -366,13 +389,25 @@ ldpe_dispatch_main(int fd, short event, void *bula) continue; nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr; +#ifdef __OpenBSD__ nbrp = nbr_params_find(leconf, nbr->id); if (nbrp && pfkey_establish(nbr, nbrp) == -1) fatalx("pfkey setup failed"); +#endif if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } break; + case IMSG_RTRID_UPDATE: + memcpy(&global.rtr_id, imsg.data, + sizeof(global.rtr_id)); + if (leconf->rtr_id.s_addr == INADDR_ANY) { + ldpe_reset_nbrs(AF_INET); + ldpe_reset_nbrs(AF_INET6); + } + if_update_all(AF_UNSPEC); + tnbr_update_all(AF_UNSPEC); + break; case IMSG_RECONF_CONF: if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL) @@ -418,6 +453,7 @@ ldpe_dispatch_main(int fd, short event, void *bula) LIST_INIT(&nl2vpn->if_list); LIST_INIT(&nl2vpn->pw_list); + LIST_INIT(&nl2vpn->pw_inactive_list); LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry); break; @@ -437,17 +473,30 @@ ldpe_dispatch_main(int fd, short event, void *bula) npw->l2vpn = nl2vpn; LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry); break; + case IMSG_RECONF_L2VPN_IPW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + npw->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->pw_inactive_list, npw, entry); + break; case IMSG_RECONF_END: merge_config(leconf, nconf); nconf = NULL; global.conf_seqnum++; break; - case IMSG_CTL_KROUTE: - case IMSG_CTL_KROUTE_ADDR: - case IMSG_CTL_IFINFO: case IMSG_CTL_END: control_imsg_relay(&imsg); break; + case IMSG_DEBUG_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(ldp_debug)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); + break; default: log_debug("ldpe_dispatch_main: error handling imsg %d", imsg.hdr.type); @@ -458,17 +507,20 @@ ldpe_dispatch_main(int fd, short event, void *bula) if (!shut) imsg_event_add(iev); else { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + ldpe_shutdown(); } + + return (0); } /* ARGSUSED */ -static void -ldpe_dispatch_lde(int fd, short event, void *bula) +static int +ldpe_dispatch_lde(struct thread *thread) { - struct imsgev *iev = bula; + struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct map map; @@ -476,18 +528,12 @@ ldpe_dispatch_lde(int fd, short event, void *bula) int n, shut = 0; struct nbr *nbr = NULL; - if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatal("imsg_read error"); - if (n == 0) /* connection closed */ - shut = 1; - } - if (event & EV_WRITE) { - if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) - fatal("ldpe_dispatch_lde: msgbuf_write"); - if (n == 0) - shut = 1; - } + iev->ev_read = NULL; + + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -592,22 +638,31 @@ ldpe_dispatch_lde(int fd, short event, void *bula) if (!shut) imsg_event_add(iev); else { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); + /* this pipe is dead, so remove the event handlers and exit */ + THREAD_READ_OFF(iev->ev_read); + THREAD_WRITE_OFF(iev->ev_write); + ldpe_shutdown(); } + + return (0); } +#ifdef __OpenBSD__ /* ARGSUSED */ -static void -ldpe_dispatch_pfkey(int fd, short event, void *bula) +static int +ldpe_dispatch_pfkey(struct thread *thread) { - if (event & EV_READ) { - if (pfkey_read(fd, NULL) == -1) { - fatal("pfkey_read failed, exiting..."); - } - } + int fd = THREAD_FD(thread); + + pfkey_ev = thread_add_read(master, ldpe_dispatch_pfkey, + NULL, global.pfkeysock); + + if (pfkey_read(fd, NULL) == -1) + fatal("pfkey_read failed, exiting..."); + + return (0); } +#endif /* __OpenBSD__ */ static void ldpe_setup_sockets(int af, int disc_socket, int edisc_socket, @@ -619,15 +674,13 @@ ldpe_setup_sockets(int af, int disc_socket, int edisc_socket, /* discovery socket */ af_global->ldp_disc_socket = disc_socket; - event_set(&af_global->disc_ev, af_global->ldp_disc_socket, - EV_READ|EV_PERSIST, disc_recv_packet, NULL); - event_add(&af_global->disc_ev, NULL); + af_global->disc_ev = thread_add_read(master, disc_recv_packet, + &af_global->disc_ev, af_global->ldp_disc_socket); /* extended discovery socket */ af_global->ldp_edisc_socket = edisc_socket; - event_set(&af_global->edisc_ev, af_global->ldp_edisc_socket, - EV_READ|EV_PERSIST, disc_recv_packet, NULL); - event_add(&af_global->edisc_ev, NULL); + af_global->edisc_ev = thread_add_read(master, disc_recv_packet, + &af_global->edisc_ev, af_global->ldp_edisc_socket); /* session socket */ af_global->ldp_session_socket = session_socket; @@ -642,16 +695,14 @@ ldpe_close_sockets(int af) af_global = ldp_af_global_get(&global, af); /* discovery socket */ - if (event_initialized(&af_global->disc_ev)) - event_del(&af_global->disc_ev); + THREAD_READ_OFF(af_global->disc_ev); if (af_global->ldp_disc_socket != -1) { close(af_global->ldp_disc_socket); af_global->ldp_disc_socket = -1; } /* extended discovery socket */ - if (event_initialized(&af_global->edisc_ev)) - event_del(&af_global->edisc_ev); + THREAD_READ_OFF(af_global->edisc_ev); if (af_global->ldp_edisc_socket != -1) { close(af_global->ldp_edisc_socket); af_global->ldp_edisc_socket = -1; @@ -745,24 +796,57 @@ ldpe_iface_ctl(struct ctl_conn *c, unsigned int idx) void ldpe_adj_ctl(struct ctl_conn *c) { - struct nbr *nbr; - struct adj *adj; - struct ctl_adj *actl; + struct iface *iface; + struct tnbr *tnbr; + struct adj *adj; + struct ctl_adj *actl; + struct ctl_disc_if ictl; + struct ctl_disc_tnbr tctl; - RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { - LIST_FOREACH(adj, &nbr->adj_list, nbr_entry) { + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, -1, NULL, 0); + + LIST_FOREACH(iface, &leconf->iface_list, entry) { + memset(&ictl, 0, sizeof(ictl)); + ictl.active_v4 = (iface->ipv4.state == IF_STA_ACTIVE); + ictl.active_v6 = (iface->ipv6.state == IF_STA_ACTIVE); + + if (!ictl.active_v4 && !ictl.active_v6) + continue; + + strlcpy(ictl.name, iface->name, sizeof(ictl.name)); + if (LIST_EMPTY(&iface->ipv4.adj_list) && + LIST_EMPTY(&iface->ipv6.adj_list)) + ictl.no_adj = 1; + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_IFACE, 0, 0, + -1, &ictl, sizeof(ictl)); + + LIST_FOREACH(adj, &iface->ipv4.adj_list, ia_entry) { actl = adj_to_ctl(adj); - imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, + 0, 0, -1, actl, sizeof(struct ctl_adj)); + } + LIST_FOREACH(adj, &iface->ipv6.adj_list, ia_entry) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0, -1, actl, sizeof(struct ctl_adj)); } } - /* show adjacencies not associated with any neighbor */ - LIST_FOREACH(adj, &global.adj_list, global_entry) { - if (adj->nbr != NULL) + + LIST_FOREACH(tnbr, &leconf->tnbr_list, entry) { + memset(&tctl, 0, sizeof(tctl)); + tctl.af = tnbr->af; + tctl.addr = tnbr->addr; + if (tnbr->adj == NULL) + tctl.no_adj = 1; + + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_TNBR, 0, 0, + -1, &tctl, sizeof(tctl)); + + if (tnbr->adj == NULL) continue; - actl = adj_to_ctl(adj); - imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISCOVERY, 0, 0, + actl = adj_to_ctl(tnbr->adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_DISC_ADJ, 0, 0, -1, actl, sizeof(struct ctl_adj)); } @@ -772,13 +856,27 @@ ldpe_adj_ctl(struct ctl_conn *c) void ldpe_nbr_ctl(struct ctl_conn *c) { + struct adj *adj; + struct ctl_adj *actl; struct nbr *nbr; struct ctl_nbr *nctl; RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) { + if (nbr->state == NBR_STA_PRESENT) + continue; + nctl = nbr_to_ctl(nbr); imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl, sizeof(struct ctl_nbr)); + + LIST_FOREACH(adj, &nbr->adj_list, nbr_entry) { + actl = adj_to_ctl(adj); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_DISC, + 0, 0, -1, actl, sizeof(struct ctl_adj)); + } + + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR_END, 0, 0, -1, + NULL, 0); } imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index e640dd67db..aab1a7fd9b 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -21,10 +21,11 @@ #ifndef _LDPE_H_ #define _LDPE_H_ -#include -#include -#include +#include "openbsd-queue.h" +#include "openbsd-tree.h" +#ifdef __OpenBSD__ #include +#endif #include "ldpd.h" @@ -48,7 +49,7 @@ struct adj { struct nbr *nbr; int ds_tlv; struct hello_source source; - struct event inactivity_timer; + struct thread *inactivity_timer; uint16_t holdtime; union ldpd_addr trans_addr; }; @@ -58,18 +59,20 @@ struct tcp_conn { int fd; struct ibuf_read *rbuf; struct evbuf wbuf; - struct event rev; + struct thread *rev; + in_port_t lport; + in_port_t rport; }; struct nbr { RB_ENTRY(nbr) id_tree, addr_tree, pid_tree; struct tcp_conn *tcp; LIST_HEAD(, adj) adj_list; /* adjacencies */ - struct event ev_connect; - struct event keepalive_timer; - struct event keepalive_timeout; - struct event init_timeout; - struct event initdelay_timer; + struct thread *ev_connect; + struct thread *keepalive_timer; + struct thread *keepalive_timeout; + struct thread *init_timeout; + struct thread *initdelay_timer; struct mapping_head mapping_list; struct mapping_head withdraw_list; @@ -117,7 +120,7 @@ struct pending_conn { int fd; int af; union ldpd_addr addr; - struct event ev_timeout; + struct thread *ev_timeout; }; #define PENDING_CONN_TIMEOUT 5 @@ -139,7 +142,7 @@ extern struct nbr_pid_head nbrs_by_pid; /* accept.c */ void accept_init(void); -int accept_add(int, void (*)(int, short, void *), void *); +int accept_add(int, int (*)(struct thread *), void *); void accept_del(int); void accept_pause(void); void accept_unpause(void); @@ -180,7 +183,7 @@ int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *, uint16_t, struct map *); /* ldpe.c */ -void ldpe(int, int); +void ldpe(const char *, const char *); int ldpe_imsg_compose_parent(int, pid_t, void *, uint16_t); int ldpe_imsg_compose_lde(int, uint32_t, pid_t, void *, @@ -200,11 +203,15 @@ void mapping_list_clr(struct mapping_head *); struct iface *if_new(struct kif *); void if_exit(struct iface *); struct iface *if_lookup(struct ldpd_conf *, unsigned short); +struct iface *if_lookup_name(struct ldpd_conf *, const char *); +void if_update_info(struct iface *, struct kif *); struct iface_af *iface_af_get(struct iface *, int); void if_addr_add(struct kaddr *); void if_addr_del(struct kaddr *); void if_update(struct iface *, int); void if_update_all(int); +uint16_t if_get_hello_holdtime(struct iface_af *); +uint16_t if_get_hello_interval(struct iface_af *); struct ctl_iface *if_to_ctl(struct iface_af *); in_addr_t if_get_ipv4_addr(struct iface *); @@ -216,11 +223,13 @@ struct adj *adj_find(struct hello_source *); int adj_get_af(struct adj *adj); void adj_start_itimer(struct adj *); void adj_stop_itimer(struct adj *); -struct tnbr *tnbr_new(struct ldpd_conf *, int, union ldpd_addr *); +struct tnbr *tnbr_new(int, union ldpd_addr *); struct tnbr *tnbr_find(struct ldpd_conf *, int, union ldpd_addr *); struct tnbr *tnbr_check(struct tnbr *); void tnbr_update(struct tnbr *); void tnbr_update_all(int); +uint16_t tnbr_get_hello_holdtime(struct tnbr *); +uint16_t tnbr_get_hello_interval(struct tnbr *); struct ctl_adj *adj_to_ctl(struct adj *); /* neighbor.c */ @@ -255,8 +264,8 @@ int gen_ldp_hdr(struct ibuf *, uint16_t); int gen_msg_hdr(struct ibuf *, uint16_t, uint16_t); int send_packet(int, int, union ldpd_addr *, struct iface_af *, void *, size_t); -void disc_recv_packet(int, short, void *); -void session_accept(int, short, void *); +int disc_recv_packet(struct thread *); +int session_accept(struct thread *); void session_accept_nbr(struct nbr *, int); void session_shutdown(struct nbr *, uint32_t, uint32_t, uint32_t); @@ -268,10 +277,12 @@ struct pending_conn *pending_conn_find(int, union ldpd_addr *); char *pkt_ptr; /* packet buffer */ /* pfkey.c */ +#ifdef __OpenBSD__ int pfkey_read(int, struct sadb_msg *); int pfkey_establish(struct nbr *, struct nbr_params *); int pfkey_remove(struct nbr *); int pfkey_init(void); +#endif /* l2vpn.c */ void ldpe_l2vpn_init(struct l2vpn *); diff --git a/ldpd/log.c b/ldpd/log.c index e14b6e51ea..77efdb4714 100644 --- a/ldpd/log.c +++ b/ldpd/log.c @@ -16,53 +16,23 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "lde.h" #include "log.h" +#include +#include "mpls.h" + static const char * const procnames[] = { "parent", "ldpe", "lde" }; -static void vlog(int, const char *, va_list); - -static int debug; -static int verbose; - -void -log_init(int n_debug) -{ - extern char *__progname; - - debug = n_debug; - - if (!debug) - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); - - tzset(); -} - -void -log_verbose(int v) -{ - verbose = v; -} +void vlog(int, const char *, va_list); void logit(int pri, const char *fmt, ...) @@ -74,23 +44,24 @@ logit(int pri, const char *fmt, ...) va_end(ap); } -static void +void vlog(int pri, const char *fmt, va_list ap) { - char *nfmt; + char buf[1024]; - if (debug) { - /* best effort in out of mem situations */ - if (asprintf(&nfmt, "%s\n", fmt) == -1) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } else { - vfprintf(stderr, nfmt, ap); - free(nfmt); - } - fflush(stderr); - } else - vsyslog(pri, fmt, ap); + switch (ldpd_process) { + case PROC_LDE_ENGINE: + vsnprintf(buf, sizeof(buf), fmt, ap); + lde_imsg_compose_parent(IMSG_LOG, pri, buf, strlen(buf) + 1); + break; + case PROC_LDP_ENGINE: + vsnprintf(buf, sizeof(buf), fmt, ap); + ldpe_imsg_compose_parent(IMSG_LOG, pri, buf, strlen(buf) + 1); + break; + case PROC_MAIN: + vzlog(NULL, pri, fmt, ap); + break; + } } void @@ -137,16 +108,24 @@ log_info(const char *emsg, ...) va_end(ap); } +void +log_notice(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_NOTICE, emsg, ap); + va_end(ap); +} + void log_debug(const char *emsg, ...) { va_list ap; - if (verbose & LDPD_OPT_VERBOSE) { - va_start(ap, emsg); - vlog(LOG_DEBUG, emsg, ap); - va_end(ap); - } + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); } void @@ -183,7 +162,7 @@ log_sockaddr(void *vp) round = (round + 1) % NUM_LOGS; - if (getnameinfo(sa, sa->sa_len, buf[round], NI_MAXHOST, NULL, 0, + if (getnameinfo(sa, sockaddr_len(sa), buf[round], NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) return ("(unknown)"); else @@ -196,7 +175,9 @@ log_in6addr(const struct in6_addr *addr) struct sockaddr_in6 sa_in6; memset(&sa_in6, 0, sizeof(sa_in6)); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sa_in6.sin6_len = sizeof(sa_in6); +#endif sa_in6.sin6_family = AF_INET6; sa_in6.sin6_addr = *addr; @@ -211,7 +192,9 @@ log_in6addr_scope(const struct in6_addr *addr, unsigned int ifindex) struct sockaddr_in6 sa_in6; memset(&sa_in6, 0, sizeof(sa_in6)); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sa_in6.sin6_len = sizeof(sa_in6); +#endif sa_in6.sin6_family = AF_INET6; sa_in6.sin6_addr = *addr; @@ -275,6 +258,39 @@ log_label(uint32_t label) return (buf); } +const char * +log_time(time_t t) +{ + char *buf; + static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ + static int idx = 0; + unsigned int sec, min, hrs, day, week; + + buf = tfbuf[idx++]; + if (idx == TF_BUFS) + idx = 0; + + week = t; + + sec = week % 60; + week /= 60; + min = week % 60; + week /= 60; + hrs = week % 24; + week /= 24; + day = week % 7; + week /= 7; + + if (week > 0) + snprintf(buf, TF_LEN, "%02uw%01ud%02uh", week, day, hrs); + else if (day > 0) + snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); + else + snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); + + return (buf); +} + char * log_hello_src(const struct hello_source *src) { @@ -365,8 +381,10 @@ af_name(int af) return ("ipv4"); case AF_INET6: return ("ipv6"); +#ifdef AF_MPLS case AF_MPLS: return ("mpls"); +#endif default: return ("UNKNOWN"); } @@ -564,37 +582,3 @@ pw_type_name(uint16_t pw_type) return (buf); } } - -static char *msgtypes[] = { - "", - "RTM_ADD: Add Route", - "RTM_DELETE: Delete Route", - "RTM_CHANGE: Change Metrics or flags", - "RTM_GET: Report Metrics", - "RTM_LOSING: Kernel Suspects Partitioning", - "RTM_REDIRECT: Told to use different route", - "RTM_MISS: Lookup failed on this address", - "RTM_LOCK: fix specified metrics", - "RTM_OLDADD: caused by SIOCADDRT", - "RTM_OLDDEL: caused by SIOCDELRT", - "RTM_RESOLVE: Route created by cloning", - "RTM_NEWADDR: address being added to iface", - "RTM_DELADDR: address being removed from iface", - "RTM_IFINFO: iface status change", - "RTM_IFANNOUNCE: iface arrival/departure", - "RTM_DESYNC: route socket overflow", -}; - -void -log_rtmsg(unsigned char rtm_type) -{ - if (!(verbose & LDPD_OPT_VERBOSE2)) - return; - - if (rtm_type > 0 && - rtm_type < sizeof(msgtypes)/sizeof(msgtypes[0])) - log_debug("kernel message: %s", msgtypes[rtm_type]); - else - log_debug("kernel message: rtm_type %d out of range", - rtm_type); -} diff --git a/ldpd/log.h b/ldpd/log.h index 94c463041d..4d6da43cac 100644 --- a/ldpd/log.h +++ b/ldpd/log.h @@ -26,8 +26,6 @@ union ldpd_addr; struct hello_source; struct fec; -void log_init(int); -void log_verbose(int); void logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 3))); void log_warn(const char *, ...) @@ -36,17 +34,22 @@ void log_warnx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_info(const char *, ...) __attribute__((__format__ (printf, 1, 2))); +void log_notice(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); void log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2))); -void fatal(const char *) __dead +void fatal(const char *) + __attribute__ ((noreturn)) __attribute__((__format__ (printf, 1, 0))); -void fatalx(const char *) __dead +void fatalx(const char *) + __attribute__ ((noreturn)) __attribute__((__format__ (printf, 1, 0))); const char *log_sockaddr(void *); const char *log_in6addr(const struct in6_addr *); const char *log_in6addr_scope(const struct in6_addr *, unsigned int); const char *log_addr(int, const union ldpd_addr *); char *log_label(uint32_t); +const char *log_time(time_t); char *log_hello_src(const struct hello_source *); const char *log_map(const struct map *); const char *log_fec(const struct fec *); @@ -58,6 +61,5 @@ const char *if_type_name(enum iface_type); const char *msg_name(uint16_t); const char *status_code_name(uint32_t); const char *pw_type_name(uint16_t); -void log_rtmsg(unsigned char); #endif /* _LOG_H_ */ diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index d3f83734f5..8376a01549 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -19,14 +19,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" @@ -37,13 +30,13 @@ static __inline int nbr_id_compare(struct nbr *, struct nbr *); static __inline int nbr_addr_compare(struct nbr *, struct nbr *); static __inline int nbr_pid_compare(struct nbr *, struct nbr *); static void nbr_update_peerid(struct nbr *); -static void nbr_ktimer(int, short, void *); +static int nbr_ktimer(struct thread *); static void nbr_start_ktimer(struct nbr *); -static void nbr_ktimeout(int, short, void *); +static int nbr_ktimeout(struct thread *); static void nbr_start_ktimeout(struct nbr *); -static void nbr_itimeout(int, short, void *); +static int nbr_itimeout(struct thread *); static void nbr_start_itimeout(struct nbr *); -static void nbr_idtimer(int, short, void *); +static int nbr_idtimer(struct thread *); static int nbr_act_session_operational(struct nbr *); static void nbr_send_labelmappings(struct nbr *); @@ -266,15 +259,17 @@ nbr_new(struct in_addr id, int af, int ds_tlv, union ldpd_addr *addr, TAILQ_INIT(&nbr->release_list); TAILQ_INIT(&nbr->abortreq_list); - /* set event structures */ - evtimer_set(&nbr->keepalive_timeout, nbr_ktimeout, nbr); - evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr); - evtimer_set(&nbr->init_timeout, nbr_itimeout, nbr); - evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr); - nbrp = nbr_params_find(leconf, nbr->id); - if (nbrp && pfkey_establish(nbr, nbrp) == -1) - fatalx("pfkey setup failed"); + if (nbrp) { +#ifdef __OpenBSD__ + if (pfkey_establish(nbr, nbrp) == -1) + fatalx("pfkey setup failed"); +#else + sock_set_md5sig( + (ldp_af_global_get(&global, nbr->af))->ldp_session_socket, + nbr->af, &nbr->raddr, nbrp->auth.md5key); +#endif + } pconn = pending_conn_find(nbr->af, &nbr->raddr); if (pconn) { @@ -291,10 +286,16 @@ nbr_del(struct nbr *nbr) log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); +#ifdef __OpenBSD__ pfkey_remove(nbr); +#else + sock_set_md5sig( + (ldp_af_global_get(&global, nbr->af))->ldp_session_socket, + nbr->af, &nbr->raddr, NULL); +#endif if (nbr_pending_connect(nbr)) - event_del(&nbr->ev_connect); + THREAD_WRITE_OFF(nbr->ev_connect); nbr_stop_ktimer(nbr); nbr_stop_ktimeout(nbr); nbr_stop_itimeout(nbr); @@ -382,176 +383,168 @@ nbr_session_active_role(struct nbr *nbr) /* Keepalive timer: timer to send keepalive message to neighbors */ -static void -nbr_ktimer(int fd, short event, void *arg) +static int +nbr_ktimer(struct thread *thread) { - struct nbr *nbr = arg; + struct nbr *nbr = THREAD_ARG(thread); + nbr->keepalive_timer = NULL; send_keepalive(nbr); nbr_start_ktimer(nbr); + + return (0); } static void nbr_start_ktimer(struct nbr *nbr) { - struct timeval tv; + int secs; /* send three keepalives per period */ - timerclear(&tv); - tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD); - if (evtimer_add(&nbr->keepalive_timer, &tv) == -1) - fatal(__func__); + secs = nbr->keepalive / KEEPALIVE_PER_PERIOD; + THREAD_TIMER_OFF(nbr->keepalive_timer); + nbr->keepalive_timer = thread_add_timer(master, nbr_ktimer, nbr, secs); } void nbr_stop_ktimer(struct nbr *nbr) { - if (evtimer_pending(&nbr->keepalive_timer, NULL) && - evtimer_del(&nbr->keepalive_timer) == -1) - fatal(__func__); + THREAD_TIMER_OFF(nbr->keepalive_timer); } /* Keepalive timeout: if the nbr hasn't sent keepalive */ -static void -nbr_ktimeout(int fd, short event, void *arg) +static int +nbr_ktimeout(struct thread *thread) { - struct nbr *nbr = arg; + struct nbr *nbr = THREAD_ARG(thread); + + nbr->keepalive_timeout = NULL; log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); session_shutdown(nbr, S_KEEPALIVE_TMR, 0, 0); + + return (0); } static void nbr_start_ktimeout(struct nbr *nbr) { - struct timeval tv; - - timerclear(&tv); - tv.tv_sec = nbr->keepalive; - - if (evtimer_add(&nbr->keepalive_timeout, &tv) == -1) - fatal(__func__); + THREAD_TIMER_OFF(nbr->keepalive_timeout); + nbr->keepalive_timeout = thread_add_timer(master, nbr_ktimeout, nbr, + nbr->keepalive); } void nbr_stop_ktimeout(struct nbr *nbr) { - if (evtimer_pending(&nbr->keepalive_timeout, NULL) && - evtimer_del(&nbr->keepalive_timeout) == -1) - fatal(__func__); + THREAD_TIMER_OFF(nbr->keepalive_timeout); } /* Session initialization timeout: if nbr got stuck in the initialization FSM */ -static void -nbr_itimeout(int fd, short event, void *arg) +static int +nbr_itimeout(struct thread *thread) { - struct nbr *nbr = arg; + struct nbr *nbr = THREAD_ARG(thread); log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + + return (0); } static void nbr_start_itimeout(struct nbr *nbr) { - struct timeval tv; + int secs; - timerclear(&tv); - tv.tv_sec = INIT_FSM_TIMEOUT; - if (evtimer_add(&nbr->init_timeout, &tv) == -1) - fatal(__func__); + secs = INIT_FSM_TIMEOUT; + THREAD_TIMER_OFF(nbr->init_timeout); + nbr->init_timeout = thread_add_timer(master, nbr_itimeout, nbr, secs); } void nbr_stop_itimeout(struct nbr *nbr) { - if (evtimer_pending(&nbr->init_timeout, NULL) && - evtimer_del(&nbr->init_timeout) == -1) - fatal(__func__); + THREAD_TIMER_OFF(nbr->init_timeout); } /* Init delay timer: timer to retry to iniziatize session */ -static void -nbr_idtimer(int fd, short event, void *arg) +static int +nbr_idtimer(struct thread *thread) { - struct nbr *nbr = arg; + struct nbr *nbr = THREAD_ARG(thread); + + nbr->initdelay_timer = NULL; log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); nbr_establish_connection(nbr); + + return (0); } void nbr_start_idtimer(struct nbr *nbr) { - struct timeval tv; + int secs; - timerclear(&tv); - - tv.tv_sec = INIT_DELAY_TMR; + secs = INIT_DELAY_TMR; switch(nbr->idtimer_cnt) { default: /* do not further increase the counter */ - tv.tv_sec = MAX_DELAY_TMR; + secs = MAX_DELAY_TMR; break; case 2: - tv.tv_sec *= 2; + secs *= 2; /* FALLTHROUGH */ case 1: - tv.tv_sec *= 2; + secs *= 2; /* FALLTHROUGH */ case 0: nbr->idtimer_cnt++; break; } - if (evtimer_add(&nbr->initdelay_timer, &tv) == -1) - fatal(__func__); + THREAD_TIMER_OFF(nbr->initdelay_timer); + nbr->initdelay_timer = thread_add_timer(master, nbr_idtimer, nbr, secs); } void nbr_stop_idtimer(struct nbr *nbr) { - if (evtimer_pending(&nbr->initdelay_timer, NULL) && - evtimer_del(&nbr->initdelay_timer) == -1) - fatal(__func__); + THREAD_TIMER_OFF(nbr->initdelay_timer); } int nbr_pending_idtimer(struct nbr *nbr) { - if (evtimer_pending(&nbr->initdelay_timer, NULL)) - return (1); - - return (0); + return (nbr->initdelay_timer != NULL); } int nbr_pending_connect(struct nbr *nbr) { - if (event_initialized(&nbr->ev_connect) && - event_pending(&nbr->ev_connect, EV_WRITE, NULL)) - return (1); - - return (0); + return (nbr->ev_connect != NULL); } -static void -nbr_connect_cb(int fd, short event, void *arg) +static int +nbr_connect_cb(struct thread *thread) { - struct nbr *nbr = arg; + struct nbr *nbr = THREAD_ARG(thread); int error; socklen_t len; + nbr->ev_connect = NULL; + len = sizeof(error); if (getsockopt(nbr->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { log_warn("%s: getsockopt SOL_SOCKET SO_ERROR", __func__); - return; + return (0); } if (error) { @@ -559,10 +552,12 @@ nbr_connect_cb(int fd, short event, void *arg) errno = error; log_debug("%s: error while connecting to %s: %s", __func__, log_addr(nbr->af, &nbr->raddr), strerror(errno)); - return; + return (0); } nbr_fsm(nbr, NBR_EVT_CONNECT_UP); + + return (0); } int @@ -572,17 +567,20 @@ nbr_establish_connection(struct nbr *nbr) struct sockaddr_storage remote_sa; struct adj *adj; struct nbr_params *nbrp; +#ifdef __OpenBSD__ int opt = 1; +#endif - nbr->fd = socket(nbr->af, - SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + nbr->fd = socket(nbr->af, SOCK_STREAM, 0); if (nbr->fd == -1) { log_warn("%s: error while creating socket", __func__); return (-1); } + sock_set_nonblock(nbr->fd); nbrp = nbr_params_find(leconf, nbr->id); if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { +#ifdef __OpenBSD__ if (sysdep.no_pfkey || sysdep.no_md5sig) { log_warnx("md5sig configured but not available"); close(nbr->fd); @@ -594,6 +592,10 @@ nbr_establish_connection(struct nbr *nbr) close(nbr->fd); return (-1); } +#else + sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, + nbrp->auth.md5key); +#endif } memcpy(&local_sa, addr2sa(nbr->af, &nbr->laddr, 0), sizeof(local_sa)); @@ -603,7 +605,7 @@ nbr_establish_connection(struct nbr *nbr) addscope((struct sockaddr_in6 *)&remote_sa, nbr->raddr_scope); if (bind(nbr->fd, (struct sockaddr *)&local_sa, - local_sa.ss_len) == -1) { + sockaddr_len((struct sockaddr *)&local_sa)) == -1) { log_warn("%s: error while binding socket to %s", __func__, log_sockaddr((struct sockaddr *)&local_sa)); close(nbr->fd); @@ -624,11 +626,10 @@ nbr_establish_connection(struct nbr *nbr) adj->source.target); if (connect(nbr->fd, (struct sockaddr *)&remote_sa, - remote_sa.ss_len) == -1) { + sockaddr_len((struct sockaddr *)&remote_sa)) == -1) { if (errno == EINPROGRESS) { - event_set(&nbr->ev_connect, nbr->fd, EV_WRITE, - nbr_connect_cb, nbr); - event_add(&nbr->ev_connect, NULL); + THREAD_WRITE_ON(master, nbr->ev_connect, nbr_connect_cb, + nbr, nbr->fd); return (0); } log_warn("%s: error while connecting to %s", __func__, @@ -682,8 +683,8 @@ nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp) return (-1); break; case AF_INET6: - if (sock_set_ipv6_minhopcount(fd, ttl) == -1) - return (-1); + /* ignore any possible error */ + sock_set_ipv6_minhopcount(fd, ttl); ttl = 255; if (sock_set_ipv6_ucast_hops(fd, ttl) == -1) return (-1); @@ -798,7 +799,10 @@ nbr_to_ctl(struct nbr *nbr) nctl.af = nbr->af; nctl.id = nbr->id; nctl.laddr = nbr->laddr; + nctl.lport = nbr->tcp->lport; nctl.raddr = nbr->raddr; + nctl.rport = nbr->tcp->rport; + nctl.holdtime = nbr->keepalive; nctl.nbr_state = nbr->state; gettimeofday(&now, NULL); diff --git a/ldpd/notification.c b/ldpd/notification.c index f30646bb83..d306361d5c 100644 --- a/ldpd/notification.c +++ b/ldpd/notification.c @@ -16,14 +16,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include +#include #include "ldpd.h" #include "ldp.h" #include "log.h" #include "ldpe.h" +#include "ldp_debug.h" void send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) @@ -65,7 +64,7 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) } if (tcp->nbr) - log_debug("msg-out: notification: lsr-id %s, status %s%s", + 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)" : ""); @@ -199,8 +198,8 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) } } - log_warnx("msg-in: notification: lsr-id %s, status %s%s", - inet_ntoa(nbr->id), status_code_name(ntohl(st.status_code)), + 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)" : ""); if (st.status_code & htonl(STATUS_FATAL)) { diff --git a/ldpd/packet.c b/ldpd/packet.c index 7cc375c317..9b3151d720 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -18,28 +18,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "sockopt.h" + static struct iface *disc_find_iface(unsigned int, int, union ldpd_addr *, int); -static void session_read(int, short, void *); -static void session_write(int, short, void *); +static int session_read(struct thread *); +static int session_write(struct thread *); static ssize_t session_get_pdu(struct ibuf_read *, char **); static void tcp_close(struct tcp_conn *); static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); -static void pending_conn_timeout(int, short, void *); +static int pending_conn_timeout(struct thread *); int gen_ldp_hdr(struct ibuf *buf, uint16_t size) @@ -50,7 +44,7 @@ gen_ldp_hdr(struct ibuf *buf, uint16_t size) ldp_hdr.version = htons(LDP_VERSION); /* exclude the 'Version' and 'PDU Length' fields from the total */ ldp_hdr.length = htons(size - LDP_HDR_DEAD_LEN); - ldp_hdr.lsr_id = leconf->rtr_id.s_addr; + ldp_hdr.lsr_id = ldp_rtr_id_get(leconf); ldp_hdr.lspace_id = 0; return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE)); @@ -104,7 +98,7 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, } sa = addr2sa(af, dst, LDP_PORT); - if (sendto(fd, pkt, len, 0, sa, sa->sa_len) == -1) { + if (sendto(fd, pkt, len, 0, sa, sockaddr_len(sa)) == -1) { log_warn("%s: error sending packet to %s", __func__, log_sockaddr(sa)); return (-1); @@ -114,19 +108,27 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, } /* Discovery functions */ -#define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo)) -void -disc_recv_packet(int fd, short event, void *bula) +int +disc_recv_packet(struct thread *thread) { + int fd = THREAD_FD(thread); + struct thread **threadp = THREAD_ARG(thread); + union { struct cmsghdr hdr; - char buf[CMSG_SPACE(CMSG_MAXLEN)]; +#ifdef HAVE_STRUCT_SOCKADDR_DL + char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; +#else + char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; +#endif } cmsgbuf; struct msghdr m; struct sockaddr_storage from; struct iovec iov; char *buf; +#ifndef MSG_MCAST struct cmsghdr *cmsg; +#endif ssize_t r; int multicast; int af; @@ -140,8 +142,8 @@ disc_recv_packet(int fd, short event, void *bula) uint16_t msg_len; struct in_addr lsr_id; - if (event != EV_READ) - return; + /* reschedule read */ + *threadp = thread_add_read(master, disc_recv_packet, threadp, fd); /* setup buffer */ memset(&m, 0, sizeof(m)); @@ -158,44 +160,68 @@ disc_recv_packet(int fd, short event, void *bula) if (errno != EAGAIN && errno != EINTR) log_debug("%s: read error: %s", __func__, strerror(errno)); - return; + return (0); } + sa2addr((struct sockaddr *)&from, &af, &src, NULL); +#ifdef MSG_MCAST multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0; - sa2addr((struct sockaddr *)&from, &af, &src); +#else + multicast = 0; + for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; + cmsg = CMSG_NXTHDR(&m, cmsg)) { +#if defined(HAVE_IP_PKTINFO) + if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo; + + pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + if (IN_MULTICAST(ntohl(pktinfo->ipi_addr.s_addr))) + multicast = 1; + break; + } +#elif defined(HAVE_IP_RECVDSTADDR) + if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr *addr; + + addr = (struct in_addr *)CMSG_DATA(cmsg); + if (IN_MULTICAST(ntohl(addr->s_addr))) + multicast = 1; + break; + } +#else +#error "Unsupported socket API" +#endif + if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo; + + pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + if (IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr)) + multicast = 1; + break; + } + } +#endif /* MSG_MCAST */ if (bad_addr(af, &src)) { log_debug("%s: invalid source address: %s", __func__, log_addr(af, &src)); - return; - } - - for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; - cmsg = CMSG_NXTHDR(&m, cmsg)) { - if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && - cmsg->cmsg_type == IP_RECVIF) { - ifindex = ((struct sockaddr_dl *) - CMSG_DATA(cmsg))->sdl_index; - break; - } - if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 && - cmsg->cmsg_type == IPV6_PKTINFO) { - ifindex = ((struct in6_pktinfo *) - CMSG_DATA(cmsg))->ipi6_ifindex; - break; - } + return (0); } + ifindex = getsockopt_ifindex(af, &m); /* find a matching interface */ iface = disc_find_iface(ifindex, af, &src, multicast); if (iface == NULL) - return; + return (0); /* check packet size */ len = (uint16_t)r; if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) { log_debug("%s: bad packet size, source %s", __func__, log_addr(af, &src)); - return; + return (0); } /* LDP header sanity checks */ @@ -203,12 +229,12 @@ disc_recv_packet(int fd, short event, void *bula) if (ntohs(ldp_hdr.version) != LDP_VERSION) { log_debug("%s: invalid LDP version %d, source %s", __func__, ntohs(ldp_hdr.version), log_addr(af, &src)); - return; + return (0); } if (ntohs(ldp_hdr.lspace_id) != 0) { log_debug("%s: invalid label space %u, source %s", __func__, ntohs(ldp_hdr.lspace_id), log_addr(af, &src)); - return; + return (0); } /* check "PDU Length" field */ pdu_len = ntohs(ldp_hdr.length); @@ -216,7 +242,7 @@ disc_recv_packet(int fd, short event, void *bula) (pdu_len > (len - LDP_HDR_DEAD_LEN))) { log_debug("%s: invalid LDP packet length %u, source %s", __func__, ntohs(ldp_hdr.length), log_addr(af, &src)); - return; + return (0); } buf += LDP_HDR_SIZE; len -= LDP_HDR_SIZE; @@ -235,7 +261,7 @@ disc_recv_packet(int fd, short event, void *bula) if (msg_len < LDP_MSG_LEN || ((msg_len + LDP_MSG_DEAD_LEN) > pdu_len)) { log_debug("%s: invalid LDP message length %u, source %s", __func__, ntohs(msg.length), log_addr(af, &src)); - return; + return (0); } buf += LDP_MSG_SIZE; len -= LDP_MSG_SIZE; @@ -249,6 +275,8 @@ disc_recv_packet(int fd, short event, void *bula) log_debug("%s: unknown LDP packet type, source %s", __func__, log_addr(af, &src)); } + + return (0); } static struct iface * @@ -306,9 +334,10 @@ disc_find_iface(unsigned int ifindex, int af, union ldpd_addr *src, return (NULL); } -void -session_accept(int fd, short event, void *bula) +int +session_accept(struct thread *thread) { + int fd = THREAD_FD(thread); struct sockaddr_storage src; socklen_t len = sizeof(src); int newfd; @@ -317,11 +346,7 @@ session_accept(int fd, short event, void *bula) struct nbr *nbr; struct pending_conn *pconn; - if (!(event & EV_READ)) - return; - - newfd = accept4(fd, (struct sockaddr *)&src, &len, - SOCK_NONBLOCK | SOCK_CLOEXEC); + newfd = accept(fd, (struct sockaddr *)&src, &len); if (newfd == -1) { /* * Pause accept if we are out of file descriptors, or @@ -333,10 +358,11 @@ session_accept(int fd, short event, void *bula) errno != ECONNABORTED) log_debug("%s: accept error: %s", __func__, strerror(errno)); - return; + return (0); } + sock_set_nonblock(newfd); - sa2addr((struct sockaddr *)&src, &af, &addr); + sa2addr((struct sockaddr *)&src, &af, &addr, NULL); /* * Since we don't support label spaces, we can identify this neighbor @@ -361,26 +387,29 @@ session_accept(int fd, short event, void *bula) close(newfd); else pending_conn_new(newfd, af, &addr); - return; + return (0); } /* protection against buggy implementations */ if (nbr_session_active_role(nbr)) { close(newfd); - return; + return (0); } if (nbr->state != NBR_STA_PRESENT) { log_debug("%s: lsr-id %s: rejecting additional transport " "connection", __func__, inet_ntoa(nbr->id)); close(newfd); - return; + return (0); } session_accept_nbr(nbr, newfd); + + return (0); } void session_accept_nbr(struct nbr *nbr, int fd) { +#ifdef __OpenBSD__ struct nbr_params *nbrp; int opt; socklen_t len; @@ -407,41 +436,42 @@ session_accept_nbr(struct nbr *nbr, int fd) return; } } +#endif nbr->tcp = tcp_new(fd, nbr); nbr_fsm(nbr, NBR_EVT_MATCH_ADJ); } -static void -session_read(int fd, short event, void *arg) +static int +session_read(struct thread *thread) { - struct nbr *nbr = arg; + int fd = THREAD_FD(thread); + struct nbr *nbr = THREAD_ARG(thread); struct tcp_conn *tcp = nbr->tcp; struct ldp_hdr *ldp_hdr; struct ldp_msg *msg; - char *buf, *pdu; + char *buf = NULL, *pdu; ssize_t n, len; uint16_t pdu_len, msg_len, msg_size, max_pdu_len; int ret; - if (event != EV_READ) - return; + tcp->rev = thread_add_read(master, session_read, nbr, fd); if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos, sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) { if (errno != EINTR && errno != EAGAIN) { log_warn("%s: read error", __func__); nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); - return; + return (0); } /* retry read */ - return; + return (0); } if (n == 0) { /* connection closed */ log_debug("%s: connection closed by remote end", __func__); nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); - return; + return (0); } tcp->rbuf->wpos += n; @@ -451,7 +481,7 @@ session_read(int fd, short event, void *arg) if (ntohs(ldp_hdr->version) != LDP_VERSION) { session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0); free(buf); - return; + return (0); } pdu_len = ntohs(ldp_hdr->length); @@ -468,14 +498,14 @@ session_read(int fd, short event, void *arg) pdu_len > max_pdu_len) { session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); free(buf); - return; + return (0); } pdu_len -= LDP_HDR_PDU_LEN; if (ldp_hdr->lsr_id != nbr->id.s_addr || ldp_hdr->lspace_id != 0) { session_shutdown(nbr, S_BAD_LDP_ID, 0, 0); free(buf); - return; + return (0); } pdu += LDP_HDR_SIZE; len -= LDP_HDR_SIZE; @@ -493,7 +523,7 @@ session_read(int fd, short event, void *arg) session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); free(buf); - return; + return (0); } msg_size = msg_len + LDP_MSG_DEAD_LEN; pdu_len -= msg_size; @@ -506,7 +536,7 @@ session_read(int fd, short event, void *arg) session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); - return; + return (0); } break; case MSG_TYPE_KEEPALIVE: @@ -515,7 +545,7 @@ session_read(int fd, short event, void *arg) session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); - return; + return (0); } break; case MSG_TYPE_ADDR: @@ -529,7 +559,7 @@ session_read(int fd, short event, void *arg) session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); - return; + return (0); } break; default: @@ -573,7 +603,7 @@ session_read(int fd, short event, void *arg) if (ret == -1) { /* parser failed, giving up */ free(buf); - return; + return (0); } /* Analyse the next message */ @@ -583,19 +613,20 @@ session_read(int fd, short event, void *arg) free(buf); if (len != 0) { session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); - return; + return (0); } } + + return (0); } -static void -session_write(int fd, short event, void *arg) +static int +session_write(struct thread *thread) { - struct tcp_conn *tcp = arg; + struct tcp_conn *tcp = THREAD_ARG(thread); struct nbr *nbr = tcp->nbr; - if (!(event & EV_WRITE)) - return; + tcp->wbuf.ev = NULL; if (msgbuf_write(&tcp->wbuf.wbuf) <= 0) if (errno != EAGAIN && nbr) @@ -607,10 +638,12 @@ session_write(int fd, short event, void *arg) * close the socket. */ tcp_close(tcp); - return; + return (0); } evbuf_event_add(&tcp->wbuf); + + return (0); } void @@ -620,7 +653,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, switch (nbr->state) { case NBR_STA_PRESENT: if (nbr_pending_connect(nbr)) - event_del(&nbr->ev_connect); + THREAD_WRITE_OFF(nbr->ev_connect); break; case NBR_STA_INITIAL: case NBR_STA_OPENREC: @@ -681,7 +714,9 @@ session_get_pdu(struct ibuf_read *r, char **b) struct tcp_conn * tcp_new(int fd, struct nbr *nbr) { - struct tcp_conn *tcp; + struct tcp_conn *tcp; + struct sockaddr_storage src; + socklen_t len = sizeof(src); if ((tcp = calloc(1, sizeof(*tcp))) == NULL) fatal(__func__); @@ -693,12 +728,15 @@ tcp_new(int fd, struct nbr *nbr) if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL) fatal(__func__); - event_set(&tcp->rev, tcp->fd, EV_READ | EV_PERSIST, - session_read, nbr); - event_add(&tcp->rev, NULL); + tcp->rev = thread_add_read(master, session_read, nbr, tcp->fd); tcp->nbr = nbr; } + getsockname(fd, (struct sockaddr *)&src, &len); + sa2addr((struct sockaddr *)&src, NULL, NULL, &tcp->lport); + getpeername(fd, (struct sockaddr *)&src, &len); + sa2addr((struct sockaddr *)&src, NULL, NULL, &tcp->rport); + return (tcp); } @@ -710,7 +748,7 @@ tcp_close(struct tcp_conn *tcp) evbuf_clear(&tcp->wbuf); if (tcp->nbr) { - event_del(&tcp->rev); + THREAD_READ_OFF(tcp->rev); free(tcp->rbuf); tcp->nbr->tcp = NULL; } @@ -724,7 +762,6 @@ static struct pending_conn * pending_conn_new(int fd, int af, union ldpd_addr *addr) { struct pending_conn *pconn; - struct timeval tv; if ((pconn = calloc(1, sizeof(*pconn))) == NULL) fatal(__func__); @@ -732,13 +769,9 @@ pending_conn_new(int fd, int af, union ldpd_addr *addr) pconn->fd = fd; pconn->af = af; pconn->addr = *addr; - evtimer_set(&pconn->ev_timeout, pending_conn_timeout, pconn); TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry); - - timerclear(&tv); - tv.tv_sec = PENDING_CONN_TIMEOUT; - if (evtimer_add(&pconn->ev_timeout, &tv) == -1) - fatal(__func__); + pconn->ev_timeout = thread_add_timer(master, pending_conn_timeout, + pconn, PENDING_CONN_TIMEOUT); return (pconn); } @@ -746,10 +779,7 @@ pending_conn_new(int fd, int af, union ldpd_addr *addr) void pending_conn_del(struct pending_conn *pconn) { - if (evtimer_pending(&pconn->ev_timeout, NULL) && - evtimer_del(&pconn->ev_timeout) == -1) - fatal(__func__); - + THREAD_TIMER_OFF(pconn->ev_timeout); TAILQ_REMOVE(&global.pending_conns, pconn, entry); free(pconn); } @@ -767,12 +797,14 @@ pending_conn_find(int af, union ldpd_addr *addr) return (NULL); } -static void -pending_conn_timeout(int fd, short event, void *arg) +static int +pending_conn_timeout(struct thread *thread) { - struct pending_conn *pconn = arg; + struct pending_conn *pconn = THREAD_ARG(thread); struct tcp_conn *tcp; + pconn->ev_timeout = NULL; + log_debug("%s: no adjacency with remote end: %s", __func__, log_addr(pconn->af, &pconn->addr)); @@ -785,4 +817,6 @@ pending_conn_timeout(int fd, short event, void *arg) msgbuf_write(&tcp->wbuf.wbuf); pending_conn_del(pconn); + + return (0); } diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c index f0f16c867f..29f763e6a6 100644 --- a/ldpd/pfkey.c +++ b/ldpd/pfkey.c @@ -17,7 +17,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef __OpenBSD__ #include +#include #include #include #include @@ -464,3 +466,4 @@ pfkey_init(void) } return (fd); } +#endif /* __OpenBSD__ */ diff --git a/ldpd/socket.c b/ldpd/socket.c index 8f26771df7..cf352d7204 100644 --- a/ldpd/socket.c +++ b/ldpd/socket.c @@ -19,25 +19,29 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include +#include #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "lib/log.h" +#include "privs.h" +#include "sockopt.h" + +extern struct zebra_privs_t ldpd_privs; +extern struct zebra_privs_t ldpe_privs; + int ldp_create_socket(int af, enum socket_type type) { int fd, domain, proto; union ldpd_addr addr; struct sockaddr_storage local_sa; +#ifdef __OpenBSD__ int opt; +#endif + int save_errno; /* create socket */ switch (type) { @@ -53,11 +57,13 @@ ldp_create_socket(int af, enum socket_type type) default: fatalx("ldp_create_socket: unknown socket type"); } - fd = socket(af, domain | SOCK_NONBLOCK | SOCK_CLOEXEC, proto); + fd = socket(af, domain, proto); if (fd == -1) { log_warn("%s: error creating socket", __func__); return (-1); } + sock_set_nonblock(fd); + sockopt_v6only(af, fd); /* bind to a local address/port */ switch (type) { @@ -72,21 +78,28 @@ ldp_create_socket(int af, enum socket_type type) addr = (ldp_af_conf_get(ldpd_conf, af))->trans_addr; memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT), sizeof(local_sa)); - if (sock_set_bindany(fd, 1) == -1) { - close(fd); - return (-1); - } + /* ignore any possible error */ + sock_set_bindany(fd, 1); break; } + if (ldpd_privs.change(ZPRIVS_RAISE)) + log_warn("%s: could not raise privs", __func__); if (sock_set_reuse(fd, 1) == -1) { close(fd); return (-1); } - if (bind(fd, (struct sockaddr *)&local_sa, local_sa.ss_len) == -1) { - log_warn("%s: error binding socket", __func__); + if (bind(fd, (struct sockaddr *)&local_sa, + sockaddr_len((struct sockaddr *)&local_sa)) == -1) { + save_errno = errno; + if (ldpd_privs.change(ZPRIVS_LOWER)) + log_warn("%s: could not lower privs", __func__); + log_warnx("%s: error binding socket: %s", __func__, + safe_strerror(save_errno)); close(fd); return (-1); } + if (ldpd_privs.change(ZPRIVS_LOWER)) + log_warn("%s: could not lower privs", __func__); /* set options */ switch (af) { @@ -111,6 +124,21 @@ ldp_create_socket(int af, enum socket_type type) close(fd); return (-1); } +#ifndef MSG_MCAST +#if defined(HAVE_IP_PKTINFO) + if (sock_set_ipv4_pktinfo(fd, 1) == -1) { + close(fd); + return (-1); + } +#elif defined(HAVE_IP_RECVDSTADDR) + if (sock_set_ipv4_recvdstaddr(fd, 1) == -1) { + close(fd); + return (-1); + } +#else +#error "Unsupported socket API" +#endif +#endif /* MSG_MCAST */ } if (type == LDP_SOCKET_SESSION) { if (sock_set_ipv4_ucast_ttl(fd, 255) == -1) { @@ -134,10 +162,8 @@ ldp_create_socket(int af, enum socket_type type) return (-1); } if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) { - if (sock_set_ipv6_minhopcount(fd, 255) == -1) { - close(fd); - return (-1); - } + /* ignore any possible error */ + sock_set_ipv6_minhopcount(fd, 255); } } if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) { @@ -163,6 +189,7 @@ ldp_create_socket(int af, enum socket_type type) if (listen(fd, LDP_BACKLOG) == -1) log_warn("%s: error listening on socket", __func__); +#ifdef __OpenBSD__ opt = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, sizeof(opt)) == -1) { @@ -174,12 +201,41 @@ ldp_create_socket(int af, enum socket_type type) return (-1); } } +#endif break; } return (fd); } +void +sock_set_nonblock(int fd) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + fatal("fcntl F_GETFL"); + + flags |= O_NONBLOCK; + + if ((flags = fcntl(fd, F_SETFL, flags)) == -1) + fatal("fcntl F_SETFL"); +} + +void +sock_set_cloexec(int fd) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFD, 0)) == -1) + fatal("fcntl F_GETFD"); + + flags |= FD_CLOEXEC; + + if ((flags = fcntl(fd, F_SETFD, flags)) == -1) + fatal("fcntl F_SETFD"); +} + void sock_set_recvbuf(int fd) { @@ -206,15 +262,68 @@ sock_set_reuse(int fd, int enable) int sock_set_bindany(int fd, int enable) { +#ifdef HAVE_SO_BINDANY + if (ldpd_privs.change(ZPRIVS_RAISE)) + log_warn("%s: could not raise privs", __func__); if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, sizeof(int)) < 0) { + if (ldpd_privs.change(ZPRIVS_LOWER)) + log_warn("%s: could not lower privs", __func__); log_warn("%s: error setting SO_BINDANY", __func__); return (-1); } - + if (ldpd_privs.change(ZPRIVS_LOWER)) + log_warn("%s: could not lower privs", __func__); return (0); +#elif defined(HAVE_IP_FREEBIND) + if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &enable, sizeof(int)) < 0) { + log_warn("%s: error setting IP_FREEBIND", __func__); + return (-1); + } + return (0); +#else + log_warnx("%s: missing SO_BINDANY and IP_FREEBIND, unable to bind " + "to a nonlocal IP address", __func__); + return (-1); +#endif /* HAVE_SO_BINDANY */ } +#ifndef __OpenBSD__ +/* + * Set MD5 key for the socket, for the given peer address. If the password + * is NULL or zero-length, the option will be disabled. + */ +int +sock_set_md5sig(int fd, int af, union ldpd_addr *addr, const char *password) +{ + int ret = -1; + int save_errno = ENOSYS; +#if HAVE_DECL_TCP_MD5SIG + union sockunion su; +#endif + + if (fd == -1) + return (0); +#if HAVE_DECL_TCP_MD5SIG + memcpy(&su, addr2sa(af, addr, 0), sizeof(su)); + + if (ldpe_privs.change(ZPRIVS_RAISE)) { + log_warn("%s: could not raise privs", __func__); + return (-1); + } + ret = sockopt_tcp_signature(fd, &su, password); + save_errno = errno; + if (ldpe_privs.change(ZPRIVS_LOWER)) + log_warn("%s: could not lower privs", __func__); +#endif /* HAVE_TCP_MD5SIG */ + if (ret < 0) + log_warnx("%s: can't set TCP_MD5SIG option on fd %d: %s", + __func__, fd, safe_strerror(save_errno)); + + return (ret); +} +#endif + int sock_set_ipv4_tos(int fd, int tos) { @@ -229,23 +338,13 @@ sock_set_ipv4_tos(int fd, int tos) int sock_set_ipv4_recvif(int fd, int enable) { - if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, - sizeof(enable)) < 0) { - log_warn("%s: error setting IP_RECVIF", __func__); - return (-1); - } - return (0); + return (setsockopt_ifindex(AF_INET, fd, enable)); } int sock_set_ipv4_minttl(int fd, int ttl) { - if (setsockopt(fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) { - log_warn("%s: error setting IP_MINTTL", __func__); - return (-1); - } - - return (0); + return (sockopt_minttl(AF_INET, fd, ttl)); } int @@ -272,15 +371,45 @@ sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl) return (0); } +#ifndef MSG_MCAST +#if defined(HAVE_IP_PKTINFO) +int +sock_set_ipv4_pktinfo(int fd, int enable) +{ + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &enable, + sizeof(enable)) < 0) { + log_warn("%s: error setting IP_PKTINFO", __func__); + return (-1); + } + + return (0); +} +#elif defined(HAVE_IP_RECVDSTADDR) +int +sock_set_ipv4_recvdstaddr(int fd, int enable) +{ + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &enable, + sizeof(enable)) < 0) { + log_warn("%s: error setting IP_RECVDSTADDR", __func__); + return (-1); + } + + return (0); +} +#else +#error "Unsupported socket API" +#endif +#endif /* MSG_MCAST */ + int sock_set_ipv4_mcast(struct iface *iface) { - in_addr_t addr; + struct in_addr if_addr; - addr = if_get_ipv4_addr(iface); + if_addr.s_addr = if_get_ipv4_addr(iface); - if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP, IP_MULTICAST_IF, - &addr, sizeof(addr)) < 0) { + if (setsockopt_ipv4_multicast_if(global.ipv4.ldp_disc_socket, + if_addr, iface->ifindex) < 0) { log_warn("%s: error setting IP_MULTICAST_IF, interface %s", __func__, iface->name); return (-1); @@ -330,13 +459,7 @@ sock_set_ipv6_pktinfo(int fd, int enable) int sock_set_ipv6_minhopcount(int fd, int hoplimit) { - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT, - &hoplimit, sizeof(hoplimit)) < 0) { - log_warn("%s: error setting IPV6_MINHOPCOUNT", __func__); - return (-1); - } - - return (0); + return (sockopt_minttl(AF_INET6, fd, hoplimit)); } int diff --git a/ldpd/util.c b/ldpd/util.c index 981a5c8c21..e735263f5f 100644 --- a/ldpd/util.c +++ b/ldpd/util.c @@ -19,8 +19,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include +#include #include "ldpd.h" #include "log.h" @@ -44,7 +43,7 @@ mask2prefixlen6(struct sockaddr_in6 *sa_in6) * the possibly truncated sin6_addr struct. */ ap = (uint8_t *)&sa_in6->sin6_addr; - ep = (uint8_t *)sa_in6 + sa_in6->sin6_len; + ep = (uint8_t *)sa_in6 + sockaddr_len((struct sockaddr *)sa_in6); for (; ap < ep; ap++) { /* this "beauty" is adopted from sbin/route/show.c ... */ switch (*ap) { @@ -317,13 +316,17 @@ addr2sa(int af, union ldpd_addr *addr, uint16_t port) switch (af) { case AF_INET: sa_in->sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sa_in->sin_len = sizeof(struct sockaddr_in); +#endif sa_in->sin_addr = addr->v4; sa_in->sin_port = htons(port); break; case AF_INET6: sa_in6->sin6_family = AF_INET6; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sa_in6->sin6_len = sizeof(struct sockaddr_in6); +#endif sa_in6->sin6_addr = addr->v6; sa_in6->sin6_port = htons(port); break; @@ -335,22 +338,48 @@ addr2sa(int af, union ldpd_addr *addr, uint16_t port) } void -sa2addr(struct sockaddr *sa, int *af, union ldpd_addr *addr) +sa2addr(struct sockaddr *sa, int *af, union ldpd_addr *addr, in_port_t *port) { struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; - memset(addr, 0, sizeof(*addr)); + if (addr) + memset(addr, 0, sizeof(*addr)); switch (sa->sa_family) { case AF_INET: - *af = AF_INET; - addr->v4 = sa_in->sin_addr; + if (af) + *af = AF_INET; + if (addr) + addr->v4 = sa_in->sin_addr; + if (port) + *port = sa_in->sin_port; break; case AF_INET6: - *af = AF_INET6; - addr->v6 = sa_in6->sin6_addr; + if (af) + *af = AF_INET6; + if (addr) + addr->v6 = sa_in6->sin6_addr; + if (port) + *port = sa_in6->sin6_port; break; default: fatalx("sa2addr: unknown af"); } } + +socklen_t +sockaddr_len(struct sockaddr *sa) +{ +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + return (sa->sa_len); +#else + switch (sa->sa_family) { + case AF_INET: + return (sizeof(struct sockaddr_in)); + case AF_INET6: + return (sizeof(struct sockaddr_in6)); + default: + fatalx("sockaddr_len: unknown af"); + } +#endif +} diff --git a/lib/Makefile.am b/lib/Makefile.am index dbb80076c5..17be655a17 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -14,7 +14,8 @@ libzebra_la_SOURCES = \ filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \ sigevent.c pqueue.c jhash.c workqueue.c nexthop.c json.c \ - ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c + ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c \ + imsg-buffer.c imsg.c BUILT_SOURCES = route_types.h gitversion.h @@ -31,7 +32,7 @@ pkginclude_HEADERS = \ privs.h sigevent.h pqueue.h jhash.h zassert.h \ workqueue.h route_types.h libospf.h nexthop.h json.h \ ptm_lib.h csv.h bfd.h vrf.h ns.h systemd.h bitfield.h \ - fifo.h memory_vty.h + fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h noinst_HEADERS = \ plist_int.h diff --git a/lib/command.c b/lib/command.c index cfdb91a5b9..bf8b1b1d33 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2550,6 +2550,19 @@ node_parent ( enum node_type node ) case LINK_PARAMS_NODE: ret = INTERFACE_NODE; break; + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + ret = LDP_NODE; + break; + case LDP_IPV4_IFACE_NODE: + ret = LDP_IPV4_NODE; + break; + case LDP_IPV6_IFACE_NODE: + ret = LDP_IPV6_NODE; + break; + case LDP_PSEUDOWIRE_NODE: + ret = LDP_L2VPN_NODE; + break; default: ret = CONFIG_NODE; break; @@ -2920,6 +2933,8 @@ DEFUN (config_exit, case RIPNG_NODE: case OSPF_NODE: case OSPF6_NODE: + case LDP_NODE: + case LDP_L2VPN_NODE: case ISIS_NODE: case KEYCHAIN_NODE: case MASC_NODE: @@ -2938,6 +2953,19 @@ DEFUN (config_exit, case BGP_IPV6M_NODE: vty->node = BGP_NODE; break; + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + vty->node = LDP_NODE; + break; + case LDP_IPV4_IFACE_NODE: + vty->node = LDP_IPV4_NODE; + break; + case LDP_IPV6_IFACE_NODE: + vty->node = LDP_IPV6_NODE; + break; + case LDP_PSEUDOWIRE_NODE: + vty->node = LDP_L2VPN_NODE; + break; case KEYCHAIN_KEY_NODE: vty->node = KEYCHAIN_NODE; break; @@ -2988,6 +3016,13 @@ DEFUN (config_end, case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: + case LDP_NODE: + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + case LDP_L2VPN_NODE: + case LDP_PSEUDOWIRE_NODE: case ISIS_NODE: case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: diff --git a/lib/command.h b/lib/command.h index 808e742059..7a4c53a616 100644 --- a/lib/command.h +++ b/lib/command.h @@ -98,6 +98,13 @@ enum node_type BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ OSPF_NODE, /* OSPF protocol mode */ OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + LDP_NODE, /* LDP protocol mode */ + LDP_IPV4_NODE, /* LDP IPv4 address family */ + LDP_IPV6_NODE, /* LDP IPv6 address family */ + LDP_IPV4_IFACE_NODE, /* LDP IPv4 Interface */ + LDP_IPV6_IFACE_NODE, /* LDP IPv6 Interface */ + LDP_L2VPN_NODE, /* LDP L2VPN node */ + LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ ISIS_NODE, /* ISIS protocol mode */ PIM_NODE, /* PIM protocol mode */ MASC_NODE, /* MASC for multicast. */ diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c index 61b2c095ae..a486fc17c1 100644 --- a/lib/imsg-buffer.c +++ b/lib/imsg-buffer.c @@ -16,17 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include +#include +#include "openbsd-queue.h" #include "imsg.h" int ibuf_realloc(struct ibuf *, size_t); @@ -258,7 +250,7 @@ msgbuf_write(struct msgbuf *msgbuf) cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; - *(int *)CMSG_DATA(cmsg) = buf->fd; + memcpy(CMSG_DATA(cmsg), &buf->fd, sizeof(int)); } again: diff --git a/lib/imsg.c b/lib/imsg.c index 0c1cb8220c..246430cdd5 100644 --- a/lib/imsg.c +++ b/lib/imsg.c @@ -16,22 +16,49 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include - -#include -#include -#include -#include +#include +#include "openbsd-queue.h" #include "imsg.h" int imsg_fd_overhead = 0; int imsg_get_fd(struct imsgbuf *); +#ifndef __OpenBSD__ +/* + * The original code calls getdtablecount() which is OpenBSD specific. Use + * available_fds() from OpenSMTPD instead. + */ +static int +available_fds(unsigned int n) +{ + unsigned int i; + int ret, fds[256]; + + if (n > (sizeof(fds)/sizeof(fds[0]))) + return (1); + + ret = 0; + for (i = 0; i < n; i++) { + fds[i] = -1; + if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) + fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); + if (fds[i] < 0) { + ret = 1; + break; + } + } + } + + for (i = 0; i < n && fds[i] >= 0; i++) + close(fds[i]); + + return (ret); +} +#endif + void imsg_init(struct imsgbuf *ibuf, int fd) { @@ -71,9 +98,14 @@ imsg_read(struct imsgbuf *ibuf) return (-1); again: +#ifdef __OpenBSD__ if (getdtablecount() + imsg_fd_overhead + (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)) >= getdtablesize()) { +#else + if (available_fds(imsg_fd_overhead + + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { +#endif errno = EAGAIN; free(ifd); return (-1); diff --git a/lib/log.c b/lib/log.c index b9edb12fbd..14fa81a9d7 100644 --- a/lib/log.c +++ b/lib/log.c @@ -51,6 +51,7 @@ const char *zlog_proto_names[] = "OSPF", "RIPNG", "OSPF6", + "LDP", "ISIS", "PIM", "MASC", @@ -177,7 +178,7 @@ time_print(FILE *fp, struct timestamp_control *ctl) /* va_list version of zlog. */ -static void +void vzlog (struct zlog *zl, int priority, const char *format, va_list args) { char proto_str[32]; diff --git a/lib/log.h b/lib/log.h index 951fa124c8..31bf2b67cf 100644 --- a/lib/log.h +++ b/lib/log.h @@ -51,6 +51,7 @@ typedef enum ZLOG_OSPF, ZLOG_RIPNG, ZLOG_OSPF6, + ZLOG_LDP, ZLOG_ISIS, ZLOG_PIM, ZLOG_MASC @@ -115,6 +116,7 @@ extern void zlog (struct zlog *zl, int priority, const char *format, ...) PRINTF_ATTRIBUTE(3, 4); /* Handy zlog functions. */ +extern void vzlog (struct zlog *zl, int priority, const char *format, va_list args); extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); diff --git a/lib/mpls.h b/lib/mpls.h index 8889868970..5a67b915d1 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -169,4 +169,13 @@ label2str (mpls_label_t label, char *buf, int len) return(buf); } +/* constants used by ldpd */ +#define MPLS_LABEL_IPV4NULL 0 /* IPv4 Explicit NULL Label */ +#define MPLS_LABEL_RTALERT 1 /* Router Alert Label */ +#define MPLS_LABEL_IPV6NULL 2 /* IPv6 Explicit NULL Label */ +#define MPLS_LABEL_IMPLNULL 3 /* Implicit NULL Label */ +/* MPLS_LABEL_RESERVED 4-15 */ /* Values 4-15 are reserved */ +#define MPLS_LABEL_RESERVED_MAX 15 +#define MPLS_LABEL_MAX ((1 << 20) - 1) + #endif diff --git a/lib/privs.c b/lib/privs.c index 9228a56d35..6cf87c18d4 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -250,12 +250,6 @@ zprivs_caps_init (struct zebra_privs_t *zprivs) exit(1); } - if ( !zprivs_state.syscaps_p ) - { - fprintf (stderr, "privs_init: capabilities enabled, " - "but no capabilities supplied\n"); - } - /* we have caps, we have no need to ever change back the original user */ if (zprivs_state.zuid) { @@ -266,6 +260,9 @@ zprivs_caps_init (struct zebra_privs_t *zprivs) exit (1); } } + + if ( !zprivs_state.syscaps_p ) + return; if ( !(zprivs_state.caps = cap_init()) ) { diff --git a/lib/route_types.txt b/lib/route_types.txt index 8fc3092cac..0ac442d85d 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -60,6 +60,7 @@ ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, "Table" +ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, "LDP" ## help strings ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only" @@ -76,3 +77,4 @@ ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" ZEBRA_ROUTE_TABLE, "Non-main Kernel Routing Table" +ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)" diff --git a/lib/vty.c b/lib/vty.c index 48b64a95c4..3f13500449 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -754,6 +754,13 @@ vty_end_config (struct vty *vty) case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: + case LDP_NODE: + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + case LDP_L2VPN_NODE: + case LDP_PSEUDOWIRE_NODE: case ISIS_NODE: case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: @@ -1158,6 +1165,13 @@ vty_stop_input (struct vty *vty) case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: + case LDP_NODE: + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + case LDP_L2VPN_NODE: + case LDP_PSEUDOWIRE_NODE: case ISIS_NODE: case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: @@ -3180,3 +3194,29 @@ vty_terminate (void) vector_free (Vvty_serv_thread); } } + +/* Utility functions to get arguments from commands generated + by the xml2cli.pl script. */ +const char * +vty_get_arg_value (struct vty_arg *args[], const char *arg) +{ + while (*args) + { + if (strcmp ((*args)->name, arg) == 0) + return (*args)->value; + args++; + } + return NULL; +} + +struct vty_arg * +vty_get_arg (struct vty_arg *args[], const char *arg) +{ + while (*args) + { + if (strcmp ((*args)->name, arg) == 0) + return *args; + args++; + } + return NULL; +} diff --git a/lib/vty.h b/lib/vty.h index 599882a382..4f1dbe653e 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -127,6 +127,14 @@ struct vty char address[SU_ADDRSTRLEN]; }; +struct vty_arg +{ + const char *name; + const char *value; + const char **argv; + int argc; +}; + /* Integrated configuration file. */ #define INTEGRATE_DEFAULT_CONFIG "Quagga.conf" @@ -292,4 +300,7 @@ extern void vty_hello (struct vty *); an async-signal-safe function. */ extern void vty_log_fixed (char *buf, size_t len); +extern const char *vty_get_arg_value (struct vty_arg **, const char *); +extern struct vty_arg *vty_get_arg (struct vty_arg **, const char *); + #endif /* _ZEBRA_VTY_H */ diff --git a/tools/xml2cli.pl b/tools/xml2cli.pl new file mode 100755 index 0000000000..43789131c3 --- /dev/null +++ b/tools/xml2cli.pl @@ -0,0 +1,436 @@ +#!/usr/bin/perl +## +## Parse a XML file containing a tree-like representation of Quagga CLI +## commands and generate a file with: +## +## - a DEFUN function for each command; +## - an initialization function. +## +## +## Copyright (C) 2012 Renato Westphal +## This file is part of GNU Zebra. +## +## GNU Zebra is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by the +## Free Software Foundation; either version 2, or (at your option) any +## later version. +## +## GNU Zebra is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with GNU Zebra; see the file COPYING. If not, write to the Free +## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +## 02111-1307, USA. +## + +use strict; +use warnings; +use Getopt::Std; +use vars qw($opt_d); +use File::Basename qw(fileparse); +use XML::LibXML; + +%::input_strs = ( + "ifname" => "IFNAME", + "word" => "WORD", + "line" => ".LINE", + "ipv4" => "A.B.C.D", + "ipv4m" => "A.B.C.D/M", + "ipv6" => "X:X::X:X", + "ipv6m" => "X:X::X:X/M", + "mtu" => "<1500-9180>", + # BGP specific + "rd" => "ASN:nn_or_IP-address:nn", + "asn" => "<1-4294967295>", + "community" => "AA:NN", + "clist" => "<1-500>", + # LDP specific + "disc_time" => "<1-65535>", + "session_time" => "<15-65535>", + "pwid" => "<1-4294967295>", + "hops" => "<1-254>" + ); + +# parse options node and store the corresponding information +# into a global hash of hashes +sub parse_options { + my $xml_node = $_[0]; + my @cmdstr; + + my $options_name = $xml_node->findvalue('./@name'); + if (not $options_name) { + die('error: "options" node without "name" attribute'); + } + + # initialize hash + $::options{$options_name}{'cmdstr'} = ""; + $::options{$options_name}{'help'} = ""; + + my @children = $xml_node->getChildnodes(); + foreach my $child(@children) { + # skip comments, random text, etc + if ($child->getType() != XML_ELEMENT_NODE) { + next; + } + + # check for error/special conditions + if ($child->getName() ne "option") { + die('error: invalid node type: "' . $child->getName() . '"'); + } + + my $name = $child->findvalue('./@name'); + my $input = $child->findvalue('./@input'); + my $help = $child->findvalue('./@help'); + if ($input) { + $name = $::input_strs{$input}; + } + + push (@cmdstr, $name); + $::options{$options_name}{'help'} .= "\n \"" . $help . "\\n\""; + } + $::options{$options_name}{'cmdstr'} = "(" . join('|', @cmdstr) . ")"; +} + +# given a subtree, replace all the corresponding include nodes by +# this subtree +sub subtree_replace_includes { + my $subtree = $_[0]; + + my $subtree_name = $subtree->findvalue('./@name'); + if (not $subtree_name) { + die("subtree without \"name\" attribute"); + } + + my $query = "//include[\@subtree='$subtree_name']"; + foreach my $include_node($::xml->findnodes($query)) { + my @children = $subtree->getChildnodes(); + foreach my $child(reverse @children) { + my $include_node_parent = $include_node->getParentNode(); + $include_node_parent->insertAfter($child->cloneNode(1), + $include_node); + } + $include_node->unbindNode(); + } + $subtree->unbindNode(); +} + +# generate arguments for a given command +sub generate_arguments { + my @nodes = @_; + my $arguments; + my $no_args = 1; + my $argc = 0; + + $arguments .= " struct vty_arg *args[] =\n"; + $arguments .= " {\n"; + for (my $i = 0; $i < @nodes; $i++) { + my %node = %{$nodes[$i]}; + my $arg_value; + + if (not $node{'arg'}) { + next; + } + $no_args = 0; + + # for input and select nodes, the value of the argument is an + # argv[] element. for the other types of nodes, the value of the + # argument is the name of the node + if ($node{'input'} or $node{'type'} eq "select") { + $arg_value = "argv[" . $argc++ . "]"; + } else { + $arg_value = '"' . $node{'name'} . '"'; + } + + if ($node{'input'} and $node{'input'} eq "line") { + # arguments of the type 'line' may have multiple spaces (i.e + # they don't fit into a single argv[] element). to properly + # handle these arguments, we need to provide direct access + # to the argv[] array and the argc variable. + my $argc_str = "argc" . (($argc > 1) ? " - " . ($argc - 1) : ""); + my $argv_str = "argv" . (($argc > 1) ? " + " . ($argc - 1) : ""); + $arguments .= " &(struct vty_arg) { " + . ".name = \"" . $node{'arg'} . "\", " + . ".argc = $argc_str, " + . ".argv = $argv_str },\n"; + } else { + # common case - each argument has a name and a single value + $arguments .= " &(struct vty_arg) { " + . ".name = \"" . $node{'arg'} . "\", " + . ".value = " . $arg_value . " },\n"; + } + } + $arguments .= " NULL\n"; + $arguments .= " };\n"; + + # handle special case + if ($no_args) { + return " struct vty_arg *args[] = { NULL };\n"; + } + + return $arguments; +} + +# generate C code +sub generate_code { + my @nodes = @_; + my $funcname = ''; + my $cmdstr = ''; + my $cmdname = ''; + my $helpstr = ''; + my $function = ''; + + for (my $i = 0; $i < @nodes; $i++) { + my %node = %{$nodes[$i]}; + if ($node{'input'}) { + $funcname .= $node{'input'} . " "; + $cmdstr .= $::input_strs{$node{'input'}} . " "; + $helpstr .= "\n \"" . $node{'help'} . "\\n\""; + } elsif ($node{'type'} eq "select") { + my $options_name = $node{'options'}; + $funcname .= $options_name . " "; + $cmdstr .= $::options{$options_name}{'cmdstr'} . " "; + $helpstr .= $::options{$options_name}{'help'}; + } else { + $funcname .= $node{'name'} . " "; + $cmdstr .= $node{'name'} . " "; + $helpstr .= "\n \"" . $node{'help'} . "\\n\""; + } + + # update the command string + if ($node{'function'} ne "inherited") { + $function = $node{'function'}; + } + } + + # rtrim + $funcname =~ s/\s+$//; + $cmdstr =~ s/\s+$//; + # lowercase + $funcname = lc($funcname); + # replace " " by "_" + $funcname =~ tr/ /_/; + # replace "-" by "_" + $funcname =~ tr/-/_/; + # add prefix + $funcname = $::cmdprefix . '_' . $funcname; + + # generate DEFUN + $cmdname = $funcname . "_cmd"; + + # don't generate same command more than once + if ($::commands{$cmdname}) { + return $cmdname; + } + $::commands{$cmdname} = "1"; + + print STDOUT "DEFUN (" . $funcname . ",\n" + . " " . $cmdname . ",\n" + . " \"" . $cmdstr . "\"," + . $helpstr . ")\n" + . "{\n" + . generate_arguments(@nodes) + . " return " . $function . " (vty, args);\n" + . "}\n\n"; + + return $cmdname; +} + +# parse tree node (recursive function) +sub parse_tree { + # get args + my $xml_node = $_[0]; + my @nodes = @{$_[1]}; + my $tree_name = $_[2]; + + # hash containing all the node attributes + my %node; + $node{'type'} = $xml_node->getName(); + + # check for error/special conditions + if ($node{'type'} eq "tree") { + goto end; + } + if ($node{'type'} eq "include") { + die('error: can not include "' + . $xml_node->findvalue('./@subtree') . '"'); + } + if (not $node{'type'} ~~ [qw(option select)]) { + die('error: invalid node type: "' . $node{'type'} . '"'); + } + if ($node{'type'} eq "select") { + my $options_name = $xml_node->findvalue('./@options'); + if (not $options_name) { + die('error: "select" node without "name" attribute'); + } + if (not $::options{$options_name}) { + die('error: can not find options'); + } + $node{'options'} = $options_name; + } + + # get node attributes + $node{'name'} = $xml_node->findvalue('./@name'); + $node{'input'} = $xml_node->findvalue('./@input'); + $node{'arg'} = $xml_node->findvalue('./@arg'); + $node{'help'} = $xml_node->findvalue('./@help'); + $node{'function'} = $xml_node->findvalue('./@function'); + $node{'ifdef'} = $xml_node->findvalue('./@ifdef'); + + # push node to stack + push (@nodes, \%node); + + # generate C code + if ($node{'function'}) { + my $cmdname = generate_code(@nodes); + push (@{$::trees{$tree_name}}, [0, $cmdname, 0]); + } + + if ($node{'ifdef'}) { + push (@{$::trees{$tree_name}}, [$node{'ifdef'}, 0, 0]); + } + +end: + # recursively process child nodes + my @children = $xml_node->getChildnodes(); + foreach my $child(@children) { + # skip comments, random text, etc + if ($child->getType() != XML_ELEMENT_NODE) { + next; + } + parse_tree($child, \@nodes, $tree_name); + } + + if ($node{'ifdef'}) { + push (@{$::trees{$tree_name}}, [0, 0, $node{'ifdef'}]); + } +} + +sub parse_node { + # get args + my $xml_node = $_[0]; + + my $node_name = $xml_node->findvalue('./@name'); + if (not $node_name) { + die('missing the "name" attribute'); + } + + my $install = $xml_node->findvalue('./@install'); + my $config_write = $xml_node->findvalue('./@config_write'); + if ($install and $install eq "1") { + print " install_node (&" .lc( $node_name) . "_node, " . $config_write . ");\n"; + } + + my $install_default = $xml_node->findvalue('./@install_default'); + if ($install_default and $install_default eq "1") { + print " install_default (" . $node_name . "_NODE);\n"; + } + + my @children = $xml_node->getChildnodes(); + foreach my $child(@children) { + # skip comments, random text, etc + if ($child->getType() != XML_ELEMENT_NODE) { + next; + } + + if ($child->getName() ne "include") { + die('error: invalid node type: "' . $child->getName() . '"'); + } + my $tree_name = $child->findvalue('./@tree'); + if (not $tree_name) { + die('missing the "tree" attribute'); + } + + foreach my $entry (@{$::trees{$tree_name}}) { + my ($ifdef, $cmdname, $endif) = @{$entry}; + + if ($ifdef) { + print ("#ifdef " . $ifdef . "\n"); + } + + if ($cmdname) { + print " install_element (" . $node_name . "_NODE, &" . $cmdname . ");\n"; + } + + if ($endif) { + print ("#endif /* " . $endif . " */\n"); + } + } + } +} + +# parse command-line arguments +if (not getopts('d')) { + die("Usage: xml2cli.pl [-d] FILE\n"); +} +my $file = shift; + +# initialize the XML parser +my $parser = new XML::LibXML; +$parser->keep_blanks(0); + +# parse XML file +$::xml = $parser->parse_file($file); +my $xmlroot = $::xml->getDocumentElement(); +if ($xmlroot->getName() ne "file") { + die('XML root element name must be "file"'); +} + +# read file attributes +my $init_function = $xmlroot->findvalue('./@init'); +if (not $init_function) { + die('missing the "init" attribute in the "file" node'); +} +$::cmdprefix = $xmlroot->findvalue('./@cmdprefix'); +if (not $::cmdprefix) { + die('missing the "cmdprefix" attribute in the "file" node'); +} +my $header = $xmlroot->findvalue('./@header'); +if (not $header) { + die('missing the "header" attribute in the "file" node'); +} + +# generate source header +print STDOUT "/* Auto-generated from " . fileparse($file) . ". */\n" + . "/* Do not edit! */\n\n" + . "#include \n\n" + . "#include \"command.h\"\n" + . "#include \"vty.h\"\n" + . "#include \"$header\"\n\n"; + +# Parse options +foreach my $options($::xml->findnodes("/file/options")) { + parse_options($options); +} + +# replace include nodes by the corresponding subtrees +foreach my $subtree(reverse $::xml->findnodes("/file/subtree")) { + subtree_replace_includes($subtree); +} + +# Parse trees +foreach my $tree($::xml->findnodes("/file/tree")) { + my @nodes = (); + my $tree_name = $tree->findvalue('./@name'); + parse_tree($tree, \@nodes, $tree_name); +} + +# install function header +print STDOUT "void\n" + . $init_function . " (void)\n" + . "{\n"; + +# Parse nodes +foreach my $node($::xml->findnodes("/file/node")) { + parse_node($node); +} + +# closing braces for the install function +print STDOUT "}"; + +# print to stderr the expanded XML file if the debug flag (-d) is given +if ($opt_d) { + print STDERR $::xml->toString(1); +} diff --git a/zebra/zserv.c b/zebra/zserv.c index 3402bf1dfb..e617ae28a2 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -641,7 +641,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { /* We don't send any nexthops when there's a multipath */ - if (rib->nexthop_active_num > 1) + if (rib->nexthop_active_num > 1 && client->proto != ZEBRA_ROUTE_LDP) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -713,7 +713,9 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, stream_putc (s, 1); stream_putl (s, nexthop->ifindex); - break; + /* ldpd needs all nexthops */ + if (client->proto != ZEBRA_ROUTE_LDP) + break; } } From 4fcbf6e2d9a3c1f13d142e9b0dbd2369ec2b0bda Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 1 Aug 2016 19:47:15 -0300 Subject: [PATCH 022/136] ldpd: add vtysh support Signed-off-by: Renato Westphal --- vtysh/Makefile.am | 4 + vtysh/extract.pl.in | 3 + vtysh/vtysh.c | 204 ++++++++++++++++++++++++++++++++++++++++++- vtysh/vtysh.h | 5 +- vtysh/vtysh_config.c | 4 + 5 files changed, 214 insertions(+), 6 deletions(-) diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index ed49acca47..77d374a6e8 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -42,6 +42,10 @@ if OSPF6D vtysh_scan += $(top_srcdir)/ospf6d/*.c endif +if LDPD +vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c +endif + if RIPD vtysh_scan += $(top_srcdir)/ripd/*.c endif diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index b7533881a3..924a6696d3 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -44,6 +44,9 @@ $ignore{'"router ripng"'} = "ignore"; $ignore{'"router ospf"'} = "ignore"; $ignore{'"router ospf <1-65535>"'} = "ignore"; $ignore{'"router ospf6"'} = "ignore"; +$ignore{'"mpls ldp"'} = "ignore"; +$ignore{'"l2vpn WORD type vpls"'} = "ignore"; +$ignore{'"member pseudowire IFNAME"'} = "ignore"; $ignore{'"router bgp"'} = "ignore"; $ignore{'"router bgp " "<1-4294967295>"'} = "ignore"; $ignore{'"router bgp " "<1-4294967295>" " (view|vrf) WORD"'} = "ignore"; diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 10bdef8cc8..9dc2eca0b7 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -69,6 +69,7 @@ struct vtysh_client vtysh_client[] = { .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .path = RIPNG_VTYSH_PATH, .next = NULL}, { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH, .next = NULL}, { .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .path = OSPF6_VTYSH_PATH, .next = NULL}, + { .fd = -1, .name = "ldpd", .flag = VTYSH_LDPD, .path = LDP_VTYSH_PATH, .next = NULL}, { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH, .next = NULL}, { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH, .next = NULL}, { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH, .next = NULL}, @@ -1032,6 +1033,48 @@ static struct cmd_node ospf6_node = "%s(config-ospf6)# " }; +static struct cmd_node ldp_node = +{ + LDP_NODE, + "%s(config-ldp)# " +}; + +static struct cmd_node ldp_ipv4_node = +{ + LDP_IPV4_NODE, + "%s(config-ldp-af)# " +}; + +static struct cmd_node ldp_ipv6_node = +{ + LDP_IPV6_NODE, + "%s(config-ldp-af)# " +}; + +static struct cmd_node ldp_ipv4_iface_node = +{ + LDP_IPV4_IFACE_NODE, + "%s(config-ldp-af-if)# " +}; + +static struct cmd_node ldp_ipv6_iface_node = +{ + LDP_IPV6_IFACE_NODE, + "%s(config-ldp-af-if)# " +}; + +static struct cmd_node ldp_l2vpn_node = +{ + LDP_L2VPN_NODE, + "%s(config-l2vpn)# " +}; + +static struct cmd_node ldp_pseudowire_node = +{ + LDP_PSEUDOWIRE_NODE, + "%s(config-l2vpn-pw)# " +}; + static struct cmd_node keychain_node = { KEYCHAIN_NODE, @@ -1321,6 +1364,86 @@ DEFUNSH (VTYSH_OSPF6D, return CMD_SUCCESS; } +DEFUNSH (VTYSH_LDPD, + ldp_mpls_ldp, + ldp_mpls_ldp_cmd, + "mpls ldp", + "Global MPLS configuration subcommands\n" + "Label Distribution Protocol\n") +{ + vty->node = LDP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_LDPD, + ldp_address_family_ipv4, + ldp_address_family_ipv4_cmd, + "address-family ipv4", + "Configure Address Family and its parameters\n" + "IPv4\n") +{ + vty->node = LDP_IPV4_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_LDPD, + ldp_address_family_ipv6, + ldp_address_family_ipv6_cmd, + "address-family ipv6", + "Configure Address Family and its parameters\n" + "IPv6\n") +{ + vty->node = LDP_IPV6_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_LDPD, + ldp_interface_ifname, + ldp_interface_ifname_cmd, + "interface IFNAME", + "Enable LDP on an interface and enter interface submode\n" + "Interface's name\n") +{ + switch (vty->node) + { + case LDP_IPV4_NODE: + vty->node = LDP_IPV4_IFACE_NODE; + break; + case LDP_IPV6_NODE: + vty->node = LDP_IPV6_IFACE_NODE; + break; + default: + break; + } + + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_LDPD, + ldp_l2vpn_word_type_vpls, + ldp_l2vpn_word_type_vpls_cmd, + "l2vpn WORD type vpls", + "Configure l2vpn commands\n" + "L2VPN name\n" + "L2VPN type\n" + "Virtual Private LAN Service\n") +{ + vty->node = LDP_L2VPN_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_LDPD, + ldp_member_pseudowire_ifname, + ldp_member_pseudowire_ifname_cmd, + "member pseudowire IFNAME", + "L2VPN member configuration\n" + "Pseudowire interface\n" + "Interface's name\n") +{ + vty->node = LDP_PSEUDOWIRE_NODE; + return CMD_SUCCESS; +} + DEFUNSH (VTYSH_ISISD, router_isis, router_isis_cmd, @@ -1411,6 +1534,8 @@ vtysh_exit (struct vty *vty) case RIPNG_NODE: case OSPF_NODE: case OSPF6_NODE: + case LDP_NODE: + case LDP_L2VPN_NODE: case ISIS_NODE: case MASC_NODE: case RMAP_NODE: @@ -1430,6 +1555,19 @@ vtysh_exit (struct vty *vty) case BGP_IPV6M_NODE: vty->node = BGP_NODE; break; + case LDP_IPV4_NODE: + case LDP_IPV6_NODE: + vty->node = LDP_NODE; + break; + case LDP_IPV4_IFACE_NODE: + vty->node = LDP_IPV4_NODE; + break; + case LDP_IPV6_IFACE_NODE: + vty->node = LDP_IPV6_NODE; + break; + case LDP_PSEUDOWIRE_NODE: + vty->node = LDP_L2VPN_NODE; + break; case KEYCHAIN_KEY_NODE: vty->node = KEYCHAIN_NODE; break; @@ -1572,6 +1710,20 @@ ALIAS (vtysh_exit_ospf6d, "quit", "Exit current mode and down to previous mode\n") +DEFUNSH (VTYSH_LDPD, + vtysh_exit_ldpd, + vtysh_exit_ldpd_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_ldpd, + vtysh_quit_ldpd_cmd, + "quit", + "Exit current mode and down to previous mode\n") + DEFUNSH (VTYSH_ISISD, vtysh_exit_isisd, vtysh_exit_isisd_cmd, @@ -1620,7 +1772,7 @@ ALIAS_SH (VTYSH_ZEBRA, VRF_CMD_HELP_STR) /* TODO Implement "no interface command in isisd. */ -DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D, +DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD, vtysh_no_interface_cmd, "no interface IFNAME", NO_STR @@ -1696,7 +1848,7 @@ ALIAS (vtysh_exit_vrf, /* TODO Implement interface description commands in ripngd, ospf6d * and isisd. */ -DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD, +DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD|VTYSH_LDPD, interface_desc_cmd, "description .LINE", "Interface specific description\n" @@ -2222,7 +2374,7 @@ DEFUN (vtysh_write_terminal, DEFUN (vtysh_write_terminal_daemon, vtysh_write_terminal_daemon_cmd, - "write terminal (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pimd)", + "write terminal (zebra|ripd|ripngd|ospfd|ospf6d|ldpd|bgpd|isisd|pimd)", "Write running configuration to memory, network, or terminal\n" "Write to terminal\n" "For the zebra daemon\n" @@ -2230,6 +2382,7 @@ DEFUN (vtysh_write_terminal_daemon, "For the ripng daemon\n" "For the ospf daemon\n" "For the ospfv6 daemon\n" + "For the ldp daemon\n" "For the bgp daemon\n" "For the isis daemon\n" "For the pim daemon\n") @@ -2419,7 +2572,7 @@ ALIAS (vtysh_write_terminal, ALIAS (vtysh_write_terminal, vtysh_show_running_config_daemon_cmd, - "show running-config (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pimd)", + "show running-config (zebra|ripd|ripngd|ospfd|ospf6d|ldpd|bgpd|isisd|pimd)", SHOW_STR "Current operating configuration\n" "For the zebra daemon\n" @@ -2427,6 +2580,7 @@ ALIAS (vtysh_write_terminal, "For the ripng daemon\n" "For the ospf daemon\n" "For the ospfv6 daemon\n" + "For the ldp daemon\n" "For the bgp daemon\n" "For the isis daemon\n" "For the pim daemon\n") @@ -2923,6 +3077,13 @@ vtysh_init_vty (void) install_node (&ripng_node, NULL); install_node (&ospf6_node, NULL); /* #endif */ + install_node (&ldp_node, NULL); + install_node (&ldp_ipv4_node, NULL); + install_node (&ldp_ipv6_node, NULL); + install_node (&ldp_ipv4_iface_node, NULL); + install_node (&ldp_ipv6_iface_node, NULL); + install_node (&ldp_l2vpn_node, NULL); + install_node (&ldp_pseudowire_node, NULL); install_node (&keychain_node, NULL); install_node (&keychain_key_node, NULL); install_node (&isis_node, NULL); @@ -2950,6 +3111,13 @@ vtysh_init_vty (void) vtysh_install_default (OSPF_NODE); vtysh_install_default (RIPNG_NODE); vtysh_install_default (OSPF6_NODE); + vtysh_install_default (LDP_NODE); + vtysh_install_default (LDP_IPV4_NODE); + vtysh_install_default (LDP_IPV6_NODE); + vtysh_install_default (LDP_IPV4_IFACE_NODE); + vtysh_install_default (LDP_IPV6_IFACE_NODE); + vtysh_install_default (LDP_L2VPN_NODE); + vtysh_install_default (LDP_PSEUDOWIRE_NODE); vtysh_install_default (ISIS_NODE); vtysh_install_default (KEYCHAIN_NODE); vtysh_install_default (KEYCHAIN_KEY_NODE); @@ -2974,6 +3142,20 @@ vtysh_init_vty (void) install_element (OSPF_NODE, &vtysh_quit_ospfd_cmd); install_element (OSPF6_NODE, &vtysh_exit_ospf6d_cmd); install_element (OSPF6_NODE, &vtysh_quit_ospf6d_cmd); + install_element (LDP_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_NODE, &vtysh_quit_ldpd_cmd); + install_element (LDP_IPV4_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_IPV4_NODE, &vtysh_quit_ldpd_cmd); + install_element (LDP_IPV6_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_IPV6_NODE, &vtysh_quit_ldpd_cmd); + install_element (LDP_IPV4_IFACE_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_IPV4_IFACE_NODE, &vtysh_quit_ldpd_cmd); + install_element (LDP_IPV6_IFACE_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_IPV6_IFACE_NODE, &vtysh_quit_ldpd_cmd); + install_element (LDP_L2VPN_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_L2VPN_NODE, &vtysh_quit_ldpd_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &vtysh_exit_ldpd_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &vtysh_quit_ldpd_cmd); install_element (BGP_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_VPNV4_NODE, &vtysh_exit_bgpd_cmd); @@ -3010,6 +3192,13 @@ vtysh_init_vty (void) install_element (RIPNG_NODE, &vtysh_end_all_cmd); install_element (OSPF_NODE, &vtysh_end_all_cmd); install_element (OSPF6_NODE, &vtysh_end_all_cmd); + install_element (LDP_NODE, &vtysh_end_all_cmd); + install_element (LDP_IPV4_NODE, &vtysh_end_all_cmd); + install_element (LDP_IPV6_NODE, &vtysh_end_all_cmd); + install_element (LDP_IPV4_IFACE_NODE, &vtysh_end_all_cmd); + install_element (LDP_IPV6_IFACE_NODE, &vtysh_end_all_cmd); + install_element (LDP_L2VPN_NODE, &vtysh_end_all_cmd); + install_element (LDP_PSEUDOWIRE_NODE, &vtysh_end_all_cmd); install_element (BGP_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV4_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV4M_NODE, &vtysh_end_all_cmd); @@ -3050,6 +3239,13 @@ vtysh_init_vty (void) #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &router_ospf6_cmd); #endif + install_element (CONFIG_NODE, &ldp_mpls_ldp_cmd); + install_element (LDP_NODE, &ldp_address_family_ipv4_cmd); + install_element (LDP_NODE, &ldp_address_family_ipv6_cmd); + install_element (LDP_IPV4_NODE, &ldp_interface_ifname_cmd); + install_element (LDP_IPV6_NODE, &ldp_interface_ifname_cmd); + install_element (CONFIG_NODE, &ldp_l2vpn_word_type_vpls_cmd); + install_element (LDP_L2VPN_NODE, &ldp_member_pseudowire_ifname_cmd); install_element (CONFIG_NODE, &router_isis_cmd); install_element (CONFIG_NODE, &router_bgp_cmd); install_element (CONFIG_NODE, &router_bgp_asn_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 3aa2bad81e..e82aba6b6f 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -33,10 +33,11 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_BGPD 0x20 #define VTYSH_ISISD 0x40 #define VTYSH_PIMD 0x100 +#define VTYSH_LDPD 0x200 -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_PIMD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_ISISD|VTYSH_PIMD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 802074a533..bf6215c401 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -202,6 +202,10 @@ vtysh_config_parse_line (const char *line) config = config_get (OSPF_NODE, line); else if (strncmp (line, "router ospf6", strlen ("router ospf6")) == 0) config = config_get (OSPF6_NODE, line); + else if (strncmp (line, "mpls ldp", strlen ("mpls ldp")) == 0) + config = config_get (LDP_NODE, line); + else if (strncmp (line, "l2vpn", strlen ("l2vpn")) == 0) + config = config_get (LDP_L2VPN_NODE, line); else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0) config = config_get (BGP_NODE, line); else if (strncmp (line, "router isis", strlen ("router isis")) == 0) From ce54994727bf237e0f89168d6818b04ea79f090d Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Jun 2016 14:19:30 -0300 Subject: [PATCH 023/136] mpls: add support for LDP LSPs Signed-off-by: Renato Westphal --- ldpd/ldp_zebra.c | 54 ++++- lib/log.c | 2 + lib/mpls.h | 9 + lib/nexthop.c | 14 +- lib/nexthop.h | 5 +- lib/zebra.h | 2 + zebra/zebra_mpls.c | 486 +++++++++++++++++++++++++++---------------- zebra/zebra_mpls.h | 52 ++++- zebra/zebra_rib.c | 2 +- zebra/zebra_static.c | 4 +- zebra/zserv.c | 71 +++++++ 11 files changed, 500 insertions(+), 201 deletions(-) diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 41ff47fba3..6e19e4f607 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -28,6 +28,7 @@ #include "command.h" #include "network.h" #include "linklist.h" +#include "mpls.h" #include "ldpd.h" #include "ldpe.h" @@ -38,6 +39,7 @@ static void ifp2kif(struct interface *, struct kif *); static void ifc2kaddr(struct interface *, struct connected *, struct kaddr *); +static int zebra_send_mpls_labels(int, struct kroute *); static int ldp_router_id_update(int, struct zclient *, zebra_size_t, vrf_id_t); static int ldp_interface_add(int, struct zclient *, zebra_size_t, @@ -89,18 +91,62 @@ ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka) } } +static int +zebra_send_mpls_labels(int cmd, struct kroute *kr) +{ + struct stream *s; + + if (kr->local_label < MPLS_LABEL_RESERVED_MAX || + kr->remote_label == NO_LABEL) + return (0); + + debug_zebra_out("prefix %s/%u nexthop %s labels %s/%s (%s)", + log_addr(kr->af, &kr->prefix), kr->prefixlen, + log_addr(kr->af, &kr->nexthop), log_label(kr->local_label), + log_label(kr->remote_label), + (cmd == ZEBRA_MPLS_LABELS_ADD) ? "add" : "delete"); + + /* Reset stream. */ + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putc(s, ZEBRA_LSP_LDP); + stream_putl(s, kr->af); + switch (kr->af) { + case AF_INET: + stream_put_in_addr(s, &kr->prefix.v4); + stream_putc(s, kr->prefixlen); + stream_put_in_addr(s, &kr->nexthop.v4); + break; + case AF_INET6: + stream_write(s, (u_char *)&kr->prefix.v6, 16); + stream_putc(s, kr->prefixlen); + stream_write(s, (u_char *)&kr->nexthop.v6, 16); + break; + default: + fatalx("kr_change: unknown af"); + } + stream_putc(s, kr->priority); + stream_putl(s, kr->local_label); + stream_putl(s, kr->remote_label); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return (zclient_send_message(zclient)); +} + int kr_change(struct kroute *kr) { - /* TODO */ - return (0); + return (zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, kr)); } int kr_delete(struct kroute *kr) { - /* TODO */ - return (0); + return (zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, kr)); } int diff --git a/lib/log.c b/lib/log.c index 14fa81a9d7..fb7b33dcf9 100644 --- a/lib/log.c +++ b/lib/log.c @@ -939,6 +939,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_INTERFACE_ENABLE_RADV), DESC_ENTRY (ZEBRA_INTERFACE_DISABLE_RADV), DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB), + DESC_ENTRY (ZEBRA_MPLS_LABELS_ADD), + DESC_ENTRY (ZEBRA_MPLS_LABELS_DELETE), }; #undef DESC_ENTRY diff --git a/lib/mpls.h b/lib/mpls.h index 5a67b915d1..1f77aaa536 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -75,8 +75,17 @@ typedef unsigned int mpls_lse_t; /* MPLS label value as a 32-bit (mostly we only care about the label value). */ typedef unsigned int mpls_label_t; +#define MPLS_NO_LABEL 0xFFFFFFFF #define MPLS_INVALID_LABEL 0xFFFFFFFF +/* LSP types. */ +enum lsp_types_t +{ + ZEBRA_LSP_NONE = 0, /* No LSP. */ + ZEBRA_LSP_STATIC = 1, /* Static LSP. */ + ZEBRA_LSP_LDP = 2 /* LDP LSP. */ +}; + /* Functions for basic label operations. */ /* Encode a label stack entry from fields; convert to network byte-order as diff --git a/lib/nexthop.c b/lib/nexthop.c index 8e775b68be..01771e253c 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -130,8 +130,8 @@ copy_nexthops (struct nexthop **tnh, struct nexthop *nh) memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); if (nh->nh_label) - nexthop_add_labels (nexthop, nh->nh_label->num_labels, - &nh->nh_label->label[0]); + nexthop_add_labels (nexthop, nh->nh_label_type, + nh->nh_label->num_labels, &nh->nh_label->label[0]); nexthop_add(tnh, nexthop); if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -164,12 +164,13 @@ nexthops_free (struct nexthop *nexthop) /* Update nexthop with label information. */ void -nexthop_add_labels (struct nexthop *nexthop, u_int8_t num_labels, - mpls_label_t *label) +nexthop_add_labels (struct nexthop *nexthop, enum lsp_types_t type, + u_int8_t num_labels, mpls_label_t *label) { struct nexthop_label *nh_label; int i; + nexthop->nh_label_type = type; nh_label = XCALLOC (MTYPE_NH_LABEL, sizeof (struct nexthop_label)); nh_label->num_labels = num_labels; for (i = 0; i < num_labels; i++) @@ -182,7 +183,10 @@ void nexthop_del_labels (struct nexthop *nexthop) { if (nexthop->nh_label) - XFREE (MTYPE_NH_LABEL, nexthop->nh_label); + { + XFREE (MTYPE_NH_LABEL, nexthop->nh_label); + nexthop->nh_label_type = ZEBRA_LSP_NONE; + } } const char * diff --git a/lib/nexthop.h b/lib/nexthop.h index c06dfe0e25..e66e0eee20 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -85,6 +85,9 @@ struct nexthop * Only one level of recursive resolution is currently supported. */ struct nexthop *resolved; + /* Type of label(s), if any */ + enum lsp_types_t nh_label_type; + /* Label(s) associated with this nexthop. */ struct nexthop_label *nh_label; }; @@ -109,7 +112,7 @@ void copy_nexthops (struct nexthop **tnh, struct nexthop *nh); void nexthop_free (struct nexthop *nexthop); void nexthops_free (struct nexthop *nexthop); -void nexthop_add_labels (struct nexthop *, u_int8_t, mpls_label_t *); +void nexthop_add_labels (struct nexthop *, enum lsp_types_t, u_int8_t, mpls_label_t *); void nexthop_del_labels (struct nexthop *); extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); diff --git a/lib/zebra.h b/lib/zebra.h index d7a441c2e9..da069d1dfa 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -422,6 +422,8 @@ typedef enum { ZEBRA_INTERFACE_DISABLE_RADV, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, ZEBRA_INTERFACE_LINK_PARAMS, + ZEBRA_MPLS_LABELS_ADD, + ZEBRA_MPLS_LABELS_DELETE, } zebra_message_types_t; /* Marker value used in new Zserv, in the byte location corresponding diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 3e84114772..8f18f326d1 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -99,15 +99,10 @@ nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, static int nhlfe_del (zebra_nhlfe_t *snhlfe); static int -static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label, - mpls_label_t out_label, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex); +mpls_lsp_uninstall_all (struct hash *lsp_table, zebra_lsp_t *lsp, + enum lsp_types_t type); static int -static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, - enum nexthop_types_t gtype, union g_addr *gate, - char *ifname, ifindex_t ifindex); -static int -static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label); +mpls_static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label); static void nhlfe_print (zebra_nhlfe_t *nhlfe, struct vty *vty); static void @@ -684,7 +679,7 @@ nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, XFREE (MTYPE_NHLFE, nhlfe); return NULL; } - nexthop_add_labels (nexthop, 1, &out_label); + nexthop_add_labels (nexthop, lsp_type, 1, &out_label); nexthop->type = gtype; switch (nexthop->type) @@ -746,190 +741,28 @@ nhlfe_del (zebra_nhlfe_t *nhlfe) return 0; } - -/* - * Install/update a static NHLFE for an LSP in the forwarding table. This may - * be a new LSP entry or a new NHLFE for an existing in-label or an update of - * the out-label for an existing NHLFE (update case). - */ static int -static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label, - mpls_label_t out_label, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex) +mpls_lsp_uninstall_all (struct hash *lsp_table, zebra_lsp_t *lsp, + enum lsp_types_t type) { - struct hash *lsp_table; - zebra_ile_t tmp_ile; - zebra_lsp_t *lsp; - zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; - - /* Lookup table. */ - lsp_table = zvrf->lsp_table; - if (!lsp_table) - return -1; - - /* If entry is present, exit. */ - tmp_ile.in_label = in_label; - lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc); - if (!lsp) - return -1; - nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex); - if (nhlfe) - { - struct nexthop *nh = nhlfe->nexthop; - - assert (nh); - assert (nh->nh_label); - - /* Clear deleted flag (in case it was set) */ - UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); - if (nh->nh_label->label[0] == out_label) - /* No change */ - return 0; - - if (IS_ZEBRA_DEBUG_MPLS) - { - nhlfe2str (nhlfe, buf, BUFSIZ); - zlog_debug ("LSP in-label %u type %d nexthop %s " - "out-label changed to %u (old %u)", - in_label, ZEBRA_LSP_STATIC, buf, - out_label, nh->nh_label->label[0]); - } - - /* Update out label, trigger processing. */ - nh->nh_label->label[0] = out_label; - } - else - { - /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_add (lsp, ZEBRA_LSP_STATIC, gtype, gate, - ifname, ifindex, out_label); - if (!nhlfe) - return -1; - - if (IS_ZEBRA_DEBUG_MPLS) - { - nhlfe2str (nhlfe, buf, BUFSIZ); - zlog_debug ("Add LSP in-label %u type %d nexthop %s " - "out-label %u", - in_label, ZEBRA_LSP_STATIC, buf, - out_label); - } - - lsp->addr_family = NHLFE_FAMILY (nhlfe); - } - - /* Mark NHLFE, queue LSP for processing. */ - SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); - if (lsp_processq_add (lsp)) - return -1; - - return 0; -} - -/* - * Uninstall a particular static NHLFE in the forwarding table. If this is - * the only NHLFE, the entire LSP forwarding entry has to be deleted. - */ -static int -static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, - enum nexthop_types_t gtype, union g_addr *gate, - char *ifname, ifindex_t ifindex) -{ - struct hash *lsp_table; - zebra_ile_t tmp_ile; - zebra_lsp_t *lsp; - zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; - - /* Lookup table. */ - lsp_table = zvrf->lsp_table; - if (!lsp_table) - return -1; - - /* If entry is not present, exit. */ - tmp_ile.in_label = in_label; - lsp = hash_lookup (lsp_table, &tmp_ile); - if (!lsp) - return 0; - nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex); - if (!nhlfe) - return 0; - - if (IS_ZEBRA_DEBUG_MPLS) - { - nhlfe2str (nhlfe, buf, BUFSIZ); - zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", - in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags); - } - - /* Mark NHLFE for delete or directly delete, as appropriate. */ - if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) - { - UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); - SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); - if (lsp_processq_add (lsp)) - return -1; - } - else - { - nhlfe_del (nhlfe); - - /* Free LSP entry if no other NHLFEs and not scheduled. */ - if (!lsp->nhlfe_list && - !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) - { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug ("Free LSP in-label %u flags 0x%x", - lsp->ile.in_label, lsp->flags); - - lsp = hash_release(lsp_table, &lsp->ile); - if (lsp) - XFREE(MTYPE_LSP, lsp); - } - } - return 0; -} - -/* - * Uninstall all static NHLFEs for a particular LSP forwarding entry. - * If no other NHLFEs exist, the entry would be deleted. - */ -static int -static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) -{ - struct hash *lsp_table; - zebra_ile_t tmp_ile; - zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe, *nhlfe_next; int schedule_lsp = 0; char buf[BUFSIZ]; - /* Lookup table. */ - lsp_table = zvrf->lsp_table; - if (!lsp_table) - return -1; - - /* If entry is not present, exit. */ - tmp_ile.in_label = in_label; - lsp = hash_lookup (lsp_table, &tmp_ile); - if (!lsp || !lsp->nhlfe_list) - return 0; - /* Mark NHLFEs for delete or directly delete, as appropriate. */ for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { nhlfe_next = nhlfe->next; /* Skip non-static NHLFEs */ - if (nhlfe->type != ZEBRA_LSP_STATIC) + if (nhlfe->type != type) continue; if (IS_ZEBRA_DEBUG_MPLS) { nhlfe2str (nhlfe, buf, BUFSIZ); zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", - in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags); + lsp->ile.in_label, type, buf, nhlfe->flags); } if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) @@ -965,6 +798,31 @@ static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) return 0; } +/* + * Uninstall all static NHLFEs for a particular LSP forwarding entry. + * If no other NHLFEs exist, the entry would be deleted. + */ +static int +mpls_static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp || !lsp->nhlfe_list) + return 0; + + return mpls_lsp_uninstall_all (lsp_table, lsp, ZEBRA_LSP_STATIC); +} + static json_object * nhlfe_json (zebra_nhlfe_t *nhlfe) { @@ -1394,6 +1252,274 @@ mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, return buf; } +/* + * Install/uninstall a FEC-To-NHLFE (FTN) binding. + */ +int +mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type, + struct prefix *prefix, union g_addr *gate, u_int8_t distance, + mpls_label_t out_label) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct nexthop *nexthop; + + /* Lookup table. */ + table = zebra_vrf_table (family2afi(prefix->family), SAFI_UNICAST, zvrf->vrf_id); + if (! table) + return -1; + + /* Lookup existing route */ + rn = route_node_get (table, prefix); + RNODE_FOREACH_RIB (rn, rib) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + if (rib->distance == distance) + break; + } + + if (rib == NULL) + return -1; + + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + switch (prefix->family) + { + case AF_INET: + if (nexthop->type != NEXTHOP_TYPE_IPV4 && + nexthop->type != NEXTHOP_TYPE_IPV4_IFINDEX) + continue; + if (! IPV4_ADDR_SAME (&nexthop->gate.ipv4, &gate->ipv4)) + continue; + goto found; + break; + case AF_INET6: + if (nexthop->type != NEXTHOP_TYPE_IPV6 && + nexthop->type != NEXTHOP_TYPE_IPV6_IFINDEX) + continue; + if (! IPV6_ADDR_SAME (&nexthop->gate.ipv6, &gate->ipv6)) + continue; + goto found; + break; + default: + break; + } + /* nexthop not found */ + return -1; + + found: + if (add) + nexthop_add_labels (nexthop, type, 1, &out_label); + else + nexthop_del_labels (nexthop); + + SET_FLAG (rib->status, RIB_ENTRY_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + rib_queue_add (rn); + + return 0; +} + +/* + * Install/update a NHLFE for an LSP in the forwarding table. This may be + * a new LSP entry or a new NHLFE for an existing in-label or an update of + * the out-label for an existing NHLFE (update case). + */ +int +mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, mpls_label_t out_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + nhlfe = nhlfe_find (lsp, type, gtype, gate, ifname, ifindex); + if (nhlfe) + { + struct nexthop *nh = nhlfe->nexthop; + + assert (nh); + assert (nh->nh_label); + + /* Clear deleted flag (in case it was set) */ + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (nh->nh_label->label[0] == out_label) + /* No change */ + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("LSP in-label %u type %d nexthop %s " + "out-label changed to %u (old %u)", + in_label, type, buf, + out_label, nh->nh_label->label[0]); + } + + /* Update out label, trigger processing. */ + nh->nh_label->label[0] = out_label; + } + else + { + /* Add LSP entry to this nexthop */ + nhlfe = nhlfe_add (lsp, type, gtype, gate, + ifname, ifindex, out_label); + if (!nhlfe) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Add LSP in-label %u type %d nexthop %s " + "out-label %u", in_label, type, buf, out_label); + } + + lsp->addr_family = NHLFE_FAMILY (nhlfe); + } + + /* Mark NHLFE, queue LSP for processing. */ + SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + if (lsp_processq_add (lsp)) + return -1; + + return 0; +} + +/* + * Uninstall a particular NHLFE in the forwarding table. If this is + * the only NHLFE, the entire LSP forwarding entry has to be deleted. + */ +int +mpls_lsp_uninstall (struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp) + return 0; + nhlfe = nhlfe_find (lsp, type, gtype, gate, ifname, ifindex); + if (!nhlfe) + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", + in_label, type, buf, nhlfe->flags); + } + + /* Mark NHLFE for delete or directly delete, as appropriate. */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (lsp_processq_add (lsp)) + return -1; + } + else + { + nhlfe_del (nhlfe); + + /* Free LSP entry if no other NHLFEs and not scheduled. */ + if (!lsp->nhlfe_list && + !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } + } + return 0; +} + +/* + * Uninstall all LDP NHLFEs for a particular LSP forwarding entry. + * If no other NHLFEs exist, the entry would be deleted. + */ +void +mpls_ldp_lsp_uninstall_all (struct hash_backet *backet, void *ctxt) +{ + zebra_lsp_t *lsp; + struct hash *lsp_table; + + lsp = (zebra_lsp_t *) backet->data; + if (!lsp || !lsp->nhlfe_list) + return; + + lsp_table = ctxt; + if (!lsp_table) + return; + + mpls_lsp_uninstall_all (lsp_table, lsp, ZEBRA_LSP_LDP); +} + +/* + * Uninstall all LDP FEC-To-NHLFE (FTN) bindings of the given address-family. + */ +void +mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct nexthop *nexthop; + int update; + + /* Process routes of interested address-families. */ + table = zebra_vrf_table (afi, SAFI_UNICAST, zvrf->vrf_id); + if (!table) + return; + + for (rn = route_top (table); rn; rn = route_next (rn)) + { + update = 0; + RNODE_FOREACH_RIB (rn, rib) + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (nexthop->nh_label_type == ZEBRA_LSP_LDP) + { + nexthop_del_labels (nexthop); + SET_FLAG (rib->status, RIB_ENTRY_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + update = 1; + } + + if (update) + rib_queue_add (rn); + } +} + /* * Check that the label values used in LSP creation are consistent. The * main criteria is that if there is ECMP, the label operation must still @@ -1511,8 +1637,8 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, } /* (Re)Install LSP in the main table. */ - if (static_lsp_install (zvrf, in_label, out_label, gtype, - gate, ifname, ifindex)) + if (mpls_lsp_install (zvrf, ZEBRA_LSP_STATIC, in_label, out_label, gtype, + gate, ifname, ifindex)) return -1; return 0; @@ -1553,7 +1679,7 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, zlog_debug ("Del static LSP in-label %u", in_label); /* Uninstall entire LSP from the main table. */ - static_lsp_uninstall_all (zvrf, in_label); + mpls_static_lsp_uninstall_all (zvrf, in_label); /* Delete all static NHLFEs */ snhlfe_del_all (slsp); @@ -1574,8 +1700,8 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, } /* Uninstall LSP from the main table. */ - static_lsp_uninstall (zvrf, in_label, gtype, - gate, ifname, ifindex); + mpls_lsp_uninstall (zvrf, ZEBRA_LSP_STATIC, in_label, gtype, gate, + ifname, ifindex); /* Delete static LSP NHLFE */ snhlfe_del (snhlfe); diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 11949c9313..7c672fa433 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -53,13 +53,6 @@ typedef struct zebra_slsp_t_ zebra_slsp_t; typedef struct zebra_nhlfe_t_ zebra_nhlfe_t; typedef struct zebra_lsp_t_ zebra_lsp_t; -/* LSP types. */ -enum lsp_types_t -{ - ZEBRA_LSP_INVALID = 0, /* Invalid. */ - ZEBRA_LSP_STATIC = 1, /* Static LSP. */ -}; - /* * (Outgoing) nexthop label forwarding entry configuration */ @@ -171,6 +164,47 @@ char * mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, char *buf, int len); +/* + * Install/uninstall a FEC-To-NHLFE (FTN) binding. + */ +int +mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type, + struct prefix *prefix, union g_addr *gate, u_int8_t distance, + mpls_label_t out_label); + +/* + * Install/update a NHLFE for an LSP in the forwarding table. This may be + * a new LSP entry or a new NHLFE for an existing in-label or an update of + * the out-label for an existing NHLFE (update case). + */ +int +mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, mpls_label_t out_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex); + +/* + * Uninstall a particular NHLFE in the forwarding table. If this is + * the only NHLFE, the entire LSP forwarding entry has to be deleted. + */ +int +mpls_lsp_uninstall (struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); + +/* + * Uninstall all LDP NHLFEs for a particular LSP forwarding entry. + * If no other NHLFEs exist, the entry would be deleted. + */ +void +mpls_ldp_lsp_uninstall_all (struct hash_backet *backet, void *ctxt); + +/* + * Uninstall all LDP FEC-To-NHLFE (FTN) bindings of the given address-family. + */ +void +mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi); + /* * Check that the label values used in LSP creation are consistent. The * main criteria is that if there is ECMP, the label operation must still @@ -282,7 +316,7 @@ lsp_type_from_rib_type (int rib_type) case ZEBRA_ROUTE_STATIC: return ZEBRA_LSP_STATIC; default: - return ZEBRA_LSP_INVALID; + return ZEBRA_LSP_NONE; } } @@ -294,6 +328,8 @@ nhlfe_type2str(enum lsp_types_t lsp_type) { case ZEBRA_LSP_STATIC: return "Static"; + case ZEBRA_LSP_LDP: + return "LDP"; default: return "Unknown"; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f79dfafa5e..f57c0b5d67 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -205,7 +205,7 @@ rib_copy_nexthops (struct rib *rib, struct nexthop *nh) memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); if (nh->nh_label) - nexthop_add_labels (nexthop, nh->nh_label->num_labels, + nexthop_add_labels (nexthop, nh->nh_label_type, nh->nh_label->num_labels, &nh->nh_label->label[0]); rib_nexthop_add(rib, nexthop); if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index fddc9d54c8..f2362e6871 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -98,7 +98,7 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro } /* Update label(s), if present. */ if (si->snh_label.num_labels) - nexthop_add_labels (nexthop, si->snh_label.num_labels, + nexthop_add_labels (nexthop, ZEBRA_LSP_STATIC, si->snh_label.num_labels, &si->snh_label.label[0]); if (IS_ZEBRA_DEBUG_RIB) @@ -162,7 +162,7 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro } /* Update label(s), if present. */ if (si->snh_label.num_labels) - nexthop_add_labels (nexthop, si->snh_label.num_labels, + nexthop_add_labels (nexthop, ZEBRA_LSP_STATIC, si->snh_label.num_labels, &si->snh_label.label[0]); /* Save the flags of this static routes (reject, blackhole) */ diff --git a/zebra/zserv.c b/zebra/zserv.c index e617ae28a2..4c68f6acb9 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -52,6 +52,7 @@ #include "zebra/interface.h" #include "zebra/zebra_ptm.h" #include "zebra/rtadv.h" +#include "zebra/zebra_mpls.h" /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; @@ -1630,6 +1631,65 @@ zread_vrf_unregister (struct zserv *client, u_short length, struct zebra_vrf *zv return 0; } +static void +zread_mpls_labels (int command, struct zserv *client, u_short length, + vrf_id_t vrf_id) +{ + struct stream *s; + enum lsp_types_t type; + struct prefix prefix; + enum nexthop_types_t gtype; + union g_addr gate; + mpls_label_t in_label, out_label; + u_int8_t distance; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup (vrf_id); + if (!zvrf) + return; + + /* Get input stream. */ + s = client->ibuf; + + /* Get data. */ + type = stream_getc (s); + prefix.family = stream_getl (s); + switch (prefix.family) + { + case AF_INET: + prefix.u.prefix4.s_addr = stream_get_ipv4 (s); + prefix.prefixlen = stream_getc (s); + gtype = NEXTHOP_TYPE_IPV4; + gate.ipv4.s_addr = stream_get_ipv4 (s); + break; + case AF_INET6: + stream_get (&prefix.u.prefix6, s, 16); + prefix.prefixlen = stream_getc (s); + gtype = NEXTHOP_TYPE_IPV6; + stream_get (&gate.ipv6, s, 16); + break; + default: + return; + } + distance = stream_getc (s); + in_label = stream_getl (s); + out_label = stream_getl (s); + + if (command == ZEBRA_MPLS_LABELS_ADD) + { + mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate, + NULL, 0); + if (out_label != MPLS_IMP_NULL_LABEL) + mpls_ftn_update (1, zvrf, type, &prefix, &gate, distance, out_label); + } + else if (command == ZEBRA_MPLS_LABELS_DELETE) + { + mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, NULL, 0); + if (out_label != MPLS_IMP_NULL_LABEL) + mpls_ftn_update (0, zvrf, type, &prefix, &gate, distance, out_label); + } +} + /* Cleanup registered nexthops (across VRFs) upon client disconnect. */ static void zebra_client_close_cleanup_rnh (struct zserv *client) @@ -1645,6 +1705,13 @@ zebra_client_close_cleanup_rnh (struct zserv *client) zebra_cleanup_rnh_client(zvrf->vrf_id, AF_INET6, client, RNH_NEXTHOP_TYPE); zebra_cleanup_rnh_client(zvrf->vrf_id, AF_INET, client, RNH_IMPORT_CHECK_TYPE); zebra_cleanup_rnh_client(zvrf->vrf_id, AF_INET6, client, RNH_IMPORT_CHECK_TYPE); + if (client->proto == ZEBRA_ROUTE_LDP) + { + hash_iterate(zvrf->lsp_table, mpls_ldp_lsp_uninstall_all, + zvrf->lsp_table); + mpls_ldp_ftn_uninstall_all (zvrf, AFI_IP); + mpls_ldp_ftn_uninstall_all (zvrf, AFI_IP6); + } } } } @@ -1926,6 +1993,10 @@ zebra_client_read (struct thread *thread) case ZEBRA_INTERFACE_DISABLE_RADV: zebra_interface_radv_set (client, sock, length, zvrf, 0); break; + case ZEBRA_MPLS_LABELS_ADD: + case ZEBRA_MPLS_LABELS_DELETE: + zread_mpls_labels (command, client, length, vrf_id); + break; default: zlog_info ("Zebra received unknown command %d", command); break; From be0dba358f7cd40ae27f012409734786a13d5ce4 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 12 Sep 2016 09:02:39 -0300 Subject: [PATCH 024/136] mpls: add null driver Signed-off-by: Renato Westphal --- configure.ac | 25 ++++++++++++ zebra/Makefile.am | 5 ++- zebra/kernel_null.c | 7 ---- zebra/rt_netlink.c | 78 +----------------------------------- zebra/rt_netlink.h | 7 ++++ zebra/zebra_mpls_netlink.c | 78 ++++++++++++++++++++++++++++++++++++ zebra/zebra_mpls_null.c | 82 ++------------------------------------ 7 files changed, 119 insertions(+), 163 deletions(-) create mode 100644 zebra/zebra_mpls_netlink.c diff --git a/configure.ac b/configure.ac index eb1f1de6c1..e15f384c21 100755 --- a/configure.ac +++ b/configure.ac @@ -309,6 +309,8 @@ AC_ARG_ENABLE(systemd, AS_HELP_STRING([--enable-systemd], [enable Systemd support])) AC_ARG_ENABLE(poll, AS_HELP_STRING([--enable-poll], [enable usage of Poll instead of select])) +AC_ARG_ENABLE(mpls, + AS_HELP_STRING([--enable-mpls], [enable MPLS support - requires compatible kernel])) AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], [enable -Werror (recommended for developers only)])) AC_ARG_ENABLE(cumulus, @@ -360,6 +362,29 @@ if test "${enable_poll}" = "yes" ; then AC_DEFINE(HAVE_POLL,,Compile systemd support in) fi +dnl ---------- +dnl MPLS check +dnl ---------- +MPLS_METHOD="" +AC_MSG_CHECKING(whether this OS has MPLS stack) +if test "x${enable_mpls}" = "xyes"; then + case "$host" in + *-linux*) + AC_DEFINE(HAVE_MPLS,,Enable MPLS) + MPLS_METHOD="zebra_mpls_netlink.o" + AC_MSG_RESULT(Linux MPLS) + ;; + *) + AC_MSG_RESULT(Unsupported kernel) + MPLS_METHOD="zebra_mpls_null.o" + ;; + esac +else + AC_MSG_RESULT(disabled) + MPLS_METHOD="zebra_mpls_null.o" +fi +AC_SUBST(MPLS_METHOD) + if test "${enable_cumulus}" = "yes" ; then AC_DEFINE(HAVE_CUMULUS,,Compile Special Cumulus Code in) fi diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 10f7850c96..b08c37f55e 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -12,9 +12,10 @@ rt_method = @RT_METHOD@ rtread_method = @RTREAD_METHOD@ kernel_method = @KERNEL_METHOD@ ioctl_method = @IOCTL_METHOD@ +mpls_method = @MPLS_METHOD@ otherobj = $(ioctl_method) $(ipforward) $(if_method) \ - $(rt_method) $(rtread_method) $(kernel_method) + $(rt_method) $(rtread_method) $(kernel_method) $(mpls_method) if HAVE_NETLINK othersrc = zebra_fpm_netlink.c @@ -38,7 +39,7 @@ testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \ zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ - zebra_memory.c zebra_mpls_null.c + zebra_memory.c zebra_mpls.c zebra_mpls_null.c noinst_HEADERS = \ zebra_memory.h \ diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index 0802570e60..17b3c7bc8d 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -30,7 +30,6 @@ #include "zebra/connected.h" #include "zebra/rt_netlink.h" #include "zebra/rib.h" -#include "zebra/zebra_mpls.h" int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } int kernel_update_ipv4 (struct prefix *a, struct rib *b) { return 0; } @@ -66,9 +65,3 @@ int kernel_neigh_update (int a, int b, uint32_t c, char *d, int e) void kernel_init (struct zebra_ns *zns) { return; } void kernel_terminate (struct zebra_ns *zns) { return; } void route_read (struct zebra_ns *zns) { return; } - -int kernel_add_lsp (zebra_lsp_t *l) { return 0; } - -int kernel_del_lsp (zebra_lsp_t *l) { return 0; } - -int kernel_upd_lsp (zebra_lsp_t *l) { return 0; } diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 07d2923028..95c8892979 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2805,7 +2805,7 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen) /* * MPLS label forwarding table change via netlink interface. */ -static int +int netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp) { mpls_lse_t lse; @@ -2977,7 +2977,7 @@ netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp) /* * Handle failure in LSP install, clear flags for NHLFE. */ -static inline void +void clear_nhlfe_installed (zebra_lsp_t *lsp) { zebra_nhlfe_t *nhlfe; @@ -2994,80 +2994,6 @@ clear_nhlfe_installed (zebra_lsp_t *lsp) } } -/* - * Install Label Forwarding entry into the kernel. - */ -int -kernel_add_lsp (zebra_lsp_t *lsp) -{ - int ret; - - if (!lsp || !lsp->best_nhlfe) // unexpected - return -1; - - UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); - ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); - if (!ret) - SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); - else - clear_nhlfe_installed (lsp); - - return ret; -} - -/* - * Update Label Forwarding entry in the kernel. This means that the Label - * forwarding entry is already installed and needs an update - either a new - * path is to be added, an installed path has changed (e.g., outgoing label) - * or an installed path (but not all paths) has to be removed. - * TODO: Performs a DEL followed by ADD now, need to change to REPLACE. Note - * that REPLACE was originally implemented for IPv4 nexthops but removed as - * it was not functioning when moving from swap to PHP as that was signaled - * through the metric field (before kernel-MPLS). This shouldn't be an issue - * any longer, so REPLACE can be reintroduced. - */ -int -kernel_upd_lsp (zebra_lsp_t *lsp) -{ - int ret; - - if (!lsp || !lsp->best_nhlfe) // unexpected - return -1; - - UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); - - /* First issue a DEL and clear the installed flag. */ - netlink_mpls_multipath (RTM_DELROUTE, lsp); - UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); - - /* Then issue an ADD. */ - ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); - if (!ret) - SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); - else - clear_nhlfe_installed (lsp); - - return ret; -} - -/* - * Delete Label Forwarding entry from the kernel. - */ -int -kernel_del_lsp (zebra_lsp_t *lsp) -{ - if (!lsp) // unexpected - return -1; - - if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) - { - netlink_mpls_multipath (RTM_DELROUTE, lsp); - UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); - } - - return 0; -} - extern struct thread_master *master; /* Kernel route reflection. */ diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 80d035e839..7d8b2e7040 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -24,6 +24,8 @@ #ifdef HAVE_NETLINK +#include "zebra/zebra_mpls.h" + #define NL_PKT_BUF_SIZE 8192 #define NL_DEFAULT_ROUTE_METRIC 20 @@ -41,6 +43,11 @@ nl_msg_type_to_str (uint16_t msg_type); extern const char * nl_rtproto_to_str (u_char rtproto); +extern void +clear_nhlfe_installed (zebra_lsp_t *lsp); +extern int +netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp); + extern int interface_lookup_netlink (struct zebra_ns *zns); extern int netlink_route_read (struct zebra_ns *zns); diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c new file mode 100644 index 0000000000..f5678ede31 --- /dev/null +++ b/zebra/zebra_mpls_netlink.c @@ -0,0 +1,78 @@ +#include +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_mpls.h" + +/* + * Install Label Forwarding entry into the kernel. + */ +int +kernel_add_lsp (zebra_lsp_t *lsp) +{ + int ret; + + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); + if (!ret) + SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + else + clear_nhlfe_installed (lsp); + + return ret; +} + +/* + * Update Label Forwarding entry in the kernel. This means that the Label + * forwarding entry is already installed and needs an update - either a new + * path is to be added, an installed path has changed (e.g., outgoing label) + * or an installed path (but not all paths) has to be removed. + * TODO: Performs a DEL followed by ADD now, need to change to REPLACE. Note + * that REPLACE was originally implemented for IPv4 nexthops but removed as + * it was not functioning when moving from swap to PHP as that was signaled + * through the metric field (before kernel-MPLS). This shouldn't be an issue + * any longer, so REPLACE can be reintroduced. + */ +int +kernel_upd_lsp (zebra_lsp_t *lsp) +{ + int ret; + + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + + /* First issue a DEL and clear the installed flag. */ + netlink_mpls_multipath (RTM_DELROUTE, lsp); + UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + + /* Then issue an ADD. */ + ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); + if (!ret) + SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + else + clear_nhlfe_installed (lsp); + + return ret; +} + +/* + * Delete Label Forwarding entry from the kernel. + */ +int +kernel_del_lsp (zebra_lsp_t *lsp) +{ + if (!lsp) // unexpected + return -1; + + if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) + { + netlink_mpls_multipath (RTM_DELROUTE, lsp); + UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + } + + return 0; +} diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 2953138dbc..9022fe30d2 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -1,81 +1,7 @@ #include -#include "nexthop.h" -#include "zebra/rib.h" -#include "zebra/zserv.h" +#include "zebra/rt.h" #include "zebra/zebra_mpls.h" -int -mpls_str2label (const char *label_str, u_int8_t *num_labels, - mpls_label_t *labels) -{ - return 0; -} - -char * -mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, - char *buf, int len) -{ - return NULL; -} - -int -zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, - mpls_label_t out_label, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex) -{ - return 1; -} - -int -zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, - mpls_label_t out_label, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex) -{ - return 0; -} - -int -zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, - enum nexthop_types_t gtype, union g_addr *gate, - char *ifname, ifindex_t ifindex) -{ - return 0; -} - -void -zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) -{ -} - -void -zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label, - u_char use_json) -{ -} - -void -zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf, - u_char use_json) -{ -} - -int -zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) -{ - return 0; -} - -void -zebra_mpls_close_tables (struct zebra_vrf *zvrf) -{ -} - -void -zebra_mpls_init_tables (struct zebra_vrf *zvrf) -{ -} - -void -zebra_mpls_init (void) -{ -} +int kernel_add_lsp (zebra_lsp_t *lsp) { return 0; } +int kernel_upd_lsp (zebra_lsp_t *lsp) { return 0; } +int kernel_del_lsp (zebra_lsp_t *lsp) { return 0; } From d3e2c74adaa2564775f2c60320213e28e329be50 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 2 Jun 2016 08:28:15 -0300 Subject: [PATCH 025/136] mpls: add support to the OpenBSD kernel Signed-off-by: Renato Westphal --- configure.ac | 5 + lib/sockunion.h | 6 ++ zebra/kernel_socket.c | 22 ++++ zebra/kernel_socket.h | 3 +- zebra/rt.h | 1 + zebra/rt_socket.c | 25 ++++- zebra/zebra_mpls.c | 1 + zebra/zebra_mpls_netlink.c | 2 + zebra/zebra_mpls_null.c | 1 + zebra/zebra_mpls_openbsd.c | 206 +++++++++++++++++++++++++++++++++++++ 10 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 zebra/zebra_mpls_openbsd.c diff --git a/configure.ac b/configure.ac index e15f384c21..ac63eaab81 100755 --- a/configure.ac +++ b/configure.ac @@ -374,6 +374,11 @@ if test "x${enable_mpls}" = "xyes"; then MPLS_METHOD="zebra_mpls_netlink.o" AC_MSG_RESULT(Linux MPLS) ;; + *-openbsd*) + AC_DEFINE(HAVE_MPLS,,Enable MPLS) + MPLS_METHOD="zebra_mpls_openbsd.o" + AC_MSG_RESULT(OpenBSD MPLS) + ;; *) AC_MSG_RESULT(Unsupported kernel) MPLS_METHOD="zebra_mpls_null.o" diff --git a/lib/sockunion.h b/lib/sockunion.h index 105b11a24c..abad43122e 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -25,12 +25,18 @@ #include "privs.h" #include "if.h" +#if defined HAVE_MPLS && defined __OpenBSD__ +#include +#endif union sockunion { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; +#if defined HAVE_MPLS && defined __OpenBSD__ + struct sockaddr_mpls smpls; +#endif }; enum connect_result diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 3a232129b6..7952f9e761 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -21,6 +21,9 @@ #include #include +#if defined HAVE_MPLS && defined __OpenBSD__ +#include +#endif #include "if.h" #include "prefix.h" @@ -1068,6 +1071,7 @@ rtm_write (int message, union sockunion *dest, union sockunion *mask, union sockunion *gate, + union sockunion *mpls, unsigned int index, int zebra_flags, int metric) @@ -1097,6 +1101,10 @@ rtm_write (int message, msg.rtm.rtm_addrs = RTA_DST; msg.rtm.rtm_addrs |= RTA_GATEWAY; msg.rtm.rtm_flags = RTF_UP; +#if defined HAVE_MPLS && defined __OpenBSD__ + msg.rtm.rtm_flags |= RTF_MPATH; + msg.rtm.rtm_fmask = RTF_MPLS; +#endif msg.rtm.rtm_index = index; if (metric != 0) @@ -1142,6 +1150,17 @@ rtm_write (int message, else if (message == RTM_ADD) msg.rtm.rtm_flags |= RTF_HOST; +#if defined HAVE_MPLS && defined __OpenBSD__ + if (mpls) + { + msg.rtm.rtm_addrs |= RTA_SRC; + msg.rtm.rtm_flags |= RTF_MPLS; + + if (mpls->smpls.smpls_label != htonl (MPLS_IMP_NULL_LABEL << MPLS_LABEL_OFFSET)) + msg.rtm.rtm_mpls = MPLS_OP_PUSH; + } +#endif + /* Tagging route with flags */ msg.rtm.rtm_flags |= (RTF_PROTO1); @@ -1166,6 +1185,9 @@ rtm_write (int message, SOCKADDRSET (dest, RTA_DST); SOCKADDRSET (gate, RTA_GATEWAY); SOCKADDRSET (mask, RTA_NETMASK); +#if defined HAVE_MPLS && defined __OpenBSD__ + SOCKADDRSET (mpls, RTA_SRC); +#endif msg.rtm.rtm_msglen = pnt - (caddr_t) &msg; diff --git a/zebra/kernel_socket.h b/zebra/kernel_socket.h index e9558ad6dd..18d69343a4 100644 --- a/zebra/kernel_socket.h +++ b/zebra/kernel_socket.h @@ -27,7 +27,8 @@ extern void rtm_read (struct rt_msghdr *); extern int ifam_read (struct ifa_msghdr *); extern int ifm_read (struct if_msghdr *); extern int rtm_write (int, union sockunion *, union sockunion *, - union sockunion *, unsigned int, int, int); + union sockunion *, union sockunion *, + unsigned int, int, int); extern const struct message rtm_type_str[]; #endif /* __ZEBRA_KERNEL_SOCKET_H */ diff --git a/zebra/rt.h b/zebra/rt.h index 1137f880f0..c4a85e6d66 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -44,5 +44,6 @@ extern int kernel_delete_ipv6 (struct prefix *, struct rib *); extern int kernel_add_lsp (zebra_lsp_t *); extern int kernel_upd_lsp (zebra_lsp_t *); extern int kernel_del_lsp (zebra_lsp_t *); +extern void mpls_kernel_init (void); #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 24671829f0..a6a1978065 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -21,6 +21,9 @@ */ #include +#if defined HAVE_MPLS && defined __OpenBSD__ +#include +#endif #include "if.h" #include "prefix.h" @@ -33,13 +36,15 @@ #include "zebra/rib.h" #include "zebra/rt.h" #include "zebra/kernel_socket.h" +#include "zebra/zebra_mpls.h" extern struct zebra_privs_t zserv_privs; /* kernel socket export */ extern int rtm_write (int message, union sockunion *dest, union sockunion *mask, union sockunion *gate, - unsigned int index, int zebra_flags, int metric); + union sockunion *mpls, unsigned int index, + int zebra_flags, int metric); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN /* Adjust netmask socket length. Return value is a adjusted sin_len @@ -73,6 +78,10 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { struct sockaddr_in *mask = NULL; struct sockaddr_in sin_dest, sin_mask, sin_gate; +#if defined HAVE_MPLS && defined __OpenBSD__ + struct sockaddr_mpls smpls; +#endif + union sockunion *smplsp = NULL; struct nexthop *nexthop, *tnexthop; int recursing; int nexthop_num = 0; @@ -147,10 +156,23 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) mask = &sin_mask; } +#if defined HAVE_MPLS && defined __OpenBSD__ + if (nexthop->nh_label) + { + memset (&smpls, 0, sizeof (smpls)); + smpls.smpls_len = sizeof (smpls); + smpls.smpls_family = AF_MPLS; + smpls.smpls_label = + htonl (nexthop->nh_label->label[0] << MPLS_LABEL_OFFSET); + smplsp = (union sockunion *)&smpls; + } +#endif + error = rtm_write (cmd, (union sockunion *)&sin_dest, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, + smplsp, ifindex, rib->flags, rib->metric); @@ -365,6 +387,7 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, (union sockunion *) &sin_dest, (union sockunion *) mask, gate ? (union sockunion *)&sin_gate : NULL, + NULL, ifindex, rib->flags, rib->metric); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 8f18f326d1..6b032707ac 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1888,5 +1888,6 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) void zebra_mpls_init (void) { + mpls_kernel_init (); mpls_processq_init (&zebrad); } diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index f5678ede31..4011b90eea 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -76,3 +76,5 @@ kernel_del_lsp (zebra_lsp_t *lsp) return 0; } + +void mpls_kernel_init (void) {}; diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 9022fe30d2..93405b88fd 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -5,3 +5,4 @@ int kernel_add_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_upd_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_del_lsp (zebra_lsp_t *lsp) { return 0; } +void mpls_kernel_init (void) {}; diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c new file mode 100644 index 0000000000..43a54adff1 --- /dev/null +++ b/zebra/zebra_mpls_openbsd.c @@ -0,0 +1,206 @@ +#include +#include +#include "zebra/rt.h" +#include "zebra/zebra_mpls.h" +#include "zebra/debug.h" + +#include "privs.h" +#include "prefix.h" +#include "interface.h" +#include "log.h" + +extern struct zebra_privs_t zserv_privs; + +struct { + u_int32_t rtseq; + int fd; +} kr_state; + +static int +kernel_send_rtmsg (int action, mpls_label_t in_label, zebra_nhlfe_t *nhlfe) +{ + struct iovec iov[5]; + struct rt_msghdr hdr; + struct sockaddr_mpls sa_label_in, sa_label_out; + struct sockaddr_in nexthop; + int iovcnt = 0; + int ret; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("kernel_send_rtmsg: 0x%x, label=%u", action, in_label); + + /* initialize header */ + bzero(&hdr, sizeof (hdr)); + hdr.rtm_version = RTM_VERSION; + + hdr.rtm_type = action; + hdr.rtm_flags = RTF_UP; + hdr.rtm_fmask = RTF_MPLS; + hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + hdr.rtm_msglen = sizeof (hdr); + hdr.rtm_hdrlen = sizeof (struct rt_msghdr); + hdr.rtm_priority = 0; + /* adjust iovec */ + iov[iovcnt].iov_base = &hdr; + iov[iovcnt++].iov_len = sizeof (hdr); + + /* in label */ + bzero(&sa_label_in, sizeof (sa_label_in)); + sa_label_in.smpls_len = sizeof (sa_label_in); + sa_label_in.smpls_family = AF_MPLS; + sa_label_in.smpls_label = htonl(in_label << MPLS_LABEL_OFFSET); + /* adjust header */ + hdr.rtm_flags |= RTF_MPLS | RTF_MPATH; + hdr.rtm_addrs |= RTA_DST; + hdr.rtm_msglen += sizeof (sa_label_in); + /* adjust iovec */ + iov[iovcnt].iov_base = &sa_label_in; + iov[iovcnt++].iov_len = sizeof (sa_label_in); + + /* nexthop */ + bzero(&nexthop, sizeof (nexthop)); + nexthop.sin_len = sizeof (nexthop); + nexthop.sin_family = AF_INET; + nexthop.sin_addr = nhlfe->nexthop->gate.ipv4; + /* adjust header */ + hdr.rtm_flags |= RTF_GATEWAY; + hdr.rtm_addrs |= RTA_GATEWAY; + hdr.rtm_msglen += sizeof (nexthop); + /* adjust iovec */ + iov[iovcnt].iov_base = &nexthop; + iov[iovcnt++].iov_len = sizeof (nexthop); + + /* If action is RTM_DELETE we have to get rid of MPLS infos */ + if (action != RTM_DELETE) + { + bzero(&sa_label_out, sizeof (sa_label_out)); + sa_label_out.smpls_len = sizeof (sa_label_out); + sa_label_out.smpls_family = AF_MPLS; + sa_label_out.smpls_label = + htonl(nhlfe->nexthop->nh_label->label[0] << MPLS_LABEL_OFFSET); + /* adjust header */ + hdr.rtm_addrs |= RTA_SRC; + hdr.rtm_flags |= RTF_MPLS; + hdr.rtm_msglen += sizeof (sa_label_out); + /* adjust iovec */ + iov[iovcnt].iov_base = &sa_label_out; + iov[iovcnt++].iov_len = sizeof (sa_label_out); + + if (nhlfe->nexthop->nh_label->label[0] == MPLS_LABEL_IMPLNULL) + hdr.rtm_mpls = MPLS_OP_POP; + else + hdr.rtm_mpls = MPLS_OP_SWAP; + } + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err ("Can't raise privileges"); + ret = writev (kr_state.fd, iov, iovcnt); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err ("Can't lower privileges"); + + if (ret == -1) + zlog_err ("kernel_send_rtmsg: %s", safe_strerror (errno)); + + return ret; +} + +static int +kernel_lsp_cmd (int action, zebra_lsp_t *lsp) +{ + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop = NULL; + int nexthop_num = 0; + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM) + break; + + /* XXX */ + if (NHLFE_FAMILY(nhlfe) == AF_INET6) + continue; + + if (((action == RTM_ADD || action == RTM_CHANGE) && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) || + (action == RTM_DELETE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))) + { + nexthop_num++; + + kernel_send_rtmsg (action, lsp->ile.in_label, nhlfe); + if (action == RTM_ADD || action == RTM_CHANGE) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + } + } + + return (0); +} + +int +kernel_add_lsp (zebra_lsp_t *lsp) +{ + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + return kernel_lsp_cmd (RTM_ADD, lsp); +} + +int +kernel_upd_lsp (zebra_lsp_t *lsp) +{ + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + return kernel_lsp_cmd (RTM_CHANGE, lsp); +} + +int +kernel_del_lsp (zebra_lsp_t *lsp) +{ + if (!lsp) // unexpected + return -1; + + return kernel_lsp_cmd (RTM_DELETE, lsp); +} + +#define MAX_RTSOCK_BUF 128 * 1024 +void +mpls_kernel_init (void) +{ + int rcvbuf, default_rcvbuf; + socklen_t optlen; + + if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + zlog_warn("kr_init: socket"); + return; + } + + /* grow receive buffer, don't wanna miss messages */ + optlen = sizeof (default_rcvbuf); + if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &default_rcvbuf, &optlen) == -1) + zlog_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF"); + else + for (rcvbuf = MAX_RTSOCK_BUF; + rcvbuf > default_rcvbuf && + setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof (rcvbuf)) == -1 && errno == ENOBUFS; + rcvbuf /= 2) + ; /* nothing */ + + kr_state.rtseq = 1; +} From 2daba5d0b8b9c474bf771b2bc807e33151daa9d6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 12 Sep 2016 15:56:27 -0300 Subject: [PATCH 026/136] build: fix package generation on CentOS 7 Signed-off-by: Renato Westphal --- doc/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index ffb0bf4651..cc4c7abc94 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -46,7 +46,7 @@ info_TEXINFOS = quagga.texi quagga.pdf: $(info_TEXINFOS) $(figures_pdf) $(quagga_TEXINFOS) $(TEXI2PDF) -o "$@" $< || true -quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi filter.texi \ +quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \ install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \ overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \ snmp.texi vtysh.texi routeserver.texi defines.texi $(figures_png) \ From b021388f4267691b6d59be5b63a6c8b46baf2448 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 12 Sep 2016 16:54:56 -0300 Subject: [PATCH 027/136] distro/redhat: add mpls and ldpd --- redhat/Makefile.am | 2 +- redhat/README.rpm_build.md | 2 ++ redhat/ldpd.init | 72 ++++++++++++++++++++++++++++++++++++++ redhat/quagga.logrotate | 8 +++++ redhat/quagga.spec.in | 29 ++++++++++++++- redhat/quagga.sysconfig | 1 + 6 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 redhat/ldpd.init diff --git a/redhat/Makefile.am b/redhat/Makefile.am index 20bca27cb2..96870689dc 100644 --- a/redhat/Makefile.am +++ b/redhat/Makefile.am @@ -1,6 +1,6 @@ EXTRA_DIST = bgpd.init isisd.init \ - ospf6d.init ospfd.init \ + ospf6d.init ospfd.init ldpd.init \ quagga.logrotate quagga.pam quagga.spec \ quagga.sysconfig ripd.init ripngd.init \ watchquagga.init pimd.init zebra.init \ diff --git a/redhat/README.rpm_build.md b/redhat/README.rpm_build.md index 3e8fa05306..05a0bdc591 100644 --- a/redhat/README.rpm_build.md +++ b/redhat/README.rpm_build.md @@ -52,6 +52,8 @@ Building your own Quagga RPM %{!?with_rtadv: %global with_rtadv 1 } %{!?with_isisd: %global with_isisd 1 } %{!?with_pimd: %global with_pimd 1 } + %{!?with_mpls: %global with_mpls 0 } + %{!?with_ldpd: %global with_ldpd 0 } %{!?with_shared: %global with_shared 1 } %{!?with_multipath: %global with_multipath 64 } %{!?quagga_user: %global quagga_user quagga } diff --git a/redhat/ldpd.init b/redhat/ldpd.init new file mode 100644 index 0000000000..b9b9538cb8 --- /dev/null +++ b/redhat/ldpd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# chkconfig: - 16 84 +# config: /etc/quagga/ldpd.conf + +### BEGIN INIT INFO +# Provides: ldpd +# Short-Description: LDP engine +# Description: LDP engine for use with Zebra +### END INIT INFO + +# source function library +. /etc/rc.d/init.d/functions + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="ldpd" +cmd=ldpd +LOCK_FILE=/var/lock/subsys/ldpd +CONF_FILE=/etc/quagga/ldpd.conf + +case "$1" in + start) + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $LDPD_OPTS -f $CONF_FILE + RETVAL=$? + [ $RETVAL -eq 0 ] && touch $LOCK_FILE + echo + ;; + stop) + echo -n $"Shutting down $PROG: " + killproc $cmd + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE + echo + ;; + restart|reload|force-reload) + $0 stop + $0 start + RETVAL=$? + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop + $0 start + fi + RETVAL=$? + ;; + status) + status $cmd + RETVAL=$? + ;; + *) + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 +esac + +exit $RETVAL diff --git a/redhat/quagga.logrotate b/redhat/quagga.logrotate index afbd40c02f..1f1baead0b 100644 --- a/redhat/quagga.logrotate +++ b/redhat/quagga.logrotate @@ -53,3 +53,11 @@ /bin/kill -USR1 `cat /var/run/quagga/ripngd.pid 2> /dev/null` 2> /dev/null || true endscript } + +/var/log/quagga/ldpd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/ldpd.pid 2> /dev/null` 2> /dev/null || true + endscript +} diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index 9ff65e9a22..40c30e40ae 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -16,6 +16,8 @@ %{!?with_ospfapi: %global with_ospfapi 1 } %{!?with_irdp: %global with_irdp 1 } %{!?with_rtadv: %global with_rtadv 1 } +%{!?with_mpls: %global with_mpls 0 } +%{!?with_ldpd: %global with_ldpd 0 } %{!?with_shared: %global with_shared 1 } %{!?with_multipath: %global with_multipath 256 } %{!?quagga_user: %global quagga_user quagga } @@ -71,13 +73,19 @@ %define daemon_list zebra ripd ospfd bgpd isisd pimd ripngd ospfd6d +%if %{with_ldpd} +%define daemon_ldpd ldpd +%else +%define daemon_ldpd "" +%endif + %if %{with_watchquagga} %define daemon_watchquagga watchquagga %else %define daemon_watchquagga "" %endif -%define all_daemons %{daemon_list} %{daemon_watchquagga} +%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_watchquagga} # allow build dir to be kept %{!?keep_build: %global keep_build 0 } @@ -198,6 +206,16 @@ developing OSPF-API and quagga applications. %else --enable-rtadv=no \ %endif +%if %{with_mpls} + --enable-mpls=yes \ +%else + --disable-mpls \ +%endif +%if %{with_ldpd} + --enable-ldpd \ +%else + --disable-ldpd \ +%endif %if %{with_pam} --with-libpam \ %endif @@ -315,6 +333,9 @@ zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" %endif zebra_spec_add_service isisd 2608/tcp "ISISd vty" zebra_spec_add_service pimd 2611/tcp "PIMd vty" +%if %{with_ldpd} +zebra_spec_add_service ldpd 2612/tcp "LDPd vty" +%endif %if "%{initsystem}" == "systemd" for daemon in %all_daemons ; do @@ -493,6 +514,9 @@ rm -rf %{buildroot} %{_sbindir}/ospf6d %{_sbindir}/pimd %{_sbindir}/isisd +%if %{with_ldpd} +%{_sbindir}/ldpd +%endif %if %{with_shared} %attr(755,root,root) %{_libdir}/lib*.so %attr(755,root,root) %{_libdir}/lib*.so.* @@ -513,6 +537,9 @@ rm -rf %{buildroot} %config /etc/rc.d/init.d/ospf6d %config /etc/rc.d/init.d/isisd %config /etc/rc.d/init.d/pimd + %if %{with_ldpd} + %config /etc/rc.d/init.d/ldpd + %endif %endif %config(noreplace) /etc/default/quagga %config(noreplace) /etc/pam.d/quagga diff --git a/redhat/quagga.sysconfig b/redhat/quagga.sysconfig index 2092cba2fa..0cc6acfbae 100644 --- a/redhat/quagga.sysconfig +++ b/redhat/quagga.sysconfig @@ -9,6 +9,7 @@ RIPD_OPTS="-A 127.0.0.1" RIPNGD_OPTS="-A ::1" ZEBRA_OPTS="-A 127.0.0.1" PIMD_OPTS="-A 127.0.0.1" +LDPD_OPTS="-A 127.0.0.1" # Watchquagga configuration for LSB initscripts # From 17d38ab79a5984470be7ef5357c7158793016e52 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 12 Sep 2016 22:18:23 -0300 Subject: [PATCH 028/136] MPLS: add support for labeled IPv6 static routes. Signed-off-by: Renato Westphal --- zebra/zebra_vty.c | 247 +++++++++++++++++++++++++++------------------- 1 file changed, 148 insertions(+), 99 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 1dfa40145b..14f8f18540 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -3921,7 +3921,8 @@ static int static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, const char *gate_str, const char *ifname, const char *flag_str, const char *tag_str, - const char *distance_str, const char *vrf_id_str) + const char *distance_str, const char *vrf_id_str, + const char *label_str) { int ret; u_char distance; @@ -3973,8 +3974,6 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, if (tag_str) tag = atoi(tag_str); - /* Labels -- not supported for IPv6 for now. */ - memset (&snh_label, 0, sizeof (struct static_nh_label)); /* When gateway is valid IPv6 addrees, then gate is treated as nexthop address other case gate is treated as interface name. */ @@ -3989,6 +3988,18 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, return CMD_WARNING; } + /* Labels */ + memset (&snh_label, 0, sizeof (struct static_nh_label)); + if (label_str) + { + if (mpls_str2label (label_str, &snh_label.num_labels, + snh_label.label)) + { + vty_out (vty, "%% Malformed label(s)%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + if (ifname) { /* When ifname is specified. It must be come with gateway @@ -4042,28 +4053,32 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, DEFUN (ipv6_route, ipv6_route_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, argv[2]); } DEFUN (ipv6_route_tag, ipv6_route_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, argv[3]); } DEFUN (ipv6_route_flags, @@ -4077,7 +4092,7 @@ DEFUN (ipv6_route_flags, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, NULL, NULL); } DEFUN (ipv6_route_flags_tag, @@ -4093,32 +4108,36 @@ DEFUN (ipv6_route_flags_tag, "Set tag for this route\n" "Tag value\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, NULL, NULL); } DEFUN (ipv6_route_ifname, ipv6_route_ifname_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, argv[3]); } DEFUN (ipv6_route_ifname_tag, ipv6_route_ifname_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, argv[4]); } DEFUN (ipv6_route_ifname_flags, @@ -4132,7 +4151,7 @@ DEFUN (ipv6_route_ifname_flags, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, NULL, NULL); } DEFUN (ipv6_route_ifname_flags_tag, @@ -4148,25 +4167,27 @@ DEFUN (ipv6_route_ifname_flags_tag, "Set tag for this route\n" "Tag value\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, NULL, NULL); } DEFUN (ipv6_route_pref, ipv6_route_pref_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, argv[3]); } DEFUN (ipv6_route_pref_tag, ipv6_route_pref_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4174,9 +4195,11 @@ DEFUN (ipv6_route_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, argv[4]); } DEFUN (ipv6_route_flags_pref, @@ -4191,7 +4214,7 @@ DEFUN (ipv6_route_flags_pref, "Silently discard pkts when matched\n" "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], NULL, NULL); } DEFUN (ipv6_route_flags_pref_tag, @@ -4208,25 +4231,27 @@ DEFUN (ipv6_route_flags_pref_tag, "Tag value\n" "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL, NULL); } DEFUN (ipv6_route_ifname_pref, ipv6_route_ifname_pref_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, argv[4]); } DEFUN (ipv6_route_ifname_pref_tag, ipv6_route_ifname_pref_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> {label WORD}", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4234,9 +4259,11 @@ DEFUN (ipv6_route_ifname_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, argv[5]); } DEFUN (ipv6_route_ifname_flags_pref, @@ -4251,7 +4278,7 @@ DEFUN (ipv6_route_ifname_flags_pref, "Silently discard pkts when matched\n" "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], NULL, NULL); } DEFUN (ipv6_route_ifname_flags_pref_tag, @@ -4268,25 +4295,27 @@ DEFUN (ipv6_route_ifname_flags_pref_tag, "Tag value\n" "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL, NULL); } DEFUN (no_ipv6_route, no_ipv6_route_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) {label WORD}", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, argv[2]); } DEFUN (no_ipv6_route_tag, no_ipv6_route_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -4294,9 +4323,11 @@ DEFUN (no_ipv6_route_tag, "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, argv[3]); } DEFUN (no_ipv6_route_flags, @@ -4311,7 +4342,7 @@ DEFUN (no_ipv6_route_flags, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, NULL, NULL, NULL); } DEFUN (no_ipv6_route_flags_tag, @@ -4328,25 +4359,27 @@ DEFUN (no_ipv6_route_flags_tag, "Set tag for this route\n" "Tag value\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], NULL, NULL, NULL); } DEFUN (no_ipv6_route_ifname, no_ipv6_route_ifname_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE {label WORD}", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, argv[3]); } DEFUN (no_ipv6_route_ifname_tag, no_ipv6_route_ifname_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -4354,9 +4387,11 @@ DEFUN (no_ipv6_route_ifname_tag, "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n") + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, argv[4]); } DEFUN (no_ipv6_route_ifname_flags, @@ -4371,7 +4406,7 @@ DEFUN (no_ipv6_route_ifname_flags, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, NULL, NULL, NULL); } DEFUN (no_ipv6_route_ifname_flags_tag, @@ -4388,26 +4423,28 @@ DEFUN (no_ipv6_route_ifname_flags_tag, "Set tag for this route\n" "Tag value\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, NULL, NULL); } DEFUN (no_ipv6_route_pref, no_ipv6_route_pref_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, argv[3]); } DEFUN (no_ipv6_route_pref_tag, no_ipv6_route_pref_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -4416,9 +4453,11 @@ DEFUN (no_ipv6_route_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, argv[4]); } DEFUN (no_ipv6_route_flags_pref, @@ -4435,7 +4474,7 @@ DEFUN (no_ipv6_route_flags_pref, "Distance value for this prefix\n") { /* We do not care about argv[2] */ - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], NULL, NULL); } DEFUN (no_ipv6_route_flags_pref_tag, @@ -4454,26 +4493,28 @@ DEFUN (no_ipv6_route_flags_pref_tag, "Distance value for this prefix\n") { /* We do not care about argv[2] */ - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL, NULL); } DEFUN (no_ipv6_route_ifname_pref, no_ipv6_route_ifname_pref_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, argv[4]); } DEFUN (no_ipv6_route_ifname_pref_tag, no_ipv6_route_ifname_pref_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> {label WORD}", NO_STR IP_STR "Establish static routes\n" @@ -4482,9 +4523,11 @@ DEFUN (no_ipv6_route_ifname_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, argv[5]); } DEFUN (no_ipv6_route_ifname_flags_pref, @@ -4500,7 +4543,7 @@ DEFUN (no_ipv6_route_ifname_flags_pref, "Silently discard pkts when matched\n" "Distance value for this prefix\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], NULL, NULL); } DEFUN (no_ipv6_route_ifname_flags_pref_tag, @@ -4518,7 +4561,7 @@ DEFUN (no_ipv6_route_ifname_flags_pref_tag, "Tag value\n" "Distance value for this prefix\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL, NULL); } DEFUN (ipv6_route_vrf, @@ -4531,7 +4574,7 @@ DEFUN (ipv6_route_vrf, "IPv6 gateway interface name\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, argv[2]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, argv[2], NULL); } DEFUN (ipv6_route_tag_vrf, @@ -4546,7 +4589,7 @@ DEFUN (ipv6_route_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3], NULL); } DEFUN (ipv6_route_flags_vrf, @@ -4561,7 +4604,7 @@ DEFUN (ipv6_route_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, argv[3], NULL); } DEFUN (ipv6_route_flags_tag_vrf, @@ -4578,7 +4621,7 @@ DEFUN (ipv6_route_flags_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4], NULL); } DEFUN (ipv6_route_ifname_vrf, @@ -4591,7 +4634,7 @@ DEFUN (ipv6_route_ifname_vrf, "IPv6 gateway interface name\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, argv[3], NULL); } DEFUN (ipv6_route_ifname_tag_vrf, ipv6_route_ifname_tag_vrf_cmd, @@ -4605,7 +4648,7 @@ DEFUN (ipv6_route_ifname_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4], NULL); } DEFUN (ipv6_route_ifname_flags_vrf, @@ -4620,7 +4663,7 @@ DEFUN (ipv6_route_ifname_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, argv[4], NULL); } DEFUN (ipv6_route_ifname_flags_tag_vrf, @@ -4637,7 +4680,7 @@ DEFUN (ipv6_route_ifname_flags_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5], NULL); } DEFUN (ipv6_route_pref_vrf, @@ -4651,7 +4694,7 @@ DEFUN (ipv6_route_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], argv[3], NULL); } DEFUN (ipv6_route_pref_tag_vrf, @@ -4667,7 +4710,7 @@ DEFUN (ipv6_route_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4], NULL); } DEFUN (ipv6_route_flags_pref_vrf, @@ -4683,7 +4726,7 @@ DEFUN (ipv6_route_flags_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], argv[4], NULL); } DEFUN (ipv6_route_flags_pref_tag_vrf, @@ -4701,7 +4744,7 @@ DEFUN (ipv6_route_flags_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5], NULL); } DEFUN (ipv6_route_ifname_pref_vrf, @@ -4715,7 +4758,7 @@ DEFUN (ipv6_route_ifname_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], argv[4], NULL); } DEFUN (ipv6_route_ifname_pref_tag_vrf, @@ -4731,7 +4774,7 @@ DEFUN (ipv6_route_ifname_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5], NULL); } DEFUN (ipv6_route_ifname_flags_pref_vrf, @@ -4747,7 +4790,7 @@ DEFUN (ipv6_route_ifname_flags_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], argv[5]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], argv[5], NULL); } DEFUN (ipv6_route_ifname_flags_pref_tag_vrf, @@ -4765,7 +4808,7 @@ DEFUN (ipv6_route_ifname_flags_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], NULL); } DEFUN (no_ipv6_route_vrf, @@ -4779,7 +4822,7 @@ DEFUN (no_ipv6_route_vrf, "IPv6 gateway interface name\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, argv[2]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, argv[2], NULL); } DEFUN (no_ipv6_route_tag_vrf, @@ -4795,7 +4838,7 @@ DEFUN (no_ipv6_route_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3], NULL); } DEFUN (no_ipv6_route_flags_vrf, @@ -4811,7 +4854,7 @@ DEFUN (no_ipv6_route_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, NULL, argv[3], NULL); } DEFUN (no_ipv6_route_flags_tag_vrf, @@ -4829,7 +4872,7 @@ DEFUN (no_ipv6_route_flags_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4], NULL); } DEFUN (no_ipv6_route_ifname_vrf, @@ -4843,7 +4886,7 @@ DEFUN (no_ipv6_route_ifname_vrf, "IPv6 gateway interface name\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, argv[3], NULL); } DEFUN (no_ipv6_route_ifname_tag_vrf, @@ -4859,7 +4902,7 @@ DEFUN (no_ipv6_route_ifname_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4], NULL); } DEFUN (no_ipv6_route_ifname_flags_vrf, @@ -4875,7 +4918,7 @@ DEFUN (no_ipv6_route_ifname_flags_vrf, "Silently discard pkts when matched\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, NULL, argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, NULL, argv[4], NULL); } DEFUN (no_ipv6_route_ifname_flags_tag_vrf, @@ -4893,7 +4936,7 @@ DEFUN (no_ipv6_route_ifname_flags_tag_vrf, "Tag value\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5], NULL); } DEFUN (no_ipv6_route_pref_vrf, @@ -4908,7 +4951,7 @@ DEFUN (no_ipv6_route_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], argv[3], NULL); } DEFUN (no_ipv6_route_pref_tag_vrf, @@ -4925,7 +4968,7 @@ DEFUN (no_ipv6_route_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4], NULL); } DEFUN (no_ipv6_route_flags_pref_vrf, @@ -4943,7 +4986,7 @@ DEFUN (no_ipv6_route_flags_pref_vrf, VRF_CMD_HELP_STR) { /* We do not care about argv[2] */ - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], argv[4], NULL); } DEFUN (no_ipv6_route_flags_pref_tag_vrf, @@ -4963,7 +5006,7 @@ DEFUN (no_ipv6_route_flags_pref_tag_vrf, VRF_CMD_HELP_STR) { /* We do not care about argv[2] */ - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5], NULL); } DEFUN (no_ipv6_route_ifname_pref_vrf, @@ -4978,7 +5021,7 @@ DEFUN (no_ipv6_route_ifname_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], argv[4], NULL); } DEFUN (no_ipv6_route_ifname_pref_tag_vrf, @@ -4995,7 +5038,7 @@ DEFUN (no_ipv6_route_ifname_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5], NULL); } DEFUN (no_ipv6_route_ifname_flags_pref_vrf, @@ -5012,7 +5055,7 @@ DEFUN (no_ipv6_route_ifname_flags_pref_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], argv[5]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], argv[5], NULL); } DEFUN (no_ipv6_route_ifname_flags_pref_tag_vrf, @@ -5031,7 +5074,7 @@ DEFUN (no_ipv6_route_ifname_flags_pref_tag_vrf, "Distance value for this prefix\n" VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], NULL); } DEFUN (show_ipv6_route, @@ -5960,6 +6003,12 @@ static_config_ipv6 (struct vty *vty) vty_out (vty, " vrf %s", zvrf->name); } + /* Label information */ + if (si->snh_label.num_labels) + vty_out (vty, " label %s", + mpls_label2str (si->snh_label.num_labels, + si->snh_label.label, buf, sizeof buf)); + vty_out (vty, "%s", VTY_NEWLINE); write = 1; From 1c1cf002a1e1975993e36c42daec14657e2bdc38 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 14 Sep 2016 15:27:21 -0300 Subject: [PATCH 029/136] MPLS: ignore hardware restrictions when --enable-cumulus is not given. Signed-off-by: Renato Westphal --- zebra/zebra_mpls.c | 3 ++- zebra/zebra_mpls.h | 2 ++ zebra/zebra_vty.c | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 6b032707ac..07b69eccd6 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1520,6 +1520,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi) } } +#if defined(HAVE_CUMULUS) /* * Check that the label values used in LSP creation are consistent. The * main criteria is that if there is ECMP, the label operation must still @@ -1575,7 +1576,7 @@ zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, /* Label values are good. */ return 1; } - +#endif /* HAVE_CUMULUS */ /* * Add static LSP entry. This may be the first entry for this incoming label diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 7c672fa433..becef524fd 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -205,6 +205,7 @@ mpls_ldp_lsp_uninstall_all (struct hash_backet *backet, void *ctxt); void mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi); +#if defined(HAVE_CUMULUS) /* * Check that the label values used in LSP creation are consistent. The * main criteria is that if there is ECMP, the label operation must still @@ -215,6 +216,7 @@ int zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, mpls_label_t out_label, enum nexthop_types_t gtype, union g_addr *gate, char *ifname, ifindex_t ifindex); +#endif /* HAVE_CUMULUS */ /* * Add static LSP entry. This may be the first entry for this incoming label diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 14f8f18540..aa3da71ce5 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2100,6 +2100,7 @@ zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, if (add_cmd) { +#if defined(HAVE_CUMULUS) /* Check that label value is consistent. */ if (!zebra_mpls_lsp_label_consistent (zvrf, in_label, out_label, gtype, &gate, NULL, 0)) @@ -2108,6 +2109,7 @@ zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, VTY_NEWLINE); return CMD_WARNING; } +#endif /* HAVE_CUMULUS */ ret = zebra_mpls_static_lsp_add (zvrf, in_label, out_label, gtype, &gate, NULL, 0); From 41675b4c5ed055e7a82e6d997ba38be512ebd335 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 14 Sep 2016 17:55:21 -0300 Subject: [PATCH 030/136] zebra: install MPLS CLI commands only if MPLS is enabled. To keep things simple, zebra's code should be the same whether MPLS is enabled or not. Then, when MPLS is not enabled, we just disable all MPLS CLI commands. This way we don't need to add a lot of #ifdef cruft in zebra's core, improving code readability. Signed-off-by: Renato Westphal --- vtysh/Makefile.am | 3 +- zebra/Makefile.am | 4 +- zebra/main.c | 3 + zebra/zebra_mpls_vty.c | 861 +++++++++++++++++++++++++++++++++++++++++ zebra/zebra_static.h | 13 + zebra/zebra_vty.c | 505 +++++------------------- zebra/zserv.h | 3 + 7 files changed, 991 insertions(+), 401 deletions(-) create mode 100644 zebra/zebra_mpls_vty.c diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 77d374a6e8..71fb0ae53e 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -67,7 +67,8 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \ $(top_srcdir)/zebra/zebra_routemap.c \ $(top_srcdir)/zebra/zebra_fpm.c \ - $(top_srcdir)/zebra/zebra_ptm.c + $(top_srcdir)/zebra/zebra_ptm.c \ + $(top_srcdir)/zebra/zebra_mpls_vty.c vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c diff --git a/zebra/Makefile.am b/zebra/Makefile.am index b08c37f55e..851e597796 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -33,13 +33,13 @@ zebra_SOURCES = \ redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ - zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c + zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \ zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ - zebra_memory.c zebra_mpls.c zebra_mpls_null.c + zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c noinst_HEADERS = \ zebra_memory.h \ diff --git a/zebra/main.c b/zebra/main.c index e06a17ca42..dd7e9d9ec3 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -350,6 +350,9 @@ main (int argc, char **argv) zebra_debug_init (); router_id_cmd_init (); zebra_vty_init (); +#if defined(HAVE_MPLS) + zebra_mpls_vty_init (); +#endif access_list_init (); prefix_list_init (); #if defined (HAVE_RTADV) diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c new file mode 100644 index 0000000000..6975ba5609 --- /dev/null +++ b/zebra/zebra_mpls_vty.c @@ -0,0 +1,861 @@ +/* Zebra MPLS VTY functions + * Copyright (C) 2002 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#if defined(HAVE_MPLS) + +#include "memory.h" +#include "if.h" +#include "prefix.h" +#include "command.h" +#include "table.h" +#include "rib.h" +#include "nexthop.h" +#include "vrf.h" +#include "mpls.h" +#include "lib/json.h" + +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_mpls.h" +#include "zebra/zebra_rnh.h" +#include "zebra/redistribute.h" +#include "zebra/zebra_routemap.h" +#include "zebra/zebra_static.h" + +static int +zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, + const char *gate_str, const char *outlabel_str, + const char *flag_str) +{ + struct zebra_vrf *zvrf; + int ret; + enum nexthop_types_t gtype; + union g_addr gate; + mpls_label_t label; + mpls_label_t in_label, out_label; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + { + vty_out (vty, "%% Default VRF does not exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!inlabel_str) + { + vty_out (vty, "%% No Label Information%s", VTY_NEWLINE); + return CMD_WARNING; + } + + out_label = MPLS_IMP_NULL_LABEL; /* as initialization */ + label = atoi(inlabel_str); + if (!IS_MPLS_UNRESERVED_LABEL(label)) + { + vty_out (vty, "%% Invalid label%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (add_cmd) + { + if (!gate_str) + { + vty_out (vty, "%% No Nexthop Information%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!outlabel_str) + { + vty_out (vty, "%% No Outgoing label Information%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + in_label = label; + gtype = NEXTHOP_TYPE_BLACKHOLE; /* as initialization */ + + if (gate_str) + { + /* Gateway is a IPv4 or IPv6 nexthop. */ + ret = inet_pton (AF_INET6, gate_str, &gate.ipv6); + if (ret) + gtype = NEXTHOP_TYPE_IPV6; + else + { + ret = inet_pton (AF_INET, gate_str, &gate.ipv4); + if (ret) + gtype = NEXTHOP_TYPE_IPV4; + else + { + vty_out (vty, "%% Invalid nexthop%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + } + + if (outlabel_str) + { + if (outlabel_str[0] == 'i') + out_label = MPLS_IMP_NULL_LABEL; + else + out_label = atoi(outlabel_str); + } + + if (add_cmd) + { +#if defined(HAVE_CUMULUS) + /* Check that label value is consistent. */ + if (!zebra_mpls_lsp_label_consistent (zvrf, in_label, out_label, gtype, + &gate, NULL, 0)) + { + vty_out (vty, "%% Label value not consistent%s", + VTY_NEWLINE); + return CMD_WARNING; + } +#endif /* HAVE_CUMULUS */ + + ret = zebra_mpls_static_lsp_add (zvrf, in_label, out_label, gtype, + &gate, NULL, 0); + } + else + ret = zebra_mpls_static_lsp_del (zvrf, in_label, gtype, &gate, NULL, 0); + + if (ret) + { + vty_out (vty, "%% LSP cannot be %s%s", + add_cmd ? "added" : "deleted", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (mpls_transit_lsp, + mpls_transit_lsp_cmd, + "mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n" + "IPv4 gateway address\n" + "IPv6 gateway address\n" + "Outgoing MPLS label\n" + "Use Implicit-Null label\n") +{ + return zebra_mpls_transit_lsp (vty, 1, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (no_mpls_transit_lsp, + no_mpls_transit_lsp_cmd, + "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X)", + NO_STR + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n" + "IPv4 gateway address\n" + "IPv6 gateway address\n") +{ + return zebra_mpls_transit_lsp (vty, 0, argv[0], argv[1], NULL, NULL); +} + +ALIAS (no_mpls_transit_lsp, + no_mpls_transit_lsp_out_label_cmd, + "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", + NO_STR + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n" + "IPv4 gateway address\n" + "IPv6 gateway address\n" + "Outgoing MPLS label\n" + "Use Implicit-Null label\n") + +DEFUN (no_mpls_transit_lsp_all, + no_mpls_transit_lsp_all_cmd, + "no mpls lsp <16-1048575>", + NO_STR + MPLS_STR + "Establish label switched path\n" + "Incoming MPLS label\n") +{ + return zebra_mpls_transit_lsp (vty, 0, argv[0], NULL, NULL, NULL); +} + +/* Static route configuration. */ +DEFUN (ip_route_label, + ip_route_label_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, + NULL, NULL, argv[2]); +} + +DEFUN (ip_route_tag_label, + ip_route_tag_label_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], + NULL, NULL, argv[3]); +} + +/* Mask as A.B.C.D format. */ +DEFUN (ip_route_mask_label, + ip_route_mask_label_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, + NULL, NULL, argv[3]); +} + +DEFUN (ip_route_mask_tag_label, + ip_route_mask_tag_label_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") + +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], + NULL, NULL, argv[4]); +} + +/* Distance option value. */ +DEFUN (ip_route_distance_label, + ip_route_distance_label_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, + argv[2], NULL, argv[3]); +} + +DEFUN (ip_route_tag_distance_label, + ip_route_tag_distance_label_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") + +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (ip_route_mask_distance_label, + ip_route_mask_distance_label_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, + argv[3], NULL, argv[4]); +} + +DEFUN (ip_route_mask_tag_distance_label, + ip_route_mask_tag_distance_label_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], + argv[4], NULL, argv[5]); +} + +DEFUN (no_ip_route_label, + no_ip_route_label_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, + NULL, NULL, argv[2]); +} + +DEFUN (no_ip_route_tag_label, + no_ip_route_tag_label_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], + NULL, NULL, argv[3]); +} + +DEFUN (no_ip_route_mask_label, + no_ip_route_mask_label_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, + NULL, NULL, argv[3]); +} + +DEFUN (no_ip_route_mask_tag_label, + no_ip_route_mask_tag_label_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], + NULL, NULL, argv[4]); +} + +DEFUN (no_ip_route_distance_label, + no_ip_route_distance_label_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, + argv[2], NULL, argv[3]); +} + +DEFUN (no_ip_route_tag_distance_label, + no_ip_route_tag_distance_label_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (no_ip_route_mask_distance_label, + no_ip_route_mask_distance_label_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, + argv[3], NULL, argv[5]); +} + +DEFUN (no_ip_route_mask_tag_distance_label, + no_ip_route_mask_tag_distance_label_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], + argv[4], NULL, argv[5]); +} + +DEFUN (ipv6_route_label, + ipv6_route_label_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, argv[2]); +} + +DEFUN (ipv6_route_tag_label, + ipv6_route_tag_label_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, argv[3]); +} + +DEFUN (ipv6_route_ifname_label, + ipv6_route_ifname_label_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, argv[3]); +} +DEFUN (ipv6_route_ifname_tag_label, + ipv6_route_ifname_tag_label_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, argv[4]); +} + +DEFUN (ipv6_route_pref_label, + ipv6_route_pref_label_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, argv[3]); +} + +DEFUN (ipv6_route_pref_tag_label, + ipv6_route_pref_tag_label_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, argv[4]); +} + +DEFUN (ipv6_route_ifname_pref_label, + ipv6_route_ifname_pref_label_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, argv[4]); +} + +DEFUN (ipv6_route_ifname_pref_tag_label, + ipv6_route_ifname_pref_tag_label_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> label WORD", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, argv[5]); +} + +DEFUN (no_ipv6_route_label, + no_ipv6_route_label_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, argv[2]); +} + +DEFUN (no_ipv6_route_tag_label, + no_ipv6_route_tag_label_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, argv[3]); +} + +DEFUN (no_ipv6_route_ifname_label, + no_ipv6_route_ifname_label_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, argv[3]); +} + +DEFUN (no_ipv6_route_ifname_tag_label, + no_ipv6_route_ifname_tag_label_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, argv[4]); +} + +DEFUN (no_ipv6_route_pref_label, + no_ipv6_route_pref_label_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, argv[3]); +} + +DEFUN (no_ipv6_route_pref_tag_label, + no_ipv6_route_pref_tag_label_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, argv[4]); +} + +DEFUN (no_ipv6_route_ifname_pref_label, + no_ipv6_route_ifname_pref_label_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, argv[4]); +} + +DEFUN (no_ipv6_route_ifname_pref_tag_label, + no_ipv6_route_ifname_pref_tag_label_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> label WORD", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + "Specify label(s) for this route\n" + "One or more labels separated by '/'\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, argv[5]); +} + +/* MPLS LSP configuration write function. */ +static int +zebra_mpls_config (struct vty *vty) +{ + int write = 0; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + write += zebra_mpls_write_lsp_config(vty, zvrf); + return write; +} + +DEFUN (show_mpls_table, + show_mpls_table_cmd, + "show mpls table {json}", + SHOW_STR + MPLS_STR + "MPLS table\n" + "JavaScript Object Notation\n") +{ + struct zebra_vrf *zvrf; + u_char use_json = (argv[0] != NULL); + + zvrf = vrf_info_lookup(VRF_DEFAULT); + zebra_mpls_print_lsp_table(vty, zvrf, use_json); + return CMD_SUCCESS; +} + +DEFUN (show_mpls_table_lsp, + show_mpls_table_lsp_cmd, + "show mpls table <16-1048575> {json}", + SHOW_STR + MPLS_STR + "MPLS table\n" + "LSP to display information about\n" + "JavaScript Object Notation\n") +{ + u_int32_t label; + struct zebra_vrf *zvrf; + u_char use_json = (argv[1] != NULL); + + zvrf = vrf_info_lookup(VRF_DEFAULT); + label = atoi(argv[0]); + zebra_mpls_print_lsp (vty, zvrf, label, use_json); + return CMD_SUCCESS; +} + +/* MPLS node for MPLS LSP. */ +static struct cmd_node mpls_node = { MPLS_NODE, "", 1 }; + +/* MPLS VTY. */ +void +zebra_mpls_vty_init (void) +{ + install_node (&mpls_node, zebra_mpls_config); + + install_element (CONFIG_NODE, &ip_route_label_cmd); + install_element (CONFIG_NODE, &ip_route_tag_label_cmd); + install_element (CONFIG_NODE, &ip_route_mask_label_cmd); + install_element (CONFIG_NODE, &ip_route_mask_tag_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_tag_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_mask_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_mask_tag_label_cmd); + install_element (CONFIG_NODE, &ip_route_distance_label_cmd); + install_element (CONFIG_NODE, &ip_route_tag_distance_label_cmd); + install_element (CONFIG_NODE, &ip_route_mask_distance_label_cmd); + install_element (CONFIG_NODE, &ip_route_mask_tag_distance_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_distance_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_tag_distance_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_mask_distance_label_cmd); + install_element (CONFIG_NODE, &no_ip_route_mask_tag_distance_label_cmd); + + install_element (CONFIG_NODE, &ipv6_route_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_ifname_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_ifname_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_pref_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_ifname_pref_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_pref_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_tag_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_ifname_tag_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_pref_tag_label_cmd); + install_element (CONFIG_NODE, &ipv6_route_ifname_pref_tag_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_tag_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_ifname_tag_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_pref_tag_label_cmd); + install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_tag_label_cmd); + + install_element (CONFIG_NODE, &mpls_transit_lsp_cmd); + install_element (CONFIG_NODE, &no_mpls_transit_lsp_cmd); + install_element (CONFIG_NODE, &no_mpls_transit_lsp_out_label_cmd); + install_element (CONFIG_NODE, &no_mpls_transit_lsp_all_cmd); + + install_element (VIEW_NODE, &show_mpls_table_cmd); + install_element (ENABLE_NODE, &show_mpls_table_cmd); + install_element (VIEW_NODE, &show_mpls_table_lsp_cmd); + install_element (ENABLE_NODE, &show_mpls_table_lsp_cmd); +} + +#endif /* HAVE_MPLS */ diff --git a/zebra/zebra_static.h b/zebra/zebra_static.h index 9178a6dd45..8c55ea84c0 100644 --- a/zebra/zebra_static.h +++ b/zebra/zebra_static.h @@ -97,5 +97,18 @@ static_delete_route (afi_t, safi_t safi, u_char type, struct prefix *p, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label); +int +zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, + const char *dest_str, const char *mask_str, + const char *gate_str, const char *flag_str, + const char *tag_str, const char *distance_str, + const char *vrf_id_str, const char *label_str); + +int +static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, + const char *gate_str, const char *ifname, + const char *flag_str, const char *tag_str, + const char *distance_str, const char *vrf_id_str, + const char *label_str); #endif diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index aa3da71ce5..e76e4ab8fa 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -53,7 +53,7 @@ static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, #define ONE_WEEK_SECOND 60*60*24*7 /* General function for static route. */ -static int +int zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, const char *dest_str, const char *mask_str, const char *gate_str, const char *flag_str, @@ -351,23 +351,21 @@ DEFUN (show_ip_rpf_addr, /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) {label WORD}", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Null interface\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, - NULL, NULL, argv[2]); + NULL, NULL, NULL); } DEFUN (ip_route_tag, ip_route_tag_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -375,34 +373,30 @@ DEFUN (ip_route_tag, "IP gateway interface name\n" "Null interface\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" "One or more labels separated by '/'\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], - NULL, NULL, argv[3]); + NULL, NULL, NULL); } DEFUN (ip_route_flags, ip_route_flags_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) {label WORD}", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole)", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Silently discard pkts when matched\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], NULL, - NULL, NULL, argv[3]); + NULL, NULL, NULL); } DEFUN (ip_route_flags_tag, ip_route_flags_tag_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> {label WORD}", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -411,13 +405,11 @@ DEFUN (ip_route_flags_tag, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], argv[2], argv[3], - NULL, NULL, argv[4]); + NULL, NULL, NULL); } DEFUN (ip_route_flags2, @@ -452,24 +444,22 @@ DEFUN (ip_route_flags2_tag, /* Mask as A.B.C.D format. */ DEFUN (ip_route_mask, ip_route_mask_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) {label WORD}", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Null interface\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, - NULL, NULL, argv[3]); + NULL, NULL, NULL); } DEFUN (ip_route_mask_tag, ip_route_mask_tag_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -478,13 +468,11 @@ DEFUN (ip_route_mask_tag, "IP gateway interface name\n" "Null interface\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], - NULL, NULL, argv[4]); + NULL, NULL, NULL); } DEFUN (ip_route_mask_flags, @@ -555,24 +543,22 @@ DEFUN (ip_route_mask_flags2_tag, /* Distance option value. */ DEFUN (ip_route_distance, ip_route_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, NULL, - argv[2], NULL, argv[3]); + argv[2], NULL, NULL); } DEFUN (ip_route_tag_distance, ip_route_tag_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -581,13 +567,11 @@ DEFUN (ip_route_tag_distance, "Null interface\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], NULL, argv[2], - argv[3], NULL, argv[4]); + argv[3], NULL, NULL); } DEFUN (ip_route_flags_distance, @@ -656,7 +640,7 @@ DEFUN (ip_route_flags_tag_distance2, DEFUN (ip_route_mask_distance, ip_route_mask_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -664,17 +648,15 @@ DEFUN (ip_route_mask_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, NULL, - argv[3], NULL, argv[4]); + argv[3], NULL, NULL); } DEFUN (ip_route_mask_tag_distance, ip_route_mask_tag_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -684,12 +666,10 @@ DEFUN (ip_route_mask_tag_distance, "Null interface\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], NULL, argv[3], - argv[4], NULL, argv[5]); + argv[4], NULL, NULL); } DEFUN (ip_route_mask_flags_tag_distance, @@ -763,24 +743,22 @@ DEFUN (ip_route_mask_flags_tag_distance2, DEFUN (no_ip_route, no_ip_route_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) {label WORD}", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", NO_STR IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Null interface\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, - NULL, NULL, argv[2]); + NULL, NULL, NULL); } DEFUN (no_ip_route_tag, no_ip_route_tag_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535>", NO_STR IP_STR "Establish static routes\n" @@ -789,12 +767,10 @@ DEFUN (no_ip_route_tag, "IP gateway interface name\n" "Null interface\n" "Tag of this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], - NULL, NULL, argv[3]); + NULL, NULL, NULL); } ALIAS (no_ip_route, @@ -855,7 +831,7 @@ DEFUN (no_ip_route_flags2_tag, DEFUN (no_ip_route_mask, no_ip_route_mask_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) {label WORD}", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", NO_STR IP_STR "Establish static routes\n" @@ -863,17 +839,15 @@ DEFUN (no_ip_route_mask, "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Null interface\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, - NULL, NULL, argv[3]); + NULL, NULL, NULL); } DEFUN (no_ip_route_mask_tag, no_ip_route_mask_tag_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> {label WORD}", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535>", NO_STR IP_STR "Establish static routes\n" @@ -883,12 +857,10 @@ DEFUN (no_ip_route_mask_tag, "IP gateway interface name\n" "Null interface\n" "Tag of this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], - NULL, NULL, argv[4]); + NULL, NULL, NULL); } ALIAS (no_ip_route_mask, @@ -953,7 +925,7 @@ DEFUN (no_ip_route_mask_flags2_tag, DEFUN (no_ip_route_distance, no_ip_route_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -961,17 +933,15 @@ DEFUN (no_ip_route_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, NULL, - argv[2], NULL, argv[3]); + argv[2], NULL, NULL); } DEFUN (no_ip_route_tag_distance, no_ip_route_tag_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -981,12 +951,10 @@ DEFUN (no_ip_route_tag_distance, "Null interface\n" "Tag of this route\n" "Tag value\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], NULL, argv[2], - argv[3], NULL, argv[4]); + argv[3], NULL, NULL); } DEFUN (no_ip_route_flags_distance, @@ -1042,7 +1010,7 @@ DEFUN (no_ip_route_flags_distance2, DEFUN (no_ip_route_flags_tag_distance2, no_ip_route_flags_tag_distance2_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> {label WORD}", + "no ip route A.B.C.D/M (reject|blackhole) tag <1-65535> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1051,17 +1019,15 @@ DEFUN (no_ip_route_flags_tag_distance2, "Silently discard pkts when matched\n" "Tag of this route\n" "Tag value\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, argv[1], argv[2], - argv[3], NULL, argv[4]); + argv[3], NULL, NULL); } DEFUN (no_ip_route_mask_distance, no_ip_route_mask_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1070,17 +1036,15 @@ DEFUN (no_ip_route_mask_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], - argv[4], NULL, argv[5]); + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, NULL, + argv[3], NULL, NULL); } DEFUN (no_ip_route_mask_tag_distance, no_ip_route_mask_tag_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> {label WORD}", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1091,12 +1055,10 @@ DEFUN (no_ip_route_mask_tag_distance, "Null interface\n" "Tag of this route\n" "Tag value\n" - "Distance value for this route\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this route\n") { return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[3], - argv[4], NULL, argv[5]); + argv[4], NULL, NULL); } DEFUN (no_ip_route_mask_flags_distance, @@ -2021,162 +1983,6 @@ DEFUN (no_ip_route_mask_flags_tag_distance2_vrf, argv[2], argv[3], argv[4], argv[5], NULL); } -static int -zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, - const char *gate_str, const char *outlabel_str, - const char *flag_str) -{ - struct zebra_vrf *zvrf; - int ret; - enum nexthop_types_t gtype; - union g_addr gate; - mpls_label_t label; - mpls_label_t in_label, out_label; - - zvrf = vrf_info_lookup(VRF_DEFAULT); - if (!zvrf) - { - vty_out (vty, "%% Default VRF does not exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!inlabel_str) - { - vty_out (vty, "%% No Label Information%s", VTY_NEWLINE); - return CMD_WARNING; - } - - out_label = MPLS_IMP_NULL_LABEL; /* as initialization */ - label = atoi(inlabel_str); - if (!IS_MPLS_UNRESERVED_LABEL(label)) - { - vty_out (vty, "%% Invalid label%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (add_cmd) - { - if (!gate_str) - { - vty_out (vty, "%% No Nexthop Information%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (!outlabel_str) - { - vty_out (vty, "%% No Outgoing label Information%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - - in_label = label; - gtype = NEXTHOP_TYPE_BLACKHOLE; /* as initialization */ - - if (gate_str) - { - /* Gateway is a IPv4 or IPv6 nexthop. */ - ret = inet_pton (AF_INET6, gate_str, &gate.ipv6); - if (ret) - gtype = NEXTHOP_TYPE_IPV6; - else - { - ret = inet_pton (AF_INET, gate_str, &gate.ipv4); - if (ret) - gtype = NEXTHOP_TYPE_IPV4; - else - { - vty_out (vty, "%% Invalid nexthop%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - } - - if (outlabel_str) - { - if (outlabel_str[0] == 'i') - out_label = MPLS_IMP_NULL_LABEL; - else - out_label = atoi(outlabel_str); - } - - if (add_cmd) - { -#if defined(HAVE_CUMULUS) - /* Check that label value is consistent. */ - if (!zebra_mpls_lsp_label_consistent (zvrf, in_label, out_label, gtype, - &gate, NULL, 0)) - { - vty_out (vty, "%% Label value not consistent%s", - VTY_NEWLINE); - return CMD_WARNING; - } -#endif /* HAVE_CUMULUS */ - - ret = zebra_mpls_static_lsp_add (zvrf, in_label, out_label, gtype, - &gate, NULL, 0); - } - else - ret = zebra_mpls_static_lsp_del (zvrf, in_label, gtype, &gate, NULL, 0); - - if (ret) - { - vty_out (vty, "%% LSP cannot be %s%s", - add_cmd ? "added" : "deleted", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN (mpls_transit_lsp, - mpls_transit_lsp_cmd, - "mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", - MPLS_STR - "Establish label switched path\n" - "Incoming MPLS label\n" - "IPv4 gateway address\n" - "IPv6 gateway address\n" - "Outgoing MPLS label\n" - "Use Implicit-Null label\n") -{ - return zebra_mpls_transit_lsp (vty, 1, argv[0], argv[1], argv[2], NULL); -} - -DEFUN (no_mpls_transit_lsp, - no_mpls_transit_lsp_cmd, - "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X)", - NO_STR - MPLS_STR - "Establish label switched path\n" - "Incoming MPLS label\n" - "IPv4 gateway address\n" - "IPv6 gateway address\n") -{ - return zebra_mpls_transit_lsp (vty, 0, argv[0], argv[1], NULL, NULL); -} - -ALIAS (no_mpls_transit_lsp, - no_mpls_transit_lsp_out_label_cmd, - "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", - NO_STR - MPLS_STR - "Establish label switched path\n" - "Incoming MPLS label\n" - "IPv4 gateway address\n" - "IPv6 gateway address\n" - "Outgoing MPLS label\n" - "Use Implicit-Null label\n") - -DEFUN (no_mpls_transit_lsp_all, - no_mpls_transit_lsp_all_cmd, - "no mpls lsp <16-1048575>", - NO_STR - MPLS_STR - "Establish label switched path\n" - "Incoming MPLS label\n") -{ - return zebra_mpls_transit_lsp (vty, 0, argv[0], NULL, NULL, NULL); -} - /* New RIB. Detailed information for IPv4 route. */ static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) @@ -3919,7 +3725,7 @@ static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) #ifdef HAVE_IPV6 /* General fucntion for IPv6 static route. */ -static int +int static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, const char *gate_str, const char *ifname, const char *flag_str, const char *tag_str, @@ -3976,7 +3782,6 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, if (tag_str) tag = atoi(tag_str); - /* When gateway is valid IPv6 addrees, then gate is treated as nexthop address other case gate is treated as interface name. */ ret = inet_pton (AF_INET6, gate_str, &gate_addr); @@ -4055,32 +3860,28 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, DEFUN (ipv6_route, ipv6_route_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) {label WORD}", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "IPv6 gateway interface name\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, argv[2]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, NULL); } DEFUN (ipv6_route_tag, ipv6_route_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> {label WORD}", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, NULL); } DEFUN (ipv6_route_flags, @@ -4115,31 +3916,27 @@ DEFUN (ipv6_route_flags_tag, DEFUN (ipv6_route_ifname, ipv6_route_ifname_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE {label WORD}", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "IPv6 gateway interface name\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, NULL); } DEFUN (ipv6_route_ifname_tag, ipv6_route_ifname_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> {label WORD}", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, NULL); } DEFUN (ipv6_route_ifname_flags, @@ -4174,22 +3971,20 @@ DEFUN (ipv6_route_ifname_flags_tag, DEFUN (ipv6_route_pref, ipv6_route_pref_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> {label WORD}", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, NULL); } DEFUN (ipv6_route_pref_tag, ipv6_route_pref_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> {label WORD}", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4197,11 +3992,9 @@ DEFUN (ipv6_route_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, NULL); } DEFUN (ipv6_route_flags_pref, @@ -4238,22 +4031,20 @@ DEFUN (ipv6_route_flags_pref_tag, DEFUN (ipv6_route_ifname_pref, ipv6_route_ifname_pref_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> {label WORD}", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, NULL); } DEFUN (ipv6_route_ifname_pref_tag, ipv6_route_ifname_pref_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> {label WORD}", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4261,11 +4052,9 @@ DEFUN (ipv6_route_ifname_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, argv[5]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, NULL); } DEFUN (ipv6_route_ifname_flags_pref, @@ -4302,22 +4091,20 @@ DEFUN (ipv6_route_ifname_flags_pref_tag, DEFUN (no_ipv6_route, no_ipv6_route_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) {label WORD}", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "IPv6 gateway interface name\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, argv[2]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, NULL, NULL); } DEFUN (no_ipv6_route_tag, no_ipv6_route_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> {label WORD}", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535>", NO_STR IP_STR "Establish static routes\n" @@ -4325,11 +4112,9 @@ DEFUN (no_ipv6_route_tag, "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL, NULL); } DEFUN (no_ipv6_route_flags, @@ -4366,22 +4151,20 @@ DEFUN (no_ipv6_route_flags_tag, DEFUN (no_ipv6_route_ifname, no_ipv6_route_ifname_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE {label WORD}", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "IPv6 gateway interface name\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, NULL, NULL); } DEFUN (no_ipv6_route_ifname_tag, no_ipv6_route_ifname_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> {label WORD}", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535>", NO_STR IP_STR "Establish static routes\n" @@ -4389,11 +4172,9 @@ DEFUN (no_ipv6_route_ifname_tag, "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Set tag for this route\n" - "Tag value\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Tag value\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL, NULL); } DEFUN (no_ipv6_route_ifname_flags, @@ -4430,23 +4211,21 @@ DEFUN (no_ipv6_route_ifname_flags_tag, DEFUN (no_ipv6_route_pref, no_ipv6_route_pref_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> {label WORD}", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], NULL, NULL); } DEFUN (no_ipv6_route_pref_tag, no_ipv6_route_pref_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> {label WORD}", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -4455,11 +4234,9 @@ DEFUN (no_ipv6_route_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL, NULL); } DEFUN (no_ipv6_route_flags_pref, @@ -4500,23 +4277,21 @@ DEFUN (no_ipv6_route_flags_pref_tag, DEFUN (no_ipv6_route_ifname_pref, no_ipv6_route_ifname_pref_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> {label WORD}", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], NULL, NULL); } DEFUN (no_ipv6_route_ifname_pref_tag, no_ipv6_route_ifname_pref_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> {label WORD}", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -4525,11 +4300,9 @@ DEFUN (no_ipv6_route_ifname_pref_tag, "IPv6 gateway interface name\n" "Set tag for this route\n" "Tag value\n" - "Distance value for this prefix\n" - "Specify label(s) for this route\n" - "One or more labels separated by '/'\n") + "Distance value for this prefix\n") { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, argv[5]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL, NULL); } DEFUN (no_ipv6_route_ifname_flags_pref, @@ -6083,56 +5856,6 @@ zebra_ip_config (struct vty *vty) return write; } -/* MPLS LSP configuration write function. */ -static int -zebra_mpls_config (struct vty *vty) -{ - int write = 0; - struct zebra_vrf *zvrf; - - zvrf = vrf_info_lookup(VRF_DEFAULT); - if (!zvrf) - return 0; - - write += zebra_mpls_write_lsp_config(vty, zvrf); - return write; -} - -DEFUN (show_mpls_table, - show_mpls_table_cmd, - "show mpls table {json}", - SHOW_STR - MPLS_STR - "MPLS table\n" - "JavaScript Object Notation\n") -{ - struct zebra_vrf *zvrf; - u_char use_json = (argv[0] != NULL); - - zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_mpls_print_lsp_table(vty, zvrf, use_json); - return CMD_SUCCESS; -} - -DEFUN (show_mpls_table_lsp, - show_mpls_table_lsp_cmd, - "show mpls table <16-1048575> {json}", - SHOW_STR - MPLS_STR - "MPLS table\n" - "LSP to display information about\n" - "JavaScript Object Notation\n") -{ - u_int32_t label; - struct zebra_vrf *zvrf; - u_char use_json = (argv[1] != NULL); - - zvrf = vrf_info_lookup(VRF_DEFAULT); - label = atoi(argv[0]); - zebra_mpls_print_lsp (vty, zvrf, label, use_json); - return CMD_SUCCESS; -} - DEFUN (ip_zebra_import_table_distance, ip_zebra_import_table_distance_cmd, "ip import-table <1-252> distance <1-255>", @@ -6300,16 +6023,12 @@ config_write_protocol (struct vty *vty) static struct cmd_node ip_node = { IP_NODE, "", 1 }; static struct cmd_node protocol_node = { PROTOCOL_NODE, "", 1 }; -/* MPLS node for MPLS LSP. */ -static struct cmd_node mpls_node = { MPLS_NODE, "", 1 }; - /* Route VTY. */ void zebra_vty_init (void) { install_node (&ip_node, zebra_ip_config); install_node (&protocol_node, config_write_protocol); - install_node (&mpls_node, zebra_mpls_config); install_element (CONFIG_NODE, &allow_external_route_update_cmd); install_element (CONFIG_NODE, &no_allow_external_route_update_cmd); @@ -6637,14 +6356,4 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd); install_element (ENABLE_NODE, &show_ipv6_mroute_vrf_all_cmd); #endif /* HAVE_IPV6 */ - - install_element (CONFIG_NODE, &mpls_transit_lsp_cmd); - install_element (CONFIG_NODE, &no_mpls_transit_lsp_cmd); - install_element (CONFIG_NODE, &no_mpls_transit_lsp_out_label_cmd); - install_element (CONFIG_NODE, &no_mpls_transit_lsp_all_cmd); - - install_element (VIEW_NODE, &show_mpls_table_cmd); - install_element (ENABLE_NODE, &show_mpls_table_cmd); - install_element (VIEW_NODE, &show_mpls_table_lsp_cmd); - install_element (ENABLE_NODE, &show_mpls_table_lsp_cmd); } diff --git a/zebra/zserv.h b/zebra/zserv.h index ce243dd6ac..ceff6a96ae 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -149,6 +149,9 @@ extern void kernel_terminate (struct zebra_ns *); extern void zebra_route_map_init (void); extern void zebra_snmp_init (void); extern void zebra_vty_init (void); +#if defined(HAVE_MPLS) +extern void zebra_mpls_vty_init (void); +#endif extern int zsend_vrf_add (struct zserv *, struct zebra_vrf *); extern int zsend_vrf_delete (struct zserv *, struct zebra_vrf *); From e2a4192a42ecda4f431ba8f9a4d47e4e75417659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Thu, 15 Sep 2016 12:28:52 +0200 Subject: [PATCH 031/136] zebra: fix bug in nexthop label allocation Label array in nexthop_label struct was not being allocated. --- lib/nexthop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/nexthop.c b/lib/nexthop.c index 01771e253c..23ee28b7dc 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -171,7 +171,8 @@ nexthop_add_labels (struct nexthop *nexthop, enum lsp_types_t type, int i; nexthop->nh_label_type = type; - nh_label = XCALLOC (MTYPE_NH_LABEL, sizeof (struct nexthop_label)); + nh_label = XCALLOC (MTYPE_NH_LABEL, sizeof (struct nexthop_label) + + num_labels * sizeof (mpls_label_t)); nh_label->num_labels = num_labels; for (i = 0; i < num_labels; i++) nh_label->label[i] = *(label + i); From e64f3c323bf176544c5d0cac9a9d55fc4510972a Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 17 Sep 2016 10:55:24 -0300 Subject: [PATCH 032/136] zebra: add explicit-null option for static MPLS LSPs Signed-off-by: Renato Westphal --- zebra/zebra_mpls.c | 19 ++++++++++++++++--- zebra/zebra_mpls_vty.c | 10 ++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 07b69eccd6..ccb81dcff5 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1837,18 +1837,31 @@ zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) zebra_slsp_t *slsp; zebra_snhlfe_t *snhlfe; struct listnode *node; - char buf[INET6_ADDRSTRLEN]; struct list *slsp_list = hash_get_sorted_list(zvrf->slsp_table, slsp_cmp); for (ALL_LIST_ELEMENTS_RO(slsp_list, node, slsp)) { for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) { + char buf[INET6_ADDRSTRLEN]; char lstr[30]; + snhlfe2str (snhlfe, buf, BUFSIZ); + switch (snhlfe->out_label) { + case MPLS_V4_EXP_NULL_LABEL: + case MPLS_V6_EXP_NULL_LABEL: + strlcpy(lstr, "explicit-null", sizeof(lstr)); + break; + case MPLS_IMP_NULL_LABEL: + strlcpy(lstr, "implicit-null", sizeof(lstr)); + break; + default: + sprintf(lstr, "%u", snhlfe->out_label); + break; + } + vty_out (vty, "mpls lsp %u %s %s%s", - slsp->ile.in_label, buf, - label2str(snhlfe->out_label, lstr, 30), VTY_NEWLINE); + slsp->ile.in_label, buf, lstr, VTY_NEWLINE); } } diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index 6975ba5609..e5f6932e87 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -115,6 +115,10 @@ zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, { if (outlabel_str[0] == 'i') out_label = MPLS_IMP_NULL_LABEL; + else if (outlabel_str[0] == 'e' && gtype == NEXTHOP_TYPE_IPV4) + out_label = MPLS_V4_EXP_NULL_LABEL; + else if (outlabel_str[0] == 'e' && gtype == NEXTHOP_TYPE_IPV6) + out_label = MPLS_V6_EXP_NULL_LABEL; else out_label = atoi(outlabel_str); } @@ -150,13 +154,14 @@ zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, DEFUN (mpls_transit_lsp, mpls_transit_lsp_cmd, - "mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", + "mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|explicit-null|implicit-null)", MPLS_STR "Establish label switched path\n" "Incoming MPLS label\n" "IPv4 gateway address\n" "IPv6 gateway address\n" "Outgoing MPLS label\n" + "Use Explicit-Null label\n" "Use Implicit-Null label\n") { return zebra_mpls_transit_lsp (vty, 1, argv[0], argv[1], argv[2], NULL); @@ -177,7 +182,7 @@ DEFUN (no_mpls_transit_lsp, ALIAS (no_mpls_transit_lsp, no_mpls_transit_lsp_out_label_cmd, - "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|implicit-null)", + "no mpls lsp <16-1048575> (A.B.C.D|X:X::X:X) (<16-1048575>|explicit-null|implicit-null)", NO_STR MPLS_STR "Establish label switched path\n" @@ -185,6 +190,7 @@ ALIAS (no_mpls_transit_lsp, "IPv4 gateway address\n" "IPv6 gateway address\n" "Outgoing MPLS label\n" + "Use Explicit-Null label\n" "Use Implicit-Null label\n") DEFUN (no_mpls_transit_lsp_all, From 7fe041ac83786e9014460a69296591d7981763f6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 21 Sep 2016 13:38:34 -0300 Subject: [PATCH 033/136] zserv: always send all information about each route Most routing daemons are not interested in certain pieces of information when a redistributed route is being removed, like its metric and distance. ldpd, in the other hand, needs to know the distance of the removed routes in order to work properly. Now, instead of adding another exception in zserv's code for ldpd, let's make zebra always send all information about each route to its clients, independently if the route is being added or removed. This is ok because all daemons are already prepared to process these additional fields when the appropriate flags are set in the zebra messages. Signed-off-by: Renato Westphal --- zebra/zserv.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/zebra/zserv.c b/zebra/zserv.c index 4c68f6acb9..5f0b4d5e20 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -720,24 +720,25 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, } } - /* Metric */ - if (cmd == ZEBRA_REDISTRIBUTE_IPV4_ADD || cmd == ZEBRA_REDISTRIBUTE_IPV6_ADD) - { - SET_FLAG (zapi_flags, ZAPI_MESSAGE_DISTANCE); - stream_putc (s, rib->distance); - SET_FLAG (zapi_flags, ZAPI_MESSAGE_METRIC); - stream_putl (s, rib->metric); + /* Distance */ + SET_FLAG (zapi_flags, ZAPI_MESSAGE_DISTANCE); + stream_putc (s, rib->distance); - /* tag */ - if (rib->tag) - { - SET_FLAG(zapi_flags, ZAPI_MESSAGE_TAG); - stream_putw(s, rib->tag); - } - SET_FLAG (zapi_flags, ZAPI_MESSAGE_MTU); - stream_putl (s, rib->mtu); + /* Metric */ + SET_FLAG (zapi_flags, ZAPI_MESSAGE_METRIC); + stream_putl (s, rib->metric); + + /* Tag */ + if (rib->tag) + { + SET_FLAG(zapi_flags, ZAPI_MESSAGE_TAG); + stream_putw(s, rib->tag); } + /* MTU */ + SET_FLAG (zapi_flags, ZAPI_MESSAGE_MTU); + stream_putl (s, rib->mtu); + /* write real message flags value */ stream_putc_at (s, messmark, zapi_flags); From a4b46f4c16bd4cb1f2c890c08ff541468d59d9b3 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 20 Sep 2016 20:50:47 -0300 Subject: [PATCH 034/136] *: remove dead code Since recently zebra uses only the ZEBRA_REDISTRIBUTE_* messages to advertise redistributed routes to its clientes. Now the old ZEBRA_IPV*_ROUTE_* messages are only used for client->zebra communication. Signed-off-by: Renato Westphal --- bgpd/bgp_zebra.c | 4 ---- isisd/isis_zebra.c | 4 ---- ldpd/ldp_zebra.c | 12 ------------ lib/zclient.c | 16 ---------------- lib/zclient.h | 4 ---- ospf6d/ospf6_zebra.c | 4 ---- ospfd/ospf_zebra.c | 2 -- pimd/pim_zebra.c | 4 ---- ripd/rip_zebra.c | 2 -- ripngd/ripng_zebra.c | 6 ------ 10 files changed, 58 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c2df521e79..15db321557 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2103,14 +2103,10 @@ bgp_zebra_init (struct thread_master *master) zclient->interface_nbr_address_add = bgp_interface_nbr_address_add; zclient->interface_nbr_address_delete = bgp_interface_nbr_address_delete; zclient->interface_vrf_update = bgp_interface_vrf_update; - zclient->ipv4_route_add = zebra_read_ipv4; - zclient->ipv4_route_delete = zebra_read_ipv4; zclient->redistribute_route_ipv4_add = zebra_read_ipv4; zclient->redistribute_route_ipv4_del = zebra_read_ipv4; zclient->interface_up = bgp_interface_up; zclient->interface_down = bgp_interface_down; - zclient->ipv6_route_add = zebra_read_ipv6; - zclient->ipv6_route_delete = zebra_read_ipv6; zclient->redistribute_route_ipv6_add = zebra_read_ipv6; zclient->redistribute_route_ipv6_del = zebra_read_ipv6; zclient->nexthop_update = bgp_read_nexthop_update; diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 569ff70d8d..45728ad2c1 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -706,13 +706,9 @@ isis_zebra_init (struct thread_master *master) zclient->interface_address_add = isis_zebra_if_address_add; zclient->interface_address_delete = isis_zebra_if_address_del; zclient->interface_link_params = isis_zebra_link_params; - zclient->ipv4_route_add = isis_zebra_read_ipv4; - zclient->ipv4_route_delete = isis_zebra_read_ipv4; zclient->redistribute_route_ipv4_add = isis_zebra_read_ipv4; zclient->redistribute_route_ipv4_del = isis_zebra_read_ipv4; #ifdef HAVE_IPV6 - zclient->ipv6_route_add = isis_zebra_read_ipv6; - zclient->ipv6_route_delete = isis_zebra_read_ipv6; zclient->redistribute_route_ipv6_add = isis_zebra_read_ipv6; zclient->redistribute_route_ipv6_del = isis_zebra_read_ipv6; #endif /* HAVE_IPV6 */ diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 6e19e4f607..1d443a6487 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -368,16 +368,12 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, return (0); switch (command) { - case ZEBRA_IPV4_ROUTE_ADD: case ZEBRA_REDISTRIBUTE_IPV4_ADD: - case ZEBRA_IPV4_ROUTE_DELETE: case ZEBRA_REDISTRIBUTE_IPV4_DEL: kr.af = AF_INET; nhlen = sizeof(struct in_addr); break; - case ZEBRA_IPV6_ROUTE_ADD: case ZEBRA_REDISTRIBUTE_IPV6_ADD: - case ZEBRA_IPV6_ROUTE_DELETE: case ZEBRA_REDISTRIBUTE_IPV6_DEL: kr.af = AF_INET6; nhlen = sizeof(struct in6_addr); @@ -419,9 +415,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, kr.ifindex = stream_getl(s); switch (command) { - case ZEBRA_IPV4_ROUTE_ADD: case ZEBRA_REDISTRIBUTE_IPV4_ADD: - case ZEBRA_IPV6_ROUTE_ADD: case ZEBRA_REDISTRIBUTE_IPV6_ADD: debug_zebra_in("route add %s/%d nexthop %s (%s)", log_addr(kr.af, &kr.prefix), kr.prefixlen, @@ -430,9 +424,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr, sizeof(kr)); break; - case ZEBRA_IPV4_ROUTE_DELETE: case ZEBRA_REDISTRIBUTE_IPV4_DEL: - case ZEBRA_IPV6_ROUTE_DELETE: case ZEBRA_REDISTRIBUTE_IPV6_DEL: debug_zebra_in("route delete %s/%d nexthop %s (%s)", log_addr(kr.af, &kr.prefix), kr.prefixlen, @@ -501,12 +493,8 @@ ldp_zebra_init(struct thread_master *master) zclient->interface_down = ldp_interface_status_change; zclient->interface_address_add = ldp_interface_address_add; zclient->interface_address_delete = ldp_interface_address_delete; - zclient->ipv4_route_add = ldp_zebra_read_route; - zclient->ipv4_route_delete = ldp_zebra_read_route; zclient->redistribute_route_ipv4_add = ldp_zebra_read_route; zclient->redistribute_route_ipv4_del = ldp_zebra_read_route; - zclient->ipv6_route_add = ldp_zebra_read_route; - zclient->ipv6_route_delete = ldp_zebra_read_route; zclient->redistribute_route_ipv6_add = ldp_zebra_read_route; zclient->redistribute_route_ipv6_del = ldp_zebra_read_route; } diff --git a/lib/zclient.c b/lib/zclient.c index 057fa77580..5193a282a6 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1583,22 +1583,6 @@ zclient_read (struct thread *thread) if (zclient->interface_vrf_update) (*zclient->interface_vrf_update) (command, zclient, length, vrf_id); break; - case ZEBRA_IPV4_ROUTE_ADD: - if (zclient->ipv4_route_add) - (*zclient->ipv4_route_add) (command, zclient, length, vrf_id); - break; - case ZEBRA_IPV4_ROUTE_DELETE: - if (zclient->ipv4_route_delete) - (*zclient->ipv4_route_delete) (command, zclient, length, vrf_id); - break; - case ZEBRA_IPV6_ROUTE_ADD: - if (zclient->ipv6_route_add) - (*zclient->ipv6_route_add) (command, zclient, length, vrf_id); - break; - case ZEBRA_IPV6_ROUTE_DELETE: - if (zclient->ipv6_route_delete) - (*zclient->ipv6_route_delete) (command, zclient, length, vrf_id); - break; case ZEBRA_NEXTHOP_UPDATE: if (zclient_debug) zlog_debug("zclient rcvd nexthop update\n"); diff --git a/lib/zclient.h b/lib/zclient.h index b95d18ec1a..231b4e9b4f 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -98,10 +98,6 @@ struct zclient int (*interface_nbr_address_add) (int, struct zclient *, uint16_t, vrf_id_t); int (*interface_nbr_address_delete) (int, struct zclient *, uint16_t, vrf_id_t); int (*interface_vrf_update) (int, struct zclient *, uint16_t, vrf_id_t); - int (*ipv4_route_add) (int, struct zclient *, uint16_t, vrf_id_t); - int (*ipv4_route_delete) (int, struct zclient *, uint16_t, vrf_id_t); - int (*ipv6_route_add) (int, struct zclient *, uint16_t, vrf_id_t); - int (*ipv6_route_delete) (int, struct zclient *, uint16_t, vrf_id_t); int (*nexthop_update) (int, struct zclient *, uint16_t, vrf_id_t); int (*import_check_update) (int, struct zclient *, uint16_t, vrf_id_t); int (*bfd_dest_replay) (int, struct zclient *, uint16_t, vrf_id_t); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 77ea01e53d..6dee1424a6 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -677,12 +677,8 @@ ospf6_zebra_init (struct thread_master *master) zclient->interface_down = ospf6_zebra_if_state_update; zclient->interface_address_add = ospf6_zebra_if_address_update_add; zclient->interface_address_delete = ospf6_zebra_if_address_update_delete; - zclient->ipv4_route_add = NULL; - zclient->ipv4_route_delete = NULL; zclient->redistribute_route_ipv4_add = NULL; zclient->redistribute_route_ipv4_del = NULL; - zclient->ipv6_route_add = ospf6_zebra_read_ipv6; - zclient->ipv6_route_delete = ospf6_zebra_read_ipv6; zclient->redistribute_route_ipv6_add = ospf6_zebra_read_ipv6; zclient->redistribute_route_ipv6_del = ospf6_zebra_read_ipv6; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index c0b94a3360..8752e83ed5 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -1601,8 +1601,6 @@ ospf_zebra_init (struct thread_master *master, u_short instance) zclient->interface_address_delete = ospf_interface_address_delete; zclient->interface_link_params = ospf_interface_link_params; - zclient->ipv4_route_add = ospf_zebra_read_ipv4; - zclient->ipv4_route_delete = ospf_zebra_read_ipv4; zclient->redistribute_route_ipv4_add = ospf_zebra_read_ipv4; zclient->redistribute_route_ipv4_del = ospf_zebra_read_ipv4; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index d357e5cc83..b25e8b94da 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -616,7 +616,6 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, switch (command) { case ZEBRA_REDISTRIBUTE_IPV4_ADD: - case ZEBRA_IPV4_ROUTE_ADD: if (PIM_DEBUG_ZEBRA) { char buf[2][INET_ADDRSTRLEN]; zlog_debug("%s: add %s %s/%d " @@ -634,7 +633,6 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, } break; case ZEBRA_REDISTRIBUTE_IPV4_DEL: - case ZEBRA_IPV4_ROUTE_DELETE: if (PIM_DEBUG_ZEBRA) { char buf[2][INET_ADDRSTRLEN]; zlog_debug("%s: delete %s %s/%d " @@ -690,8 +688,6 @@ void pim_zebra_init(char *zebra_sock_path) qpim_zclient_update->interface_down = pim_zebra_if_state_down; qpim_zclient_update->interface_address_add = pim_zebra_if_address_add; qpim_zclient_update->interface_address_delete = pim_zebra_if_address_del; - qpim_zclient_update->ipv4_route_add = redist_read_ipv4_route; - qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route; qpim_zclient_update->redistribute_route_ipv4_add = redist_read_ipv4_route; qpim_zclient_update->redistribute_route_ipv4_del = redist_read_ipv4_route; diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 6ca27d01dd..c312641d44 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -723,8 +723,6 @@ rip_zclient_init (struct thread_master *master) zclient->interface_delete = rip_interface_delete; zclient->interface_address_add = rip_interface_address_add; zclient->interface_address_delete = rip_interface_address_delete; - zclient->ipv4_route_add = rip_zebra_read_ipv4; - zclient->ipv4_route_delete = rip_zebra_read_ipv4; zclient->interface_up = rip_interface_up; zclient->interface_down = rip_interface_down; zclient->redistribute_route_ipv4_add = rip_zebra_read_ipv4; diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index d8667cb68c..1184cd0db6 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -557,12 +557,6 @@ zebra_init (struct thread_master *master) zclient->interface_delete = ripng_interface_delete; zclient->interface_address_add = ripng_interface_address_add; zclient->interface_address_delete = ripng_interface_address_delete; - zclient->ipv4_route_add = NULL; - zclient->ipv4_route_delete = NULL; - zclient->redistribute_route_ipv4_add = NULL; - zclient->redistribute_route_ipv4_del = NULL; - zclient->ipv6_route_add = ripng_zebra_read_ipv6; - zclient->ipv6_route_delete = ripng_zebra_read_ipv6; zclient->redistribute_route_ipv6_add = ripng_zebra_read_ipv6; zclient->redistribute_route_ipv6_del = ripng_zebra_read_ipv6; From fe6c7157bf2babbc552dc45115da586bde1b92e7 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 21 Sep 2016 23:59:57 -0300 Subject: [PATCH 035/136] zebra: check at startup if the kernel supports MPLS Replace all HAVE_MPLS #ifdef's by a run-time check if MPLS is supported by the kernel or not. This way we don't need to create multiple packages for each OS distribution. Signed-off-by: Renato Westphal --- configure.ac | 37 ++++++++++++++----------------------- lib/sockunion.h | 4 ++-- zebra/kernel_socket.c | 8 ++++---- zebra/main.c | 4 +--- zebra/rt.h | 2 +- zebra/rt_socket.c | 6 +++--- zebra/zebra_mpls.c | 10 +++++++++- zebra/zebra_mpls.h | 9 +++++++++ zebra/zebra_mpls_netlink.c | 14 +++++++++++++- zebra/zebra_mpls_null.c | 2 +- zebra/zebra_mpls_openbsd.c | 8 +++++--- zebra/zebra_mpls_vty.c | 22 ++++++++++++++++++---- zebra/zserv.c | 3 +++ zebra/zserv.h | 3 --- 14 files changed, 83 insertions(+), 49 deletions(-) diff --git a/configure.ac b/configure.ac index ac63eaab81..20c9d8b96a 100755 --- a/configure.ac +++ b/configure.ac @@ -309,8 +309,6 @@ AC_ARG_ENABLE(systemd, AS_HELP_STRING([--enable-systemd], [enable Systemd support])) AC_ARG_ENABLE(poll, AS_HELP_STRING([--enable-poll], [enable usage of Poll instead of select])) -AC_ARG_ENABLE(mpls, - AS_HELP_STRING([--enable-mpls], [enable MPLS support - requires compatible kernel])) AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], [enable -Werror (recommended for developers only)])) AC_ARG_ENABLE(cumulus, @@ -367,27 +365,20 @@ dnl MPLS check dnl ---------- MPLS_METHOD="" AC_MSG_CHECKING(whether this OS has MPLS stack) -if test "x${enable_mpls}" = "xyes"; then - case "$host" in - *-linux*) - AC_DEFINE(HAVE_MPLS,,Enable MPLS) - MPLS_METHOD="zebra_mpls_netlink.o" - AC_MSG_RESULT(Linux MPLS) - ;; - *-openbsd*) - AC_DEFINE(HAVE_MPLS,,Enable MPLS) - MPLS_METHOD="zebra_mpls_openbsd.o" - AC_MSG_RESULT(OpenBSD MPLS) - ;; - *) - AC_MSG_RESULT(Unsupported kernel) - MPLS_METHOD="zebra_mpls_null.o" - ;; - esac -else - AC_MSG_RESULT(disabled) - MPLS_METHOD="zebra_mpls_null.o" -fi +case "$host" in + *-linux*) + MPLS_METHOD="zebra_mpls_netlink.o" + AC_MSG_RESULT(Linux MPLS) + ;; + *-openbsd*) + MPLS_METHOD="zebra_mpls_openbsd.o" + AC_MSG_RESULT(OpenBSD MPLS) + ;; + *) + MPLS_METHOD="zebra_mpls_null.o" + AC_MSG_RESULT(Unsupported kernel) + ;; +esac AC_SUBST(MPLS_METHOD) if test "${enable_cumulus}" = "yes" ; then diff --git a/lib/sockunion.h b/lib/sockunion.h index abad43122e..7dbd247dca 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -25,7 +25,7 @@ #include "privs.h" #include "if.h" -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ #include #endif @@ -34,7 +34,7 @@ union sockunion struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ struct sockaddr_mpls smpls; #endif }; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 7952f9e761..f3f0a2777e 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -21,7 +21,7 @@ #include #include -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ #include #endif @@ -1101,7 +1101,7 @@ rtm_write (int message, msg.rtm.rtm_addrs = RTA_DST; msg.rtm.rtm_addrs |= RTA_GATEWAY; msg.rtm.rtm_flags = RTF_UP; -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ msg.rtm.rtm_flags |= RTF_MPATH; msg.rtm.rtm_fmask = RTF_MPLS; #endif @@ -1150,7 +1150,7 @@ rtm_write (int message, else if (message == RTM_ADD) msg.rtm.rtm_flags |= RTF_HOST; -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ if (mpls) { msg.rtm.rtm_addrs |= RTA_SRC; @@ -1185,7 +1185,7 @@ rtm_write (int message, SOCKADDRSET (dest, RTA_DST); SOCKADDRSET (gate, RTA_GATEWAY); SOCKADDRSET (mask, RTA_NETMASK); -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ SOCKADDRSET (mpls, RTA_SRC); #endif diff --git a/zebra/main.c b/zebra/main.c index dd7e9d9ec3..faa6cdb317 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -350,9 +350,6 @@ main (int argc, char **argv) zebra_debug_init (); router_id_cmd_init (); zebra_vty_init (); -#if defined(HAVE_MPLS) - zebra_mpls_vty_init (); -#endif access_list_init (); prefix_list_init (); #if defined (HAVE_RTADV) @@ -367,6 +364,7 @@ main (int argc, char **argv) #endif zebra_mpls_init (); + zebra_mpls_vty_init (); /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/rt.h b/zebra/rt.h index c4a85e6d66..1899ef17da 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -44,6 +44,6 @@ extern int kernel_delete_ipv6 (struct prefix *, struct rib *); extern int kernel_add_lsp (zebra_lsp_t *); extern int kernel_upd_lsp (zebra_lsp_t *); extern int kernel_del_lsp (zebra_lsp_t *); -extern void mpls_kernel_init (void); +extern int mpls_kernel_init (void); #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index a6a1978065..f23f9d5da3 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -21,7 +21,7 @@ */ #include -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ #include #endif @@ -78,7 +78,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { struct sockaddr_in *mask = NULL; struct sockaddr_in sin_dest, sin_mask, sin_gate; -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ struct sockaddr_mpls smpls; #endif union sockunion *smplsp = NULL; @@ -156,7 +156,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) mask = &sin_mask; } -#if defined HAVE_MPLS && defined __OpenBSD__ +#ifdef __OpenBSD__ if (nexthop->nh_label) { memset (&smpls, 0, sizeof (smpls)); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index ccb81dcff5..185cddea64 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -53,6 +53,8 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object") DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE, "MPLS static nexthop object") DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE_IFNAME, "MPLS static nexthop ifname") +int mpls_enabled; + /* Default rtm_table for all clients */ extern struct zebra_t zebrad; @@ -1902,6 +1904,12 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) void zebra_mpls_init (void) { - mpls_kernel_init (); + if (mpls_kernel_init () < 0) + { + zlog_warn ("Disabling MPLS support (no kernel support)"); + return; + } + + mpls_enabled = 1; mpls_processq_init (&zebrad); } diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index becef524fd..9f24689595 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -292,6 +292,12 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf); void zebra_mpls_init (void); +/* + * MPLS VTY. + */ +void +zebra_mpls_vty_init (void); + /* Inline functions. */ /* @@ -364,4 +370,7 @@ mpls_should_lsps_be_processed(struct zebra_vrf *zvrf) return ((zvrf->mpls_flags & MPLS_FLAG_SCHEDULE_LSPS) ? 1 : 0); } +/* Global variables. */ +extern int mpls_enabled; + #endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index 4011b90eea..1f894b33c6 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -77,4 +77,16 @@ kernel_del_lsp (zebra_lsp_t *lsp) return 0; } -void mpls_kernel_init (void) {}; +int +mpls_kernel_init (void) +{ + struct stat st; + + /* + * Check if the MPLS module is loaded in the kernel. + */ + if (stat ("/proc/sys/net/mpls", &st) != 0) + return -1; + + return 0; +}; diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 93405b88fd..7727c84a88 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -5,4 +5,4 @@ int kernel_add_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_upd_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_del_lsp (zebra_lsp_t *lsp) { return 0; } -void mpls_kernel_init (void) {}; +int mpls_kernel_init (void) { return -1; }; diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 43a54adff1..bae1de66bf 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -178,15 +178,15 @@ kernel_del_lsp (zebra_lsp_t *lsp) } #define MAX_RTSOCK_BUF 128 * 1024 -void +int mpls_kernel_init (void) { int rcvbuf, default_rcvbuf; socklen_t optlen; if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { - zlog_warn("kr_init: socket"); - return; + zlog_warn("%s: socket", __func__); + return -1; } /* grow receive buffer, don't wanna miss messages */ @@ -203,4 +203,6 @@ mpls_kernel_init (void) ; /* nothing */ kr_state.rtseq = 1; + + return 0; } diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index e5f6932e87..e7338a10c2 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -21,8 +21,6 @@ #include -#if defined(HAVE_MPLS) - #include "memory.h" #include "if.h" #include "prefix.h" @@ -810,6 +808,18 @@ DEFUN (show_mpls_table_lsp, return CMD_SUCCESS; } +DEFUN (show_mpls_status, + show_mpls_status_cmd, + "show mpls status", + SHOW_STR + "MPLS information\n" + "MPLS status\n") +{ + vty_out (vty, "MPLS support enabled: %s%s", (mpls_enabled) ? "yes" : + "no (mpls kernel extensions not detected)", VTY_NEWLINE); + return CMD_SUCCESS; +} + /* MPLS node for MPLS LSP. */ static struct cmd_node mpls_node = { MPLS_NODE, "", 1 }; @@ -817,6 +827,12 @@ static struct cmd_node mpls_node = { MPLS_NODE, "", 1 }; void zebra_mpls_vty_init (void) { + install_element (VIEW_NODE, &show_mpls_status_cmd); + install_element (ENABLE_NODE, &show_mpls_status_cmd); + + if (! mpls_enabled) + return; + install_node (&mpls_node, zebra_mpls_config); install_element (CONFIG_NODE, &ip_route_label_cmd); @@ -863,5 +879,3 @@ zebra_mpls_vty_init (void) install_element (VIEW_NODE, &show_mpls_table_lsp_cmd); install_element (ENABLE_NODE, &show_mpls_table_lsp_cmd); } - -#endif /* HAVE_MPLS */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 5f0b4d5e20..4cfeead887 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1676,6 +1676,9 @@ zread_mpls_labels (int command, struct zserv *client, u_short length, in_label = stream_getl (s); out_label = stream_getl (s); + if (! mpls_enabled) + return; + if (command == ZEBRA_MPLS_LABELS_ADD) { mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate, diff --git a/zebra/zserv.h b/zebra/zserv.h index ceff6a96ae..ce243dd6ac 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -149,9 +149,6 @@ extern void kernel_terminate (struct zebra_ns *); extern void zebra_route_map_init (void); extern void zebra_snmp_init (void); extern void zebra_vty_init (void); -#if defined(HAVE_MPLS) -extern void zebra_mpls_vty_init (void); -#endif extern int zsend_vrf_add (struct zserv *, struct zebra_vrf *); extern int zsend_vrf_delete (struct zserv *, struct zebra_vrf *); From 598a7bcb8bff2968024238d64050c46432278fe6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 22 Sep 2016 11:42:06 -0300 Subject: [PATCH 036/136] vtysh: relax error condition on build If the number of CLI collisions is smaller than the expected one, there's a good chance that Quagga is being built with one or more daemons disabled. In this case, just print a warning and don't abort the compilation to allow partial builds. Signed-off-by: Renato Westphal --- vtysh/extract.pl.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 924a6696d3..cd19fa754b 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -233,7 +233,9 @@ my $bad_cli_stomps = 108; # please fix your code before submittal if ($cli_stomp != $bad_cli_stomps) { warn "Expected $bad_cli_stomps command line stomps, but got $cli_stomp instead\n"; - exit $cli_stomp; + if ($cli_stomp > $bad_cli_stomps) { + exit $cli_stomp; + } } # Check finaly alive $cmd; From 85eda2c98520a9553bdc05c136618f9d04917e9b Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 16 Sep 2016 21:55:37 +0200 Subject: [PATCH 037/136] Make route flags a 32bit field Signed-off-by: Christian Franke --- bgpd/bgp_zebra.c | 8 ++++---- isisd/isis_zebra.c | 9 +++++---- lib/zclient.c | 6 +++--- lib/zclient.h | 4 ++-- ospf6d/ospf6_zebra.c | 2 +- ospfd/ospf_zebra.c | 10 +++++----- pimd/pim_zebra.c | 2 +- ripd/rip_zebra.c | 2 +- ripngd/ripng_zebra.c | 2 +- zebra/rib.h | 2 +- zebra/zserv.c | 12 ++++++------ 11 files changed, 30 insertions(+), 29 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 15db321557..c3e1b76a3c 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -601,7 +601,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv4 prefix. */ @@ -716,7 +716,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv6 prefix. */ @@ -1199,7 +1199,7 @@ void bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, afi_t afi, safi_t safi) { - int flags; + u_int32_t flags; u_char distance; struct peer *peer; struct bgp_info *mpinfo; @@ -1620,7 +1620,7 @@ bgp_zebra_announce_table (struct bgp *bgp, afi_t afi, safi_t safi) void bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) { - int flags; + u_int32_t flags; struct peer *peer; peer = info->peer; diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 45728ad2c1..f43987bc18 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -257,7 +257,8 @@ static void isis_zebra_route_add_ipv4 (struct prefix *prefix, struct isis_route_info *route_info) { - u_char message, flags; + u_char message; + u_int32_t flags; int psize; struct stream *stream; struct isis_nexthop *nexthop; @@ -285,7 +286,7 @@ isis_zebra_route_add_ipv4 (struct prefix *prefix, /* instance */ stream_putw (stream, 0); /* flags */ - stream_putc (stream, flags); + stream_putl (stream, flags); /* message */ stream_putc (stream, message); /* SAFI */ @@ -566,7 +567,7 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.type = stream_getc (stream); api.instance = stream_getw (stream); - api.flags = stream_getc (stream); + api.flags = stream_getl (stream); api.message = stream_getc (stream); p.family = AF_INET; @@ -623,7 +624,7 @@ isis_zebra_read_ipv6 (int command, struct zclient *zclient, ifindex = 0; api.type = stream_getc(stream); - api.flags = stream_getc(stream); + api.flags = stream_getl(stream); api.message = stream_getc(stream); p.family = AF_INET6; diff --git a/lib/zclient.c b/lib/zclient.c index 5193a282a6..c70ff90a11 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -733,7 +733,7 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putc (s, api->flags); + stream_putl (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); @@ -801,7 +801,7 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putc (s, api->flags); + stream_putl (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); @@ -867,7 +867,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putc (s, api->flags); + stream_putl (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); diff --git a/lib/zclient.h b/lib/zclient.h index 231b4e9b4f..4edbd7636e 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -134,7 +134,7 @@ struct zapi_ipv4 u_char type; u_short instance; - u_char flags; + u_int32_t flags; u_char message; @@ -222,7 +222,7 @@ struct zapi_ipv6 u_char type; u_short instance; - u_char flags; + u_int32_t flags; u_char message; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 6dee1424a6..b4381cf816 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -229,7 +229,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv6 prefix. */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 8752e83ed5..062b4d601e 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -355,7 +355,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; - u_char flags; + u_int32_t flags; int psize; struct stream *s; struct ospf_path *path; @@ -393,7 +393,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putw (s, ospf->instance); - stream_putc (s, flags); + stream_putl (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); @@ -492,7 +492,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; - u_char flags; + u_int32_t flags; int psize; struct stream *s; struct ospf_path *path; @@ -516,7 +516,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) zclient_create_header (s, ZEBRA_IPV4_ROUTE_DELETE, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putw (s, ospf->instance); - stream_putc (s, flags); + stream_putl (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); @@ -1064,7 +1064,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv4 prefix. */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index b25e8b94da..1a8d5f22bc 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -552,7 +552,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc(s); api.instance = stream_getw (s); - api.flags = stream_getc(s); + api.flags = stream_getl(s); api.message = stream_getc(s); /* IPv4 prefix length. */ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index c312641d44..627755e1c2 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -147,7 +147,7 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv4 prefix. */ diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 1184cd0db6..803fd74157 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -143,7 +143,7 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv6 prefix. */ diff --git a/zebra/rib.h b/zebra/rib.h index 0f7f70ada7..285166f067 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -79,7 +79,7 @@ struct rib * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed * to clients via Zserv */ - u_char flags; + u_int32_t flags; /* RIB internal status */ u_char status; diff --git a/zebra/zserv.c b/zebra/zserv.c index 4cfeead887..3b2095d656 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -628,7 +628,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, /* Put type and nexthop. */ stream_putc (s, rib->type); stream_putw (s, rib->instance); - stream_putc (s, rib->flags); + stream_putl (s, rib->flags); /* marker for message flags field */ messmark = stream_get_endp (s); @@ -1055,7 +1055,7 @@ zread_ipv4_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getc (s); + rib->flags = stream_getl (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1159,7 +1159,7 @@ zread_ipv4_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); api.safi = stream_getw (s); @@ -1265,7 +1265,7 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getc (s); + rib->flags = stream_getl (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1392,7 +1392,7 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getc (s); + rib->flags = stream_getl (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1506,7 +1506,7 @@ zread_ipv6_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); api.safi = stream_getw (s); From 72f3a8fb09433ee1e4d079522cd70999bb3b8e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Fri, 15 Jan 2016 17:36:30 +0200 Subject: [PATCH 038/136] zebra: use link scope for interface routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In linux, 'scope' is a hint of distance of the IP. And this is evident from the fact that only lower scope can be used as recursive via lookup result. This changes all interface routes scope to link so kernel will allow regular routes to use it as via. Then we do not need to use the 'onlink' attribute. Signed-off-by: Timo Teräs --- zebra/rt_netlink.c | 5 ++++- zebra/zebra_rib.c | 23 ++++++----------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 95c8892979..79f20259a1 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2428,7 +2428,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, req.r.rtm_family = family; req.r.rtm_dst_len = p->prefixlen; req.r.rtm_protocol = RTPROT_ZEBRA; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_scope = RT_SCOPE_LINK; if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) discard = 1; @@ -2508,6 +2508,9 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, if (cmd == RTM_DELROUTE && !CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) continue; + if (nexthop->type != NEXTHOP_TYPE_IFINDEX) + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + nexthop_num++; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f57c0b5d67..775619ac2d 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -483,27 +483,16 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, { resolved_hop->type = newhop->type; resolved_hop->gate.ipv4 = newhop->gate.ipv4; - - if (newhop->ifindex) - { - resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - resolved_hop->ifindex = newhop->ifindex; - if (newhop->flags & NEXTHOP_FLAG_ONLINK) - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - } + resolved_hop->ifindex = newhop->ifindex; } - /* If the resolving route is an interface route, - * it means the gateway we are looking up is connected - * to that interface. (The actual network is _not_ onlink). - * Therefore, the resolved route should have the original - * gateway as nexthop as it is directly connected. - * - * On Linux, we have to set the onlink netlink flag because - * otherwise, the kernel won't accept the route. */ + /* If the resolving route is an interface route, it + * means the gateway we are looking up is connected + * to that interface. Therefore, the resolved route + * should have the original gateway as nexthop as it + * is directly connected. */ if (newhop->type == NEXTHOP_TYPE_IFINDEX) { - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; resolved_hop->gate.ipv4 = nexthop->gate.ipv4; resolved_hop->ifindex = newhop->ifindex; From 7569ae8bb7390224e16ee416cc0745d427a29818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Fri, 15 Jan 2016 17:36:31 +0200 Subject: [PATCH 039/136] zebra: support FIB override routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FIB override routes are for routing protocols that establish shortcut routes, or establish point-to-point routes that should not be redistributed. Namely this is useful NHRP daemon to come. Zebra is extended to select two entries from RIB the "best" entry from routing protocols, and the FIB entry to install to kernel. FIB override routes are never selected as best entry, and thus are never adverticed to other routing daemons. The best FIB override, or if it does not exist the otherwise best RIB is selected as FIB entry to be installed. Signed-off-by: Timo Teräs Acked-by: Donald Sharp [CF: Massage to fit cumulus tree] Signed-off-by: Christian Franke --- lib/zebra.h | 1 + zebra/rib.h | 1 + zebra/zebra_fpm.c | 2 +- zebra/zebra_rib.c | 326 +++++++++++++++++++++++++------------------ zebra/zebra_static.c | 16 ++- zebra/zebra_vty.c | 7 +- zebra/zserv.c | 5 +- 7 files changed, 215 insertions(+), 143 deletions(-) diff --git a/lib/zebra.h b/lib/zebra.h index da069d1dfa..5bb7dfaf45 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -471,6 +471,7 @@ extern const char *zserv_command_string (unsigned int command); #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_REJECT 0x80 #define ZEBRA_FLAG_SCOPE_LINK 0x100 +#define ZEBRA_FLAG_FIB_OVERRIDE 0x200 #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ diff --git a/zebra/rib.h b/zebra/rib.h index 285166f067..96301a8af4 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -87,6 +87,7 @@ struct rib /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */ #define RIB_ENTRY_NEXTHOPS_CHANGED 0x2 #define RIB_ENTRY_CHANGED 0x4 +#define RIB_ENTRY_SELECTED_FIB 0x8 /* Nexthop information. */ u_char nexthop_num; diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 220fddaf67..bc9d22e001 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -892,7 +892,7 @@ zfpm_route_for_update (rib_dest_t *dest) RIB_DEST_FOREACH_ROUTE (dest, rib) { - if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) continue; return rib; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 775619ac2d..6bd5417ac9 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -432,7 +432,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, /* if the next hop is imported from another table, skip it */ if (match->type == ZEBRA_ROUTE_TABLE) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -628,7 +628,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -793,7 +793,7 @@ rib_match (afi_t afi, safi_t safi, vrf_id_t vrf_id, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -929,7 +929,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -949,7 +949,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) /* * This clone function, unlike its original rib_lookup_ipv4(), checks * if specified IPv4 route record (prefix/mask -> gate) exists in - * the whole RIB and has ZEBRA_FLAG_SELECTED set. + * the whole RIB and has RIB_ENTRY_SELECTED_FIB set. * * Return values: * -1: error @@ -989,7 +989,7 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -1299,14 +1299,19 @@ rib_uninstall (struct route_node *rn, struct rib *rib) { rib_table_info_t *info = rn->table->info; - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) { if (info->safi == SAFI_UNICAST) zfpm_trigger_update (rn, "rib_uninstall"); - redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); + UNSET_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB); + } + + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + { + redistribute_delete (&rn->p, rib); UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED); } } @@ -1374,76 +1379,70 @@ rib_gc_dest (struct route_node *rn) } static void -rib_process_add_route (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *select) +rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *new) { char buf[INET6_ADDRSTRLEN]; - int installed = 1; zfpm_trigger_update (rn, "new route selected"); /* Update real nexthop. This may actually determine if nexthop is active or not. */ - if (!nexthop_active_update (rn, select, 1)) + if (!nexthop_active_update (rn, new, 1)) { - UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); return; } - SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + SET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_debug ("%u:%s/%d: Adding route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type); } - if (!RIB_SYSTEM_ROUTE (select)) + if (!RIB_SYSTEM_ROUTE (new)) { - if (rib_install_kernel (rn, select, 0)) + if (rib_install_kernel (rn, new, 0)) { - installed = 0; inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_warn ("%u:%s/%d: Route install failed", zvrf->vrf_id, buf, rn->p.prefixlen); } } - /* Update for redistribution. */ - if (installed) - redistribute_update (&rn->p, select, NULL); - UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); } static void -rib_process_del_route (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *fib) +rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *old) { char buf[INET6_ADDRSTRLEN]; zfpm_trigger_update (rn, "removing existing route"); - /* Withdraw redistribute and uninstall from kernel. */ + /* Uninstall from kernel. */ if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, fib, fib->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, old, old->type); } - redistribute_delete(&rn->p, fib); - if (!RIB_SYSTEM_ROUTE (fib)) - rib_uninstall_kernel (rn, fib); + if (!RIB_SYSTEM_ROUTE (old)) + rib_uninstall_kernel (rn, old); - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); + UNSET_FLAG (old->status, RIB_ENTRY_SELECTED_FIB); /* Update nexthop for route, reset changed flag. */ - nexthop_active_update (rn, fib, 1); - UNSET_FLAG(fib->status, RIB_ENTRY_CHANGED); + nexthop_active_update (rn, old, 1); + UNSET_FLAG(old->status, RIB_ENTRY_CHANGED); } static void -rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *select, struct rib *fib) +rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *old, struct rib *new) { char buf[INET6_ADDRSTRLEN]; struct nexthop *nexthop = NULL, *tnexthop; @@ -1458,13 +1457,13 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, * We have to install or update if a new route has been selected or * something has changed. */ - if (select != fib || - CHECK_FLAG (select->status, RIB_ENTRY_CHANGED)) + if (new != old || + CHECK_FLAG (new->status, RIB_ENTRY_CHANGED)) { zfpm_trigger_update (rn, "updating existing route"); /* Update the nexthop; we could determine here that nexthop is inactive. */ - if (nexthop_active_update (rn, select, 1)) + if (nexthop_active_update (rn, new, 1)) nh_active = 1; /* If nexthop is active, install the selected route, if appropriate. If @@ -1475,18 +1474,18 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, { if (IS_ZEBRA_DEBUG_RIB) { - if (select != fib) + if (new != old) zlog_debug ("%u:%s/%d: Updating route rn %p, rib %p (type %d) " "old %p (type %d)", zvrf->vrf_id, buf, rn->p.prefixlen, - rn, select, select->type, fib, fib->type); + rn, new, new->type, old, old->type); else zlog_debug ("%u:%s/%d: Updating route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type); } /* Non-system route should be installed. */ - if (!RIB_SYSTEM_ROUTE (select)) + if (!RIB_SYSTEM_ROUTE (new)) { - if (rib_install_kernel (rn, select, 1)) + if (rib_install_kernel (rn, new, 1)) { installed = 0; inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); @@ -1496,26 +1495,23 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, } /* If install succeeded or system route, cleanup flags for prior route. */ - if (installed && select != fib) + if (installed && new != old) { - if (RIB_SYSTEM_ROUTE(select)) + if (RIB_SYSTEM_ROUTE(new)) { - if (!RIB_SYSTEM_ROUTE (fib)) - rib_uninstall_kernel (rn, fib); + if (!RIB_SYSTEM_ROUTE (old)) + rib_uninstall_kernel (rn, old); } else { - for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = old->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } /* Update for redistribution. */ if (installed) - { - SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); - redistribute_update (&rn->p, select, (select == fib) ? NULL : fib); - } + SET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); } /* @@ -1524,28 +1520,22 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, */ if (!nh_active || !installed) { - struct rib *del; - if (IS_ZEBRA_DEBUG_RIB) { - if (select != fib) + if (new != old) zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d) " "old %p (type %d) - %s", zvrf->vrf_id, buf, rn->p.prefixlen, - rn, select, select->type, fib, fib->type, + rn, new, new->type, old, old->type, nh_active ? "install failed" : "nexthop inactive"); else zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d) - %s", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type, + zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type, nh_active ? "install failed" : "nexthop inactive"); } - del = (select == fib) ? select : fib; - - redistribute_delete(&rn->p, del); - - if (!RIB_SYSTEM_ROUTE (del)) - rib_uninstall_kernel (rn, del); - UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + if (!RIB_SYSTEM_ROUTE (old)) + rib_uninstall_kernel (rn, old); + UNSET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); } } else @@ -1556,33 +1546,33 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, * netlink reporting interface up before IPv4 or IPv6 protocol is ready * to add routes. */ - if (!RIB_SYSTEM_ROUTE (select)) + if (!RIB_SYSTEM_ROUTE (new)) { int in_fib = 0; - for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing)) + for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { in_fib = 1; break; } if (!in_fib) - rib_install_kernel (rn, select, 0); + rib_install_kernel (rn, new, 0); } } /* Update prior route. */ - if (select != fib) + if (new != old) { - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); + UNSET_FLAG (old->status, RIB_ENTRY_SELECTED_FIB); /* Set real nexthop. */ - nexthop_active_update (rn, fib, 1); - UNSET_FLAG(fib->status, RIB_ENTRY_CHANGED); + nexthop_active_update (rn, old, 1); + UNSET_FLAG(old->status, RIB_ENTRY_CHANGED); } /* Clear changed flag. */ - UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); } /* Check if 'alternate' RIB entry is better than 'current'. */ @@ -1633,33 +1623,32 @@ rib_process (struct route_node *rn) { struct rib *rib; struct rib *next; - struct rib *fib = NULL; - struct rib *select = NULL; - struct rib *del = NULL; + struct rib *old_selected = NULL; + struct rib *new_selected = NULL; + struct rib *old_fib = NULL; + struct rib *new_fib = NULL; struct rib *best = NULL; char buf[INET6_ADDRSTRLEN]; rib_dest_t *dest; struct zebra_vrf *zvrf = NULL; vrf_id_t vrf_id = VRF_UNKNOWN; - rib_table_info_t *info; assert (rn); - info = rn->table->info; - + dest = rib_dest_from_rnode (rn); if (dest) { zvrf = rib_dest_vrf (dest); vrf_id = zvrf->vrf_id; } - + if (IS_ZEBRA_DEBUG_RIB) inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u:%s/%d: Processing rn %p", vrf_id, buf, rn->p.prefixlen, rn); - RNODE_FOREACH_RIB_SAFE (rn, rib, next) + RNODE_FOREACH_RIB (rn, rib) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u:%s/%d: Examine rib %p (type %d) status %x flags %x " @@ -1669,31 +1658,23 @@ rib_process (struct route_node *rn) UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); - /* Currently installed rib. */ + /* Currently selected rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { - assert (fib == NULL); - fib = rib; + assert (old_selected == NULL); + old_selected = rib; + } + /* Currently in fib */ + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + { + assert (old_fib == NULL); + old_fib = rib; } - /* Unlock removed routes, so they'll be freed, bar the FIB entry, - * which we need to do do further work with below. - */ + /* Skip deleted entries from selection */ if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) - { - if (rib != fib) - { - if (IS_ZEBRA_DEBUG_RIB) - rnode_debug (rn, vrf_id, "rn %p, removing rib %p", - (void *)rn, (void *)rib); - rib_unlink (rn, rib); - } - else - del = rib; - - continue; - } - + continue; + /* Skip unreachable nexthop. */ /* This first call to nexthop_active_update is merely to determine if * there's any change to nexthops associated with this RIB entry. Now, @@ -1710,9 +1691,15 @@ rib_process (struct route_node *rn) { if (rib->type == ZEBRA_ROUTE_TABLE) { + /* XXX: HERE BE DRAGONS!!!!! + * In all honesty, I have not yet figured out what this part + * does or why the RIB_ENTRY_CHANGED test above is correct + * or why we need to delete a route here, and also not whether + * this concerns both selected and fib route, or only selected + * or only fib */ /* This entry was denied by the 'ip protocol table' route-map, we * need to delete it */ - if (rib != fib) + if (rib != old_selected) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: %s/%d: imported via import-table but denied " @@ -1721,15 +1708,12 @@ rib_process (struct route_node *rn) rib_unlink (rn, rib); } else - del = rib; + SET_FLAG (rib->status, RIB_ENTRY_REMOVED); } continue; } - if (info->safi == SAFI_MULTICAST) - continue; - /* Infinite distance. */ if (rib->distance == DISTANCE_INFINITY) { @@ -1737,33 +1721,101 @@ rib_process (struct route_node *rn) continue; } - best = rib_choose_best(select, rib); - if (select && best != select) - UNSET_FLAG (select->status, RIB_ENTRY_CHANGED); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) + { + best = rib_choose_best(new_fib, rib); + if (new_fib && best != new_fib) + UNSET_FLAG (new_fib->status, RIB_ENTRY_CHANGED); + new_fib = best; + } + else + { + best = rib_choose_best(new_selected, rib); + if (new_selected && best != new_selected) + UNSET_FLAG (new_selected->status, RIB_ENTRY_CHANGED); + new_selected = best; + } if (best != rib) UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED); - select = best; - } /* RNODE_FOREACH_RIB_SAFE */ + } /* RNODE_FOREACH_RIB */ + + /* If no FIB override route, use the selected route also for FIB */ + if (new_fib == NULL) + new_fib = new_selected; /* After the cycle is finished, the following pointers will be set: - * select --- the winner RIB entry, if any was found, otherwise NULL - * fib --- the SELECTED RIB entry, if any, otherwise NULL - * del --- equal to fib, if fib is queued for deletion, NULL otherwise - * rib --- NULL + * old_selected --- RIB entry currently having SELECTED + * new_selected --- RIB entry that is newly SELECTED + * old_fib --- RIB entry currently in kernel FIB + * new_fib --- RIB entry that is newly to be in kernel FIB + * + * new_selected will get SELECTED flag, and is going to be redistributed + * the zclients. new_fib (which can be new_selected) will be installed in kernel. */ - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug ("%u:%s/%d: After processing: select %p fib %p del %p", - vrf_id, buf, rn->p.prefixlen, select, fib, del); - /* Same RIB entry is selected. Update FIB and finish. */ - if (select && select == fib) - rib_process_update_route (zvrf, rn, select, select); - else if (select && fib) - rib_process_update_route (zvrf, rn, select, fib); - else if (select) - rib_process_add_route (zvrf, rn, select); - else if (fib) - rib_process_del_route (zvrf, rn, fib); + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + { + zlog_debug ("%u:%s/%d: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + vrf_id, buf, rn->p.prefixlen, + (void *)old_selected, + (void *)new_selected, + (void *)old_fib, + (void *)new_fib); + } + + /* Buffer RIB_ENTRY_CHANGED here, because it will get cleared if + * fib == selected */ + bool selected_changed = new_selected && CHECK_FLAG(new_selected->status, + RIB_ENTRY_CHANGED); + + /* Update fib according to selection results */ + if (new_fib && old_fib) + rib_process_update_fib (zvrf, rn, old_fib, new_fib); + else if (new_fib) + rib_process_add_fib (zvrf, rn, new_fib); + else if (old_fib) + rib_process_del_fib (zvrf, rn, old_fib); + + /* Redistribute SELECTED entry */ + if (old_selected != new_selected || selected_changed) + { + struct nexthop *nexthop, *tnexthop; + int recursing; + + /* Check if we have a FIB route for the destination, otherwise, + * don't redistribute it */ + for (ALL_NEXTHOPS_RO(new_fib ? new_fib->nexthop : NULL, nexthop, + tnexthop, recursing)) + { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + { + break; + } + } + if (!nexthop) + new_selected = NULL; + + if (new_selected && new_selected != new_fib) + { + nexthop_active_update(rn, new_selected, 1); + UNSET_FLAG(new_selected->status, RIB_ENTRY_CHANGED); + } + + if (old_selected) + { + if (!new_selected) + redistribute_delete(&rn->p, old_selected); + if (old_selected != new_selected) + UNSET_FLAG (old_selected->flags, ZEBRA_FLAG_SELECTED); + } + + if (new_selected) + { + /* Install new or replace existing redistributed entry */ + SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED); + redistribute_update (&rn->p, new_selected, old_selected); + } + } #if 0 if (select && select == fib) @@ -1940,12 +1992,18 @@ rib_process (struct route_node *rn) } #endif - /* FIB route was removed, should be deleted */ - if (del) + /* Remove all RIB entries queued for removal */ + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - if (IS_ZEBRA_DEBUG_RIB) - rnode_debug (rn, vrf_id, "Deleting fib %p, rn %p", (void *)del, (void *)rn); - rib_unlink (rn, del); + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + { + if (IS_ZEBRA_DEBUG_RIB) + { + rnode_debug (rn, vrf_id, "rn %p, removing rib %p", + (void *)rn, (void *)rib); + } + rib_unlink(rn, rib); + } } /* @@ -2512,7 +2570,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p, vrf_id_t vrf_id) */ RNODE_FOREACH_RIB (rn, rib) { - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) && + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) && ! RIB_SYSTEM_ROUTE (rib)) { changed = 1; @@ -2658,7 +2716,7 @@ rib_delete (afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) fib = rib; if (rib->type != type) @@ -2719,7 +2777,7 @@ rib_delete (afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); + UNSET_FLAG (fib->status, RIB_ENTRY_SELECTED_FIB); } else { @@ -3096,7 +3154,7 @@ rib_close_table (struct route_table *table) for (rn = route_top (table); rn; rn = route_next (rn)) RNODE_FOREACH_RIB (rn, rib) { - if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) continue; if (info->safi == SAFI_UNICAST) diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index f2362e6871..1dc54e171c 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -320,13 +320,21 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ /* If there are other active nexthops, do an update. */ if (rib->nexthop_active_num > 1) { - rib_install_kernel (rn, rib, 1); - redistribute_update (&rn->p, rib, NULL); + /* Update route in kernel if it's in fib */ + if (CHECK_FLAG(rib->status, RIB_ENTRY_SELECTED_FIB)) + rib_install_kernel (rn, rib, 1); + /* Update redistribution if it's selected */ + if (CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED)) + redistribute_update (&rn->p, rib, NULL); } else { - redistribute_delete (&rn->p, rib); - rib_uninstall_kernel (rn, rib); + /* Remove from redistribute if selected route becomes inactive */ + if (CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED)) + redistribute_delete (&rn->p, rib); + /* Remove from kernel if fib route becomes inactive */ + if (CHECK_FLAG(rib->status, RIB_ENTRY_SELECTED_FIB)) + rib_uninstall_kernel (rn, rib); } } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index e76e4ab8fa..41e1293e7c 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2023,6 +2023,10 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) } if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) vty_out (vty, ", best"); + else if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + vty_out (vty, ", fib"); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) + vty_out (vty, ", fib-override"); if (rib->refcnt) vty_out (vty, ", refcnt %ld", rib->refcnt); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) @@ -2299,7 +2303,8 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, len += vty_out (vty, "[%d]", rib->instance); len += vty_out (vty, "%c%c %s", CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) - ? '>' : ' ', + ? '>' : CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) + ? '!' : ' ', CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', prefix2str (&rn->p, buf, sizeof buf)); diff --git a/zebra/zserv.c b/zebra/zserv.c index 3b2095d656..969d860c20 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -668,8 +668,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, break; } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) - || nexthop_has_fib_child(nexthop)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -927,7 +926,7 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr, struc * are looking up. Therefore, we will just iterate over the top * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) num += zsend_write_nexthop (s, nexthop); stream_putc_at (s, nump, num); /* store nexthop_num */ From 510dc06033392675bddef7c04652d23c539c3577 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Sep 2016 12:11:00 -0400 Subject: [PATCH 040/136] Revert "zebra: support FIB override routes" This reverts commit 7569ae8bb7390224e16ee416cc0745d427a29818. --- lib/zebra.h | 1 - zebra/rib.h | 1 - zebra/zebra_fpm.c | 2 +- zebra/zebra_rib.c | 324 ++++++++++++++++++------------------------- zebra/zebra_static.c | 16 +-- zebra/zebra_vty.c | 7 +- zebra/zserv.c | 5 +- 7 files changed, 142 insertions(+), 214 deletions(-) diff --git a/lib/zebra.h b/lib/zebra.h index 5bb7dfaf45..da069d1dfa 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -471,7 +471,6 @@ extern const char *zserv_command_string (unsigned int command); #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_REJECT 0x80 #define ZEBRA_FLAG_SCOPE_LINK 0x100 -#define ZEBRA_FLAG_FIB_OVERRIDE 0x200 #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ diff --git a/zebra/rib.h b/zebra/rib.h index 96301a8af4..285166f067 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -87,7 +87,6 @@ struct rib /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */ #define RIB_ENTRY_NEXTHOPS_CHANGED 0x2 #define RIB_ENTRY_CHANGED 0x4 -#define RIB_ENTRY_SELECTED_FIB 0x8 /* Nexthop information. */ u_char nexthop_num; diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index bc9d22e001..220fddaf67 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -892,7 +892,7 @@ zfpm_route_for_update (rib_dest_t *dest) RIB_DEST_FOREACH_ROUTE (dest, rib) { - if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) continue; return rib; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6bd5417ac9..775619ac2d 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -432,7 +432,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, /* if the next hop is imported from another table, skip it */ if (match->type == ZEBRA_ROUTE_TABLE) continue; - if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; } @@ -628,7 +628,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; } @@ -793,7 +793,7 @@ rib_match (afi_t afi, safi_t safi, vrf_id_t vrf_id, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; } @@ -929,7 +929,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; } @@ -949,7 +949,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) /* * This clone function, unlike its original rib_lookup_ipv4(), checks * if specified IPv4 route record (prefix/mask -> gate) exists in - * the whole RIB and has RIB_ENTRY_SELECTED_FIB set. + * the whole RIB and has ZEBRA_FLAG_SELECTED set. * * Return values: * -1: error @@ -989,7 +989,7 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; } @@ -1299,19 +1299,14 @@ rib_uninstall (struct route_node *rn, struct rib *rib) { rib_table_info_t *info = rn->table->info; - if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { if (info->safi == SAFI_UNICAST) zfpm_trigger_update (rn, "rib_uninstall"); + redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); - UNSET_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB); - } - - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) - { - redistribute_delete (&rn->p, rib); UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED); } } @@ -1379,70 +1374,76 @@ rib_gc_dest (struct route_node *rn) } static void -rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *new) +rib_process_add_route (struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *select) { char buf[INET6_ADDRSTRLEN]; + int installed = 1; zfpm_trigger_update (rn, "new route selected"); /* Update real nexthop. This may actually determine if nexthop is active or not. */ - if (!nexthop_active_update (rn, new, 1)) + if (!nexthop_active_update (rn, select, 1)) { - UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); return; } - SET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); + SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_debug ("%u:%s/%d: Adding route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type); } - if (!RIB_SYSTEM_ROUTE (new)) + if (!RIB_SYSTEM_ROUTE (select)) { - if (rib_install_kernel (rn, new, 0)) + if (rib_install_kernel (rn, select, 0)) { + installed = 0; inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_warn ("%u:%s/%d: Route install failed", zvrf->vrf_id, buf, rn->p.prefixlen); } } - UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); + /* Update for redistribution. */ + if (installed) + redistribute_update (&rn->p, select, NULL); + UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); } static void -rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *old) +rib_process_del_route (struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *fib) { char buf[INET6_ADDRSTRLEN]; zfpm_trigger_update (rn, "removing existing route"); - /* Uninstall from kernel. */ + /* Withdraw redistribute and uninstall from kernel. */ if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, old, old->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, fib, fib->type); } - if (!RIB_SYSTEM_ROUTE (old)) - rib_uninstall_kernel (rn, old); + redistribute_delete(&rn->p, fib); + if (!RIB_SYSTEM_ROUTE (fib)) + rib_uninstall_kernel (rn, fib); - UNSET_FLAG (old->status, RIB_ENTRY_SELECTED_FIB); + UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); /* Update nexthop for route, reset changed flag. */ - nexthop_active_update (rn, old, 1); - UNSET_FLAG(old->status, RIB_ENTRY_CHANGED); + nexthop_active_update (rn, fib, 1); + UNSET_FLAG(fib->status, RIB_ENTRY_CHANGED); } static void -rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *old, struct rib *new) +rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *select, struct rib *fib) { char buf[INET6_ADDRSTRLEN]; struct nexthop *nexthop = NULL, *tnexthop; @@ -1457,13 +1458,13 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, * We have to install or update if a new route has been selected or * something has changed. */ - if (new != old || - CHECK_FLAG (new->status, RIB_ENTRY_CHANGED)) + if (select != fib || + CHECK_FLAG (select->status, RIB_ENTRY_CHANGED)) { zfpm_trigger_update (rn, "updating existing route"); /* Update the nexthop; we could determine here that nexthop is inactive. */ - if (nexthop_active_update (rn, new, 1)) + if (nexthop_active_update (rn, select, 1)) nh_active = 1; /* If nexthop is active, install the selected route, if appropriate. If @@ -1474,18 +1475,18 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, { if (IS_ZEBRA_DEBUG_RIB) { - if (new != old) + if (select != fib) zlog_debug ("%u:%s/%d: Updating route rn %p, rib %p (type %d) " "old %p (type %d)", zvrf->vrf_id, buf, rn->p.prefixlen, - rn, new, new->type, old, old->type); + rn, select, select->type, fib, fib->type); else zlog_debug ("%u:%s/%d: Updating route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type); } /* Non-system route should be installed. */ - if (!RIB_SYSTEM_ROUTE (new)) + if (!RIB_SYSTEM_ROUTE (select)) { - if (rib_install_kernel (rn, new, 1)) + if (rib_install_kernel (rn, select, 1)) { installed = 0; inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); @@ -1495,23 +1496,26 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, } /* If install succeeded or system route, cleanup flags for prior route. */ - if (installed && new != old) + if (installed && select != fib) { - if (RIB_SYSTEM_ROUTE(new)) + if (RIB_SYSTEM_ROUTE(select)) { - if (!RIB_SYSTEM_ROUTE (old)) - rib_uninstall_kernel (rn, old); + if (!RIB_SYSTEM_ROUTE (fib)) + rib_uninstall_kernel (rn, fib); } else { - for (nexthop = old->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } /* Update for redistribution. */ if (installed) - SET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); + { + SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + redistribute_update (&rn->p, select, (select == fib) ? NULL : fib); + } } /* @@ -1520,22 +1524,28 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, */ if (!nh_active || !installed) { + struct rib *del; + if (IS_ZEBRA_DEBUG_RIB) { - if (new != old) + if (select != fib) zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d) " "old %p (type %d) - %s", zvrf->vrf_id, buf, rn->p.prefixlen, - rn, new, new->type, old, old->type, + rn, select, select->type, fib, fib->type, nh_active ? "install failed" : "nexthop inactive"); else zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d) - %s", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type, + zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type, nh_active ? "install failed" : "nexthop inactive"); } - if (!RIB_SYSTEM_ROUTE (old)) - rib_uninstall_kernel (rn, old); - UNSET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); + del = (select == fib) ? select : fib; + + redistribute_delete(&rn->p, del); + + if (!RIB_SYSTEM_ROUTE (del)) + rib_uninstall_kernel (rn, del); + UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); } } else @@ -1546,33 +1556,33 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, * netlink reporting interface up before IPv4 or IPv6 protocol is ready * to add routes. */ - if (!RIB_SYSTEM_ROUTE (new)) + if (!RIB_SYSTEM_ROUTE (select)) { int in_fib = 0; - for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing)) + for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { in_fib = 1; break; } if (!in_fib) - rib_install_kernel (rn, new, 0); + rib_install_kernel (rn, select, 0); } } /* Update prior route. */ - if (new != old) + if (select != fib) { - UNSET_FLAG (old->status, RIB_ENTRY_SELECTED_FIB); + UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); /* Set real nexthop. */ - nexthop_active_update (rn, old, 1); - UNSET_FLAG(old->status, RIB_ENTRY_CHANGED); + nexthop_active_update (rn, fib, 1); + UNSET_FLAG(fib->status, RIB_ENTRY_CHANGED); } /* Clear changed flag. */ - UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); } /* Check if 'alternate' RIB entry is better than 'current'. */ @@ -1623,32 +1633,33 @@ rib_process (struct route_node *rn) { struct rib *rib; struct rib *next; - struct rib *old_selected = NULL; - struct rib *new_selected = NULL; - struct rib *old_fib = NULL; - struct rib *new_fib = NULL; + struct rib *fib = NULL; + struct rib *select = NULL; + struct rib *del = NULL; struct rib *best = NULL; char buf[INET6_ADDRSTRLEN]; rib_dest_t *dest; struct zebra_vrf *zvrf = NULL; vrf_id_t vrf_id = VRF_UNKNOWN; + rib_table_info_t *info; assert (rn); - + info = rn->table->info; + dest = rib_dest_from_rnode (rn); if (dest) { zvrf = rib_dest_vrf (dest); vrf_id = zvrf->vrf_id; } - + if (IS_ZEBRA_DEBUG_RIB) inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u:%s/%d: Processing rn %p", vrf_id, buf, rn->p.prefixlen, rn); - RNODE_FOREACH_RIB (rn, rib) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u:%s/%d: Examine rib %p (type %d) status %x flags %x " @@ -1658,23 +1669,31 @@ rib_process (struct route_node *rn) UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); - /* Currently selected rib. */ + /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { - assert (old_selected == NULL); - old_selected = rib; - } - /* Currently in fib */ - if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) - { - assert (old_fib == NULL); - old_fib = rib; + assert (fib == NULL); + fib = rib; } - /* Skip deleted entries from selection */ + /* Unlock removed routes, so they'll be freed, bar the FIB entry, + * which we need to do do further work with below. + */ if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) - continue; - + { + if (rib != fib) + { + if (IS_ZEBRA_DEBUG_RIB) + rnode_debug (rn, vrf_id, "rn %p, removing rib %p", + (void *)rn, (void *)rib); + rib_unlink (rn, rib); + } + else + del = rib; + + continue; + } + /* Skip unreachable nexthop. */ /* This first call to nexthop_active_update is merely to determine if * there's any change to nexthops associated with this RIB entry. Now, @@ -1691,15 +1710,9 @@ rib_process (struct route_node *rn) { if (rib->type == ZEBRA_ROUTE_TABLE) { - /* XXX: HERE BE DRAGONS!!!!! - * In all honesty, I have not yet figured out what this part - * does or why the RIB_ENTRY_CHANGED test above is correct - * or why we need to delete a route here, and also not whether - * this concerns both selected and fib route, or only selected - * or only fib */ /* This entry was denied by the 'ip protocol table' route-map, we * need to delete it */ - if (rib != old_selected) + if (rib != fib) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: %s/%d: imported via import-table but denied " @@ -1708,12 +1721,15 @@ rib_process (struct route_node *rn) rib_unlink (rn, rib); } else - SET_FLAG (rib->status, RIB_ENTRY_REMOVED); + del = rib; } continue; } + if (info->safi == SAFI_MULTICAST) + continue; + /* Infinite distance. */ if (rib->distance == DISTANCE_INFINITY) { @@ -1721,101 +1737,33 @@ rib_process (struct route_node *rn) continue; } - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) - { - best = rib_choose_best(new_fib, rib); - if (new_fib && best != new_fib) - UNSET_FLAG (new_fib->status, RIB_ENTRY_CHANGED); - new_fib = best; - } - else - { - best = rib_choose_best(new_selected, rib); - if (new_selected && best != new_selected) - UNSET_FLAG (new_selected->status, RIB_ENTRY_CHANGED); - new_selected = best; - } + best = rib_choose_best(select, rib); + if (select && best != select) + UNSET_FLAG (select->status, RIB_ENTRY_CHANGED); if (best != rib) UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED); - } /* RNODE_FOREACH_RIB */ - - /* If no FIB override route, use the selected route also for FIB */ - if (new_fib == NULL) - new_fib = new_selected; + select = best; + } /* RNODE_FOREACH_RIB_SAFE */ /* After the cycle is finished, the following pointers will be set: - * old_selected --- RIB entry currently having SELECTED - * new_selected --- RIB entry that is newly SELECTED - * old_fib --- RIB entry currently in kernel FIB - * new_fib --- RIB entry that is newly to be in kernel FIB - * - * new_selected will get SELECTED flag, and is going to be redistributed - * the zclients. new_fib (which can be new_selected) will be installed in kernel. + * select --- the winner RIB entry, if any was found, otherwise NULL + * fib --- the SELECTED RIB entry, if any, otherwise NULL + * del --- equal to fib, if fib is queued for deletion, NULL otherwise + * rib --- NULL */ - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - { - zlog_debug ("%u:%s/%d: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", - vrf_id, buf, rn->p.prefixlen, - (void *)old_selected, - (void *)new_selected, - (void *)old_fib, - (void *)new_fib); - } + zlog_debug ("%u:%s/%d: After processing: select %p fib %p del %p", + vrf_id, buf, rn->p.prefixlen, select, fib, del); - /* Buffer RIB_ENTRY_CHANGED here, because it will get cleared if - * fib == selected */ - bool selected_changed = new_selected && CHECK_FLAG(new_selected->status, - RIB_ENTRY_CHANGED); - - /* Update fib according to selection results */ - if (new_fib && old_fib) - rib_process_update_fib (zvrf, rn, old_fib, new_fib); - else if (new_fib) - rib_process_add_fib (zvrf, rn, new_fib); - else if (old_fib) - rib_process_del_fib (zvrf, rn, old_fib); - - /* Redistribute SELECTED entry */ - if (old_selected != new_selected || selected_changed) - { - struct nexthop *nexthop, *tnexthop; - int recursing; - - /* Check if we have a FIB route for the destination, otherwise, - * don't redistribute it */ - for (ALL_NEXTHOPS_RO(new_fib ? new_fib->nexthop : NULL, nexthop, - tnexthop, recursing)) - { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - { - break; - } - } - if (!nexthop) - new_selected = NULL; - - if (new_selected && new_selected != new_fib) - { - nexthop_active_update(rn, new_selected, 1); - UNSET_FLAG(new_selected->status, RIB_ENTRY_CHANGED); - } - - if (old_selected) - { - if (!new_selected) - redistribute_delete(&rn->p, old_selected); - if (old_selected != new_selected) - UNSET_FLAG (old_selected->flags, ZEBRA_FLAG_SELECTED); - } - - if (new_selected) - { - /* Install new or replace existing redistributed entry */ - SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED); - redistribute_update (&rn->p, new_selected, old_selected); - } - } + /* Same RIB entry is selected. Update FIB and finish. */ + if (select && select == fib) + rib_process_update_route (zvrf, rn, select, select); + else if (select && fib) + rib_process_update_route (zvrf, rn, select, fib); + else if (select) + rib_process_add_route (zvrf, rn, select); + else if (fib) + rib_process_del_route (zvrf, rn, fib); #if 0 if (select && select == fib) @@ -1992,18 +1940,12 @@ rib_process (struct route_node *rn) } #endif - /* Remove all RIB entries queued for removal */ - RNODE_FOREACH_RIB_SAFE (rn, rib, next) + /* FIB route was removed, should be deleted */ + if (del) { - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) - { - if (IS_ZEBRA_DEBUG_RIB) - { - rnode_debug (rn, vrf_id, "rn %p, removing rib %p", - (void *)rn, (void *)rib); - } - rib_unlink(rn, rib); - } + if (IS_ZEBRA_DEBUG_RIB) + rnode_debug (rn, vrf_id, "Deleting fib %p, rn %p", (void *)del, (void *)rn); + rib_unlink (rn, del); } /* @@ -2570,7 +2512,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p, vrf_id_t vrf_id) */ RNODE_FOREACH_RIB (rn, rib) { - if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) && + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) && ! RIB_SYSTEM_ROUTE (rib)) { changed = 1; @@ -2716,7 +2658,7 @@ rib_delete (afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) fib = rib; if (rib->type != type) @@ -2777,7 +2719,7 @@ rib_delete (afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - UNSET_FLAG (fib->status, RIB_ENTRY_SELECTED_FIB); + UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); } else { @@ -3154,7 +3096,7 @@ rib_close_table (struct route_table *table) for (rn = route_top (table); rn; rn = route_next (rn)) RNODE_FOREACH_RIB (rn, rib) { - if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) continue; if (info->safi == SAFI_UNICAST) diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 1dc54e171c..f2362e6871 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -320,21 +320,13 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ /* If there are other active nexthops, do an update. */ if (rib->nexthop_active_num > 1) { - /* Update route in kernel if it's in fib */ - if (CHECK_FLAG(rib->status, RIB_ENTRY_SELECTED_FIB)) - rib_install_kernel (rn, rib, 1); - /* Update redistribution if it's selected */ - if (CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED)) - redistribute_update (&rn->p, rib, NULL); + rib_install_kernel (rn, rib, 1); + redistribute_update (&rn->p, rib, NULL); } else { - /* Remove from redistribute if selected route becomes inactive */ - if (CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED)) - redistribute_delete (&rn->p, rib); - /* Remove from kernel if fib route becomes inactive */ - if (CHECK_FLAG(rib->status, RIB_ENTRY_SELECTED_FIB)) - rib_uninstall_kernel (rn, rib); + redistribute_delete (&rn->p, rib); + rib_uninstall_kernel (rn, rib); } } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 41e1293e7c..e76e4ab8fa 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2023,10 +2023,6 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) } if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) vty_out (vty, ", best"); - else if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) - vty_out (vty, ", fib"); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) - vty_out (vty, ", fib-override"); if (rib->refcnt) vty_out (vty, ", refcnt %ld", rib->refcnt); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) @@ -2303,8 +2299,7 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, len += vty_out (vty, "[%d]", rib->instance); len += vty_out (vty, "%c%c %s", CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) - ? '>' : CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) - ? '!' : ' ', + ? '>' : ' ', CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', prefix2str (&rn->p, buf, sizeof buf)); diff --git a/zebra/zserv.c b/zebra/zserv.c index 969d860c20..3b2095d656 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -668,7 +668,8 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, break; } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -926,7 +927,7 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr, struc * are looking up. Therefore, we will just iterate over the top * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) num += zsend_write_nexthop (s, nexthop); stream_putc_at (s, nump, num); /* store nexthop_num */ From 877a0aba09f68977e9d07d7d4162d652844cf800 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Sep 2016 12:11:09 -0400 Subject: [PATCH 041/136] Revert "zebra: use link scope for interface routes" This reverts commit 72f3a8fb09433ee1e4d079522cd70999bb3b8e79. --- zebra/rt_netlink.c | 5 +---- zebra/zebra_rib.c | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 79f20259a1..95c8892979 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2428,7 +2428,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, req.r.rtm_family = family; req.r.rtm_dst_len = p->prefixlen; req.r.rtm_protocol = RTPROT_ZEBRA; - req.r.rtm_scope = RT_SCOPE_LINK; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) discard = 1; @@ -2508,9 +2508,6 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, if (cmd == RTM_DELROUTE && !CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) continue; - if (nexthop->type != NEXTHOP_TYPE_IFINDEX) - req.r.rtm_scope = RT_SCOPE_UNIVERSE; - nexthop_num++; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 775619ac2d..f57c0b5d67 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -483,16 +483,27 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, { resolved_hop->type = newhop->type; resolved_hop->gate.ipv4 = newhop->gate.ipv4; - resolved_hop->ifindex = newhop->ifindex; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + if (newhop->flags & NEXTHOP_FLAG_ONLINK) + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + } } - /* If the resolving route is an interface route, it - * means the gateway we are looking up is connected - * to that interface. Therefore, the resolved route - * should have the original gateway as nexthop as it - * is directly connected. */ + /* If the resolving route is an interface route, + * it means the gateway we are looking up is connected + * to that interface. (The actual network is _not_ onlink). + * Therefore, the resolved route should have the original + * gateway as nexthop as it is directly connected. + * + * On Linux, we have to set the onlink netlink flag because + * otherwise, the kernel won't accept the route. */ if (newhop->type == NEXTHOP_TYPE_IFINDEX) { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; resolved_hop->gate.ipv4 = nexthop->gate.ipv4; resolved_hop->ifindex = newhop->ifindex; From b99c382167cae31082e3eed7a4bb2ddb294dbad8 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Sep 2016 12:11:21 -0400 Subject: [PATCH 042/136] Revert "Make route flags a 32bit field" This reverts commit 85eda2c98520a9553bdc05c136618f9d04917e9b. --- bgpd/bgp_zebra.c | 8 ++++---- isisd/isis_zebra.c | 9 ++++----- lib/zclient.c | 6 +++--- lib/zclient.h | 4 ++-- ospf6d/ospf6_zebra.c | 2 +- ospfd/ospf_zebra.c | 10 +++++----- pimd/pim_zebra.c | 2 +- ripd/rip_zebra.c | 2 +- ripngd/ripng_zebra.c | 2 +- zebra/rib.h | 2 +- zebra/zserv.c | 12 ++++++------ 11 files changed, 29 insertions(+), 30 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c3e1b76a3c..15db321557 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -601,7 +601,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); /* IPv4 prefix. */ @@ -716,7 +716,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); /* IPv6 prefix. */ @@ -1199,7 +1199,7 @@ void bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, afi_t afi, safi_t safi) { - u_int32_t flags; + int flags; u_char distance; struct peer *peer; struct bgp_info *mpinfo; @@ -1620,7 +1620,7 @@ bgp_zebra_announce_table (struct bgp *bgp, afi_t afi, safi_t safi) void bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) { - u_int32_t flags; + int flags; struct peer *peer; peer = info->peer; diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index f43987bc18..45728ad2c1 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -257,8 +257,7 @@ static void isis_zebra_route_add_ipv4 (struct prefix *prefix, struct isis_route_info *route_info) { - u_char message; - u_int32_t flags; + u_char message, flags; int psize; struct stream *stream; struct isis_nexthop *nexthop; @@ -286,7 +285,7 @@ isis_zebra_route_add_ipv4 (struct prefix *prefix, /* instance */ stream_putw (stream, 0); /* flags */ - stream_putl (stream, flags); + stream_putc (stream, flags); /* message */ stream_putc (stream, message); /* SAFI */ @@ -567,7 +566,7 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.type = stream_getc (stream); api.instance = stream_getw (stream); - api.flags = stream_getl (stream); + api.flags = stream_getc (stream); api.message = stream_getc (stream); p.family = AF_INET; @@ -624,7 +623,7 @@ isis_zebra_read_ipv6 (int command, struct zclient *zclient, ifindex = 0; api.type = stream_getc(stream); - api.flags = stream_getl(stream); + api.flags = stream_getc(stream); api.message = stream_getc(stream); p.family = AF_INET6; diff --git a/lib/zclient.c b/lib/zclient.c index c70ff90a11..5193a282a6 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -733,7 +733,7 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putl (s, api->flags); + stream_putc (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); @@ -801,7 +801,7 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putl (s, api->flags); + stream_putc (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); @@ -867,7 +867,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putl (s, api->flags); + stream_putc (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); diff --git a/lib/zclient.h b/lib/zclient.h index 4edbd7636e..231b4e9b4f 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -134,7 +134,7 @@ struct zapi_ipv4 u_char type; u_short instance; - u_int32_t flags; + u_char flags; u_char message; @@ -222,7 +222,7 @@ struct zapi_ipv6 u_char type; u_short instance; - u_int32_t flags; + u_char flags; u_char message; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index b4381cf816..6dee1424a6 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -229,7 +229,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); /* IPv6 prefix. */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 062b4d601e..8752e83ed5 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -355,7 +355,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; - u_int32_t flags; + u_char flags; int psize; struct stream *s; struct ospf_path *path; @@ -393,7 +393,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putw (s, ospf->instance); - stream_putl (s, flags); + stream_putc (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); @@ -492,7 +492,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; - u_int32_t flags; + u_char flags; int psize; struct stream *s; struct ospf_path *path; @@ -516,7 +516,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) zclient_create_header (s, ZEBRA_IPV4_ROUTE_DELETE, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putw (s, ospf->instance); - stream_putl (s, flags); + stream_putc (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); @@ -1064,7 +1064,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); /* IPv4 prefix. */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 1a8d5f22bc..b25e8b94da 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -552,7 +552,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc(s); api.instance = stream_getw (s); - api.flags = stream_getl(s); + api.flags = stream_getc(s); api.message = stream_getc(s); /* IPv4 prefix length. */ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 627755e1c2..c312641d44 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -147,7 +147,7 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); /* IPv4 prefix. */ diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 803fd74157..1184cd0db6 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -143,7 +143,7 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); /* IPv6 prefix. */ diff --git a/zebra/rib.h b/zebra/rib.h index 285166f067..0f7f70ada7 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -79,7 +79,7 @@ struct rib * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed * to clients via Zserv */ - u_int32_t flags; + u_char flags; /* RIB internal status */ u_char status; diff --git a/zebra/zserv.c b/zebra/zserv.c index 3b2095d656..4cfeead887 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -628,7 +628,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, /* Put type and nexthop. */ stream_putc (s, rib->type); stream_putw (s, rib->instance); - stream_putl (s, rib->flags); + stream_putc (s, rib->flags); /* marker for message flags field */ messmark = stream_get_endp (s); @@ -1055,7 +1055,7 @@ zread_ipv4_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getl (s); + rib->flags = stream_getc (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1159,7 +1159,7 @@ zread_ipv4_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); api.safi = stream_getw (s); @@ -1265,7 +1265,7 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getl (s); + rib->flags = stream_getc (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1392,7 +1392,7 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getl (s); + rib->flags = stream_getc (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1506,7 +1506,7 @@ zread_ipv6_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getl (s); + api.flags = stream_getc (s); api.message = stream_getc (s); api.safi = stream_getw (s); From bb4ac22a1f22d1b9b1950fa3f373cb2953411863 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 21 Sep 2016 12:30:00 +0200 Subject: [PATCH 043/136] ospf6d: fix fallout from ifindex_t change Only one of these variables is in fact an interface index... (this fixes -Werror build.) Signed-off-by: David Lamparter --- ospf6d/ospf6_snmp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index 382cf62f72..86cfd17c83 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -625,7 +625,8 @@ ospfv3WwLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { struct ospf6_lsa *lsa = NULL; - ifindex_t ifindex, area_id, id, instid, adv_router; + ifindex_t ifindex; + uint32_t area_id, id, instid, adv_router; u_int16_t type; int len; oid *offset; From 01673c6817bcd443118dd1d35aaf7071001aaf3f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 21 Sep 2016 12:49:30 +0200 Subject: [PATCH 044/136] lib: fix ICC warning in route-map code ICC thinks we're storing a pointer and might be losing bits at the top: error #810: conversion from "void *" to "route_map_event_t={enum }" may lose significant bits Build is warning-free on ICC 14.0.3 (for Linux x86_64) with this. Signed-off-by: David Lamparter --- lib/routemap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routemap.c b/lib/routemap.c index 10e5ed304c..09afc31bc0 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1341,7 +1341,7 @@ static void route_map_process_dependency (struct hash_backet *backet, void *data) { char *rmap_name; - route_map_event_t type = (route_map_event_t )data; + route_map_event_t type = (route_map_event_t)(ptrdiff_t)data; rmap_name = (char *)backet->data; From 8d9e99a6917774bbc2ca0d90b39c26d866510964 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 19 Sep 2016 18:32:17 +0200 Subject: [PATCH 045/136] vtysh: make extract.pl more whitespace-robust "DEFUN (" could only match with exactly one space between DEFUN and opening brace. Allow any amount of space. Signed-off-by: David Lamparter --- vtysh/extract.pl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index cd19fa754b..b4884b9687 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -94,7 +94,7 @@ foreach (@ARGV) { # $_ will contain the entire string including the DEFUN, ALIAS, etc. # We need to extract the DEFUN/ALIAS from everything in ()s. # The /s at the end tells the regex to allow . to match newlines. - $_ =~ /^(.*?) \((.*)\)$/s; + $_ =~ /^(.*?)\s*\((.*)\)$/s; my (@defun_array); $defun_or_alias = $1; From 1dec2166deabab95572bcf68d4e73a00460bc32e Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Wed, 25 May 2016 14:47:00 +0100 Subject: [PATCH 046/136] ripd: split-horizon default differed between rip_interface_new and _reset * rip_interface.c: Default for split_horizon_default differed between rip_interface_new and rip_interface_reset, causing at least some issues after interface events. See patchwork #604. Fix, and consolidate code. (rip_interface_{reset,clean}) rename these to 'interface', as that's more appropriate. Spin the ri specific bodies of these functions out to rip_interface_{reset,clean} helpers. Factor out the overlaps, so rip_interface_reset uses rip_interface_clean. (rip_interface_new) just use rip_interface_reset. * ripd.h: Update for (rip_interface_{reset,clean}) Reported by xufeng zhang, with a suggested fix on which this commit expands. See patchwork #604. This commit addresses only the split-horizon discrepency, issue #2. The other issue they reported, #1, is not addressed, though suggested fix seems inappropriate. Cc: xufeng.zhang@windriver.com --- ripd/rip_interface.c | 141 ++++++++++++++++++++----------------------- ripd/ripd.c | 4 +- ripd/ripd.h | 4 +- 3 files changed, 71 insertions(+), 78 deletions(-) diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 09b35d00b9..604343be89 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -112,6 +112,8 @@ ipv4_multicast_leave (int sock, return ret; } +static void rip_interface_reset (struct rip_interface *); + /* Allocate new RIP's interface configuration. */ static struct rip_interface * rip_interface_new (void) @@ -119,19 +121,9 @@ rip_interface_new (void) struct rip_interface *ri; ri = XCALLOC (MTYPE_RIP_INTERFACE, sizeof (struct rip_interface)); - - /* Default authentication type is simple password for Cisco - compatibility. */ - ri->auth_type = RIP_NO_AUTH; - ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE; - - /* Set default split-horizon behavior. If the interface is Frame - Relay or SMDS is enabled, the default value for split-horizon is - off. But currently Zebra does detect Frame Relay or SMDS - interface. So all interface is set to split horizon. */ - ri->split_horizon_default = RIP_SPLIT_HORIZON; - ri->split_horizon = ri->split_horizon_default; - + + rip_interface_reset (ri); + return ri; } @@ -503,81 +495,82 @@ rip_interface_delete (int command, struct zclient *zclient, return 0; } -void -rip_interface_clean (void) +static void +rip_interface_clean (struct rip_interface *ri) { - struct listnode *node; - struct interface *ifp; - struct rip_interface *ri; + ri->enable_network = 0; + ri->enable_interface = 0; + ri->running = 0; - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) + if (ri->t_wakeup) { - ri = ifp->info; - - ri->enable_network = 0; - ri->enable_interface = 0; - ri->running = 0; - - if (ri->t_wakeup) - { - thread_cancel (ri->t_wakeup); - ri->t_wakeup = NULL; - } + thread_cancel (ri->t_wakeup); + ri->t_wakeup = NULL; } } void -rip_interface_reset (void) +rip_interfaces_clean (void) { struct listnode *node; struct interface *ifp; - struct rip_interface *ri; for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) + rip_interface_clean (ifp->info); +} + +static void +rip_interface_reset (struct rip_interface *ri) +{ + /* Default authentication type is simple password for Cisco + compatibility. */ + ri->auth_type = RIP_NO_AUTH; + ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE; + + /* Set default split-horizon behavior. If the interface is Frame + Relay or SMDS is enabled, the default value for split-horizon is + off. But currently Zebra does detect Frame Relay or SMDS + interface. So all interface is set to split horizon. */ + ri->split_horizon_default = RIP_SPLIT_HORIZON; + ri->split_horizon = ri->split_horizon_default; + + ri->ri_send = RI_RIP_UNSPEC; + ri->ri_receive = RI_RIP_UNSPEC; + + if (ri->auth_str) { - ri = ifp->info; - - ri->enable_network = 0; - ri->enable_interface = 0; - ri->running = 0; - - ri->ri_send = RI_RIP_UNSPEC; - ri->ri_receive = RI_RIP_UNSPEC; - - ri->auth_type = RIP_NO_AUTH; - - if (ri->auth_str) - { - free (ri->auth_str); - ri->auth_str = NULL; - } - if (ri->key_chain) - { - free (ri->key_chain); - ri->key_chain = NULL; - } - - ri->split_horizon = RIP_NO_SPLIT_HORIZON; - ri->split_horizon_default = RIP_NO_SPLIT_HORIZON; - - ri->list[RIP_FILTER_IN] = NULL; - ri->list[RIP_FILTER_OUT] = NULL; - - ri->prefix[RIP_FILTER_IN] = NULL; - ri->prefix[RIP_FILTER_OUT] = NULL; - - if (ri->t_wakeup) - { - thread_cancel (ri->t_wakeup); - ri->t_wakeup = NULL; - } - - ri->recv_badpackets = 0; - ri->recv_badroutes = 0; - ri->sent_updates = 0; - - ri->passive = 0; + free (ri->auth_str); + ri->auth_str = NULL; } + if (ri->key_chain) + { + free (ri->key_chain); + ri->key_chain = NULL; + } + + ri->list[RIP_FILTER_IN] = NULL; + ri->list[RIP_FILTER_OUT] = NULL; + + ri->prefix[RIP_FILTER_IN] = NULL; + ri->prefix[RIP_FILTER_OUT] = NULL; + + ri->recv_badpackets = 0; + ri->recv_badroutes = 0; + ri->sent_updates = 0; + + ri->passive = 0; + + rip_interface_clean (ri); +} + +void +rip_interfaces_reset (void) +{ + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) + rip_interface_reset (ifp->info); } int diff --git a/ripd/ripd.c b/ripd/ripd.c index 3a8cd80e7a..698de7e817 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -4030,7 +4030,7 @@ rip_clean (void) rip_clean_network (); rip_passive_nondefault_clean (); rip_offset_clean (); - rip_interface_clean (); + rip_interfaces_clean (); rip_distance_reset (); rip_redistribute_clean (); } @@ -4054,7 +4054,7 @@ rip_reset (void) distribute_list_reset (); - rip_interface_reset (); + rip_interfaces_reset (); rip_distance_reset (); rip_zclient_reset (); diff --git a/ripd/ripd.h b/ripd/ripd.h index 7c77b26d41..588da1d5f7 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -383,8 +383,8 @@ extern void rip_init (void); extern void rip_reset (void); extern void rip_clean (void); extern void rip_clean_network (void); -extern void rip_interface_clean (void); -extern void rip_interface_reset (void); +extern void rip_interfaces_clean (void); +extern void rip_interfaces_reset (void); extern void rip_passive_nondefault_clean (void); extern void rip_if_init (void); extern void rip_if_down_all (void); From e6f8d09592499a659b73661b23ef4dc0e47f579a Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 14 Jan 2013 23:41:57 +0100 Subject: [PATCH 047/136] lib: update Solaris multicast API (BZ#725) On OpenIndiana/Solaris the build fails with "unsupported multicast API". It's only in the IPv4 part where setsockopt IP_MULTICAST_IF needs a local address and not the index (IPv6 wants the index). The following code walks the list of interfaces until it finds the matching index and uses the interface's local address for the setsockopt call. I don't know if it works on Solaris < 10 (I guess yes, but I don't have any machine to verify it). [NB: this breaks unnumbered setups that use the same IPv4 address on multiple interfaces. -- equinox@opensourcerouting.org] Reported-by: Brian Utterback Signed-off-by: Christian Franke Patchwork #762 --- lib/sockopt.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/sockopt.c b/lib/sockopt.c index 31b2edbacf..c480cee0d7 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -20,6 +20,11 @@ */ #include + +#ifdef SUNOS_5 +#include +#endif + #include "log.h" #include "sockopt.h" #include "sockunion.h" @@ -346,6 +351,35 @@ setsockopt_ipv4_multicast_if(int sock, struct in_addr if_addr, #endif return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m)); +#elif defined(SUNOS_5) + char ifname[IF_NAMESIZE]; + struct ifaddrs *ifa, *ifap; + struct in_addr ifaddr; + + if (if_indextoname(ifindex, ifname) == NULL) + return -1; + + if (getifaddrs(&ifa) != 0) + return -1; + + for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) + { + struct sockaddr_in *sa; + + if (strcmp(ifap->ifa_name, ifname) != 0) + continue; + if (ifap->ifa_addr->sa_family != AF_INET) + continue; + sa = (struct sockaddr_in*)ifap->ifa_addr; + memcpy(&ifaddr, &sa->sin_addr, sizeof(ifaddr)); + break; + } + + freeifaddrs(ifa); + if (!ifap) /* This means we did not find an IP */ + return -1; + + return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&ifaddr, sizeof(ifaddr)); #else #error "Unsupported multicast API" #endif From cd4ab724a1a104f076b0fb205dfcc5384122f4b0 Mon Sep 17 00:00:00 2001 From: boris yakubov Date: Fri, 26 Apr 2013 14:38:34 -0400 Subject: [PATCH 048/136] isisd: Segmentation fault on isis daemon fixes I have a fix for 2 segmentation fault scenarios on the isis daemon: 1. When running a command "isis passive" on an interface in the following context: "end" "configure terminal " "interface dummy0" "isis passive" The trace back collected: isis_adjacency.c:521 family=2, root_sysid=0x20aee6d0 "", parent=0x20af4d68) at isis_spf.c:999 sysid=0x20aee6d0 "") at isis_spf.c:1217 isis_spf.c:1372 isis_lsp.c:416 isis_lsp.c:1660 isis_main.c:368 The fix location: file name: isisd/isis_adjacency.c routine name: isis_adj_build_up_list 2. When deleting the existing isis router instance: "end" "configure terminal " "no router isis DEAD" The fix location: isisd/isis_events.c, routine circuit_resign_level isisd/isis_lsp.c, routine lsp_destroy isisd/isis_route.c, isis_route_validate The trace back collection: "DEAD") at isisd.c:252 argc=1, argv=0xbfc39054) at isisd.c:1520 vty=0x20d6f528, cmd=0x0) at command.c:2121 cmd=0x0, vtysh=0) at command.c:2155 isis DEAD") at vty.c:433 isis_main.c:368 and "DEAD") at isisd.c:260 argc=1, argv=0xbfd6cf54) at isisd.c:1520 vty=0x208cb528, cmd=0x0) at command.c:2121 cmd=0x0, vtysh=0) at command.c:2155 isis DEAD") at vty.c:433 isis_main.c:368 The patch is included. patchwork #833: http://patchwork.quagga.net/patch/833/ --- isisd/isis_adjacency.c | 5 +++++ isisd/isis_events.c | 6 ++++-- isisd/isis_lsp.c | 18 ++++++++++-------- isisd/isis_route.c | 3 +++ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index c7ab83ba0b..8afabede4e 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -507,6 +507,11 @@ isis_adj_build_up_list (struct list *adjdb, struct list *list) struct isis_adjacency *adj; struct listnode *node; + if (adjdb == NULL) { + zlog_warn ("isis_adj_build_up_list(): adjacency DB is empty"); + return; + } + if (!list) { zlog_warn ("isis_adj_build_up_list(): NULL list"); diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 460b1d25ba..abc4471cad 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -137,8 +137,10 @@ circuit_resign_level (struct isis_circuit *circuit, int level) THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[idx]); circuit->lsp_regenerate_pending[idx] = 0; circuit->u.bc.run_dr_elect[idx] = 0; - list_delete (circuit->u.bc.lan_neighs[idx]); - circuit->u.bc.lan_neighs[idx] = NULL; + if (circuit->u.bc.lan_neighs[idx] != NULL) { + list_delete (circuit->u.bc.lan_neighs[idx]); + circuit->u.bc.lan_neighs[idx] = NULL; + } } return; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 0177a9423a..55887f901d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -138,14 +138,16 @@ lsp_destroy (struct isis_lsp *lsp) if (!lsp) return; - for (ALL_LIST_ELEMENTS_RO (lsp->area->circuit_list, cnode, circuit)) - { - if (circuit->lsp_queue == NULL) - continue; - for (ALL_LIST_ELEMENTS (circuit->lsp_queue, lnode, lnnode, lsp_in_list)) - if (lsp_in_list == lsp) - list_delete_node(circuit->lsp_queue, lnode); - } + if (lsp->area->circuit_list) { + for (ALL_LIST_ELEMENTS_RO (lsp->area->circuit_list, cnode, circuit)) + { + if (circuit->lsp_queue == NULL) + continue; + for (ALL_LIST_ELEMENTS (circuit->lsp_queue, lnode, lnnode, lsp_in_list)) + if (lsp_in_list == lsp) + list_delete_node(circuit->lsp_queue, lnode); + } + } ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags); diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 67d45c8f10..cc3ecba0d3 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -643,6 +643,9 @@ isis_route_validate (struct isis_area *area) isis_route_validate_merge (area, AF_INET6); #endif + if (!area->circuit_list) { + return; + } /* walk all circuits and reset any spf specific flags */ for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); From 711ff0ba94d29f059dde90f3c24db307e0d92d4c Mon Sep 17 00:00:00 2001 From: Udaya Shankara KS Date: Thu, 11 Feb 2016 21:42:29 +0530 Subject: [PATCH 049/136] zebra: Enable fpm module to connect to remote fpm server FPM aims to provide cross platform mechanism to support the scenario where the router has forwarding path distinct fromt the kernel.Commonly Hardware based fast path.Hence it is non-configurable paramter.This limits us to use funcationality to update FIB information to remote hosts, like SDN controller. This implementation provides the CLI to configure remote hosts and port information of remote fpm controller.Otherwise default fpm server will be localhost and default fpm port will be well know port 2620. * zebra_fpm.c: added fpm_server paramter to zfpm_global_t handler. Implemented CLI for configuring the fpm server and no fpm command to revert back to default configuration. * zserv.c: Install zebra node to write fpm configuration info on console/config file. Further documentation supplied: ------------------------------- ZEBRA : CLI CONFIGURATION FOR FPM MODULE ======================================================== 1. INTRODUCTION ================================ 1.1 scope This memo discusses the configuration option for zebra to update FIB information to local and remote modules. This will also helps to address the issue associated with CORD project. https://jira.onosproject.org/browse/CORD-411 2. REFERENCE ================================ Quagga version 99.24+ ( main branch committed on 29-sep-2015) 3. PROBLEM DESCRIPTION ================================ Once FPM is enabled, Quagga periodically tries to initiate fpm connection to localhost:2620. These values are non configurable in existing implementation. There is no CLI available to configure "host:port". hence limits us to use it for hardware based fast path modules only. 4. PROPOSED CHANGES ================================ Following changes are done to the quagga code a) Added new CLI to configure "host address : port". The CLI format $ fpm connection ip port and no fpm command to revert back to default $ no fpm connection ip port b) Allowed values are ipv4 address and tcp port range <1-65535> c) FPM initialization code has been enhanced to pick the "host address : port" values from zebra.conf. if not found then default values as localhost:2620 will be used. and updated the information on to config file on write config command 5. FILES MODIFIED ================================ 1) fpm/fpm.h : a) Added MACRO to represent network order loopback ip 2) zebra/zebra_fpm.h : a) introduced fpm_server variable in zfpm_glob_t handler to hold the remote fpm server address b) Hooked 'fpm_remote_ip_cmd' and 'no_fpm_remote_ip_cmd' at CONFIG node to configure remote fpm detail and to revert back to default respectively 3) zebra/zserv.c : a) Hooked 'config_write_fpm' callback function, at ZEBRA_NODE to display the fpm connection details on console on entering command $ show running_config and to write to configuration file on entering command $ write config 6. TESTING DETAILS ================================ 6.1. default behavior In default configuration FPM will attempt to connect to localhost:2620 6.2. update fpm info a) Using CLI command user can configure fpm host:port details and can be able to write to config file(zebra.conf) using write config command. this parameters has no dependency/impact on other parameters of config file b) show running-config/write config will display the fpm information if configured. and will not display any information related to fpm for default configuration c) these configured information will be stored to config file. only on write config command. 6.3 loading from config file a) zebra attempts to connect to fpm server if fpm parameter found in config file.else connects to default parameters. b) if fpm connection drops, fpm will periodically attempts to connect to remote server. c) if fpm connections already established. then newly configured fpm parameters will not disconnect the existing connection. new connection to the different fpm server will happen only after existing connection closes by either of the end. fix fpm prototype --- fpm/fpm.h | 14 ++++++++ zebra/zebra_fpm.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_fpm.h | 1 + zebra/zserv.c | 21 ++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) diff --git a/fpm/fpm.h b/fpm/fpm.h index 96f05f4865..9a1dbf2b0d 100644 --- a/fpm/fpm.h +++ b/fpm/fpm.h @@ -87,6 +87,14 @@ * table(s) when it reconnects. */ +/* + * Local host as a default server for fpm connection + */ +#define FPM_DEFAULT_IP (htonl (INADDR_LOOPBACK)) + +/* + * default port for fpm connections + */ #define FPM_DEFAULT_PORT 2620 /* @@ -270,4 +278,10 @@ fpm_msg_ok (const fpm_msg_hdr_t *hdr, size_t len) return 1; } +// tcp maximum range +#define TCP_MAX_PORT 65535 + +// tcp minimum range +#define TCP_MIN_PORT 1 + #endif /* _FPM_H */ diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 220fddaf67..34f0068514 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -156,6 +156,7 @@ typedef struct zfpm_glob_t_ zfpm_state_t state; + in_addr_t fpm_server; /* * Port on which the FPM is running. */ @@ -1129,7 +1130,10 @@ zfpm_connect_cb (struct thread *t) #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN serv.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (!zfpm_g->fpm_server) + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + else + serv.sin_addr.s_addr = (zfpm_g->fpm_server); /* * Connect to the FPM. @@ -1520,6 +1524,76 @@ DEFUN (clear_zebra_fpm_stats, return CMD_SUCCESS; } +/* + * update fpm connection information + */ +DEFUN ( fpm_remote_ip, + fpm_remote_ip_cmd, + "fpm connection ip A.B.C.D port <1-65535>", + "fpm connection remote ip and port\n" + "Remote fpm server ip A.B.C.D\n" + "Enter ip ") +{ + + in_addr_t fpm_server; + uint32_t port_no; + + fpm_server = inet_addr (argv[0]); + if (fpm_server == INADDR_NONE) + return CMD_ERR_INCOMPLETE; + + port_no = atoi (argv[1]); + if (port_no < TCP_MIN_PORT || port_no > TCP_MAX_PORT) + return CMD_ERR_INCOMPLETE; + + zfpm_g->fpm_server = fpm_server; + zfpm_g->fpm_port = port_no; + + + return CMD_SUCCESS; +} + +DEFUN ( no_fpm_remote_ip, + no_fpm_remote_ip_cmd, + "no fpm connection ip A.B.C.D port <1-65535>", + "fpm connection remote ip and port\n" + "Connection\n" + "Remote fpm server ip A.B.C.D\n" + "Enter ip ") +{ + if (zfpm_g->fpm_server != inet_addr (argv[0]) || + zfpm_g->fpm_port != atoi (argv[1])) + return CMD_ERR_NO_MATCH; + + zfpm_g->fpm_server = FPM_DEFAULT_IP; + zfpm_g->fpm_port = FPM_DEFAULT_PORT; + + return CMD_SUCCESS; +} + + +/** + * fpm_remote_srv_write + * + * Module to write remote fpm connection + * + * Returns ZERO on success. + */ + +int fpm_remote_srv_write (struct vty *vty ) +{ + struct in_addr in; + + in.s_addr = zfpm_g->fpm_server; + + if (zfpm_g->fpm_server != FPM_DEFAULT_IP || + zfpm_g->fpm_port != FPM_DEFAULT_PORT) + vty_out (vty,"fpm connection ip %s port %d%s", inet_ntoa (in),zfpm_g->fpm_port,VTY_NEWLINE); + + return 0; +} + + /** * zfpm_init * @@ -1563,11 +1637,16 @@ zfpm_init (struct thread_master *master, int enable, uint16_t port) install_element (ENABLE_NODE, &show_zebra_fpm_stats_cmd); install_element (ENABLE_NODE, &clear_zebra_fpm_stats_cmd); + install_element (CONFIG_NODE, &fpm_remote_ip_cmd); + install_element (CONFIG_NODE, &no_fpm_remote_ip_cmd); if (!enable) { return 1; } + if (!zfpm_g->fpm_server) + zfpm_g->fpm_server = FPM_DEFAULT_IP; + if (!port) port = FPM_DEFAULT_PORT; diff --git a/zebra/zebra_fpm.h b/zebra/zebra_fpm.h index 44dec02868..7fa71e40bc 100644 --- a/zebra/zebra_fpm.h +++ b/zebra/zebra_fpm.h @@ -30,5 +30,6 @@ */ extern int zfpm_init (struct thread_master *master, int enable, uint16_t port); extern void zfpm_trigger_update (struct route_node *rn, const char *reason); +extern int fpm_remote_srv_write (struct vty *vty); #endif /* _ZEBRA_FPM_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 4cfeead887..27d10a1718 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2576,6 +2576,24 @@ static struct cmd_node forwarding_node = 1 }; +#ifdef HAVE_FPM +/* function to write the fpm config info */ +static int +config_write_fpm (struct vty *vty) +{ + return + fpm_remote_srv_write (vty); +} + +/* Zebra node */ +static struct cmd_node zebra_node = +{ + ZEBRA_NODE, + "", + 1 +}; +#endif + /* Initialisation of zebra and installation of commands. */ void @@ -2587,6 +2605,9 @@ zebra_init (void) /* Install configuration write function. */ install_node (&table_node, config_write_table); install_node (&forwarding_node, config_write_forwarding); +#ifdef HAVE_FPM + install_node (&zebra_node, config_write_fpm); +#endif install_element (VIEW_NODE, &show_ip_forwarding_cmd); install_element (ENABLE_NODE, &show_ip_forwarding_cmd); From 6d24eb2b752354d91e518419f3f5ed421cf06f21 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 16 Feb 2016 19:50:15 +0100 Subject: [PATCH 050/136] isisd: fix assert warning icc (the Intel C Compiler) "knows" that assert() can be disabled by setting specific optimisation flags, and therefore emits a warning about missing a return value after an "always-error" assert. Workaround by returning a value - this probably needs discussion and a better fix (for all places where the code needs to abort due to internal errors). Signed-off-by: David Lamparter --- isisd/isis_redist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 5311b5c69c..1ce6a925e7 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -52,6 +52,7 @@ redist_protocol(int family) return 1; assert(!"Unsupported address family!"); + return 0; } static int From dad253b46d62d71b61d11cab94a7fe68acfed677 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Mon, 4 Apr 2016 10:54:55 -0700 Subject: [PATCH 051/136] qpb: Add support for protobuf. Infrastructure that allows protocol buffers to be used in Quagga. The changes below comprise of: - Build hooks - Protobuf definitions for common types. - Library routines for working with protobuf, including functions that help translate between common quagga types and their protobuf equivalents. Changes: * qpb/{Makefile.am,README.txt,qpb.h,.gitignore} Add the qpb library, which provides shared code and definitions for using protocol buffers in quagga code. * qpb/qpb.proto Protobuf definitions that can be shared by all of quagga. * qpb/linear_allocator.h An allocator that allocates memory by walking down towards the end of a buffer. This is used to cheaply allocate/deallocate memory on the stack for protobuf operations. * qpb/qpb_allocator.[ch] Thin layer that allows a linear allocator to be used with the protobuf-c library. * common.am This is an automake fragment that is intended to be shared by Makefile.am files in the tree. It currently includes definitions related to protobuf. * configure.ac - Add logic to optionally build protobuf code. By default, protobuf support is enabled if the protobuf C compiler (protoc-c) is available, and the associated header files/library can be found. The user can choose to override this behavior via the new --disable-protobuf/--enable-protobuf flags. - Include the quagga protobuf library (qpb) in the build. * .gitignore Ignore source code generated by protobuf compiler. * Makefile.am Add 'qpb' to the list of subdirectories. Signed-off-by: Avneesh Sachdev Edited: Paul Jakma : Change the sense of the configure enable option to require explicit specifying, as an experimental feature. --- .gitignore | 3 + Makefile.am | 4 +- common.am | 41 +++++ configure.ac | 47 +++++- qpb/.gitignore | 15 ++ qpb/Makefile.am | 30 ++++ qpb/README.txt | 1 + qpb/linear_allocator.h | 207 +++++++++++++++++++++++ qpb/qpb.c | 29 ++++ qpb/qpb.h | 372 +++++++++++++++++++++++++++++++++++++++++ qpb/qpb.proto | 121 ++++++++++++++ qpb/qpb_allocator.c | 67 ++++++++ qpb/qpb_allocator.h | 113 +++++++++++++ 13 files changed, 1047 insertions(+), 3 deletions(-) create mode 100644 common.am create mode 100644 qpb/.gitignore create mode 100644 qpb/Makefile.am create mode 100644 qpb/README.txt create mode 100644 qpb/linear_allocator.h create mode 100644 qpb/qpb.c create mode 100644 qpb/qpb.h create mode 100644 qpb/qpb.proto create mode 100644 qpb/qpb_allocator.c create mode 100644 qpb/qpb_allocator.h diff --git a/.gitignore b/.gitignore index 4e75acbac5..9a55091fe6 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ debian/quagga/ debian/tmp/ *.swp cscope.* +*.pb.h +*.pb-c.h +*.pb-c.c diff --git a/Makefile.am b/Makefile.am index a3cbdc919c..44c83ef7b1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,10 @@ ## Process this file with automake to produce Makefile.in. -SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ +SUBDIRS = lib qpb @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ redhat @SOLARIS@ tests tools cumulus -DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d ldpd \ +DIST_SUBDIRS = lib qpb zebra bgpd ripd ripngd ospfd ospf6d ldpd \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ solaris pimd tools cumulus diff --git a/common.am b/common.am new file mode 100644 index 0000000000..ac7a3230da --- /dev/null +++ b/common.am @@ -0,0 +1,41 @@ +# +# Automake fragment intended to be shared by Makefile.am files in the +# tree. +# + +if HAVE_PROTOBUF + +# Uncomment to use an non-system version of libprotobuf-c. +# +# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src +# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la + +Q_PROTOBUF_C_CLIENT_INCLUDES= +Q_PROTOBUF_C_CLIENT_LDOPTS=-lprotobuf-c + +Q_PROTOC=protoc +Q_PROTOC_C=protoc-c + +Q_PROTOBUF_CFILES = $(filter %.pb-c.c,$(SOURCES)) + +Q_PROTOBUF_SRCS = $(Q_PROTOBUF_CFILES) $(Q_PROTOBUF_HFILES) + +# Rules +%.pb.h: %.proto + $(Q_PROTOC) $(PROTOBUF_INCLUDES) --cpp_out=$(top_srcdir) $(top_srcdir)/$(PROTOBUF_PACKAGE)/$^ + +%.pb-c.c %.pb-c.h: %.proto + $(Q_PROTOC_C) $(PROTOBUF_INCLUDES) --c_out=$(top_srcdir) $(top_srcdir)/$(PROTOBUF_PACKAGE)/$^ + +# +# Information about how to link to various libraries. +# +Q_QUAGGA_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libquagga_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS) + +Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfpm_pb.la $(Q_QUAGGA_PB_CLIENT_LDOPTS) + +endif # HAVE_PROTOBUF + +Q_CLEANFILES = $(Q_PROTOBUF_SRCS) + +Q_BUILT_SRCS = $(Q_PROTOBUF_SRCS) diff --git a/configure.ac b/configure.ac index 20c9d8b96a..62a4284719 100755 --- a/configure.ac +++ b/configure.ac @@ -315,6 +315,8 @@ AC_ARG_ENABLE(cumulus, AS_HELP_STRING([--enable-cumulus], [enable Cumulus Switch Special Extensions])) AC_ARG_ENABLE(rr-semantics, AS_HELP_STRING([--disable-rr-semantics], [disable the v6 Route Replace semantics])) +AC_ARG_ENABLE([protobuf], + AS_HELP_STRING([--enable-protobuf], [Enable experimental protobuf support])) AC_CHECK_HEADERS(json-c/json.h) AC_CHECK_LIB(json-c, json_object_get, LIBS="$LIBS -ljson-c") @@ -393,6 +395,48 @@ if test "${enable_fpm}" = "yes"; then AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support) fi +# +# Logic for protobuf support. +# +if test "$enable_protobuf" = "yes"; then + have_protobuf=yes + + # Check for protoc-c + AC_CHECK_PROG([PROTOC_C], [protoc-c], [protoc-c], [/bin/false]) + if test "x$PROTOC_C" = "x/bin/false"; then + have_protobuf=no + else + found_protobuf_c=no + PKG_CHECK_MODULES([PROTOBUF_C], libprotobuf-c >= 0.14, + [found_protobuf_c=yes], + [AC_MSG_RESULT([pkg-config did not find libprotobuf-c])]) + + if test "x$found_protobuf_c" = "xyes"; then + LDFLAGS="$LDFLAGS $PROTOBUF_C_LIBS" + CFLAGS="$CFLAGS $PROTOBUF_C_CFLAGS" + else + AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], + [have_protobuf=no; AC_MSG_RESULT([Couldn't find google/protobuf-c.h])]) + fi + fi +fi + +# Fail if the user explicity enabled protobuf support and we couldn't +# find the compiler or libraries. +if test "x$have_protobuf" = "xno" && test "x$enable_protobuf" = "xyes"; then + AC_MSG_ERROR([Protobuf enabled explicitly but can't find libraries/tools]) +fi + +if test "x$have_protobuf" = "xyes"; then + AC_DEFINE(HAVE_PROTOBUF,, protobuf) +fi + +AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$have_protobuf" = "xyes"]) + +# +# End of logic for protobuf support. +# + if test "${enable_tcp_zebra}" = "yes"; then AC_DEFINE(HAVE_TCP_ZEBRA,,Use TCP for zebra communication) fi @@ -1673,7 +1717,7 @@ AC_CACHE_VAL(ac_cv_htonl_works, ) AC_MSG_RESULT($ac_cv_htonl_works) -AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile +AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile ospf6d/Makefile ldpd/Makefile isisd/Makefile vtysh/Makefile doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile @@ -1715,6 +1759,7 @@ group to run as : ${enable_group} group for vty sockets : ${enable_vty_group} config file mask : ${enable_configfile_mask} log file mask : ${enable_logfile_mask} +zebra protobuf enabled : ${have_protobuf:-no} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." diff --git a/qpb/.gitignore b/qpb/.gitignore new file mode 100644 index 0000000000..b133c52a42 --- /dev/null +++ b/qpb/.gitignore @@ -0,0 +1,15 @@ +Makefile +Makefile.in +*.o +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.a +*.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/qpb/Makefile.am b/qpb/Makefile.am new file mode 100644 index 0000000000..0fbda61f3c --- /dev/null +++ b/qpb/Makefile.am @@ -0,0 +1,30 @@ +include ../common.am + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib $(Q_PROTOBUF_C_CLIENT_INCLUDES) + +PROTOBUF_INCLUDES=-I$(top_srcdir) +PROTOBUF_PACKAGE = qpb + +lib_LTLIBRARIES = libquagga_pb.la +libquagga_pb_la_LDFLAGS = -version-info 0:0:0 + +if HAVE_PROTOBUF +protobuf_srcs = \ + qpb_allocator.c + +protobuf_srcs_nodist = \ + qpb.pb-c.c +endif + +libquagga_pb_la_SOURCES = \ + linear_allocator.h \ + qpb.h \ + qpb.c \ + qpb_allocator.h \ + $(protobuf_srcs) + +nodist_libquagga_pb_la_SOURCES = $(protobuf_srcs_nodist) + +CLEANFILES = $(Q_CLEANFILES) +BUILT_SOURCES = $(Q_PROTOBUF_SRCS) +EXTRA_DIST = qpb.proto diff --git a/qpb/README.txt b/qpb/README.txt new file mode 100644 index 0000000000..99ccd05511 --- /dev/null +++ b/qpb/README.txt @@ -0,0 +1 @@ +Protobuf definitions and code that is applicable to all of quagga. diff --git a/qpb/linear_allocator.h b/qpb/linear_allocator.h new file mode 100644 index 0000000000..e3ebbc64f3 --- /dev/null +++ b/qpb/linear_allocator.h @@ -0,0 +1,207 @@ +/* + * linear_allocator.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Header file for the linear allocator. + * + * An allocator that allocates memory by walking down towards the end + * of a buffer. No attempt is made to reuse blocks that are freed + * subsequently. The assumption is that the buffer is big enough to + * cover allocations for a given purpose. + */ +#include +#include +#include +#include + +/* + * Alignment for block allocated by the allocator. Must be a power of 2. + */ +#define LINEAR_ALLOCATOR_ALIGNMENT 8 + +#define LINEAR_ALLOCATOR_ALIGN(value) \ + (((value) + LINEAR_ALLOCATOR_ALIGNMENT - 1) & ~(LINEAR_ALLOCATOR_ALIGNMENT - 1)); + +/* + * linear_allocator_align_ptr + */ +static inline char * +linear_allocator_align_ptr (char *ptr) +{ + return (char *) LINEAR_ALLOCATOR_ALIGN ((intptr_t) ptr); +} + +typedef struct linear_allocator_t_ +{ + char *buf; + + /* + * Current location in the buffer. + */ + char *cur; + + /* + * End of buffer. + */ + char *end; + + /* + * Version number of the allocator, this is bumped up when the allocator + * is reset and helps identifies bad frees. + */ + uint32_t version; + + /* + * The number of blocks that are currently allocated. + */ + int num_allocated; +} linear_allocator_t; + +/* + * linear_allocator_block_t + * + * Header structure at the begining of each block. + */ +typedef struct linear_allocator_block_t_ +{ + uint32_t flags; + + /* + * The version of the allocator when this block was allocated. + */ + uint32_t version; + char data[0]; +} linear_allocator_block_t; + +#define LINEAR_ALLOCATOR_BLOCK_IN_USE 0x01 + +#define LINEAR_ALLOCATOR_HDR_SIZE (sizeof(linear_allocator_block_t)) + +/* + * linear_allocator_block_size + * + * The total amount of space a block will take in the buffer, + * including the size of the header. + */ +static inline size_t +linear_allocator_block_size (size_t user_size) +{ + return LINEAR_ALLOCATOR_ALIGN (LINEAR_ALLOCATOR_HDR_SIZE + user_size); +} + +/* + * linear_allocator_ptr_to_block + */ +static inline linear_allocator_block_t * +linear_allocator_ptr_to_block (void *ptr) +{ + void *block_ptr; + block_ptr = ((char *) ptr) - offsetof (linear_allocator_block_t, data); + return block_ptr; +} + +/* + * linear_allocator_init + */ +static inline void +linear_allocator_init (linear_allocator_t * allocator, char *buf, + size_t buf_len) +{ + memset (allocator, 0, sizeof (*allocator)); + + assert (linear_allocator_align_ptr (buf) == buf); + allocator->buf = buf; + allocator->cur = buf; + allocator->end = buf + buf_len; +} + +/* + * linear_allocator_reset + * + * Prepare an allocator for reuse. + * + * *** NOTE ** This implicitly frees all the blocks in the allocator. + */ +static inline void +linear_allocator_reset (linear_allocator_t *allocator) +{ + allocator->num_allocated = 0; + allocator->version++; + allocator->cur = allocator->buf; +} + +/* + * linear_allocator_alloc + */ +static inline void * +linear_allocator_alloc (linear_allocator_t *allocator, size_t user_size) +{ + size_t block_size; + linear_allocator_block_t *block; + + block_size = linear_allocator_block_size (user_size); + + if (allocator->cur + block_size > allocator->end) + { + return NULL; + } + + block = (linear_allocator_block_t *) allocator->cur; + allocator->cur += block_size; + + block->flags = LINEAR_ALLOCATOR_BLOCK_IN_USE; + block->version = allocator->version; + allocator->num_allocated++; + return block->data; +} + +/* + * linear_allocator_free + */ +static inline void +linear_allocator_free (linear_allocator_t *allocator, void *ptr) +{ + linear_allocator_block_t *block; + + if (((char *) ptr) < allocator->buf || ((char *) ptr) >= allocator->end) + { + assert (0); + return; + } + + block = linear_allocator_ptr_to_block (ptr); + if (block->version != allocator->version) + { + assert (0); + return; + } + + block->flags = block->flags & ~LINEAR_ALLOCATOR_BLOCK_IN_USE; + + if (--allocator->num_allocated < 0) + { + assert (0); + } +} diff --git a/qpb/qpb.c b/qpb/qpb.c new file mode 100644 index 0000000000..1b2b47fce5 --- /dev/null +++ b/qpb/qpb.c @@ -0,0 +1,29 @@ +/* + * qpb.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Main file for the qpb library. + */ + diff --git a/qpb/qpb.h b/qpb/qpb.h new file mode 100644 index 0000000000..55c1deb19d --- /dev/null +++ b/qpb/qpb.h @@ -0,0 +1,372 @@ +/* + * qpb.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Main public header file for the quagga protobuf library. + */ + +#ifndef _QPB_H +#define _QPB_H + +#include "prefix.h" + +#include "qpb/qpb.pb-c.h" + +#include "qpb/qpb_allocator.h" + +/* + * qpb__address_family__set + */ +#define qpb_address_family_set qpb__address_family__set +static inline int +qpb__address_family__set (Qpb__AddressFamily *pb_family, u_char family) +{ + switch (family) { + case AF_INET: + *pb_family = QPB__ADDRESS_FAMILY__IPV4; + return 1; + + case AF_INET6: + *pb_family = QPB__ADDRESS_FAMILY__IPV6; + return 1; + + default: + *pb_family = QPB__ADDRESS_FAMILY__UNKNOWN_AF; + } + + return 0; +} + +/* + * qpb__address_family__get + */ +#define qpb_address_family_get qpb__address_family__get +static inline int +qpb__address_family__get (Qpb__AddressFamily pb_family, u_char *family) +{ + + switch (pb_family) { + case QPB__ADDRESS_FAMILY__IPV4: + *family = AF_INET; + return 1; + + case QPB__ADDRESS_FAMILY__IPV6: + *family = AF_INET6; + return 1; + + case QPB__ADDRESS_FAMILY__UNKNOWN_AF: + return 0; + } + + return 0; +} + +/* + * qpb__l3_prefix__create + */ +#define qpb_l3_prefix_create qpb__l3_prefix__create +static inline Qpb__L3Prefix * +qpb__l3_prefix__create (qpb_allocator_t *allocator, struct prefix *p) +{ + Qpb__L3Prefix *prefix; + + prefix = QPB_ALLOC(allocator, typeof(*prefix)); + if (!prefix) { + return NULL; + } + qpb__l3_prefix__init(prefix); + prefix->length = p->prefixlen; + prefix->bytes.len = (p->prefixlen + 7)/8; + prefix->bytes.data = qpb_alloc(allocator, prefix->bytes.len); + if (!prefix->bytes.data) { + return NULL; + } + + memcpy(prefix->bytes.data, &p->u.prefix, prefix->bytes.len); + + return prefix; +} + +/* + * qpb__l3_prefix__get + */ +#define qpb_l3_prefix_get qpb__l3_prefix__get +static inline int +qpb__l3_prefix__get (const Qpb__L3Prefix *pb_prefix, u_char family, + struct prefix *prefix) +{ + + switch (family) + { + + case AF_INET: + memset(prefix, 0, sizeof(struct prefix_ipv4)); + break; + + case AF_INET6: + memset(prefix, 0, sizeof(struct prefix_ipv6)); + break; + + default: + memset(prefix, 0, sizeof(*prefix)); + } + + prefix->prefixlen = pb_prefix->length; + prefix->family = family; + memcpy(&prefix->u.prefix, pb_prefix->bytes.data, pb_prefix->bytes.len); + return 1; +} + +/* + * qpb__protocol__set + * + * Translate a quagga route type to a protobuf protocol. + */ +#define qpb_protocol_set qpb__protocol__set +static inline int +qpb__protocol__set (Qpb__Protocol *pb_proto, int route_type) +{ + switch (route_type) { + case ZEBRA_ROUTE_KERNEL: + *pb_proto = QPB__PROTOCOL__KERNEL; + break; + + case ZEBRA_ROUTE_CONNECT: + *pb_proto = QPB__PROTOCOL__CONNECTED; + break; + + case ZEBRA_ROUTE_STATIC: + *pb_proto = QPB__PROTOCOL__STATIC; + break; + + case ZEBRA_ROUTE_RIP: + *pb_proto = QPB__PROTOCOL__RIP; + break; + + case ZEBRA_ROUTE_RIPNG: + *pb_proto = QPB__PROTOCOL__RIPNG; + break; + + case ZEBRA_ROUTE_OSPF: + case ZEBRA_ROUTE_OSPF6: + *pb_proto = QPB__PROTOCOL__OSPF; + break; + + case ZEBRA_ROUTE_ISIS: + *pb_proto = QPB__PROTOCOL__ISIS; + break; + + case ZEBRA_ROUTE_BGP: + *pb_proto = QPB__PROTOCOL__BGP; + break; + + case ZEBRA_ROUTE_HSLS: + case ZEBRA_ROUTE_OLSR: + case ZEBRA_ROUTE_BABEL: + case ZEBRA_ROUTE_MAX: + case ZEBRA_ROUTE_SYSTEM: + default: + *pb_proto = QPB__PROTOCOL__OTHER; + } + + return 1; +} + +/* + * qpb__ipv4_address__create + */ +static inline Qpb__Ipv4Address * +qpb__ipv4_address__create (qpb_allocator_t *allocator, + struct in_addr *addr) +{ + Qpb__Ipv4Address *v4; + + v4 = QPB_ALLOC(allocator, typeof(*v4)); + if (!v4) { + return NULL; + } + qpb__ipv4_address__init(v4); + + v4->value = ntohl(addr->s_addr); + return v4; +} + +/* + * qpb__ipv4_address__get + */ +static inline int +qpb__ipv4_address__get (const Qpb__Ipv4Address *v4, struct in_addr *addr) +{ + addr->s_addr = htonl(v4->value); + return 1; +} + +/* + * qpb__ipv6_address__create + */ +static inline Qpb__Ipv6Address * +qpb__ipv6_address__create (qpb_allocator_t *allocator, struct in6_addr *addr) +{ + Qpb__Ipv6Address *v6; + + v6 = QPB_ALLOC(allocator, typeof(*v6)); + if (!v6) + return NULL; + + qpb__ipv6_address__init(v6); + v6->bytes.len = 16; + v6->bytes.data = qpb_alloc(allocator, 16); + if (!v6->bytes.data) + return NULL; + + memcpy(v6->bytes.data, addr->s6_addr, v6->bytes.len); + return v6; +} + +/* + * qpb__ipv6_address__get + * + * Read out information from a protobuf ipv6 address structure. + */ +static inline int +qpb__ipv6_address__get (const Qpb__Ipv6Address *v6, struct in6_addr *addr) +{ + if (v6->bytes.len != 16) + return 0; + + memcpy(addr->s6_addr, v6->bytes.data, v6->bytes.len); + return 1; +} + +/* + * qpb__l3_address__create + */ +#define qpb_l3_address_create qpb__l3_address__create +static inline Qpb__L3Address * +qpb__l3_address__create (qpb_allocator_t *allocator, union g_addr *addr, + u_char family) +{ + Qpb__L3Address *l3_addr; + + l3_addr = QPB_ALLOC(allocator, typeof(*l3_addr)); + if (!l3_addr) + return NULL; + + qpb__l3_address__init(l3_addr); + + switch (family) { + + case AF_INET: + l3_addr->v4 = qpb__ipv4_address__create (allocator, &addr->ipv4); + if (!l3_addr->v4) + return NULL; + + break; + + case AF_INET6: + l3_addr->v6 = qpb__ipv6_address__create (allocator, &addr->ipv6); + if (!l3_addr->v6) + return NULL; + + break; + } + return l3_addr; +} + +/* + * qpb__l3_address__get + * + * Read out a gateway address from a protobuf l3 address. + */ +#define qpb_l3_address_get qpb__l3_address__get +static inline int +qpb__l3_address__get (const Qpb__L3Address *l3_addr, + u_char *family, union g_addr *addr) +{ + if (l3_addr->v4) + { + qpb__ipv4_address__get (l3_addr->v4, &addr->ipv4); + *family = AF_INET; + return 1; + } + + if (l3_addr->v6) + { + qpb__ipv6_address__get(l3_addr->v6, &addr->ipv6); + *family = AF_INET6; + return 1; + } + + return 0; +} + +/* + * qpb__if_identifier__create + */ +#define qpb_if_identifier_create qpb__if_identifier__create +static inline Qpb__IfIdentifier * +qpb__if_identifier__create (qpb_allocator_t *allocator, uint if_index) +{ + Qpb__IfIdentifier *if_id; + + if_id = QPB_ALLOC(allocator, typeof(*if_id)); + if (!if_id) { + return NULL; + } + qpb__if_identifier__init(if_id); + if_id->has_index = 1; + if_id->index = if_index; + return if_id; +} + +/* + * qpb__if_identifier__get + * + * Get interface name and/or if_index from an if identifier. + */ +#define qpb_if_identifier_get qpb__if_identifier__get +static inline int +qpb__if_identifier__get (Qpb__IfIdentifier *if_id, uint *if_index, + char **name) +{ + char *str; + uint ix; + + if (!if_index) + if_index = &ix; + + if (!name) + name = &str; + + if (if_id->has_index) + *if_index = if_id->index; + else + *if_index = 0; + + *name = if_id->name; + return 1; +} + +#endif diff --git a/qpb/qpb.proto b/qpb/qpb.proto new file mode 100644 index 0000000000..7ee409df87 --- /dev/null +++ b/qpb/qpb.proto @@ -0,0 +1,121 @@ +/* + * qpb.proto + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * Permission is granted to use, copy, modify and/or distribute this + * software under either one of the licenses below. + * + * Note that if you use other files from the Quagga tree directly or + * indirectly, then the licenses in those files still apply. + * + * Please retain both licenses below when modifying this code in the + * Quagga tree. + */ + +/* + * License Option 1: GPL + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * License Option 2: ISC License + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice appear + * in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Protobuf definitions pertaining to the Quagga Protobuf component. + */ +package qpb; + +enum AddressFamily { + UNKNOWN_AF = 0; + IPV4 = 1; // IP version 4 + IPV6 = 2; // IP version 6 +}; + +enum SubAddressFamily { + UNKNOWN_SAF = 0; + UNICAST = 1; + MULTICAST = 2; +}; + +// +// An IP version 4 address, such as 10.1.1.1. +// +message Ipv4Address { + required fixed32 value = 1 ; +}; + +message Ipv6Address { + + // 16 bytes. + required bytes bytes = 1; +}; + +// +// An IP version 4 or IP version 6 address. +// +message L3Address { + optional Ipv4Address v4 = 1; + optional Ipv6Address v6 = 2; +}; + +// +// An IP prefix, such as 10.1/16. +// We use the message below to represent both IPv4 and IPv6 prefixes. +message L3Prefix { + required uint32 length = 1; + required bytes bytes = 2; +}; + +// +// Something that identifies an interface on a machine. It can either +// be a name (for instance, 'eth0') or a number currently. +// +message IfIdentifier { + optional uint32 index = 1; + optional string name = 2; +}; + +enum Protocol { + UNKNOWN_PROTO = 0; + LOCAL = 1; + CONNECTED = 2; + KERNEL = 3; + STATIC = 4; + RIP = 5; + RIPNG = 6; + OSPF = 7; + ISIS = 8; + BGP = 9; + OTHER = 10; +} \ No newline at end of file diff --git a/qpb/qpb_allocator.c b/qpb/qpb_allocator.c new file mode 100644 index 0000000000..4b4830a476 --- /dev/null +++ b/qpb/qpb_allocator.c @@ -0,0 +1,67 @@ +/* + * qpb_allocator.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "linear_allocator.h" + +#include "qpb_allocator.h" + +/* + * _qpb_alloc + */ +static void * +_qpb_alloc (void *allocator_data, size_t size) +{ + return linear_allocator_alloc (allocator_data, size); +} + +/* + * _qpb_free + */ +static void +_qpb_free (void *allocator_data, void *ptr) +{ + linear_allocator_free (allocator_data, ptr); +} + +static ProtobufCAllocator allocator_template = { + _qpb_alloc, + _qpb_free, + NULL, + 8192, + NULL +}; + +/* + * qpb_allocator_init_linear + * + * Initialize qpb_allocator_t with the given linear allocator. + */ +void +qpb_allocator_init_linear (qpb_allocator_t *allocator, + linear_allocator_t *linear_allocator) +{ + *allocator = allocator_template; + allocator->allocator_data = linear_allocator; +} diff --git a/qpb/qpb_allocator.h b/qpb/qpb_allocator.h new file mode 100644 index 0000000000..83ddf56cbc --- /dev/null +++ b/qpb/qpb_allocator.h @@ -0,0 +1,113 @@ +/* + * qpb_allocator.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Header file for quagga protobuf memory management code. + */ + +#ifndef _QPB_ALLOCATOR_H_ +#define _QPB_ALLOCATOR_H_ + +#include + +struct linear_allocator_t_; + +/* + * Alias for ProtobufCAllocator that is easier on the fingers. + */ +typedef ProtobufCAllocator qpb_allocator_t; + +/* + * qpb_alloc + */ +static inline void * +qpb_alloc (qpb_allocator_t *allocator, size_t size) +{ + return allocator->alloc (allocator->allocator_data, size); +} + +/* + * qpb_alloc_ptr_array + * + * Allocate space for the specified number of pointers. + */ +static inline void * +qpb_alloc_ptr_array (qpb_allocator_t *allocator, size_t num_ptrs) +{ + return qpb_alloc (allocator, num_ptrs * sizeof (void *)); +} + +/* + * qpb_free + */ +static inline void +qpb_free (qpb_allocator_t *allocator, void *ptr) +{ + allocator->free (allocator->allocator_data, ptr); +} + +/* + * QPB_ALLOC + * + * Convenience macro to reduce the probability of allocating memory of + * incorrect size. It returns enough memory to store the given type, + * and evaluates to an appropriately typed pointer. + */ +#define QPB_ALLOC(allocator, type) \ + (type *) qpb_alloc(allocator, sizeof(type)) + + +/* + * Externs. + */ +extern void qpb_allocator_init_linear (qpb_allocator_t *, + struct linear_allocator_t_ *); + +/* + * The following macros are for the common case where a qpb allocator + * is being used alongside a linear allocator that allocates memory + * off of the stack. + */ +#define QPB_DECLARE_STACK_ALLOCATOR(allocator, size) \ + qpb_allocator_t allocator; \ + linear_allocator_t lin_ ## allocator; \ + char lin_ ## allocator ## _buf[size] + +#define QPB_INIT_STACK_ALLOCATOR(allocator) \ + do \ + { \ + linear_allocator_init(&(lin_ ## allocator), \ + lin_ ## allocator ## _buf, \ + sizeof(lin_ ## allocator ## _buf)); \ + qpb_allocator_init_linear(&allocator, &(lin_ ## allocator)); \ + } while (0) + +#define QPB_RESET_STACK_ALLOCATOR(allocator) \ + do \ + { \ + linear_allocator_reset (&(lin_ ## allocator)); \ + } while (0) + +#endif /* _QPB_ALLOCATOR_H_ */ From f77cba1bf5ead6a07a734e533895bc5f43516957 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Mon, 4 Apr 2016 10:54:56 -0700 Subject: [PATCH 052/136] build: turn off automake portability warnings Modify configure.ac to disable portability warnings for automake -- our automake code (in particular common.am) uses some constructs specific to gmake. Signed-off-by: Avneesh Sachdev --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 62a4284719..b0ae5f349d 100755 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,9 @@ AC_CANONICAL_BUILD() AC_CANONICAL_HOST() AC_CANONICAL_TARGET() -AM_INIT_AUTOMAKE(1.6) +# Disable portability warnings -- our automake code (in particular +# common.am) uses some constructs specific to gmake. +AM_INIT_AUTOMAKE([1.6 -Wno-portability]) m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])]) AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS(config.h) From 93c7bed1c9e0ce297e966851ef778ed8bcbe3b6e Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Mon, 4 Apr 2016 10:54:57 -0700 Subject: [PATCH 053/136] fpm: Add protobuf support for FPM. Code that allows a client to convey routes to a Forwarding Plane Manager component using protobuf instead of netlink.. * fpm/fpm.proto Protobuf definitions pertaining to the Forwarding Plane Manager. In particular, this file defines the AddRoute and DeleteRoute messages. * fpm/fpm.h Tweak FPM message header definition to also allow messages to be encoded in protobuf format. * fpm/{fpm_pb.h,.gitignore,.Makefile.am} Add the fpm_pb library, which contains code for interfacing with the FPM using protobuf. * configure.ac Generate fpm/Makefile. * Makefile.am Add fpm subdirectory to build. * common.am Add flags to be used by clients of the fpm_pb library. Signed-off-by: Avneesh Sachdev --- Makefile.am | 7 ++- configure.ac | 1 + fpm/.gitignore | 15 ++++++ fpm/Makefile.am | 29 ++++++++++++ fpm/fpm.h | 38 ++++++++++++---- fpm/fpm.proto | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ fpm/fpm_pb.c | 28 ++++++++++++ fpm/fpm_pb.h | 63 +++++++++++++++++++++++++ 8 files changed, 288 insertions(+), 12 deletions(-) create mode 100644 fpm/.gitignore create mode 100644 fpm/Makefile.am create mode 100644 fpm/fpm.proto create mode 100644 fpm/fpm_pb.c create mode 100644 fpm/fpm_pb.h diff --git a/Makefile.am b/Makefile.am index 44c83ef7b1..031afac9d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,10 @@ ## Process this file with automake to produce Makefile.in. -SUBDIRS = lib qpb @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ +SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ redhat @SOLARIS@ tests tools cumulus -DIST_SUBDIRS = lib qpb zebra bgpd ripd ripngd ospfd ospf6d ldpd \ +DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d ldpd \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ solaris pimd tools cumulus @@ -12,8 +12,7 @@ EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ update-autotools \ vtysh/Makefile.in vtysh/Makefile.am \ tools/rrcheck.pl tools/rrlookup.pl tools/zc.pl \ - tools/zebra.el tools/multiple-bgpd.sh \ - fpm/fpm.h + tools/zebra.el tools/multiple-bgpd.sh if HAVE_LATEX diff --git a/configure.ac b/configure.ac index b0ae5f349d..0fd87431fa 100755 --- a/configure.ac +++ b/configure.ac @@ -1730,6 +1730,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile tools/Makefile cumulus/Makefile pkgsrc/Makefile + fpm/Makefile redhat/quagga.spec lib/version.h doc/defines.texi diff --git a/fpm/.gitignore b/fpm/.gitignore new file mode 100644 index 0000000000..b133c52a42 --- /dev/null +++ b/fpm/.gitignore @@ -0,0 +1,15 @@ +Makefile +Makefile.in +*.o +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.a +*.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/fpm/Makefile.am b/fpm/Makefile.am new file mode 100644 index 0000000000..83ab31ce3f --- /dev/null +++ b/fpm/Makefile.am @@ -0,0 +1,29 @@ +include ../common.am + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib $(Q_PROTOBUF_C_CLIENT_INCLUDES) + +PROTOBUF_INCLUDES=-I$(top_srcdir) +PROTOBUF_PACKAGE = fpm + +lib_LTLIBRARIES = libfpm_pb.la +libfpm_pb_la_LDFLAGS = -version-info 0:0:0 + +if HAVE_PROTOBUF +protobuf_srcs = + +protobuf_srcs_nodist = \ + fpm.pb-c.c +endif + +libfpm_pb_la_SOURCES = \ + fpm.h \ + fpm_pb.h \ + fpm_pb.c \ + $(protobuf_srcs) + +nodist_libfpm_pb_la_SOURCES = $(protobuf_srcs_nodist) + +CLEANFILES = $(Q_CLEANFILES) + +BUILT_SOURCES = $(Q_PROTOBUF_SRCS) +EXTRA_DIST = fpm.proto diff --git a/fpm/fpm.h b/fpm/fpm.h index 9a1dbf2b0d..85285996ca 100644 --- a/fpm/fpm.h +++ b/fpm/fpm.h @@ -102,6 +102,10 @@ */ #define FPM_MAX_MSG_LEN 4096 +#ifdef __SUNPRO_C +#pragma pack(1) +#endif + /* * Header that precedes each fpm message to/from the FPM. */ @@ -120,13 +124,13 @@ typedef struct fpm_msg_hdr_t_ /* * Length of entire message, including the header, in network byte * order. - * - * Note that msg_len is rounded up to make sure that message is at - * the desired alignment. This means that some payloads may need - * padding at the end. */ uint16_t msg_len; -} fpm_msg_hdr_t; +} __attribute__ ((packed)) fpm_msg_hdr_t; + +#ifdef __SUNPRO_C +#pragma pack() +#endif /* * The current version of the FPM protocol is 1. @@ -139,8 +143,14 @@ typedef enum fpm_msg_type_e_ { /* * Indicates that the payload is a completely formed netlink * message. + * + * XXX Netlink cares about the alignment of messages. When any + * FPM_MSG_TYPE_NETLINK messages are sent over a channel, then all + * messages should be sized such that netlink alignment is + * maintained. */ FPM_MSG_TYPE_NETLINK = 1, + FPM_MSG_TYPE_PROTOBUF = 2, } fpm_msg_type_e; /* @@ -154,6 +164,8 @@ typedef enum fpm_msg_type_e_ { * fpm_msg_align * * Round up the given length to the desired alignment. + * + * **NB**: Alignment is required only when netlink messages are used. */ static inline size_t fpm_msg_align (size_t len) @@ -165,7 +177,13 @@ fpm_msg_align (size_t len) * The (rounded up) size of the FPM message header. This ensures that * the message payload always starts at an aligned address. */ -#define FPM_MSG_HDR_LEN (fpm_msg_align (sizeof (fpm_msg_hdr_t))) +#define FPM_MSG_HDR_LEN (sizeof (fpm_msg_hdr_t)) + +#ifndef COMPILE_ASSERT +#define COMPILE_ASSERT(x) extern int __dummy[2 * !!(x) - 1] +#endif + +COMPILE_ASSERT(FPM_MSG_ALIGNTO == FPM_MSG_HDR_LEN); /* * fpm_data_len_to_msg_len @@ -176,7 +194,7 @@ fpm_msg_align (size_t len) static inline size_t fpm_data_len_to_msg_len (size_t data_len) { - return fpm_msg_align (data_len) + FPM_MSG_HDR_LEN; + return data_len + FPM_MSG_HDR_LEN; } /* @@ -250,7 +268,11 @@ fpm_msg_hdr_ok (const fpm_msg_hdr_t *hdr) if (msg_len < FPM_MSG_HDR_LEN || msg_len > FPM_MAX_MSG_LEN) return 0; - if (fpm_msg_align (msg_len) != msg_len) + /* + * Netlink messages must be aligned properly. + */ + if (hdr->msg_type == FPM_MSG_TYPE_NETLINK && + fpm_msg_align (msg_len) != msg_len) return 0; return 1; diff --git a/fpm/fpm.proto b/fpm/fpm.proto new file mode 100644 index 0000000000..26d6346302 --- /dev/null +++ b/fpm/fpm.proto @@ -0,0 +1,119 @@ +// +// fpm.proto +// +// @copyright Copyright (C) 2016 Sproute Networks, Inc. +// +// @author Avneesh Sachdev +// +// Permission is granted to use, copy, modify and/or distribute this +// software under either one of the licenses below. +// +// Note that if you use other files from the Quagga tree directly or +// indirectly, then the licenses in those files still apply. +// +// Please retain both licenses below when modifying this code in the +// Quagga tree. +// + +// +// License Option 1: GPL +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful,but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +// +// License Option 2: ISC License +// +// Permission to use, copy, modify, and/or distribute this software +// for any purpose with or without fee is hereby granted, provided +// that the above copyright notice and this permission notice appear +// in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +// +// Protobuf definitions pertaining to the Forwarding Plane Manager component. +// + +package fpm; + +import "qpb/qpb.proto"; + +// +// A Nexthop for a route. It indicates how packets to a given prefix +// should be forwarded (for instance, send them out of a specified +// interface to a specified address). +// +message Nexthop { + optional qpb.IfIdentifier if_id = 2; + optional qpb.L3Address address = 3; +} + +message RouteKey { + optional qpb.L3Prefix prefix = 1; +} + +message DeleteRoute { + required uint32 vrf_id = 1; + required qpb.AddressFamily address_family = 2; + required qpb.SubAddressFamily sub_address_family = 3; + required RouteKey key = 4; +} + +enum RouteType { + UNKNOWN = 0; + NORMAL = 1; + UNREACHABLE = 2; + BLACKHOLE = 3; +} + +message AddRoute { + required uint32 vrf_id = 1; + required qpb.AddressFamily address_family = 2; + required qpb.SubAddressFamily sub_address_family = 3; + required RouteKey key = 4; + + optional RouteType route_type = 5; + + required qpb.Protocol protocol = 6; + + required int32 metric = 8; + + repeated Nexthop nexthops = 9; +} + +// +// Any message from the FPM. +// +message Message { + enum Type { + UNKNOWN_MSG = 0; + ADD_ROUTE = 1; + DELETE_ROUTE = 2; + }; + + optional Type type = 1; + + optional AddRoute add_route = 2; + optional DeleteRoute delete_route = 3; +} diff --git a/fpm/fpm_pb.c b/fpm/fpm_pb.c new file mode 100644 index 0000000000..ba18627ea1 --- /dev/null +++ b/fpm/fpm_pb.c @@ -0,0 +1,28 @@ +/* + * fpm_pb.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Main file for the fpm_pb library. + */ diff --git a/fpm/fpm_pb.h b/fpm/fpm_pb.h new file mode 100644 index 0000000000..8f74ac06eb --- /dev/null +++ b/fpm/fpm_pb.h @@ -0,0 +1,63 @@ +/* + * fpm_pb.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Public header file for fpm protobuf definitions. + */ + +#ifndef _FPM_PB_H +#define _FPM_PB_H + +#include "route_types.h" +#include "qpb/qpb.h" + +#include "fpm/fpm.pb-c.h" + +/* + * fpm__route_key__create + */ +#define fpm_route_key_create fpm__route_key__create +static inline Fpm__RouteKey * +fpm__route_key__create (qpb_allocator_t *allocator, struct prefix *prefix) +{ + Fpm__RouteKey *key; + + key = QPB_ALLOC (allocator, typeof (*key)); + if (!key) + { + return NULL; + } + fpm__route_key__init (key); + + key->prefix = qpb__l3_prefix__create (allocator, prefix); + if (!key->prefix) + { + return NULL; + } + + return key; +} + +#endif From 4087409cdba71b5f95662e202cc33cf92ed055cd Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Thu, 14 Jul 2016 09:59:30 -0700 Subject: [PATCH 054/136] Use only the ISC license for .proto files. Simplify licensing for protobuf files based on discussion on the quagga-dev mailing list. Previously, users could choose between the GPL and the ISC license. Signed-off-by: Avneesh Sachdev Signed-off-by: Avneesh Sachdev --- fpm/fpm.proto | 31 ------------------------------- qpb/qpb.proto | 31 ------------------------------- 2 files changed, 62 deletions(-) diff --git a/fpm/fpm.proto b/fpm/fpm.proto index 26d6346302..318e80a92e 100644 --- a/fpm/fpm.proto +++ b/fpm/fpm.proto @@ -5,37 +5,6 @@ // // @author Avneesh Sachdev // -// Permission is granted to use, copy, modify and/or distribute this -// software under either one of the licenses below. -// -// Note that if you use other files from the Quagga tree directly or -// indirectly, then the licenses in those files still apply. -// -// Please retain both licenses below when modifying this code in the -// Quagga tree. -// - -// -// License Option 1: GPL -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful,but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// - -// -// License Option 2: ISC License -// // Permission to use, copy, modify, and/or distribute this software // for any purpose with or without fee is hereby granted, provided // that the above copyright notice and this permission notice appear diff --git a/qpb/qpb.proto b/qpb/qpb.proto index 7ee409df87..8323d3ed73 100644 --- a/qpb/qpb.proto +++ b/qpb/qpb.proto @@ -5,37 +5,6 @@ * * @author Avneesh Sachdev * - * Permission is granted to use, copy, modify and/or distribute this - * software under either one of the licenses below. - * - * Note that if you use other files from the Quagga tree directly or - * indirectly, then the licenses in those files still apply. - * - * Please retain both licenses below when modifying this code in the - * Quagga tree. - */ - -/* - * License Option 1: GPL - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * License Option 2: ISC License - * * Permission to use, copy, modify, and/or distribute this software * for any purpose with or without fee is hereby granted, provided * that the above copyright notice and this permission notice appear From fb0aa88623f72b7e4d0f35e8df3f96aa090fc5c8 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Mon, 4 Apr 2016 10:54:58 -0700 Subject: [PATCH 055/136] zebra: optionally use protobuf with FPM Change zebra so that it can optionally use protobuf serialization when communicating with a Forwarding Plane Manager component. * zebra/main.c Add the --fpm-format/-F command line option. This allows the user to control the format (protbuf|netlink) that is used to communicate with the FPM. * zebra/zebra_fpm.c - zebra_init_msg_format(), This new function is invoked on process startup to determine the FPM format that should be used. - zfpm_init() Change to accept any 'FPM message format' specified by the user (via the new command line flag). - zebra_encode_route() Tweak to use the selected FPM format. * zebra_fpm_protobuf.c New code to build protobuf messages to be sent to the FPM. * zebra/Makefile.am - Include common.am - Build new file zebra_fpm_protobuf.c when protobuf is available. - Link with the fpm_pb library. Signed-off-by: Avneesh Sachdev --- zebra/Makefile.am | 11 +- zebra/main.c | 14 +- zebra/zebra_fpm.c | 139 ++++++++++++++--- zebra/zebra_fpm.h | 3 +- zebra/zebra_fpm_private.h | 5 + zebra/zebra_fpm_protobuf.c | 311 +++++++++++++++++++++++++++++++++++++ 6 files changed, 455 insertions(+), 28 deletions(-) create mode 100644 zebra/zebra_fpm_protobuf.c diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 851e597796..26c283b9c1 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -1,3 +1,5 @@ +include ../common.am + ## Process this file with automake to produce Makefile.in. AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib @@ -21,6 +23,10 @@ if HAVE_NETLINK othersrc = zebra_fpm_netlink.c endif +if HAVE_PROTOBUF +protobuf_srcs = zebra_fpm_protobuf.c +endif + AM_CFLAGS = $(WERROR) sbin_PROGRAMS = zebra @@ -33,7 +39,8 @@ zebra_SOURCES = \ redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ - zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c + zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \ + $(protobuf_srcs) testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ @@ -49,7 +56,7 @@ noinst_HEADERS = \ zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \ zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h -zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) +zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS) testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) diff --git a/zebra/main.c b/zebra/main.c index faa6cdb317..da7e6b6fb8 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -83,6 +83,7 @@ struct option longopts[] = { "daemon", no_argument, NULL, 'd'}, { "allow_delete", no_argument, NULL, 'a'}, { "keep_kernel", no_argument, NULL, 'k'}, + { "fpm_format", required_argument, NULL, 'F'}, { "config_file", required_argument, NULL, 'f'}, { "pid_file", required_argument, NULL, 'i'}, { "socket", required_argument, NULL, 'z'}, @@ -143,6 +144,7 @@ usage (char *progname, int status) "-d, --daemon Runs in daemon mode\n"\ "-a, --allow_delete Allow other processes to delete Quagga Routes\n" \ "-f, --config_file Set configuration file name\n"\ + "-F, --fpm_format Set fpm format to 'netlink' or 'protobuf'\n"\ "-i, --pid_file Set process identifier file name\n"\ "-z, --socket Set path of zebra socket\n"\ "-k, --keep_kernel Don't delete old routes which installed by "\ @@ -238,6 +240,7 @@ main (int argc, char **argv) char *progname; struct thread thread; char *zserv_path = NULL; + char *fpm_format = NULL; /* Set umask before anything for security */ umask (0027); @@ -257,9 +260,9 @@ main (int argc, char **argv) int opt; #ifdef HAVE_NETLINK - opt = getopt_long (argc, argv, "bdakf:i:z:hA:P:ru:g:vs:C", longopts, 0); + opt = getopt_long (argc, argv, "bdakf:F:i:z:hA:P:ru:g:vs:C", longopts, 0); #else - opt = getopt_long (argc, argv, "bdakf:i:z:hA:P:ru:g:vC", longopts, 0); + opt = getopt_long (argc, argv, "bdakf:F:i:z:hA:P:ru:g:vC", longopts, 0); #endif /* HAVE_NETLINK */ if (opt == EOF) @@ -286,6 +289,9 @@ main (int argc, char **argv) case 'f': config_file = optarg; break; + case 'F': + fpm_format = optarg; + break; case 'A': vty_addr = optarg; break; @@ -377,9 +383,9 @@ main (int argc, char **argv) #endif /* HAVE_SNMP */ #ifdef HAVE_FPM - zfpm_init (zebrad.master, 1, 0); + zfpm_init (zebrad.master, 1, 0, fpm_format); #else - zfpm_init (zebrad.master, 0, 0); + zfpm_init (zebrad.master, 0, 0, fpm_format); #endif /* Process the configuration file. Among other configuration diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 34f0068514..5a68bcbfac 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -141,6 +141,15 @@ typedef enum { } zfpm_state_t; +/* + * Message format to be used to communicate with the FPM. + */ +typedef enum +{ + ZFPM_MSG_FORMAT_NONE, + ZFPM_MSG_FORMAT_NETLINK, + ZFPM_MSG_FORMAT_PROTOBUF, +} zfpm_msg_format_e; /* * Globals. */ @@ -152,6 +161,11 @@ typedef struct zfpm_glob_t_ */ int enabled; + /* + * Message format to be used to communicate with the fpm. + */ + zfpm_msg_format_e message_format; + struct thread_master *master; zfpm_state_t state; @@ -866,19 +880,40 @@ zfpm_writes_pending (void) */ static inline int zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf, - size_t in_buf_len) + size_t in_buf_len, fpm_msg_type_e *msg_type) { -#ifndef HAVE_NETLINK - return 0; -#else - + size_t len; int cmd; + len = 0; - cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE; + *msg_type = FPM_MSG_TYPE_NONE; - return zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len); + switch (zfpm_g->message_format) { + case ZFPM_MSG_FORMAT_PROTOBUF: +#ifdef HAVE_PROTOBUF + len = zfpm_protobuf_encode_route (dest, rib, (uint8_t *) in_buf, + in_buf_len); + *msg_type = FPM_MSG_TYPE_PROTOBUF; +#endif + break; + + case ZFPM_MSG_FORMAT_NETLINK: +#ifdef HAVE_NETLINK + *msg_type = FPM_MSG_TYPE_NETLINK; + cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE; + len = zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len); + assert(fpm_msg_align(len) == len); + *msg_type = FPM_MSG_TYPE_NETLINK; #endif /* HAVE_NETLINK */ + break; + + default: + break; + } + + return len; + } /* @@ -886,7 +921,7 @@ zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf, * * Returns the rib that is to be sent to the FPM for a given dest. */ -static struct rib * +struct rib * zfpm_route_for_update (rib_dest_t *dest) { struct rib *rib; @@ -922,6 +957,7 @@ zfpm_build_updates (void) fpm_msg_hdr_t *hdr; struct rib *rib; int is_add, write_msg; + fpm_msg_type_e msg_type; s = zfpm_g->obuf; @@ -946,7 +982,6 @@ zfpm_build_updates (void) hdr = (fpm_msg_hdr_t *) buf; hdr->version = FPM_PROTO_VERSION; - hdr->msg_type = FPM_MSG_TYPE_NETLINK; data = fpm_msg_data (hdr); @@ -966,11 +1001,13 @@ zfpm_build_updates (void) } if (write_msg) { - data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data); + data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data, + &msg_type); assert (data_len); if (data_len) { + hdr->msg_type = msg_type; msg_len = fpm_data_len_to_msg_len (data_len); hdr->msg_len = htons (msg_len); stream_forward_endp (s, msg_len); @@ -1572,6 +1609,64 @@ DEFUN ( no_fpm_remote_ip, } +/* + * zfpm_init_message_format + */ +static inline void +zfpm_init_message_format (const char *format) +{ + int have_netlink, have_protobuf; + + have_netlink = have_protobuf = 0; + +#ifdef HAVE_NETLINK + have_netlink = 1; +#endif + +#ifdef HAVE_PROTOBUF + have_protobuf = 1; +#endif + + zfpm_g->message_format = ZFPM_MSG_FORMAT_NONE; + + if (!format) + { + if (have_netlink) + { + zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK; + } + else if (have_protobuf) + { + zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF; + } + return; + } + + if (!strcmp ("netlink", format)) + { + if (!have_netlink) + { + zlog_err ("FPM netlink message format is not available"); + return; + } + zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK; + return; + } + + if (!strcmp ("protobuf", format)) + { + if (!have_protobuf) + { + zlog_err ("FPM protobuf message format is not available"); + return; + } + zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF; + return; + } + + zlog_warn ("Unknown fpm format '%s'", format); +} + /** * fpm_remote_srv_write * @@ -1601,11 +1696,13 @@ int fpm_remote_srv_write (struct vty *vty ) * * @param[in] port port at which FPM is running. * @param[in] enable TRUE if the zebra FPM module should be enabled + * @param[in] format to use to talk to the FPM. Can be 'netink' or 'protobuf'. * * Returns TRUE on success. */ int -zfpm_init (struct thread_master *master, int enable, uint16_t port) +zfpm_init (struct thread_master *master, int enable, uint16_t port, + const char *format) { static int initialized = 0; @@ -1621,16 +1718,6 @@ zfpm_init (struct thread_master *master, int enable, uint16_t port) zfpm_g->sock = -1; zfpm_g->state = ZFPM_STATE_IDLE; - /* - * Netlink must currently be available for the Zebra-FPM interface - * to be enabled. - */ -#ifndef HAVE_NETLINK - enable = 0; -#endif - - zfpm_g->enabled = enable; - zfpm_stats_init (&zfpm_g->stats); zfpm_stats_init (&zfpm_g->last_ivl_stats); zfpm_stats_init (&zfpm_g->cumulative_stats); @@ -1640,6 +1727,16 @@ zfpm_init (struct thread_master *master, int enable, uint16_t port) install_element (CONFIG_NODE, &fpm_remote_ip_cmd); install_element (CONFIG_NODE, &no_fpm_remote_ip_cmd); + zfpm_init_message_format(format); + + /* + * Disable FPM interface if no suitable format is available. + */ + if (zfpm_g->message_format == ZFPM_MSG_FORMAT_NONE) + enable = 0; + + zfpm_g->enabled = enable; + if (!enable) { return 1; } diff --git a/zebra/zebra_fpm.h b/zebra/zebra_fpm.h index 7fa71e40bc..fdb069965b 100644 --- a/zebra/zebra_fpm.h +++ b/zebra/zebra_fpm.h @@ -28,7 +28,8 @@ /* * Externs. */ -extern int zfpm_init (struct thread_master *master, int enable, uint16_t port); +extern int zfpm_init (struct thread_master *master, int enable, uint16_t port, + const char *message_format); extern void zfpm_trigger_update (struct route_node *rn, const char *reason); extern int fpm_remote_srv_write (struct vty *vty); diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h index 809a70a445..1c4fd4c22f 100644 --- a/zebra/zebra_fpm_private.h +++ b/zebra/zebra_fpm_private.h @@ -53,4 +53,9 @@ extern int zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, char *in_buf, size_t in_buf_len); +extern int +zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib, + uint8_t *in_buf, size_t in_buf_len); + +extern struct rib *zfpm_route_for_update (rib_dest_t *dest); #endif /* _ZEBRA_FPM_PRIVATE_H */ diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c new file mode 100644 index 0000000000..beef310b17 --- /dev/null +++ b/zebra/zebra_fpm_protobuf.c @@ -0,0 +1,311 @@ +/* + * zebra_fpm_protobuf.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "log.h" +#include "rib.h" + +#include "qpb/qpb.pb-c.h" +#include "qpb/qpb.h" +#include "qpb/qpb_allocator.h" +#include "qpb/linear_allocator.h" +#include "fpm/fpm_pb.h" + +#include "zebra_fpm_private.h" + +/* + * create_delete_route_message + */ +static Fpm__DeleteRoute * +create_delete_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, + struct rib *rib) +{ + Fpm__DeleteRoute *msg; + + msg = QPB_ALLOC(allocator, typeof(*msg)); + if (!msg) { + assert(0); + return NULL; + } + + fpm__delete_route__init(msg); + msg->vrf_id = rib_dest_vrf(dest)->vrf_id; + + qpb_address_family_set(&msg->address_family, rib_dest_af(dest)); + + /* + * XXX Hardcode subaddress family for now. + */ + msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; + msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest)); + if (!msg->key) { + assert(0); + return NULL; + } + + return msg; +} + +/* + * add_nexthop + */ +static inline int +add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest, + struct nexthop *nexthop) +{ + uint32_t if_index; + union g_addr *gateway, *src; + + gateway = src = NULL; + + if_index = nexthop->ifindex; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + gateway = &nexthop->gate; + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + gateway = &nexthop->gate; + } + + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + { + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + + if (!gateway && if_index == 0) + return 0; + + /* + * We have a valid nexthop. + */ + { + Fpm__Nexthop *pb_nh; + pb_nh = QPB_ALLOC(allocator, typeof(*pb_nh)); + if (!pb_nh) { + assert(0); + return 0; + } + + fpm__nexthop__init(pb_nh); + + if (if_index != 0) { + pb_nh->if_id = qpb_if_identifier_create (allocator, if_index); + } + + if (gateway) { + pb_nh->address = qpb_l3_address_create (allocator, gateway, + rib_dest_af(dest)); + } + + msg->nexthops[msg->n_nexthops++] = pb_nh; + } + + // TODO: Use src. + + return 1; +} + +/* + * create_add_route_message + */ +static Fpm__AddRoute * +create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, + struct rib *rib) +{ + Fpm__AddRoute *msg; + int discard; + struct nexthop *nexthop, *tnexthop; + int recursing; + uint num_nhs, u; + struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)]; + + msg = QPB_ALLOC(allocator, typeof(*msg)); + if (!msg) { + assert(0); + return NULL; + } + + fpm__add_route__init(msg); + + msg->vrf_id = rib_dest_vrf(dest)->vrf_id; + + qpb_address_family_set (&msg->address_family, rib_dest_af(dest)); + + /* + * XXX Hardcode subaddress family for now. + */ + msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; + msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest)); + qpb_protocol_set (&msg->protocol, rib->type); + + if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) + discard = 1; + else + discard = 0; + + if (discard) + { + if (rib->flags & ZEBRA_FLAG_BLACKHOLE) { + msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE; + } else if (rib->flags & ZEBRA_FLAG_REJECT) { + msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE; + } else { + assert (0); + } + return msg; + } + else { + msg->route_type = FPM__ROUTE_TYPE__NORMAL; + } + + msg->metric = rib->metric; + + /* + * Figure out the set of nexthops to be added to the message. + */ + num_nhs = 0; + for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing)) + { + if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM) + break; + + if (num_nhs >= ZEBRA_NUM_OF(nexthops)) + break; + + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + nexthops[num_nhs] = nexthop; + num_nhs++; + } + + if (!num_nhs) { + zfpm_debug ("netlink_encode_route(): No useful nexthop."); + assert(0); + return NULL; + } + + /* + * And add them to the message. + */ + if (!(msg->nexthops = qpb_alloc_ptr_array(allocator, num_nhs))) { + assert(0); + return NULL; + } + + msg->n_nexthops = 0; + for (u = 0; u < num_nhs; u++) { + if (!add_nexthop(allocator, msg, dest, nexthops[u])) { + assert(0); + return NULL; + } + } + + assert(msg->n_nexthops == num_nhs); + + return msg; +} + +/* + * create_route_message + */ +static Fpm__Message * +create_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, + struct rib *rib) +{ + Fpm__Message *msg; + + msg = QPB_ALLOC(allocator, typeof(*msg)); + if (!msg) { + assert(0); + return NULL; + } + + fpm__message__init(msg); + + if (!rib) { + msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE; + msg->delete_route = create_delete_route_message(allocator, dest, rib); + if (!msg->delete_route) { + assert(0); + return NULL; + } + return msg; + } + + msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE; + msg->add_route = create_add_route_message(allocator, dest, rib); + if (!msg->add_route) { + assert(0); + return NULL; + } + + return msg; +} + +/* + * zfpm_protobuf_encode_route + * + * Create a protobuf message corresponding to the given route in the + * given buffer space. + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +int +zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib, + uint8_t *in_buf, size_t in_buf_len) +{ + Fpm__Message *msg; + QPB_DECLARE_STACK_ALLOCATOR (allocator, 4096); + size_t len; + + QPB_INIT_STACK_ALLOCATOR (allocator); + + msg = create_route_message(&allocator, dest, rib); + if (!msg) { + assert(0); + return 0; + } + + len = fpm__message__pack(msg, (uint8_t *) in_buf); + assert(len <= in_buf_len); + + QPB_RESET_STACK_ALLOCATOR (allocator); + return len; +} From 0e957b367b54c3126ea75dec9eb3b8148dbd5747 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Mon, 4 Apr 2016 10:54:59 -0700 Subject: [PATCH 056/136] doc: add blurb on use of protobuf with FPM Add text about using protobuf as an alternative format for the FPM interface. Signed-off-by: Avneesh Sachdev --- doc/main.texi | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/main.texi b/doc/main.texi index 29ed17c82b..5302c9687d 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -407,8 +407,32 @@ routes that it may have picked up from the kernel. The existing interaction of zebra with the kernel remains unchanged -- that is, the kernel continues to receive FIB updates as before. -The format of the messages exchanged with the FPM is defined by the -file @file{fpm/fpm.h} in the quagga tree. +The encapsulation header for the messages exchanged with the FPM is +defined by the file @file{fpm/fpm.h} in the quagga tree. The routes +themselves are encoded in netlink or protobuf format, with netlink +being the default. + +Protobuf is one of a number of new serialization formats wherein the +message schema is expressed in a purpose-built language. Code for +encoding/decoding to/from the wire format is generated from the +schema. Protobuf messages can be extended easily while maintaining +backward-compatibility with older code. Protobuf has the following +advantages over netlink: + +@itemize +@item +Code for serialization/deserialization is generated +automatically. This reduces the likelihood of bugs, allows third-party +programs to be integrated quickly, and makes it easy to add fields. +@item +The message format is not tied to an OS (Linux), and can be evolved +independently. +@end itemize + +As mentioned before, zebra encodes routes sent to the FPM in netlink +format by default. The format can be controlled via the +@code{--fpm_format} command-line option to zebra, which currently +takes the values @code{netlink} and @code{protobuf}. The zebra FPM interface uses replace semantics. That is, if a 'route add' message for a prefix is followed by another 'route add' message, From 3b8282a8e8bdf97ab78cb11321f07063488640fa Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Fri, 11 Mar 2016 12:21:24 -0800 Subject: [PATCH 057/136] build: support for "development build" * configure.ac Add the --enable-dev-build flag. It controls the DEV_BUILD define for autoconf and automake, which can be used to conditionally build in code that is only intended for development.. Signed-off-by: Avneesh Sachdev --- configure.ac | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure.ac b/configure.ac index 0fd87431fa..00c71eb9ff 100755 --- a/configure.ac +++ b/configure.ac @@ -329,6 +329,9 @@ if test $ac_cv_lib_json_c_json_object_get = no; then fi fi +AC_ARG_ENABLE([dev_build], + AS_HELP_STRING([--enable-dev-build], [build for development])) + if test x"${enable_gcc_rdynamic}" != x"no" ; then if test x"${enable_gcc_rdynamic}" = x"yes" -o x"$COMPILER" = x"GCC"; then LDFLAGS="${LDFLAGS} -rdynamic" @@ -397,6 +400,11 @@ if test "${enable_fpm}" = "yes"; then AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support) fi +if test "x${enable_dev_build}" = "xyes"; then + AC_DEFINE(DEV_BUILD,,Build for development) +fi +AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"]) + # # Logic for protobuf support. # From b80f3b245caa3ff1cf40b5a0d7be47540ffd6d4c Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Fri, 11 Mar 2016 12:21:26 -0800 Subject: [PATCH 058/136] zebra: add developer test functions for FPM code Add test functions for the zebra code that interfaces with the Forwarding Plane Manager. These functions can be invoked in a development build via the recently-added 'invoke' command. For example: # invoke zebra function zfpm_dt_benchmark_protobuf_encode 100000 Changes: * zebra/zebra_fpm_dt.c Add the following functions. Each function encodes or decodes a route in a particular FPM format a specified number of times. - zfpm_dt_benchmark_netlink_encode() - zfpm_dt_benchmark_protobuf_encode() - zfpm_dt_benchmark_protobuf_decode() * zebra/Makefile.am Compile zebra_fpm_dt when building a development build. Signed-off-by: Avneesh Sachdev --- zebra/Makefile.am | 7 +- zebra/zebra_fpm_dt.c | 275 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 zebra/zebra_fpm_dt.c diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 26c283b9c1..ab67ef5898 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -27,6 +27,10 @@ if HAVE_PROTOBUF protobuf_srcs = zebra_fpm_protobuf.c endif +if DEV_BUILD +dev_srcs = zebra_fpm_dt.c +endif + AM_CFLAGS = $(WERROR) sbin_PROGRAMS = zebra @@ -40,7 +44,8 @@ zebra_SOURCES = \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \ - $(protobuf_srcs) + $(protobuf_srcs) \ + $(dev_srcs) testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c new file mode 100644 index 0000000000..bd171c89b2 --- /dev/null +++ b/zebra/zebra_fpm_dt.c @@ -0,0 +1,275 @@ +/* + * zebra_fpm_dt.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * Developer tests for the zebra code that interfaces with the + * forwarding plane manager. + * + * The functions here are built into developer builds of zebra (when + * DEV_BUILD is defined), and can be called via the 'invoke' cli + * command. + * + * For example: + * + * # invoke zebra function zfpm_dt_benchmark_protobuf_encode 100000 + * + */ + +#include +#include "log.h" +#include "vrf.h" + +#include "zebra/rib.h" + +#include "zebra_fpm_private.h" + +#include "qpb/qpb_allocator.h" +#include "qpb/linear_allocator.h" + +#include "qpb/qpb.h" +#include "fpm/fpm.pb-c.h" + +/* + * Externs. + */ +extern int zfpm_dt_benchmark_netlink_encode (int argc, const char **argv); +extern int zfpm_dt_benchmark_protobuf_encode (int argc, const char **argv); +extern int zfpm_dt_benchmark_protobuf_decode (int argc, const char **argv); + +/* + * zfpm_dt_find_route + * + * Selects a suitable rib destination for fpm interface tests. + */ +static int +zfpm_dt_find_route (rib_dest_t **dest_p, struct rib **rib_p) +{ + struct route_node *rnode; + route_table_iter_t iter; + struct route_table *table; + rib_dest_t *dest; + struct rib *rib; + int ret; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); + if (!table) + return 0; + + route_table_iter_init(&iter, table); + while ((rnode = route_table_iter_next(&iter))) + { + dest = rib_dest_from_rnode (rnode); + + if (!dest) + continue; + + rib = zfpm_route_for_update(dest); + if (!rib) + continue; + + if (rib->nexthop_active_num <= 0) + continue; + + *dest_p = dest; + *rib_p = rib; + ret = 1; + goto done; + } + + ret = 0; + + done: + route_table_iter_cleanup(&iter); + return ret; +} +#ifdef HAVE_NETLINK + +/* + * zfpm_dt_benchmark_netlink_encode + */ +int +zfpm_dt_benchmark_netlink_encode (int argc, const char **argv) +{ + int times, i, len; + rib_dest_t *dest; + struct rib *rib; + char buf[4096]; + + times = 100000; + if (argc > 0) { + times = atoi(argv[0]); + } + + if (!zfpm_dt_find_route(&dest, &rib)) { + return 1; + } + + for (i = 0; i < times; i++) { + len = zfpm_netlink_encode_route(RTM_NEWROUTE, dest, rib, buf, sizeof(buf)); + if (len <= 0) { + return 2; + } + } + return 0; +} + +#endif /* HAVE_NETLINK */ + +#ifdef HAVE_PROTOBUF + +/* + * zfpm_dt_benchmark_protobuf_encode + */ +int +zfpm_dt_benchmark_protobuf_encode (int argc, const char **argv) +{ + int times, i, len; + rib_dest_t *dest; + struct rib *rib; + uint8_t buf[4096]; + + times = 100000; + if (argc > 0) { + times = atoi(argv[0]); + } + + if (!zfpm_dt_find_route(&dest, &rib)) { + return 1; + } + + for (i = 0; i < times; i++) { + len = zfpm_protobuf_encode_route(dest, rib, buf, sizeof(buf)); + if (len <= 0) { + return 2; + } + } + return 0; +} + +/* + * zfpm_dt_log_fpm_message + */ +static void +zfpm_dt_log_fpm_message (Fpm__Message *msg) +{ + Fpm__AddRoute *add_route; + Fpm__Nexthop *nexthop; + struct prefix prefix; + u_char family, nh_family; + uint if_index; + char *if_name; + size_t i; + char buf[INET6_ADDRSTRLEN]; + union g_addr nh_addr; + + if (msg->type != FPM__MESSAGE__TYPE__ADD_ROUTE) + return; + + zfpm_debug ("Add route message"); + add_route = msg->add_route; + + if (!qpb_address_family_get (add_route->address_family, &family)) + return; + + if (!qpb_l3_prefix_get (add_route->key->prefix, family, &prefix)) + return; + + zfpm_debug ("Vrf id: %d, Prefix: %s/%d, Metric: %d", add_route->vrf_id, + inet_ntop (family, &prefix.u.prefix, buf, sizeof (buf)), + prefix.prefixlen, add_route->metric); + + /* + * Go over nexthops. + */ + for (i = 0; i < add_route->n_nexthops; i++) + { + nexthop = add_route->nexthops[i]; + if (!qpb_if_identifier_get (nexthop->if_id, &if_index, &if_name)) + continue; + + if (nexthop->address) + qpb_l3_address_get (nexthop->address, &nh_family, &nh_addr); + + zfpm_debug ("Nexthop - if_index: %d (%s), gateway: %s, ", if_index, + if_name ? if_name : "name not specified", + nexthop->address ? inet_ntoa (nh_addr.ipv4) : "None"); + } +} + +/* + * zfpm_dt_benchmark_protobuf_decode + */ +int +zfpm_dt_benchmark_protobuf_decode (int argc, const char **argv) +{ + int times, i, len; + rib_dest_t *dest; + struct rib *rib; + uint8_t msg_buf[4096]; + QPB_DECLARE_STACK_ALLOCATOR (allocator, 8192); + Fpm__Message *fpm_msg; + + QPB_INIT_STACK_ALLOCATOR (allocator); + + times = 100000; + if (argc > 0) + times = atoi(argv[0]); + + if (!zfpm_dt_find_route (&dest, &rib)) + return 1; + + /* + * Encode the route into the message buffer once only. + */ + len = zfpm_protobuf_encode_route (dest, rib, msg_buf, sizeof (msg_buf)); + if (len <= 0) + return 2; + + // Decode once, and display the decoded message + fpm_msg = fpm__message__unpack(&allocator, len, msg_buf); + + if (fpm_msg) + { + zfpm_dt_log_fpm_message(fpm_msg); + QPB_RESET_STACK_ALLOCATOR (allocator); + } + + /* + * Decode encoded message the specified number of times. + */ + for (i = 0; i < times; i++) + { + fpm_msg = fpm__message__unpack (&allocator, len, msg_buf); + + if (!fpm_msg) + return 3; + + // fpm__message__free_unpacked(msg, NULL); + QPB_RESET_STACK_ALLOCATOR (allocator); + } + return 0; +} + +#endif /* HAVE_PROTOBUF */ From 518acd6fd802dbb5ab30db7d12b9995f582b824f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 21 Sep 2016 12:45:46 +0200 Subject: [PATCH 059/136] fpm/protobuf: fix compile errors & warnings BABEL was removed, ifname nexthops were removed, additional includes were needed, and lastly the protobuf enum-handling triggers a warning. Signed-off-by: David Lamparter --- qpb/qpb.h | 3 ++- zebra/zebra_fpm_protobuf.c | 6 +++--- zebra/zserv.c | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qpb/qpb.h b/qpb/qpb.h index 55c1deb19d..ad5bdc8b8e 100644 --- a/qpb/qpb.h +++ b/qpb/qpb.h @@ -78,6 +78,8 @@ qpb__address_family__get (Qpb__AddressFamily pb_family, u_char *family) case QPB__ADDRESS_FAMILY__UNKNOWN_AF: return 0; + default: /* protobuf "magic value" _QPB__ADDRESS_FAMILY_IS_INT_SIZE */ + return 0; } return 0; @@ -184,7 +186,6 @@ qpb__protocol__set (Qpb__Protocol *pb_proto, int route_type) case ZEBRA_ROUTE_HSLS: case ZEBRA_ROUTE_OLSR: - case ZEBRA_ROUTE_BABEL: case ZEBRA_ROUTE_MAX: case ZEBRA_ROUTE_SYSTEM: default: diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index beef310b17..702c355f1a 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -26,6 +26,8 @@ #include "log.h" #include "rib.h" +#include "zserv.h" +#include "zebra_vrf.h" #include "qpb/qpb.pb-c.h" #include "qpb/qpb.h" @@ -91,14 +93,12 @@ add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest, } if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { gateway = &nexthop->gate; } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME) + if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { if (nexthop->src.ipv4.s_addr) src = &nexthop->src; diff --git a/zebra/zserv.c b/zebra/zserv.c index 27d10a1718..8fcc837c53 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -53,6 +53,7 @@ #include "zebra/zebra_ptm.h" #include "zebra/rtadv.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_fpm.h" /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; From 6b143a68d3a44be42f2370b9d91409eceb14cebd Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 21 Sep 2016 18:20:49 +0200 Subject: [PATCH 060/136] lib: force local MIN/MAX macros Linux/glibc sys/param.h has definitions of MIN/MAX that result in multiple evaluations of its parameters. Force local definitions. Signed-off-by: David Lamparter --- lib/checksum.c | 2 +- lib/zebra.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/checksum.c b/lib/checksum.c index 116aaafc97..3d6cd45791 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -47,7 +47,7 @@ in_cksum(void *parg, int nbytes) } /* Fletcher Checksum -- Refer to RFC1008. */ -#define MODX 4102 /* 5802 should be fine */ +#define MODX 4102U /* 5802 should be fine */ /* To be consistent, offset is 0-based index, rather than the 1-based index required in the specification ISO 8473, Annex C.1 */ diff --git a/lib/zebra.h b/lib/zebra.h index da069d1dfa..8763abd766 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -352,18 +352,21 @@ struct in_pktinfo #endif /* ndef BYTE_ORDER */ /* MAX / MIN are not commonly defined, but useful */ -#ifndef MAX +/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */ +#ifdef MAX +#undef MAX +#endif #define MAX(a, b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a > _b ? _a : _b; }) +#ifdef MIN +#undef MIN #endif -#ifndef MIN #define MIN(a, b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a < _b ? _a : _b; }) -#endif #define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0])) From d91788284ed910bcf945c01ceb18334423cc352d Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Tue, 9 Feb 2016 15:23:03 +0000 Subject: [PATCH 061/136] lib: Check prefix length from zebra is sensible * zclient.c: prefix length on router-id and interface address add messages not sanity checked. fix. * */*_zebra.c: Prefix length on zebra route read was not checked, and clients use it to write to storage. An evil zebra could overflow client structures by sending overly long prefixlen. Prompted by discussions with: Donald Sharp --- bgpd/bgp_zebra.c | 4 ++-- isisd/isis_zebra.c | 2 +- lib/zclient.c | 45 +++++++++++++++++++++++++++----------------- ospf6d/ospf6_zebra.c | 2 +- ospfd/ospf_zebra.c | 2 +- ripd/rip_zebra.c | 2 +- ripngd/ripng_zebra.c | 2 +- 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 15db321557..0f5c3ce7aa 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -607,7 +607,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* IPv4 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, stream_getc (s)); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ @@ -722,7 +722,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, stream_getc (s)); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 45728ad2c1..a75e293f96 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -570,7 +570,7 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.message = stream_getc (stream); p.family = AF_INET; - p.prefixlen = stream_getc (stream); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, stream_getc (stream)); stream_get (&p.prefix, stream, PSIZE (p.prefixlen)); if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) diff --git a/lib/zclient.c b/lib/zclient.c index 5193a282a6..6ccc6e371b 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -942,18 +942,30 @@ zebra_redistribute_send (int command, struct zclient *zclient, afi_t afi, int ty return zclient_send_message(zclient); } +/* Get prefix in ZServ format; family should be filled in on prefix */ +static void +zclient_stream_get_prefix (struct stream *s, struct prefix *p) +{ + size_t plen = prefix_blen (p); + u_char c; + p->prefixlen = 0; + + if (plen == 0) + return; + + stream_get (&p->u.prefix, s, plen); + c = stream_getc(s); + p->prefixlen = MIN(plen * 8, c); +} + /* Router-id update from zebra daemon. */ void zebra_router_id_update_read (struct stream *s, struct prefix *rid) { - int plen; - /* Fetch interface address. */ rid->family = stream_getc (s); - - plen = prefix_blen (rid); - stream_get (&rid->u.prefix, s, plen); - rid->prefixlen = stream_getc (s); + + zclient_stream_get_prefix (s, rid); } /* Interface addition from zebra daemon. */ @@ -1263,8 +1275,7 @@ zebra_interface_address_read (int type, struct stream *s, vrf_id_t vrf_id) ifindex_t ifindex; struct interface *ifp; struct connected *ifc; - struct prefix p, d; - int family; + struct prefix p, d, *dp; int plen; u_char ifc_flags; @@ -1288,24 +1299,24 @@ zebra_interface_address_read (int type, struct stream *s, vrf_id_t vrf_id) ifc_flags = stream_getc (s); /* Fetch interface address. */ - family = p.family = stream_getc (s); - - plen = prefix_blen (&p); - stream_get (&p.u.prefix, s, plen); - p.prefixlen = stream_getc (s); + d.family = p.family = stream_getc (s); + plen = prefix_blen (&d); + + zclient_stream_get_prefix (s, &p); /* Fetch destination address. */ stream_get (&d.u.prefix, s, plen); - d.family = family; - + + /* N.B. NULL destination pointers are encoded as all zeroes */ + dp = memconstant(&d.u.prefix,0,plen) ? NULL : &d; + if (type == ZEBRA_INTERFACE_ADDRESS_ADD) { ifc = connected_lookup_prefix_exact (ifp, &p); if (!ifc) { /* N.B. NULL destination pointers are encoded as all zeroes */ - ifc = connected_add_by_prefix(ifp, &p, (memconstant(&d.u.prefix,0,plen) ? - NULL : &d)); + ifc = connected_add_by_prefix(ifp, &p, dp); } if (ifc) { diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 6dee1424a6..5969ef7b69 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -235,7 +235,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, stream_getc (s)); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 8752e83ed5..0938ce8d1e 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -1070,7 +1070,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, /* IPv4 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, stream_getc (s)); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); if (IPV4_NET127(ntohl(p.prefix.s_addr))) diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index c312641d44..06e840464d 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -153,7 +153,7 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* IPv4 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, stream_getc (s)); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 1184cd0db6..e1ede095ec 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -149,7 +149,7 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, stream_getc (s)); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ From 0fc452dc57467bd1ab1dabe119be979d996af3f9 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 16 Sep 2016 21:55:37 +0200 Subject: [PATCH 062/136] Make route flags a 32bit field Signed-off-by: Christian Franke --- bgpd/bgp_zebra.c | 8 ++++---- isisd/isis_zebra.c | 9 +++++---- lib/zclient.c | 6 +++--- lib/zclient.h | 4 ++-- ospf6d/ospf6_zebra.c | 2 +- ospfd/ospf_zebra.c | 10 +++++----- pimd/pim_zebra.c | 2 +- ripd/rip_zebra.c | 2 +- ripngd/ripng_zebra.c | 2 +- zebra/rib.h | 2 +- zebra/zserv.c | 12 ++++++------ 11 files changed, 30 insertions(+), 29 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 0f5c3ce7aa..7089d140b9 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -601,7 +601,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv4 prefix. */ @@ -716,7 +716,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv6 prefix. */ @@ -1199,7 +1199,7 @@ void bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, afi_t afi, safi_t safi) { - int flags; + u_int32_t flags; u_char distance; struct peer *peer; struct bgp_info *mpinfo; @@ -1620,7 +1620,7 @@ bgp_zebra_announce_table (struct bgp *bgp, afi_t afi, safi_t safi) void bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) { - int flags; + u_int32_t flags; struct peer *peer; peer = info->peer; diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index a75e293f96..646c5fa88e 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -257,7 +257,8 @@ static void isis_zebra_route_add_ipv4 (struct prefix *prefix, struct isis_route_info *route_info) { - u_char message, flags; + u_char message; + u_int32_t flags; int psize; struct stream *stream; struct isis_nexthop *nexthop; @@ -285,7 +286,7 @@ isis_zebra_route_add_ipv4 (struct prefix *prefix, /* instance */ stream_putw (stream, 0); /* flags */ - stream_putc (stream, flags); + stream_putl (stream, flags); /* message */ stream_putc (stream, message); /* SAFI */ @@ -566,7 +567,7 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.type = stream_getc (stream); api.instance = stream_getw (stream); - api.flags = stream_getc (stream); + api.flags = stream_getl (stream); api.message = stream_getc (stream); p.family = AF_INET; @@ -623,7 +624,7 @@ isis_zebra_read_ipv6 (int command, struct zclient *zclient, ifindex = 0; api.type = stream_getc(stream); - api.flags = stream_getc(stream); + api.flags = stream_getl(stream); api.message = stream_getc(stream); p.family = AF_INET6; diff --git a/lib/zclient.c b/lib/zclient.c index 6ccc6e371b..a02cbbe4dd 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -733,7 +733,7 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putc (s, api->flags); + stream_putl (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); @@ -801,7 +801,7 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putc (s, api->flags); + stream_putl (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); @@ -867,7 +867,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, /* Put type and nexthop. */ stream_putc (s, api->type); stream_putw (s, api->instance); - stream_putc (s, api->flags); + stream_putl (s, api->flags); stream_putc (s, api->message); stream_putw (s, api->safi); diff --git a/lib/zclient.h b/lib/zclient.h index 231b4e9b4f..4edbd7636e 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -134,7 +134,7 @@ struct zapi_ipv4 u_char type; u_short instance; - u_char flags; + u_int32_t flags; u_char message; @@ -222,7 +222,7 @@ struct zapi_ipv6 u_char type; u_short instance; - u_char flags; + u_int32_t flags; u_char message; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 5969ef7b69..45165fdf2e 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -229,7 +229,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv6 prefix. */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 0938ce8d1e..4458e95ba0 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -355,7 +355,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; - u_char flags; + u_int32_t flags; int psize; struct stream *s; struct ospf_path *path; @@ -393,7 +393,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putw (s, ospf->instance); - stream_putc (s, flags); + stream_putl (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); @@ -492,7 +492,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) { u_char message; u_char distance; - u_char flags; + u_int32_t flags; int psize; struct stream *s; struct ospf_path *path; @@ -516,7 +516,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) zclient_create_header (s, ZEBRA_IPV4_ROUTE_DELETE, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putw (s, ospf->instance); - stream_putc (s, flags); + stream_putl (s, flags); stream_putc (s, message); stream_putw (s, SAFI_UNICAST); @@ -1064,7 +1064,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv4 prefix. */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index b25e8b94da..1a8d5f22bc 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -552,7 +552,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc(s); api.instance = stream_getw (s); - api.flags = stream_getc(s); + api.flags = stream_getl(s); api.message = stream_getc(s); /* IPv4 prefix length. */ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 06e840464d..ad39f75d68 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -147,7 +147,7 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv4 prefix. */ diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index e1ede095ec..637e8f5a27 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -143,7 +143,7 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); /* IPv6 prefix. */ diff --git a/zebra/rib.h b/zebra/rib.h index 0f7f70ada7..285166f067 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -79,7 +79,7 @@ struct rib * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed * to clients via Zserv */ - u_char flags; + u_int32_t flags; /* RIB internal status */ u_char status; diff --git a/zebra/zserv.c b/zebra/zserv.c index 8fcc837c53..32c5ea7142 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -629,7 +629,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, /* Put type and nexthop. */ stream_putc (s, rib->type); stream_putw (s, rib->instance); - stream_putc (s, rib->flags); + stream_putl (s, rib->flags); /* marker for message flags field */ messmark = stream_get_endp (s); @@ -1056,7 +1056,7 @@ zread_ipv4_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getc (s); + rib->flags = stream_getl (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1160,7 +1160,7 @@ zread_ipv4_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); api.safi = stream_getw (s); @@ -1266,7 +1266,7 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getc (s); + rib->flags = stream_getl (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1393,7 +1393,7 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ rib->type = stream_getc (s); rib->instance = stream_getw (s); - rib->flags = stream_getc (s); + rib->flags = stream_getl (s); message = stream_getc (s); safi = stream_getw (s); rib->uptime = time (NULL); @@ -1507,7 +1507,7 @@ zread_ipv6_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Type, flags, message. */ api.type = stream_getc (s); api.instance = stream_getw (s); - api.flags = stream_getc (s); + api.flags = stream_getl (s); api.message = stream_getc (s); api.safi = stream_getw (s); From ee5bb56117a7732f13924d101434452f46c2a3bc Mon Sep 17 00:00:00 2001 From: Matthieu Boutier Date: Thu, 22 Sep 2016 18:11:04 -0300 Subject: [PATCH 063/136] lib: simplify distribute.c's code. Use loops and variables instead of doing each cases by hand. Use static functions instead of rewriting code. --- lib/distribute.c | 298 ++++++++++++++++++++--------------------------- 1 file changed, 126 insertions(+), 172 deletions(-) diff --git a/lib/distribute.c b/lib/distribute.c index d0d637fbb4..e94b4d446c 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -49,22 +49,35 @@ distribute_new (void) static void distribute_free (struct distribute *dist) { + int i = 0; + if (dist->ifname) XFREE (MTYPE_DISTRIBUTE_IFNAME, dist->ifname); - if (dist->list[DISTRIBUTE_IN]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[DISTRIBUTE_IN]); - if (dist->list[DISTRIBUTE_OUT]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[DISTRIBUTE_OUT]); + for (i = 0; i < DISTRIBUTE_MAX; i++) + if (dist->list[i]) + XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[i]); - if (dist->prefix[DISTRIBUTE_IN]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[DISTRIBUTE_IN]); - if (dist->prefix[DISTRIBUTE_OUT]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[DISTRIBUTE_OUT]); + for (i = 0; i < DISTRIBUTE_MAX; i++) + if (dist->prefix[i]) + XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[i]); XFREE (MTYPE_DISTRIBUTE, dist); } +static void +distribute_free_if_empty(struct distribute *dist) +{ + int i; + + for (i = 0; i < DISTRIBUTE_MAX; i++) + if (dist->list[i] != NULL || dist->prefix[i] != NULL) + return; + + hash_release (disthash, dist); + distribute_free (dist); +} + /* Lookup interface's distribute list. */ struct distribute * distribute_lookup (const char *ifname) @@ -149,36 +162,27 @@ distribute_cmp (const struct distribute *dist1, const struct distribute *dist2) /* Set access-list name to the distribute list. */ static struct distribute * -distribute_list_set (const char *ifname, enum distribute_type type, +distribute_list_set (const char *ifname, enum distribute_type type, const char *alist_name) { struct distribute *dist; dist = distribute_get (ifname); - if (type == DISTRIBUTE_IN) - { - if (dist->list[DISTRIBUTE_IN]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[DISTRIBUTE_IN]); - dist->list[DISTRIBUTE_IN] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, alist_name); - } - if (type == DISTRIBUTE_OUT) - { - if (dist->list[DISTRIBUTE_OUT]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[DISTRIBUTE_OUT]); - dist->list[DISTRIBUTE_OUT] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, alist_name); - } + if (dist->list[type]) + XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); + dist->list[type] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, alist_name); /* Apply this distribute-list to the interface. */ (*distribute_add_hook) (dist); - + return dist; } /* Unset distribute-list. If matched distribute-list exist then return 1. */ static int -distribute_list_unset (const char *ifname, enum distribute_type type, +distribute_list_unset (const char *ifname, enum distribute_type type, const char *alist_name) { struct distribute *dist; @@ -187,41 +191,19 @@ distribute_list_unset (const char *ifname, enum distribute_type type, if (!dist) return 0; - if (type == DISTRIBUTE_IN) - { - if (!dist->list[DISTRIBUTE_IN]) - return 0; - if (strcmp (dist->list[DISTRIBUTE_IN], alist_name) != 0) - return 0; + if (!dist->list[type]) + return 0; + if (strcmp (dist->list[type], alist_name) != 0) + return 0; - XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[DISTRIBUTE_IN]); - dist->list[DISTRIBUTE_IN] = NULL; - } - - if (type == DISTRIBUTE_OUT) - { - if (!dist->list[DISTRIBUTE_OUT]) - return 0; - if (strcmp (dist->list[DISTRIBUTE_OUT], alist_name) != 0) - return 0; - - XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[DISTRIBUTE_OUT]); - dist->list[DISTRIBUTE_OUT] = NULL; - } + XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); + dist->list[type] = NULL; /* Apply this distribute-list to the interface. */ (*distribute_delete_hook) (dist); - /* If both out and in is NULL then free distribute list. */ - if (dist->list[DISTRIBUTE_IN] == NULL && - dist->list[DISTRIBUTE_OUT] == NULL && - dist->prefix[DISTRIBUTE_IN] == NULL && - dist->prefix[DISTRIBUTE_OUT] == NULL) - { - hash_release (disthash, dist); - distribute_free (dist); - } - + /* If all dist are NULL, then free distribute list. */ + distribute_free_if_empty(dist); return 1; } @@ -234,18 +216,9 @@ distribute_list_prefix_set (const char *ifname, enum distribute_type type, dist = distribute_get (ifname); - if (type == DISTRIBUTE_IN) - { - if (dist->prefix[DISTRIBUTE_IN]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[DISTRIBUTE_IN]); - dist->prefix[DISTRIBUTE_IN] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, plist_name); - } - if (type == DISTRIBUTE_OUT) - { - if (dist->prefix[DISTRIBUTE_OUT]) - XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[DISTRIBUTE_OUT]); - dist->prefix[DISTRIBUTE_OUT] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, plist_name); - } + if (dist->prefix[type]) + XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); + dist->prefix[type] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, plist_name); /* Apply this distribute-list to the interface. */ (*distribute_add_hook) (dist); @@ -265,41 +238,19 @@ distribute_list_prefix_unset (const char *ifname, enum distribute_type type, if (!dist) return 0; - if (type == DISTRIBUTE_IN) - { - if (!dist->prefix[DISTRIBUTE_IN]) - return 0; - if (strcmp (dist->prefix[DISTRIBUTE_IN], plist_name) != 0) - return 0; + if (!dist->prefix[type]) + return 0; + if (strcmp (dist->prefix[type], plist_name) != 0) + return 0; - XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[DISTRIBUTE_IN]); - dist->prefix[DISTRIBUTE_IN] = NULL; - } - - if (type == DISTRIBUTE_OUT) - { - if (!dist->prefix[DISTRIBUTE_OUT]) - return 0; - if (strcmp (dist->prefix[DISTRIBUTE_OUT], plist_name) != 0) - return 0; - - XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[DISTRIBUTE_OUT]); - dist->prefix[DISTRIBUTE_OUT] = NULL; - } + XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); + dist->prefix[type] = NULL; /* Apply this distribute-list to the interface. */ (*distribute_delete_hook) (dist); - /* If both out and in is NULL then free distribute list. */ - if (dist->list[DISTRIBUTE_IN] == NULL && - dist->list[DISTRIBUTE_OUT] == NULL && - dist->prefix[DISTRIBUTE_IN] == NULL && - dist->prefix[DISTRIBUTE_OUT] == NULL) - { - hash_release (disthash, dist); - distribute_free (dist); - } - + /* If all dist are NULL, then free distribute list. */ + distribute_free_if_empty(dist); return 1; } @@ -407,7 +358,7 @@ DEFUN (distribute_list, distribute_list_set (argv[2], type, argv[0]); return CMD_SUCCESS; -} +} ALIAS (distribute_list, ipv6_distribute_list_cmd, @@ -448,7 +399,7 @@ DEFUN (no_distribute_list, no_distribute_list_cmd, return CMD_WARNING; } return CMD_SUCCESS; -} +} ALIAS (no_distribute_list, no_ipv6_distribute_list_cmd, "no distribute-list WORD (in|out) WORD", @@ -486,7 +437,7 @@ DEFUN (distribute_list_prefix_all, distribute_list_prefix_set (NULL, type, argv[0]); return CMD_SUCCESS; -} +} ALIAS (distribute_list_prefix_all, ipv6_distribute_list_prefix_all_cmd, @@ -529,7 +480,7 @@ DEFUN (no_distribute_list_prefix_all, return CMD_WARNING; } return CMD_SUCCESS; -} +} ALIAS (no_distribute_list_prefix_all, no_ipv6_distribute_list_prefix_all_cmd, @@ -568,7 +519,7 @@ DEFUN (distribute_list_prefix, distribute_list_prefix_cmd, distribute_list_prefix_set (argv[2], type, argv[0]); return CMD_SUCCESS; -} +} ALIAS (distribute_list_prefix, ipv6_distribute_list_prefix_cmd, "distribute-list prefix WORD (in|out) WORD", @@ -611,7 +562,7 @@ DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, return CMD_WARNING; } return CMD_SUCCESS; -} +} ALIAS (no_distribute_list_prefix, no_ipv6_distribute_list_prefix_cmd, "no distribute-list prefix WORD (in|out) WORD", @@ -623,80 +574,96 @@ ALIAS (no_distribute_list_prefix, no_ipv6_distribute_list_prefix_cmd, "Filter outgoing routing updates\n" "Interface name\n") +static int +distribute_print (struct vty *vty, char *tab[], int is_prefix, + enum distribute_type type, int has_print) +{ + if (tab[type]) { + vty_out (vty, "%s %s%s", + has_print ? "," : "", + is_prefix ? "(prefix-list) " : "", + tab[type]); + return 1; + } + return has_print; +} + int config_show_distribute (struct vty *vty) { unsigned int i; + int has_print = 0; struct hash_backet *mp; struct distribute *dist; /* Output filter configuration. */ dist = distribute_lookup (NULL); - if (dist && (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT])) + vty_out(vty, " Outgoing update filter list for all interface is"); + has_print = 0; + if (dist) { - vty_out (vty, " Outgoing update filter list for all interface is"); - if (dist->list[DISTRIBUTE_OUT]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]); - if (dist->prefix[DISTRIBUTE_OUT]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_OUT] ? "," : "", - dist->prefix[DISTRIBUTE_OUT]); - vty_out (vty, "%s", VTY_NEWLINE); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_OUT, has_print); } + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); else - vty_out (vty, " Outgoing update filter list for all interface is not set%s", VTY_NEWLINE); + vty_out (vty, " not set%s", VTY_NEWLINE); for (i = 0; i < disthash->size; i++) for (mp = disthash->index[i]; mp; mp = mp->next) { dist = mp->data; if (dist->ifname) - if (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT]) - { - vty_out (vty, " %s filtered by", dist->ifname); - if (dist->list[DISTRIBUTE_OUT]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]); - if (dist->prefix[DISTRIBUTE_OUT]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_OUT] ? "," : "", - dist->prefix[DISTRIBUTE_OUT]); - vty_out (vty, "%s", VTY_NEWLINE); - } + { + vty_out (vty, " %s filtered by", dist->ifname); + has_print = 0; + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_OUT, has_print); + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); + else + vty_out(vty, " nothing%s", VTY_NEWLINE); + } } /* Input filter configuration. */ dist = distribute_lookup (NULL); - if (dist && (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN])) + vty_out(vty, " Incoming update filter list for all interface is"); + has_print = 0; + if (dist) { - vty_out (vty, " Incoming update filter list for all interface is"); - if (dist->list[DISTRIBUTE_IN]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]); - if (dist->prefix[DISTRIBUTE_IN]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_IN] ? "," : "", - dist->prefix[DISTRIBUTE_IN]); - vty_out (vty, "%s", VTY_NEWLINE); - } + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_IN, has_print); } + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); else - vty_out (vty, " Incoming update filter list for all interface is not set%s", VTY_NEWLINE); + vty_out (vty, " not set%s", VTY_NEWLINE); for (i = 0; i < disthash->size; i++) for (mp = disthash->index[i]; mp; mp = mp->next) { dist = mp->data; - if (dist->ifname) - if (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN]) - { - vty_out (vty, " %s filtered by", dist->ifname); - if (dist->list[DISTRIBUTE_IN]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]); - if (dist->prefix[DISTRIBUTE_IN]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_IN] ? "," : "", - dist->prefix[DISTRIBUTE_IN]); - vty_out (vty, "%s", VTY_NEWLINE); - } + if (dist->ifname) + { + vty_out (vty, " %s filtered by", dist->ifname); + has_print = 0; + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_IN, has_print); + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); + else + vty_out(vty, " nothing%s", VTY_NEWLINE); + } } return 0; } @@ -706,6 +673,8 @@ int config_write_distribute (struct vty *vty) { unsigned int i; + int j; + int output; struct hash_backet *mp; int write = 0; @@ -716,38 +685,23 @@ config_write_distribute (struct vty *vty) dist = mp->data; - if (dist->list[DISTRIBUTE_IN]) - { - vty_out (vty, " distribute-list %s in %s%s", - dist->list[DISTRIBUTE_IN], + for (j = 0; j < DISTRIBUTE_MAX; j++) + if (dist->list[j]) { + output = j == DISTRIBUTE_OUT; + vty_out (vty, " distribute-list %s %s %s%s", + dist->list[j], + output ? "out" : "in", dist->ifname ? dist->ifname : "", VTY_NEWLINE); write++; } - if (dist->list[DISTRIBUTE_OUT]) - { - vty_out (vty, " distribute-list %s out %s%s", - - dist->list[DISTRIBUTE_OUT], - dist->ifname ? dist->ifname : "", - VTY_NEWLINE); - write++; - } - - if (dist->prefix[DISTRIBUTE_IN]) - { - vty_out (vty, " distribute-list prefix %s in %s%s", - dist->prefix[DISTRIBUTE_IN], - dist->ifname ? dist->ifname : "", - VTY_NEWLINE); - write++; - } - - if (dist->prefix[DISTRIBUTE_OUT]) - { - vty_out (vty, " distribute-list prefix %s out %s%s", - dist->prefix[DISTRIBUTE_OUT], + for (j = 0; j < DISTRIBUTE_MAX; j++) + if (dist->prefix[j]) { + output = j == DISTRIBUTE_OUT; + vty_out (vty, " distribute-list prefix %s %s %s%s", + dist->prefix[j], + output ? "out" : "in", dist->ifname ? dist->ifname : "", VTY_NEWLINE); write++; From 7c989d74b54af77d06df8760bebc5d0d377b57c9 Mon Sep 17 00:00:00 2001 From: Matthieu Boutier Date: Thu, 22 Sep 2016 18:11:05 -0300 Subject: [PATCH 064/136] ripd: code simplification for redistribution. Use loops and variables instead of doing each cases by hand. Use boolean instead of having 2 almost identical functions. --- ripd/ripd.c | 118 +++++++++++----------------------------------------- 1 file changed, 25 insertions(+), 93 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index 698de7e817..ef0be41f09 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -316,32 +316,35 @@ rip_timeout_update (struct rip_info *rinfo) } static int -rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) +rip_filter (int rip_distribute, struct prefix_ipv4 *p, struct rip_interface *ri) { struct distribute *dist; struct access_list *alist; struct prefix_list *plist; + int distribute = rip_distribute == RIP_FILTER_OUT ? + DISTRIBUTE_OUT : DISTRIBUTE_IN; + const char *inout = rip_distribute == RIP_FILTER_OUT ? "out" : "in"; /* Input distribute-list filtering. */ - if (ri->list[RIP_FILTER_IN]) + if (ri->list[rip_distribute]) { - if (access_list_apply (ri->list[RIP_FILTER_IN], + if (access_list_apply (ri->list[rip_distribute], (struct prefix *) p) == FILTER_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } - if (ri->prefix[RIP_FILTER_IN]) + if (ri->prefix[rip_distribute]) { - if (prefix_list_apply (ri->prefix[RIP_FILTER_IN], + if (prefix_list_apply (ri->prefix[rip_distribute], (struct prefix *) p) == PREFIX_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -350,104 +353,33 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) dist = distribute_lookup (NULL); if (dist) { - if (dist->list[DISTRIBUTE_IN]) + if (dist->list[distribute]) { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]); - + alist = access_list_lookup (AFI_IP, dist->list[distribute]); + if (alist) { - if (access_list_apply (alist, - (struct prefix *) p) == FILTER_DENY) + if (access_list_apply (alist, (struct prefix *) p) == FILTER_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } } - if (dist->prefix[DISTRIBUTE_IN]) + if (dist->prefix[distribute]) { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]); - + plist = prefix_list_lookup (AFI_IP, dist->prefix[distribute]); + if (plist) { if (prefix_list_apply (plist, (struct prefix *) p) == PREFIX_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } - } - return 0; -} - -static int -rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri) -{ - struct distribute *dist; - struct access_list *alist; - struct prefix_list *plist; - - if (ri->list[RIP_FILTER_OUT]) - { - if (access_list_apply (ri->list[RIP_FILTER_OUT], - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by distribute out", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - if (ri->prefix[RIP_FILTER_OUT]) - { - if (prefix_list_apply (ri->prefix[RIP_FILTER_OUT], - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by prefix-list out", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - - /* All interface filter check. */ - dist = distribute_lookup (NULL); - if (dist) - { - if (dist->list[DISTRIBUTE_OUT]) - { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]); - - if (alist) - { - if (access_list_apply (alist, - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute out", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } - if (dist->prefix[DISTRIBUTE_OUT]) - { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]); - - if (plist) - { - if (prefix_list_apply (plist, - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list out", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -511,7 +443,7 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* Apply input filters. */ ri = ifp->info; - ret = rip_incoming_filter (&p, ri); + ret = rip_filter (RIP_FILTER_IN, &p, ri); if (ret < 0) return; @@ -2301,7 +2233,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, p = (struct prefix_ipv4 *) &rp->p; /* Apply output filters. */ - ret = rip_outgoing_filter (p, ri); + ret = rip_filter (RIP_FILTER_OUT, p, ri); if (ret < 0) continue; From 644ed6c5deb1e37b45b978e1d302785be6a25c18 Mon Sep 17 00:00:00 2001 From: Matthieu Boutier Date: Thu, 22 Sep 2016 18:11:06 -0300 Subject: [PATCH 065/136] ripngd: code simplification for redistribution. Use loops and variables instead of doing each cases by hand. Use boolean instead of having 2 almost identical functions. --- ripngd/ripngd.c | 118 +++++++++++------------------------------------- 1 file changed, 26 insertions(+), 92 deletions(-) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 2858bb6462..3d76dc5254 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -610,32 +610,36 @@ ripng_timeout_update (struct ripng_info *rinfo) } static int -ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri) +ripng_filter (int ripng_distribute, struct prefix_ipv6 *p, + struct ripng_interface *ri) { struct distribute *dist; struct access_list *alist; struct prefix_list *plist; + int distribute = ripng_distribute == RIPNG_FILTER_OUT ? + DISTRIBUTE_OUT : DISTRIBUTE_IN; + const char *inout = ripng_distribute == RIPNG_FILTER_OUT ? "out" : "in"; /* Input distribute-list filtering. */ - if (ri->list[RIPNG_FILTER_IN]) + if (ri->list[ripng_distribute]) { - if (access_list_apply (ri->list[RIPNG_FILTER_IN], + if (access_list_apply (ri->list[ripng_distribute], (struct prefix *) p) == FILTER_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } - if (ri->prefix[RIPNG_FILTER_IN]) + if (ri->prefix[ripng_distribute]) { - if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN], + if (prefix_list_apply (ri->prefix[ripng_distribute], (struct prefix *) p) == PREFIX_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -644,104 +648,34 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri) dist = distribute_lookup (NULL); if (dist) { - if (dist->list[DISTRIBUTE_IN]) + if (dist->list[distribute]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]); - + alist = access_list_lookup (AFI_IP6, dist->list[distribute]); + if (alist) { if (access_list_apply (alist, (struct prefix *) p) == FILTER_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } } - if (dist->prefix[DISTRIBUTE_IN]) + if (dist->prefix[distribute]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]); - + plist = prefix_list_lookup (AFI_IP6, dist->prefix[distribute]); + if (plist) { if (prefix_list_apply (plist, (struct prefix *) p) == PREFIX_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } - } - return 0; -} - -static int -ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri) -{ - struct distribute *dist; - struct access_list *alist; - struct prefix_list *plist; - - if (ri->list[RIPNG_FILTER_OUT]) - { - if (access_list_apply (ri->list[RIPNG_FILTER_OUT], - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by distribute out", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - if (ri->prefix[RIPNG_FILTER_OUT]) - { - if (prefix_list_apply (ri->prefix[RIPNG_FILTER_OUT], - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by prefix-list out", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - - /* All interface filter check. */ - dist = distribute_lookup (NULL); - if (dist) - { - if (dist->list[DISTRIBUTE_OUT]) - { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]); - - if (alist) - { - if (access_list_apply (alist, - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute out", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } - if (dist->prefix[DISTRIBUTE_OUT]) - { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]); - - if (plist) - { - if (prefix_list_apply (plist, - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list out", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -781,7 +715,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* Apply input filters. */ ri = ifp->info; - ret = ripng_incoming_filter (&p, ri); + ret = ripng_filter (RIPNG_FILTER_IN, &p, ri); if (ret < 0) return; @@ -1676,7 +1610,7 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, rinfo->nexthop_out = rinfo->nexthop; /* Apply output filters. */ - ret = ripng_outgoing_filter (p, ri); + ret = ripng_filter (RIPNG_FILTER_OUT, p, ri); if (ret < 0) continue; @@ -1805,7 +1739,7 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, memset(&aggregate->nexthop_out, 0, sizeof(aggregate->nexthop_out)); /* Apply output filters.*/ - ret = ripng_outgoing_filter (p, ri); + ret = ripng_filter (RIPNG_FILTER_OUT, p, ri); if (ret < 0) continue; From fb23cf4abe0731dce18b8b4cd13d57fcf40d8c5c Mon Sep 17 00:00:00 2001 From: Matthieu Boutier Date: Thu, 22 Sep 2016 18:11:07 -0300 Subject: [PATCH 066/136] lib: Make distribute.c accepts both v4 and v6. distribute.c doesn't allow to manage both v4 and v6 distribute lists. This patch fix this problem by having 4 DISTRIBUTE* values in the enumeration instead of two. The code in all daemons using distribute.c is adapted. --- lib/distribute.c | 379 +++++++++++++++++++++++++++++++++++++++++------ lib/distribute.h | 6 +- ripd/ripd.c | 18 +-- ripngd/ripngd.c | 18 +-- 4 files changed, 355 insertions(+), 66 deletions(-) diff --git a/lib/distribute.c b/lib/distribute.c index e94b4d446c..498410c22d 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -266,9 +266,9 @@ DEFUN (distribute_list_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", @@ -282,8 +282,36 @@ DEFUN (distribute_list_all, return CMD_SUCCESS; } -ALIAS (distribute_list_all, +DEFUN (ipv6_distribute_list_all, ipv6_distribute_list_all_cmd, + "ipv6 distribute-list WORD (in|out)", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_set (NULL, type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list_all, + ipv6_as_v4_distribute_list_all_cmd, "distribute-list WORD (in|out)", "Filter networks in routing updates\n" "Access-list name\n" @@ -304,9 +332,9 @@ DEFUN (no_distribute_list_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", @@ -323,8 +351,41 @@ DEFUN (no_distribute_list_all, return CMD_SUCCESS; } -ALIAS (no_distribute_list_all, +DEFUN (no_ipv6_distribute_list_all, no_ipv6_distribute_list_all_cmd, + "no ipv6 distribute-list WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_unset (NULL, type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_distribute_list_all, + no_ipv6_as_v4_distribute_list_all_cmd, "no distribute-list WORD (in|out)", NO_STR "Filter networks in routing updates\n" @@ -345,9 +406,9 @@ DEFUN (distribute_list, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); @@ -360,8 +421,36 @@ DEFUN (distribute_list, return CMD_SUCCESS; } -ALIAS (distribute_list, +DEFUN (ipv6_distribute_list, ipv6_distribute_list_cmd, + "ipv6 distribute-list WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list, + ipv6_as_v4_distribute_list_cmd, "distribute-list WORD (in|out) WORD", "Filter networks in routing updates\n" "Access-list name\n" @@ -383,9 +472,9 @@ DEFUN (no_distribute_list, no_distribute_list_cmd, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); @@ -401,7 +490,41 @@ DEFUN (no_distribute_list, no_distribute_list_cmd, return CMD_SUCCESS; } -ALIAS (no_distribute_list, no_ipv6_distribute_list_cmd, +DEFUN (no_ipv6_distribute_list, + no_ipv6_distribute_list_cmd, + "no ipv6 distribute-list WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_distribute_list, + no_ipv6_as_v4_distribute_list_cmd, "no distribute-list WORD (in|out) WORD", NO_STR "Filter networks in routing updates\n" @@ -423,12 +546,12 @@ DEFUN (distribute_list_prefix_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -439,8 +562,37 @@ DEFUN (distribute_list_prefix_all, return CMD_SUCCESS; } -ALIAS (distribute_list_prefix_all, +DEFUN (ipv6_distribute_list_prefix_all, ipv6_distribute_list_prefix_all_cmd, + "ipv6 distribute-list prefix WORD (in|out)", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_prefix_set (NULL, type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list_prefix_all, + ipv6_as_v4_distribute_list_prefix_all_cmd, "distribute-list prefix WORD (in|out)", "Filter networks in routing updates\n" "Filter prefixes in routing updates\n" @@ -463,12 +615,12 @@ DEFUN (no_distribute_list_prefix_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -482,8 +634,42 @@ DEFUN (no_distribute_list_prefix_all, return CMD_SUCCESS; } -ALIAS (no_distribute_list_prefix_all, +DEFUN (no_ipv6_distribute_list_prefix_all, no_ipv6_distribute_list_prefix_all_cmd, + "no ipv6 distribute-list prefix WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_prefix_unset (NULL, type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_distribute_list_prefix_all, + no_ipv6_as_v4_distribute_list_prefix_all_cmd, "no distribute-list prefix WORD (in|out)", NO_STR "Filter networks in routing updates\n" @@ -505,12 +691,12 @@ DEFUN (distribute_list_prefix, distribute_list_prefix_cmd, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -521,7 +707,38 @@ DEFUN (distribute_list_prefix, distribute_list_prefix_cmd, return CMD_SUCCESS; } -ALIAS (distribute_list_prefix, ipv6_distribute_list_prefix_cmd, +DEFUN (ipv6_distribute_list_prefix, + ipv6_distribute_list_prefix_cmd, + "ipv6 distribute-list prefix WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_prefix_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list_prefix, + ipv6_as_v4_distribute_list_prefix_cmd, "distribute-list prefix WORD (in|out) WORD", "Filter networks in routing updates\n" "Filter prefixes in routing updates\n" @@ -545,12 +762,12 @@ DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -564,7 +781,43 @@ DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, return CMD_SUCCESS; } -ALIAS (no_distribute_list_prefix, no_ipv6_distribute_list_prefix_cmd, +DEFUN (no_ipv6_distribute_list_prefix, + no_ipv6_distribute_list_prefix_cmd, + "no ipv6 distribute-list prefix WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_prefix_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_distribute_list_prefix, + no_ipv6_as_v4_distribute_list_prefix_cmd, "no distribute-list prefix WORD (in|out) WORD", NO_STR "Filter networks in routing updates\n" @@ -603,9 +856,13 @@ config_show_distribute (struct vty *vty) if (dist) { has_print = distribute_print(vty, dist->list, 0, - DISTRIBUTE_OUT, has_print); + DISTRIBUTE_V4_OUT, has_print); has_print = distribute_print(vty, dist->prefix, 1, - DISTRIBUTE_OUT, has_print); + DISTRIBUTE_V4_OUT, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_OUT, has_print); } if (has_print) vty_out (vty, "%s", VTY_NEWLINE); @@ -621,9 +878,13 @@ config_show_distribute (struct vty *vty) vty_out (vty, " %s filtered by", dist->ifname); has_print = 0; has_print = distribute_print(vty, dist->list, 0, - DISTRIBUTE_OUT, has_print); + DISTRIBUTE_V4_OUT, has_print); has_print = distribute_print(vty, dist->prefix, 1, - DISTRIBUTE_OUT, has_print); + DISTRIBUTE_V4_OUT, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_OUT, has_print); if (has_print) vty_out (vty, "%s", VTY_NEWLINE); else @@ -639,9 +900,14 @@ config_show_distribute (struct vty *vty) if (dist) { has_print = distribute_print(vty, dist->list, 0, - DISTRIBUTE_IN, has_print); + DISTRIBUTE_V4_IN, has_print); has_print = distribute_print(vty, dist->prefix, 1, - DISTRIBUTE_IN, has_print); } + DISTRIBUTE_V4_IN, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_IN, has_print); + } if (has_print) vty_out (vty, "%s", VTY_NEWLINE); else @@ -656,9 +922,13 @@ config_show_distribute (struct vty *vty) vty_out (vty, " %s filtered by", dist->ifname); has_print = 0; has_print = distribute_print(vty, dist->list, 0, - DISTRIBUTE_IN, has_print); + DISTRIBUTE_V4_IN, has_print); has_print = distribute_print(vty, dist->prefix, 1, - DISTRIBUTE_IN, has_print); + DISTRIBUTE_V4_IN, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_IN, has_print); if (has_print) vty_out (vty, "%s", VTY_NEWLINE); else @@ -674,7 +944,7 @@ config_write_distribute (struct vty *vty) { unsigned int i; int j; - int output; + int output, v6; struct hash_backet *mp; int write = 0; @@ -687,8 +957,10 @@ config_write_distribute (struct vty *vty) for (j = 0; j < DISTRIBUTE_MAX; j++) if (dist->list[j]) { - output = j == DISTRIBUTE_OUT; - vty_out (vty, " distribute-list %s %s %s%s", + output = j == DISTRIBUTE_V4_OUT || j == DISTRIBUTE_V6_OUT; + v6 = j == DISTRIBUTE_V6_IN || j == DISTRIBUTE_V6_OUT; + vty_out (vty, " %sdistribute-list %s %s %s%s", + v6 ? "ipv6 " : "", dist->list[j], output ? "out" : "in", dist->ifname ? dist->ifname : "", @@ -698,8 +970,10 @@ config_write_distribute (struct vty *vty) for (j = 0; j < DISTRIBUTE_MAX; j++) if (dist->prefix[j]) { - output = j == DISTRIBUTE_OUT; - vty_out (vty, " distribute-list prefix %s %s %s%s", + output = j == DISTRIBUTE_V4_OUT || j == DISTRIBUTE_V6_OUT; + v6 = j == DISTRIBUTE_V6_IN || j == DISTRIBUTE_V6_OUT; + vty_out (vty, " %sdistribute-list prefix %s %s %s%s", + v6 ? "ipv6 " : "", dist->prefix[j], output ? "out" : "in", dist->ifname ? dist->ifname : "", @@ -723,8 +997,8 @@ distribute_list_init (int node) { disthash = hash_create (distribute_hash_make, (int (*) (const void *, const void *)) distribute_cmp); - - if(node==RIP_NODE) { + /* install v4 */ + if (node == RIP_NODE) { install_element (node, &distribute_list_all_cmd); install_element (node, &no_distribute_list_all_cmd); install_element (node, &distribute_list_cmd); @@ -733,9 +1007,10 @@ distribute_list_init (int node) install_element (node, &no_distribute_list_prefix_all_cmd); install_element (node, &distribute_list_prefix_cmd); install_element (node, &no_distribute_list_prefix_cmd); - } else if (node == RIPNG_NODE) { - /* WARNING: two identical commands installed do a crash, so be worry with - * aliases */ + } + + /* install v6 */ + if (node == RIPNG_NODE) { install_element (node, &ipv6_distribute_list_all_cmd); install_element (node, &no_ipv6_distribute_list_all_cmd); install_element (node, &ipv6_distribute_list_cmd); @@ -745,4 +1020,16 @@ distribute_list_init (int node) install_element (node, &ipv6_distribute_list_prefix_cmd); install_element (node, &no_ipv6_distribute_list_prefix_cmd); } + + /* install v4 syntax command for v6 only protocols. */ + if (node == RIPNG_NODE) { + install_element (node, &ipv6_as_v4_distribute_list_all_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_all_cmd); + install_element (node, &ipv6_as_v4_distribute_list_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_cmd); + install_element (node, &ipv6_as_v4_distribute_list_prefix_all_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_prefix_all_cmd); + install_element (node, &ipv6_as_v4_distribute_list_prefix_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_prefix_cmd); + } } diff --git a/lib/distribute.h b/lib/distribute.h index a2ffffd5fc..e9625a3548 100644 --- a/lib/distribute.h +++ b/lib/distribute.h @@ -29,8 +29,10 @@ /* Disctirubte list types. */ enum distribute_type { - DISTRIBUTE_IN, - DISTRIBUTE_OUT, + DISTRIBUTE_V4_IN, + DISTRIBUTE_V6_IN, + DISTRIBUTE_V4_OUT, + DISTRIBUTE_V6_OUT, DISTRIBUTE_MAX }; diff --git a/ripd/ripd.c b/ripd/ripd.c index ef0be41f09..22ba2fe998 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -322,7 +322,7 @@ rip_filter (int rip_distribute, struct prefix_ipv4 *p, struct rip_interface *ri) struct access_list *alist; struct prefix_list *plist; int distribute = rip_distribute == RIP_FILTER_OUT ? - DISTRIBUTE_OUT : DISTRIBUTE_IN; + DISTRIBUTE_V4_OUT : DISTRIBUTE_V4_IN; const char *inout = rip_distribute == RIP_FILTER_OUT ? "out" : "in"; /* Input distribute-list filtering. */ @@ -3804,9 +3804,9 @@ rip_distribute_update (struct distribute *dist) ri = ifp->info; - if (dist->list[DISTRIBUTE_IN]) + if (dist->list[DISTRIBUTE_V4_IN]) { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]); + alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_IN]); if (alist) ri->list[RIP_FILTER_IN] = alist; else @@ -3815,9 +3815,9 @@ rip_distribute_update (struct distribute *dist) else ri->list[RIP_FILTER_IN] = NULL; - if (dist->list[DISTRIBUTE_OUT]) + if (dist->list[DISTRIBUTE_V4_OUT]) { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]); + alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_OUT]); if (alist) ri->list[RIP_FILTER_OUT] = alist; else @@ -3826,9 +3826,9 @@ rip_distribute_update (struct distribute *dist) else ri->list[RIP_FILTER_OUT] = NULL; - if (dist->prefix[DISTRIBUTE_IN]) + if (dist->prefix[DISTRIBUTE_V4_IN]) { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]); + plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_IN]); if (plist) ri->prefix[RIP_FILTER_IN] = plist; else @@ -3837,9 +3837,9 @@ rip_distribute_update (struct distribute *dist) else ri->prefix[RIP_FILTER_IN] = NULL; - if (dist->prefix[DISTRIBUTE_OUT]) + if (dist->prefix[DISTRIBUTE_V4_OUT]) { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]); + plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_OUT]); if (plist) ri->prefix[RIP_FILTER_OUT] = plist; else diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 3d76dc5254..7d2c66b0b2 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -617,7 +617,7 @@ ripng_filter (int ripng_distribute, struct prefix_ipv6 *p, struct access_list *alist; struct prefix_list *plist; int distribute = ripng_distribute == RIPNG_FILTER_OUT ? - DISTRIBUTE_OUT : DISTRIBUTE_IN; + DISTRIBUTE_V6_OUT : DISTRIBUTE_V6_IN; const char *inout = ripng_distribute == RIPNG_FILTER_OUT ? "out" : "in"; /* Input distribute-list filtering. */ @@ -2759,9 +2759,9 @@ ripng_distribute_update (struct distribute *dist) ri = ifp->info; - if (dist->list[DISTRIBUTE_IN]) + if (dist->list[DISTRIBUTE_V6_IN]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]); + alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_IN]); if (alist) ri->list[RIPNG_FILTER_IN] = alist; else @@ -2770,9 +2770,9 @@ ripng_distribute_update (struct distribute *dist) else ri->list[RIPNG_FILTER_IN] = NULL; - if (dist->list[DISTRIBUTE_OUT]) + if (dist->list[DISTRIBUTE_V6_OUT]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]); + alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_OUT]); if (alist) ri->list[RIPNG_FILTER_OUT] = alist; else @@ -2781,9 +2781,9 @@ ripng_distribute_update (struct distribute *dist) else ri->list[RIPNG_FILTER_OUT] = NULL; - if (dist->prefix[DISTRIBUTE_IN]) + if (dist->prefix[DISTRIBUTE_V6_IN]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]); + plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_IN]); if (plist) ri->prefix[RIPNG_FILTER_IN] = plist; else @@ -2792,9 +2792,9 @@ ripng_distribute_update (struct distribute *dist) else ri->prefix[RIPNG_FILTER_IN] = NULL; - if (dist->prefix[DISTRIBUTE_OUT]) + if (dist->prefix[DISTRIBUTE_V6_OUT]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]); + plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_OUT]); if (plist) ri->prefix[RIPNG_FILTER_OUT] = plist; else From f134bf2b52e09e0a6e059a3ed5e683dfe3c50ac8 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 23 Sep 2016 09:58:30 -0300 Subject: [PATCH 067/136] zebra: add missing files to EXTRA_DIST This fixes RPM package generation on CentOS 7. Signed-off-by: Renato Westphal --- configure.ac | 1 - zebra/Makefile.am | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 00c71eb9ff..badd2b645c 100755 --- a/configure.ac +++ b/configure.ac @@ -370,7 +370,6 @@ fi dnl ---------- dnl MPLS check dnl ---------- -MPLS_METHOD="" AC_MSG_CHECKING(whether this OS has MPLS stack) case "$host" in *-linux*) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index ab67ef5898..e22dea0998 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -73,6 +73,7 @@ EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \ rt_socket.c rtread_netlink.c rtread_sysctl.c \ rtread_getmsg.c kernel_socket.c kernel_netlink.c \ ioctl.c ioctl_solaris.c \ + zebra_mpls_netlink.c zebra_mpls_openbsd.c \ GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB client : client_main.o ../lib/libzebra.la From 0cf74a83920687f3c8be84a8c7d796acf2cb6e95 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Sep 2016 14:06:23 -0400 Subject: [PATCH 068/136] lib: Allow more space in thread.c output When displaying thread time for long running/busy protocols, the space allocated may not be sufficient. Allow the runtime to take a bit more space. Signed-off-by: Donald Sharp --- lib/thread.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index a26eb6bfd2..26be17585e 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -222,12 +222,12 @@ vty_out_cpu_thread_history(struct vty* vty, struct cpu_thread_history *a) { #ifdef HAVE_RUSAGE - vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld", + vty_out(vty, "%10ld.%03ld %9d %8ld %9ld %8ld %9ld", a->cpu.total/1000, a->cpu.total%1000, a->total_calls, a->cpu.total/a->total_calls, a->cpu.max, a->real.total/a->total_calls, a->real.max); #else - vty_out(vty, "%7ld.%03ld %9d %8ld %9ld", + vty_out(vty, "%10ld.%03ld %9d %8ld %9ld", a->real.total/1000, a->real.total%1000, a->total_calls, a->real.total/a->total_calls, a->real.max); #endif @@ -278,7 +278,7 @@ cpu_record_print(struct vty *vty, thread_type filter) vty_out(vty, "%21s %18s %18s%s", "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE); #endif - vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs"); + vty_out(vty, " Runtime(ms) Invoked Avg uSec Max uSecs"); #ifdef HAVE_RUSAGE vty_out(vty, " Avg uSec Max uSecs"); #endif From 276887bb1c2961fa37b42ce7160346f1417577a8 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Tue, 27 Sep 2016 15:56:36 +0000 Subject: [PATCH 069/136] tools: quagga-reload should raise Exception instead of exiting Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp NCLU imports quagga-reload.py and uses its Config class to parse Quagga.conf. The Config class will call 'vtysh -m -f Quagga.conf" and if that exited with an error Config would call sys.exit(1) which in my cases causes the NCLU daemon to exit which is bad. The fix is to have the Config class raise an exception instead of exiting, then NCLU can catch the exception, log it and move on. --- tools/quagga-reload.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/quagga-reload.py b/tools/quagga-reload.py index 900ed55c43..ed36b940a9 100755 --- a/tools/quagga-reload.py +++ b/tools/quagga-reload.py @@ -26,6 +26,10 @@ from pprint import pformat log = logging.getLogger(__name__) +class VtyshMarkException(Exception): + pass + + class Context(object): """ @@ -88,9 +92,7 @@ class Config(object): try: file_output = subprocess.check_output(['/usr/bin/vtysh', '-m', '-f', filename]) except subprocess.CalledProcessError as e: - log.error('vtysh marking of config file %s failed with error %s:', filename, str(e)) - print "vtysh marking of file %s failed with error: %s" % (filename, str(e)) - sys.exit(1) + raise VtyshMarkException(str(e)) for line in file_output.split('\n'): line = line.strip() @@ -115,9 +117,7 @@ class Config(object): "/usr/bin/vtysh -c 'show run' | /usr/bin/tail -n +4 | /usr/bin/vtysh -m -f -", shell=True) except subprocess.CalledProcessError as e: - log.error('vtysh marking of running config failed with error %s:', str(e)) - print "vtysh marking of running config failed with error %s:" % (str(e)) - sys.exit(1) + raise VtyshMarkException(str(e)) for line in config_text.split('\n'): line = line.strip() From 032bfaaf28f919b029835cd600ad12000ad77498 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Tue, 27 Sep 2016 15:57:56 +0000 Subject: [PATCH 070/136] zebra: "ip import-table" display is hosed Signed-off-by: Daniel Walton Reviewed-by: Don Slice Ticket: CM-13020 Now that we have evpn we have the following AFIs /* Address family numbers from RFC1700. */ typedef enum { AFI_IP = 1, AFI_IP6 = 2, AFI_ETHER = 3, /* RFC 1700 has "6" for 802.* */ AFI_MAX = 4 } afi_t; The import-table code was treating the afi as a flag which was fine before when the only choices were 1 and 2 but now that we have #3 that doesn't work. The fix is to change zebra_import_table_used to a [AFI_MAX][ZEBRA_KERNEL_TABLE_MAX] array to track if import-table is enabled. --- zebra/redistribute.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 4e7538327f..6f91c94f7e 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -46,20 +46,14 @@ /* array holding redistribute info about table redistribution */ /* bit AFI is set if that AFI is redistributing routes from this table */ -static u_char zebra_import_table_used[ZEBRA_KERNEL_TABLE_MAX]; +static int zebra_import_table_used[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; static u_int32_t zebra_import_table_distance[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; int is_zebra_import_table_enabled(afi_t afi, u_int32_t table_id) { if (is_zebra_valid_kernel_table(table_id)) - { - if (CHECK_FLAG(zebra_import_table_used[table_id], (u_char)afi)) - return 1; - else - return 0; - } - + return zebra_import_table_used[afi][table_id]; return 0; } @@ -672,12 +666,12 @@ zebra_import_table (afi_t afi, u_int32_t table_id, u_int32_t distance, const cha zebra_del_import_table_route_map (afi, table_id); } - SET_FLAG(zebra_import_table_used[table_id], afi); + zebra_import_table_used[afi][table_id] = 1; zebra_import_table_distance[afi][table_id] = distance; } else { - UNSET_FLAG(zebra_import_table_used[table_id], (u_char)afi); + zebra_import_table_used[afi][table_id] = 0; zebra_import_table_distance[afi][table_id] = ZEBRA_TABLE_DISTANCE_DEFAULT; rmap_name = zebra_get_import_table_route_map (afi, table_id); @@ -721,7 +715,7 @@ zebra_import_table_config (struct vty *vty) int i; afi_t afi; int write = 0; - char afi_str[AFI_MAX][6] = {"", "ip", "ipv6"}; + char afi_str[AFI_MAX][10] = {"", "ip", "ipv6", "ethernet"}; const char *rmap_name; for (afi = AFI_IP; afi < AFI_MAX; afi++) From 8d62b1417ee85862a85454f635168e5e3fcf682d Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Tue, 27 Sep 2016 15:56:36 +0000 Subject: [PATCH 071/136] tools: quagga-reload should raise Exception instead of exiting Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp NCLU imports quagga-reload.py and uses its Config class to parse Quagga.conf. The Config class will call 'vtysh -m -f Quagga.conf" and if that exited with an error Config would call sys.exit(1) which in my cases causes the NCLU daemon to exit which is bad. The fix is to have the Config class raise an exception instead of exiting, then NCLU can catch the exception, log it and move on. (cherry picked from commit 276887bb1c2961fa37b42ce7160346f1417577a8) --- tools/quagga-reload.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/quagga-reload.py b/tools/quagga-reload.py index 900ed55c43..ed36b940a9 100755 --- a/tools/quagga-reload.py +++ b/tools/quagga-reload.py @@ -26,6 +26,10 @@ from pprint import pformat log = logging.getLogger(__name__) +class VtyshMarkException(Exception): + pass + + class Context(object): """ @@ -88,9 +92,7 @@ class Config(object): try: file_output = subprocess.check_output(['/usr/bin/vtysh', '-m', '-f', filename]) except subprocess.CalledProcessError as e: - log.error('vtysh marking of config file %s failed with error %s:', filename, str(e)) - print "vtysh marking of file %s failed with error: %s" % (filename, str(e)) - sys.exit(1) + raise VtyshMarkException(str(e)) for line in file_output.split('\n'): line = line.strip() @@ -115,9 +117,7 @@ class Config(object): "/usr/bin/vtysh -c 'show run' | /usr/bin/tail -n +4 | /usr/bin/vtysh -m -f -", shell=True) except subprocess.CalledProcessError as e: - log.error('vtysh marking of running config failed with error %s:', str(e)) - print "vtysh marking of running config failed with error %s:" % (str(e)) - sys.exit(1) + raise VtyshMarkException(str(e)) for line in config_text.split('\n'): line = line.strip() From 2aac57679569485060a36bdee1dcf309198f07fd Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Wed, 28 Sep 2016 04:52:36 +0000 Subject: [PATCH 072/136] use JSON_C_TO_STRING_PRETTY for json_object_to_json_string Signed-off-by: Daniel Walton --- bgpd/bgp_mplsvpn.c | 4 ++-- bgpd/bgp_route.c | 17 ++++++++++------- bgpd/bgp_vty.c | 8 ++++---- lib/plist.c | 2 +- ospfd/ospf_vty.c | 18 +++++++++--------- zebra/zebra_mpls.c | 4 ++-- zebra/zebra_vty.c | 4 ++-- 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 5c1df6715c..4656f7502f 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -536,7 +536,7 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u if (use_json) { json_object_object_add(json, "routes", json_routes); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } return CMD_SUCCESS; @@ -743,7 +743,7 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, if (use_json) { json_object_object_add(json, "routes", json_nroute); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 5b0cf8e225..cb2aec578c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -7543,6 +7543,9 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, if (use_json) { + /* This can produce a LOT of text so do not use + * JSON_C_TO_STRING_PRETTY here + */ json_object_object_add(json, "routes", json_routes); vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); json_object_free(json); @@ -7873,7 +7876,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, if (display) json_object_object_add(json, "paths", json_paths); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -11897,7 +11900,7 @@ bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, u_c if (use_json) { json_object_string_add(json, "warning", "No such neighbor or address family"); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -11932,7 +11935,7 @@ bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, u_c json_object_string_add(json, "pfxctDriftFor", peer->host); json_object_string_add(json, "recommended", "Please report this bug, with the above command output"); } - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -12169,7 +12172,7 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, if (use_json) { json_object_string_add(json, "alert", "no BGP"); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -12310,7 +12313,7 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, } if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } @@ -12330,7 +12333,7 @@ peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, if (use_json) { json_object_string_add(json, "warning", "No such neighbor or address family"); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -12344,7 +12347,7 @@ peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, if (use_json) { json_object_string_add(json, "warning", "Inbound soft reconfiguration not enabled"); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 012d1d25a2..cf5ba1f6ec 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -10117,7 +10117,7 @@ DEFUN (show_bgp_vrfs, json_object_int_add(json, "totalVrfs", count); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -10561,7 +10561,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_int_add(json, "totalPeers", count); json_object_int_add(json, "dynamicPeers", dn_count); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -12674,7 +12674,7 @@ bgp_show_neighbor (struct vty *vty, struct bgp *bgp, enum show_type type, if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -12705,7 +12705,7 @@ bgp_show_neighbor_vty (struct vty *vty, const char *name, if (use_json) { json_object_boolean_true_add(json, "bgpNoSuchInstance"); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else diff --git a/lib/plist.c b/lib/plist.c index eedb830c1a..336c55a084 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -2839,7 +2839,7 @@ prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name, u_char use_ else json_object_object_add(json, "ipv6PrefixList", json_prefix); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 1b26c67866..d9a4289edf 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3575,7 +3575,7 @@ show_ip_ospf_common (struct vty *vty, struct ospf *ospf, u_char use_json) if (use_json) { json_object_object_add(json, "areas", json_areas); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -3983,7 +3983,7 @@ show_ip_ospf_interface_common (struct vty *vty, struct ospf *ospf, int argc, if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4140,7 +4140,7 @@ show_ip_ospf_neighbor_common (struct vty *vty, struct ospf *ospf, u_char use_jso if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4251,7 +4251,7 @@ show_ip_ospf_neighbor_all_common (struct vty *vty, struct ospf *ospf, u_char use if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4345,7 +4345,7 @@ show_ip_ospf_neighbor_int_common (struct vty *vty, struct ospf *ospf, int arg_ba if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4711,7 +4711,7 @@ show_ip_ospf_neighbor_id_common (struct vty *vty, struct ospf *ospf, if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4802,7 +4802,7 @@ show_ip_ospf_neighbor_detail_common (struct vty *vty, struct ospf *ospf, u_char if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4897,7 +4897,7 @@ show_ip_ospf_neighbor_detail_all_common (struct vty *vty, struct ospf *ospf, u_c if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -5001,7 +5001,7 @@ show_ip_ospf_neighbor_int_detail_common (struct vty *vty, struct ospf *ospf, if (use_json) { - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 185cddea64..977c84d4dd 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1761,7 +1761,7 @@ zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t labe if (use_json) { json = lsp_json(lsp); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -1791,7 +1791,7 @@ zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf, json_object_object_add(json, label2str(lsp->ile.in_label, buf, BUFSIZ), lsp_json(lsp)); - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index e76e4ab8fa..1a9fc2294e 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2487,7 +2487,7 @@ do_show_ip_route (struct vty *vty, const char *vrf_name, safi_t safi, } } - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else @@ -4923,7 +4923,7 @@ DEFUN (show_ipv6_route, } } - vty_out (vty, "%s%s", json_object_to_json_string(json), VTY_NEWLINE); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); json_object_free(json); } else From 615d4265871653605bb53becc09bd77b48716dbb Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 16 Dec 2015 19:38:23 +0100 Subject: [PATCH 073/136] lib: fix vrf_bitmap leak in zclient_free() zclient_stop(), which is used as antagonist to zclient_init(), needs to undo the vrf_bitmap allocation. Otherwise zclient_init() will leak the allocated memory, for example when zclient_reset() is used. Reported-by: Lou Berger Signed-off-by: David Lamparter --- lib/zclient.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/zclient.c b/lib/zclient.c index a02cbbe4dd..24d9b589df 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -140,6 +140,9 @@ redist_del_instance (struct redist_proto *red, u_short instance) void zclient_stop (struct zclient *zclient) { + afi_t afi; + int i; + if (zclient_debug) zlog_debug ("zclient stopped"); @@ -162,6 +165,15 @@ zclient_stop (struct zclient *zclient) zclient->sock = -1; } zclient->fail = 0; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + vrf_bitmap_free(zclient->redist[afi][i]); + zclient->redist[afi][i] = VRF_BITMAP_NULL; + } + vrf_bitmap_free(zclient->default_information); + zclient->default_information = VRF_BITMAP_NULL; } void From 3f67fb9c307c5f83011b3fc2837436881289ecea Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 28 Sep 2016 12:25:16 -0300 Subject: [PATCH 074/136] ldpd: make route flags a 32bit field This is a followup to commit 0fc452dc5, which updated all daemons except ldpd. Signed-off-by: Renato Westphal --- ldpd/ldp_zebra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 1d443a6487..6c4137d305 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -361,7 +361,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, type = stream_getc(s); if (type == ZEBRA_ROUTE_CONNECT) kr.flags |= F_CONNECTED; - stream_getc(s); /* flags, unused */ + stream_getl(s); /* flags, unused */ stream_getw(s); /* instance, unused */ message_flags = stream_getc(s); if (!CHECK_FLAG(message_flags, ZAPI_MESSAGE_NEXTHOP)) From 134970a2a183d5021a1de42e8eede2fc3aa32e56 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 28 Sep 2016 12:25:17 -0300 Subject: [PATCH 075/136] ldpd: fix processing of redistributed routes Commit 5048fe changed the way zebra behave when a route is updated. Now, whenever a route is changed, zebra advertises its new version without withdrawing the old one. This patch adapts ldpd to understand this new behavior. After processing a ZEBRA_REDISTRIBUTE_IPV[46]_ADD message, we need to check for nexthops that were removed and, for each of them (if any), withdraw the associated labels from zebra. Signed-off-by: Renato Westphal --- ldpd/lde.c | 4 ++++ ldpd/lde.h | 3 +++ ldpd/lde_lib.c | 31 ++++++++++++++++++++++++++++++- ldpd/ldp_zebra.c | 4 ++++ ldpd/ldpd.h | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ldpd/lde.c b/ldpd/lde.c index ae29ef6a7d..904d0f8d9a 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -406,6 +406,7 @@ lde_dispatch_parent(struct thread *thread) switch (imsg.hdr.type) { case IMSG_NETWORK_ADD: + case IMSG_NETWORK_ADD_END: case IMSG_NETWORK_DEL: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) { log_warnx("%s: wrong imsg len", __func__); @@ -433,6 +434,9 @@ lde_dispatch_parent(struct thread *thread) lde_kernel_insert(&fec, kr.af, &kr.nexthop, kr.priority, kr.flags & F_CONNECTED, NULL); break; + case IMSG_NETWORK_ADD_END: + lde_kernel_reevaluate(&fec); + break; case IMSG_NETWORK_DEL: lde_kernel_remove(&fec, kr.af, &kr.nexthop, kr.priority); diff --git a/ldpd/lde.h b/ldpd/lde.h index 0fce5565a2..cf8f2129af 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -102,7 +102,9 @@ struct fec_nh { union ldpd_addr nexthop; uint32_t remote_label; uint8_t priority; + uint8_t flags; }; +#define F_FEC_NH_NEW 0x01 struct fec_node { struct fec fec; @@ -167,6 +169,7 @@ void lde_kernel_insert(struct fec *, int, union ldpd_addr *, uint8_t, int, void *); void lde_kernel_remove(struct fec *, int, union ldpd_addr *, uint8_t); +void lde_kernel_reevaluate(struct fec *); void lde_check_mapping(struct map *, struct lde_nbr *); void lde_check_request(struct map *, struct lde_nbr *); void lde_check_release(struct map *, struct lde_nbr *); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 568761bd61..c72a25985d 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -334,8 +334,11 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, fn = (struct fec_node *)fec_find(&ft, fec); if (fn == NULL) fn = fec_add(fec); - if (fec_nh_find(fn, af, nexthop, priority) != NULL) + fnh = fec_nh_find(fn, af, nexthop, priority); + if (fnh != NULL) { + fnh->flags |= F_FEC_NH_NEW; return; + } if (fn->fec.type == FEC_TYPE_PWID) fn->data = data; @@ -352,6 +355,7 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, } fnh = fec_nh_add(fn, af, nexthop, priority); + fnh->flags |= F_FEC_NH_NEW; lde_send_change_klabel(fn, fnh); switch (fn->fec.type) { @@ -402,6 +406,31 @@ lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop, } } +/* + * Whenever a route is changed, zebra advertises its new version without + * withdrawing the old one. So, after processing a ZEBRA_REDISTRIBUTE_IPV[46]_ADD + * message, we need to check for nexthops that were removed and, for each of + * them (if any), withdraw the associated labels from zebra. + */ +void +lde_kernel_reevaluate(struct fec *fec) +{ + struct fec_node *fn; + struct fec_nh *fnh, *safe; + + fn = (struct fec_node *)fec_find(&ft, fec); + if (fn == NULL) + return; + + LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) { + if (fnh->flags & F_FEC_NH_NEW) + fnh->flags &= ~F_FEC_NH_NEW; + else + lde_kernel_remove(fec, fnh->af, &fnh->nexthop, + fnh->priority); + } +} + void lde_check_mapping(struct map *map, struct lde_nbr *ln) { diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 6c4137d305..b796b6f6d6 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -438,6 +438,10 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, } } + if (command == ZEBRA_REDISTRIBUTE_IPV4_ADD || + command == ZEBRA_REDISTRIBUTE_IPV6_ADD) + main_imsg_compose_lde(IMSG_NETWORK_ADD_END, 0, &kr, sizeof(kr)); + return (0); } diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 9601f25f70..b94794208a 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -117,6 +117,7 @@ enum imsg_type { IMSG_NEIGHBOR_UP, IMSG_NEIGHBOR_DOWN, IMSG_NETWORK_ADD, + IMSG_NETWORK_ADD_END, IMSG_NETWORK_DEL, IMSG_SOCKET_IPC, IMSG_SOCKET_NET, From daca38aecede78f3d4b1cc93ccd0623c250c4778 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 28 Sep 2016 12:25:18 -0300 Subject: [PATCH 076/136] ldpd: always advertise labels upon receiving a redistributed route Whenever a routing daemon advertises a new version of a route to zebra, zebra removes the old version of this route (implicit withdraw) and then create a new 'rib' structure for the new version of the route. In this process, the previously received label(s) from ldpd are lost. This is because upon receiving a ZEBRA_MPLS_LABELS_ADD message, zebra only adds a label to a nexthop of an existing route. And routes are volatile, they can be removed while being updated. To workaround this issue, this patch makes ldpd always advertise the appropriate labels whenever it receives a redistributed route, even if it was already received before (an older version). This way, when ldpd receives the updated version of a route, it will readvertise the appropriate label(s) and zebra will reinstall them. Signed-off-by: Renato Westphal --- ldpd/lde_lib.c | 1 + zebra/zebra_mpls.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index c72a25985d..43e5f92f2f 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -336,6 +336,7 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, fn = fec_add(fec); fnh = fec_nh_find(fn, af, nexthop, priority); if (fnh != NULL) { + lde_send_change_klabel(fn, fnh); fnh->flags |= F_FEC_NH_NEW; return; } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 977c84d4dd..15e5c330e4 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1311,10 +1311,12 @@ mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type, return -1; found: - if (add) + if (add && nexthop->nh_label_type == ZEBRA_LSP_NONE) nexthop_add_labels (nexthop, type, 1, &out_label); - else + else if (!add && nexthop->nh_label_type == type) nexthop_del_labels (nexthop); + else + return 0; SET_FLAG (rib->status, RIB_ENTRY_CHANGED); SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); From afe0c07b416cd89305848e746e5fca7f1cb3a6a3 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 28 Sep 2016 20:35:12 -0400 Subject: [PATCH 077/136] lib: Fix compile for json When compiling with the json library instead of the json-c library allow it to switch back to the old way of printing. Signed-off-by: Donald Sharp --- lib/json.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/json.h b/lib/json.h index 5dbad601a3..c8d7fae1cd 100644 --- a/lib/json.h +++ b/lib/json.h @@ -26,6 +26,12 @@ #include #else #include + +/* + * json_object_to_json_string_ext is only available for json-c + * so let's just turn it back to the original usage. + */ +#define json_object_to_json_string_ext(A, B) json_object_to_json_string (A) #endif extern int use_json(const int argc, const char *argv[]); From bb5ea4a6d799a7b38d05dc0df44678b278915942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Mon, 26 Sep 2016 11:07:01 +0200 Subject: [PATCH 078/136] ospfd: Fix OSPF daemon pid file param When -i is specified on the cli, ospf was ignoring this value. --- ospfd/ospf_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 0ed39af411..43aa683fa4 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -203,6 +203,8 @@ main (int argc, char **argv) ospf_apiserver_enable = 0; #endif /* SUPPORT_OSPF_API */ + strcpy(pid_file, PATH_OSPFD_PID); + /* get program name */ progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); @@ -363,7 +365,6 @@ main (int argc, char **argv) } else { - strcpy(pid_file, PATH_OSPFD_PID); strcpy(vty_path, OSPF_VTYSH_PATH); } /* Process id file create. */ From 3694c43ac09e03fb34fa8aed6c0e054a295f2414 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 29 Sep 2016 21:47:07 -0400 Subject: [PATCH 079/136] lib: Only display memory items that have allocations When displaying memory via a 'show run' only display items that have actual memory allocated. Signed-off-by: Donald Sharp --- lib/memory_vty.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/memory_vty.c b/lib/memory_vty.c index e4cb295cf0..73acafe23b 100644 --- a/lib/memory_vty.c +++ b/lib/memory_vty.c @@ -82,13 +82,15 @@ static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt) if (!mt) vty_out (vty, "--- qmem %s ---%s", mg->name, VTY_NEWLINE); else { - char size[32]; - snprintf(size, sizeof(size), "%6zu", mt->size); - vty_out (vty, "%-30s: %10zu %s%s", - mt->name, mt->n_alloc, - mt->size == 0 ? "" : - mt->size == SIZE_VAR ? "(variably sized)" : - size, VTY_NEWLINE); + if (mt->n_alloc != 0) { + char size[32]; + snprintf(size, sizeof(size), "%6zu", mt->size); + vty_out (vty, "%-30s: %10zu %s%s", + mt->name, mt->n_alloc, + mt->size == 0 ? "" : + mt->size == SIZE_VAR ? "(variably sized)" : + size, VTY_NEWLINE); + } } return 0; } From 446bb95e0f2ab602a522ec170a7d9c5e9265547a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Fri, 15 Jan 2016 17:36:31 +0200 Subject: [PATCH 080/136] zebra: support FIB override routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FIB override routes are for routing protocols that establish shortcut routes, or establish point-to-point routes that should not be redistributed. Namely this is useful NHRP daemon to come. Zebra is extended to select two entries from RIB the "best" entry from routing protocols, and the FIB entry to install to kernel. FIB override routes are never selected as best entry, and thus are never adverticed to other routing daemons. The best FIB override, or if it does not exist the otherwise best RIB is selected as FIB entry to be installed. Signed-off-by: Timo Teräs Acked-by: Donald Sharp [CF: Massage to fit cumulus tree] Signed-off-by: Christian Franke --- lib/zebra.h | 1 + zebra/rib.h | 1 + zebra/zebra_fpm.c | 2 +- zebra/zebra_rib.c | 326 +++++++++++++++++++++++++------------------ zebra/zebra_static.c | 16 ++- zebra/zebra_vty.c | 7 +- zebra/zserv.c | 5 +- 7 files changed, 215 insertions(+), 143 deletions(-) diff --git a/lib/zebra.h b/lib/zebra.h index 8763abd766..599daa3509 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -474,6 +474,7 @@ extern const char *zserv_command_string (unsigned int command); #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_REJECT 0x80 #define ZEBRA_FLAG_SCOPE_LINK 0x100 +#define ZEBRA_FLAG_FIB_OVERRIDE 0x200 #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ diff --git a/zebra/rib.h b/zebra/rib.h index 285166f067..96301a8af4 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -87,6 +87,7 @@ struct rib /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */ #define RIB_ENTRY_NEXTHOPS_CHANGED 0x2 #define RIB_ENTRY_CHANGED 0x4 +#define RIB_ENTRY_SELECTED_FIB 0x8 /* Nexthop information. */ u_char nexthop_num; diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 5a68bcbfac..a5a953c51f 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -928,7 +928,7 @@ zfpm_route_for_update (rib_dest_t *dest) RIB_DEST_FOREACH_ROUTE (dest, rib) { - if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) continue; return rib; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f57c0b5d67..b7d12a5cb4 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -432,7 +432,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, /* if the next hop is imported from another table, skip it */ if (match->type == ZEBRA_ROUTE_TABLE) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -639,7 +639,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -804,7 +804,7 @@ rib_match (afi_t afi, safi_t safi, vrf_id_t vrf_id, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -940,7 +940,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -960,7 +960,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) /* * This clone function, unlike its original rib_lookup_ipv4(), checks * if specified IPv4 route record (prefix/mask -> gate) exists in - * the whole RIB and has ZEBRA_FLAG_SELECTED set. + * the whole RIB and has RIB_ENTRY_SELECTED_FIB set. * * Return values: * -1: error @@ -1000,7 +1000,7 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate, { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -1310,14 +1310,19 @@ rib_uninstall (struct route_node *rn, struct rib *rib) { rib_table_info_t *info = rn->table->info; - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) { if (info->safi == SAFI_UNICAST) zfpm_trigger_update (rn, "rib_uninstall"); - redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); + UNSET_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB); + } + + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + { + redistribute_delete (&rn->p, rib); UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED); } } @@ -1385,76 +1390,70 @@ rib_gc_dest (struct route_node *rn) } static void -rib_process_add_route (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *select) +rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *new) { char buf[INET6_ADDRSTRLEN]; - int installed = 1; zfpm_trigger_update (rn, "new route selected"); /* Update real nexthop. This may actually determine if nexthop is active or not. */ - if (!nexthop_active_update (rn, select, 1)) + if (!nexthop_active_update (rn, new, 1)) { - UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); return; } - SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + SET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_debug ("%u:%s/%d: Adding route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type); } - if (!RIB_SYSTEM_ROUTE (select)) + if (!RIB_SYSTEM_ROUTE (new)) { - if (rib_install_kernel (rn, select, 0)) + if (rib_install_kernel (rn, new, 0)) { - installed = 0; inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_warn ("%u:%s/%d: Route install failed", zvrf->vrf_id, buf, rn->p.prefixlen); } } - /* Update for redistribution. */ - if (installed) - redistribute_update (&rn->p, select, NULL); - UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); } static void -rib_process_del_route (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *fib) +rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *old) { char buf[INET6_ADDRSTRLEN]; zfpm_trigger_update (rn, "removing existing route"); - /* Withdraw redistribute and uninstall from kernel. */ + /* Uninstall from kernel. */ if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, fib, fib->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, old, old->type); } - redistribute_delete(&rn->p, fib); - if (!RIB_SYSTEM_ROUTE (fib)) - rib_uninstall_kernel (rn, fib); + if (!RIB_SYSTEM_ROUTE (old)) + rib_uninstall_kernel (rn, old); - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); + UNSET_FLAG (old->status, RIB_ENTRY_SELECTED_FIB); /* Update nexthop for route, reset changed flag. */ - nexthop_active_update (rn, fib, 1); - UNSET_FLAG(fib->status, RIB_ENTRY_CHANGED); + nexthop_active_update (rn, old, 1); + UNSET_FLAG(old->status, RIB_ENTRY_CHANGED); } static void -rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, - struct rib *select, struct rib *fib) +rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, + struct rib *old, struct rib *new) { char buf[INET6_ADDRSTRLEN]; struct nexthop *nexthop = NULL, *tnexthop; @@ -1469,13 +1468,13 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, * We have to install or update if a new route has been selected or * something has changed. */ - if (select != fib || - CHECK_FLAG (select->status, RIB_ENTRY_CHANGED)) + if (new != old || + CHECK_FLAG (new->status, RIB_ENTRY_CHANGED)) { zfpm_trigger_update (rn, "updating existing route"); /* Update the nexthop; we could determine here that nexthop is inactive. */ - if (nexthop_active_update (rn, select, 1)) + if (nexthop_active_update (rn, new, 1)) nh_active = 1; /* If nexthop is active, install the selected route, if appropriate. If @@ -1486,18 +1485,18 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, { if (IS_ZEBRA_DEBUG_RIB) { - if (select != fib) + if (new != old) zlog_debug ("%u:%s/%d: Updating route rn %p, rib %p (type %d) " "old %p (type %d)", zvrf->vrf_id, buf, rn->p.prefixlen, - rn, select, select->type, fib, fib->type); + rn, new, new->type, old, old->type); else zlog_debug ("%u:%s/%d: Updating route rn %p, rib %p (type %d)", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type); + zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type); } /* Non-system route should be installed. */ - if (!RIB_SYSTEM_ROUTE (select)) + if (!RIB_SYSTEM_ROUTE (new)) { - if (rib_install_kernel (rn, select, 1)) + if (rib_install_kernel (rn, new, 1)) { installed = 0; inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); @@ -1507,26 +1506,23 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, } /* If install succeeded or system route, cleanup flags for prior route. */ - if (installed && select != fib) + if (installed && new != old) { - if (RIB_SYSTEM_ROUTE(select)) + if (RIB_SYSTEM_ROUTE(new)) { - if (!RIB_SYSTEM_ROUTE (fib)) - rib_uninstall_kernel (rn, fib); + if (!RIB_SYSTEM_ROUTE (old)) + rib_uninstall_kernel (rn, old); } else { - for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = old->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } /* Update for redistribution. */ if (installed) - { - SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); - redistribute_update (&rn->p, select, (select == fib) ? NULL : fib); - } + SET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); } /* @@ -1535,28 +1531,22 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, */ if (!nh_active || !installed) { - struct rib *del; - if (IS_ZEBRA_DEBUG_RIB) { - if (select != fib) + if (new != old) zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d) " "old %p (type %d) - %s", zvrf->vrf_id, buf, rn->p.prefixlen, - rn, select, select->type, fib, fib->type, + rn, new, new->type, old, old->type, nh_active ? "install failed" : "nexthop inactive"); else zlog_debug ("%u:%s/%d: Deleting route rn %p, rib %p (type %d) - %s", - zvrf->vrf_id, buf, rn->p.prefixlen, rn, select, select->type, + zvrf->vrf_id, buf, rn->p.prefixlen, rn, new, new->type, nh_active ? "install failed" : "nexthop inactive"); } - del = (select == fib) ? select : fib; - - redistribute_delete(&rn->p, del); - - if (!RIB_SYSTEM_ROUTE (del)) - rib_uninstall_kernel (rn, del); - UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + if (!RIB_SYSTEM_ROUTE (old)) + rib_uninstall_kernel (rn, old); + UNSET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); } } else @@ -1567,33 +1557,33 @@ rib_process_update_route (struct zebra_vrf *zvrf, struct route_node *rn, * netlink reporting interface up before IPv4 or IPv6 protocol is ready * to add routes. */ - if (!RIB_SYSTEM_ROUTE (select)) + if (!RIB_SYSTEM_ROUTE (new)) { int in_fib = 0; - for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing)) + for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { in_fib = 1; break; } if (!in_fib) - rib_install_kernel (rn, select, 0); + rib_install_kernel (rn, new, 0); } } /* Update prior route. */ - if (select != fib) + if (new != old) { - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); + UNSET_FLAG (old->status, RIB_ENTRY_SELECTED_FIB); /* Set real nexthop. */ - nexthop_active_update (rn, fib, 1); - UNSET_FLAG(fib->status, RIB_ENTRY_CHANGED); + nexthop_active_update (rn, old, 1); + UNSET_FLAG(old->status, RIB_ENTRY_CHANGED); } /* Clear changed flag. */ - UNSET_FLAG(select->status, RIB_ENTRY_CHANGED); + UNSET_FLAG(new->status, RIB_ENTRY_CHANGED); } /* Check if 'alternate' RIB entry is better than 'current'. */ @@ -1644,33 +1634,32 @@ rib_process (struct route_node *rn) { struct rib *rib; struct rib *next; - struct rib *fib = NULL; - struct rib *select = NULL; - struct rib *del = NULL; + struct rib *old_selected = NULL; + struct rib *new_selected = NULL; + struct rib *old_fib = NULL; + struct rib *new_fib = NULL; struct rib *best = NULL; char buf[INET6_ADDRSTRLEN]; rib_dest_t *dest; struct zebra_vrf *zvrf = NULL; vrf_id_t vrf_id = VRF_UNKNOWN; - rib_table_info_t *info; assert (rn); - info = rn->table->info; - + dest = rib_dest_from_rnode (rn); if (dest) { zvrf = rib_dest_vrf (dest); vrf_id = zvrf->vrf_id; } - + if (IS_ZEBRA_DEBUG_RIB) inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u:%s/%d: Processing rn %p", vrf_id, buf, rn->p.prefixlen, rn); - RNODE_FOREACH_RIB_SAFE (rn, rib, next) + RNODE_FOREACH_RIB (rn, rib) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u:%s/%d: Examine rib %p (type %d) status %x flags %x " @@ -1680,31 +1669,23 @@ rib_process (struct route_node *rn) UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); - /* Currently installed rib. */ + /* Currently selected rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { - assert (fib == NULL); - fib = rib; + assert (old_selected == NULL); + old_selected = rib; + } + /* Currently in fib */ + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + { + assert (old_fib == NULL); + old_fib = rib; } - /* Unlock removed routes, so they'll be freed, bar the FIB entry, - * which we need to do do further work with below. - */ + /* Skip deleted entries from selection */ if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) - { - if (rib != fib) - { - if (IS_ZEBRA_DEBUG_RIB) - rnode_debug (rn, vrf_id, "rn %p, removing rib %p", - (void *)rn, (void *)rib); - rib_unlink (rn, rib); - } - else - del = rib; - - continue; - } - + continue; + /* Skip unreachable nexthop. */ /* This first call to nexthop_active_update is merely to determine if * there's any change to nexthops associated with this RIB entry. Now, @@ -1721,9 +1702,15 @@ rib_process (struct route_node *rn) { if (rib->type == ZEBRA_ROUTE_TABLE) { + /* XXX: HERE BE DRAGONS!!!!! + * In all honesty, I have not yet figured out what this part + * does or why the RIB_ENTRY_CHANGED test above is correct + * or why we need to delete a route here, and also not whether + * this concerns both selected and fib route, or only selected + * or only fib */ /* This entry was denied by the 'ip protocol table' route-map, we * need to delete it */ - if (rib != fib) + if (rib != old_selected) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: %s/%d: imported via import-table but denied " @@ -1732,15 +1719,12 @@ rib_process (struct route_node *rn) rib_unlink (rn, rib); } else - del = rib; + SET_FLAG (rib->status, RIB_ENTRY_REMOVED); } continue; } - if (info->safi == SAFI_MULTICAST) - continue; - /* Infinite distance. */ if (rib->distance == DISTANCE_INFINITY) { @@ -1748,33 +1732,101 @@ rib_process (struct route_node *rn) continue; } - best = rib_choose_best(select, rib); - if (select && best != select) - UNSET_FLAG (select->status, RIB_ENTRY_CHANGED); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) + { + best = rib_choose_best(new_fib, rib); + if (new_fib && best != new_fib) + UNSET_FLAG (new_fib->status, RIB_ENTRY_CHANGED); + new_fib = best; + } + else + { + best = rib_choose_best(new_selected, rib); + if (new_selected && best != new_selected) + UNSET_FLAG (new_selected->status, RIB_ENTRY_CHANGED); + new_selected = best; + } if (best != rib) UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED); - select = best; - } /* RNODE_FOREACH_RIB_SAFE */ + } /* RNODE_FOREACH_RIB */ + + /* If no FIB override route, use the selected route also for FIB */ + if (new_fib == NULL) + new_fib = new_selected; /* After the cycle is finished, the following pointers will be set: - * select --- the winner RIB entry, if any was found, otherwise NULL - * fib --- the SELECTED RIB entry, if any, otherwise NULL - * del --- equal to fib, if fib is queued for deletion, NULL otherwise - * rib --- NULL + * old_selected --- RIB entry currently having SELECTED + * new_selected --- RIB entry that is newly SELECTED + * old_fib --- RIB entry currently in kernel FIB + * new_fib --- RIB entry that is newly to be in kernel FIB + * + * new_selected will get SELECTED flag, and is going to be redistributed + * the zclients. new_fib (which can be new_selected) will be installed in kernel. */ - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug ("%u:%s/%d: After processing: select %p fib %p del %p", - vrf_id, buf, rn->p.prefixlen, select, fib, del); - /* Same RIB entry is selected. Update FIB and finish. */ - if (select && select == fib) - rib_process_update_route (zvrf, rn, select, select); - else if (select && fib) - rib_process_update_route (zvrf, rn, select, fib); - else if (select) - rib_process_add_route (zvrf, rn, select); - else if (fib) - rib_process_del_route (zvrf, rn, fib); + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + { + zlog_debug ("%u:%s/%d: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + vrf_id, buf, rn->p.prefixlen, + (void *)old_selected, + (void *)new_selected, + (void *)old_fib, + (void *)new_fib); + } + + /* Buffer RIB_ENTRY_CHANGED here, because it will get cleared if + * fib == selected */ + bool selected_changed = new_selected && CHECK_FLAG(new_selected->status, + RIB_ENTRY_CHANGED); + + /* Update fib according to selection results */ + if (new_fib && old_fib) + rib_process_update_fib (zvrf, rn, old_fib, new_fib); + else if (new_fib) + rib_process_add_fib (zvrf, rn, new_fib); + else if (old_fib) + rib_process_del_fib (zvrf, rn, old_fib); + + /* Redistribute SELECTED entry */ + if (old_selected != new_selected || selected_changed) + { + struct nexthop *nexthop, *tnexthop; + int recursing; + + /* Check if we have a FIB route for the destination, otherwise, + * don't redistribute it */ + for (ALL_NEXTHOPS_RO(new_fib ? new_fib->nexthop : NULL, nexthop, + tnexthop, recursing)) + { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + { + break; + } + } + if (!nexthop) + new_selected = NULL; + + if (new_selected && new_selected != new_fib) + { + nexthop_active_update(rn, new_selected, 1); + UNSET_FLAG(new_selected->status, RIB_ENTRY_CHANGED); + } + + if (old_selected) + { + if (!new_selected) + redistribute_delete(&rn->p, old_selected); + if (old_selected != new_selected) + UNSET_FLAG (old_selected->flags, ZEBRA_FLAG_SELECTED); + } + + if (new_selected) + { + /* Install new or replace existing redistributed entry */ + SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED); + redistribute_update (&rn->p, new_selected, old_selected); + } + } #if 0 if (select && select == fib) @@ -1951,12 +2003,18 @@ rib_process (struct route_node *rn) } #endif - /* FIB route was removed, should be deleted */ - if (del) + /* Remove all RIB entries queued for removal */ + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - if (IS_ZEBRA_DEBUG_RIB) - rnode_debug (rn, vrf_id, "Deleting fib %p, rn %p", (void *)del, (void *)rn); - rib_unlink (rn, del); + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + { + if (IS_ZEBRA_DEBUG_RIB) + { + rnode_debug (rn, vrf_id, "rn %p, removing rib %p", + (void *)rn, (void *)rib); + } + rib_unlink(rn, rib); + } } /* @@ -2523,7 +2581,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p, vrf_id_t vrf_id) */ RNODE_FOREACH_RIB (rn, rib) { - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) && + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) && ! RIB_SYSTEM_ROUTE (rib)) { changed = 1; @@ -2669,7 +2727,7 @@ rib_delete (afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) fib = rib; if (rib->type != type) @@ -2730,7 +2788,7 @@ rib_delete (afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); + UNSET_FLAG (fib->status, RIB_ENTRY_SELECTED_FIB); } else { @@ -3107,7 +3165,7 @@ rib_close_table (struct route_table *table) for (rn = route_top (table); rn; rn = route_next (rn)) RNODE_FOREACH_RIB (rn, rib) { - if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) continue; if (info->safi == SAFI_UNICAST) diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index f2362e6871..1dc54e171c 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -320,13 +320,21 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ /* If there are other active nexthops, do an update. */ if (rib->nexthop_active_num > 1) { - rib_install_kernel (rn, rib, 1); - redistribute_update (&rn->p, rib, NULL); + /* Update route in kernel if it's in fib */ + if (CHECK_FLAG(rib->status, RIB_ENTRY_SELECTED_FIB)) + rib_install_kernel (rn, rib, 1); + /* Update redistribution if it's selected */ + if (CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED)) + redistribute_update (&rn->p, rib, NULL); } else { - redistribute_delete (&rn->p, rib); - rib_uninstall_kernel (rn, rib); + /* Remove from redistribute if selected route becomes inactive */ + if (CHECK_FLAG(rib->flags, ZEBRA_FLAG_SELECTED)) + redistribute_delete (&rn->p, rib); + /* Remove from kernel if fib route becomes inactive */ + if (CHECK_FLAG(rib->status, RIB_ENTRY_SELECTED_FIB)) + rib_uninstall_kernel (rn, rib); } } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 1a9fc2294e..054979fd0f 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2023,6 +2023,10 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) } if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) vty_out (vty, ", best"); + else if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + vty_out (vty, ", fib"); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) + vty_out (vty, ", fib-override"); if (rib->refcnt) vty_out (vty, ", refcnt %ld", rib->refcnt); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) @@ -2299,7 +2303,8 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, len += vty_out (vty, "[%d]", rib->instance); len += vty_out (vty, "%c%c %s", CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) - ? '>' : ' ', + ? '>' : CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) + ? '!' : ' ', CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', prefix2str (&rn->p, buf, sizeof buf)); diff --git a/zebra/zserv.c b/zebra/zserv.c index 32c5ea7142..25f028af69 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -669,8 +669,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, break; } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) - || nexthop_has_fib_child(nexthop)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -928,7 +927,7 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr, struc * are looking up. Therefore, we will just iterate over the top * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) num += zsend_write_nexthop (s, nexthop); stream_putc_at (s, nump, num); /* store nexthop_num */ From 1a39c60a6a0202e06fd0a390416ab843527bda88 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Thu, 16 Jun 2016 10:16:52 -0400 Subject: [PATCH 081/136] bgpd: eliminate RD related duplicate code in bgp_encap.c decode_rd_... apis are declared global in bgp_mplsvpn.c --- bgpd/bgp_encap.c | 45 --------------------------------------------- bgpd/bgp_mplsvpn.c | 8 ++++---- bgpd/bgp_mplsvpn.h | 5 +++++ 3 files changed, 9 insertions(+), 49 deletions(-) diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c index 4b1b6757a9..3f7712b24d 100644 --- a/bgpd/bgp_encap.c +++ b/bgpd/bgp_encap.c @@ -45,51 +45,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" #include "bgpd/bgp_encap.h" -static u_int16_t -decode_rd_type (u_char *pnt) -{ - u_int16_t v; - - v = ((u_int16_t) *pnt++ << 8); - v |= (u_int16_t) *pnt; - return v; -} - - -static void -decode_rd_as (u_char *pnt, struct rd_as *rd_as) -{ - rd_as->as = (u_int16_t) *pnt++ << 8; - rd_as->as |= (u_int16_t) *pnt++; - - rd_as->val = ((u_int32_t) *pnt++) << 24; - rd_as->val |= ((u_int32_t) *pnt++) << 16; - rd_as->val |= ((u_int32_t) *pnt++) << 8; - rd_as->val |= (u_int32_t) *pnt; -} - -static void -decode_rd_as4 (u_char *pnt, struct rd_as *rd_as) -{ - rd_as->as = (u_int32_t) *pnt++ << 24; - rd_as->as |= (u_int32_t) *pnt++ << 16; - rd_as->as |= (u_int32_t) *pnt++ << 8; - rd_as->as |= (u_int32_t) *pnt++; - - rd_as->val = ((u_int32_t) *pnt++ << 8); - rd_as->val |= (u_int32_t) *pnt; -} - -static void -decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) -{ - memcpy (&rd_ip->ip, pnt, 4); - pnt += 4; - - rd_ip->val = ((u_int16_t) *pnt++ << 8); - rd_ip->val |= (u_int16_t) *pnt; -} - static void ecom2prd(struct ecommunity *ecom, struct prefix_rd *prd) { diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 4656f7502f..91f0e91b39 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -35,7 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_attr.h" #include "bgpd/bgp_mplsvpn.h" -static u_int16_t +u_int16_t decode_rd_type (u_char *pnt) { u_int16_t v; @@ -57,7 +57,7 @@ decode_label (u_char *pnt) } /* type == RD_TYPE_AS */ -static void +void decode_rd_as (u_char *pnt, struct rd_as *rd_as) { rd_as->as = (u_int16_t) *pnt++ << 8; @@ -70,7 +70,7 @@ decode_rd_as (u_char *pnt, struct rd_as *rd_as) } /* type == RD_TYPE_AS4 */ -static void +void decode_rd_as4 (u_char *pnt, struct rd_as *rd_as) { rd_as->as = (u_int32_t) *pnt++ << 24; @@ -83,7 +83,7 @@ decode_rd_as4 (u_char *pnt, struct rd_as *rd_as) } /* type == RD_TYPE_IP */ -static void +void decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) { memcpy (&rd_ip->ip, pnt, 4); diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 3299b9cb9a..3fbbd33540 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -41,9 +41,14 @@ struct rd_ip u_int16_t val; }; +extern u_int16_t decode_rd_type (u_char *); extern void bgp_mplsvpn_init (void); extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *); extern u_int32_t decode_label (u_char *); +extern void encode_label(u_int32_t, u_char *); +extern void decode_rd_as (u_char *, struct rd_as *); +extern void decode_rd_as4 (u_char *, struct rd_as *); +extern void decode_rd_ip (u_char *, struct rd_ip *); extern int str2prefix_rd (const char *, struct prefix_rd *); extern int str2tag (const char *, u_char *); extern char *prefix_rd2str (struct prefix_rd *, char *, size_t); From c634f609a6090e2e28d99725c16aa2c5c3bab11f Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Tue, 19 Apr 2016 16:06:35 -0400 Subject: [PATCH 082/136] lib: add route_table_get_default_delegate --- lib/table.c | 6 ++++++ lib/table.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/lib/table.c b/lib/table.c index 8858aea0fd..d0e084ead2 100644 --- a/lib/table.c +++ b/lib/table.c @@ -523,6 +523,12 @@ static route_table_delegate_t default_delegate = { .destroy_node = route_node_destroy }; +route_table_delegate_t * +route_table_get_default_delegate(void) +{ + return &default_delegate; +} + /* * route_table_init */ diff --git a/lib/table.h b/lib/table.h index 34c196aa47..e6cdcfef1e 100644 --- a/lib/table.h +++ b/lib/table.h @@ -144,6 +144,9 @@ extern struct route_table *route_table_init (void); extern struct route_table * route_table_init_with_delegate (route_table_delegate_t *); +extern route_table_delegate_t * +route_table_get_default_delegate(void); + extern void route_table_finish (struct route_table *); extern void route_unlock_node (struct route_node *node); extern struct route_node *route_top (struct route_table *); From 17d06b64d0229ef6dcf8e8132f45b886e26ab954 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Fri, 23 Sep 2016 13:40:19 -0400 Subject: [PATCH 083/136] log.h: restore vzlog extern --- lib/log.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/log.h b/lib/log.h index 31bf2b67cf..cb8fac78c3 100644 --- a/lib/log.h +++ b/lib/log.h @@ -123,6 +123,8 @@ extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +extern void vzlog (struct zlog *, int , const char *, va_list ); + extern void zlog_thread_info (int log_level); /* Set logging level for the given destination. If the log_level From f9fe6278862d31a6d9fb2552e7ff8a63ac0812db Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Sat, 24 Sep 2016 09:18:23 -0400 Subject: [PATCH 084/136] memory: restore 'memstats:' keyword when logging memstats - useful in log --- lib/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/memory.c b/lib/memory.c index 38e424da7d..99b191c2be 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -134,7 +134,7 @@ qmem_exit_walker (void *arg, struct memgroup *mg, struct memtype *mt) char size[32]; eda->error++; snprintf (size, sizeof (size), "%10zu", mt->size); - fprintf (stderr, "%s: %-30s: %6zu * %s\n", + fprintf (stderr, "%s: memstats: %-30s: %6zu * %s\n", eda->prefix, mt->name, mt->n_alloc, mt->size == SIZE_VAR ? "(variably sized)" : size); } From e55281983efaa904d40ce903996b30d20f74eb52 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Tue, 27 Sep 2016 07:54:56 -0400 Subject: [PATCH 085/136] bgpd: remove old/duplicate, and now out of order prefixlen test --- bgpd/bgp_mplsvpn.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 91f0e91b39..36ba65af1a 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -146,17 +146,17 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, pnt += BGP_ADDPATH_ID_LEN; } + /* Fetch prefix length. */ + prefixlen = *pnt++; + p.family = afi2family (packet->afi); + psize = PSIZE (prefixlen); + if (prefixlen < 88) { zlog_err ("prefix length is less than 88: %d", prefixlen); return -1; } - /* Fetch prefix length. */ - prefixlen = *pnt++; - p.family = afi2family (packet->afi); - psize = PSIZE (prefixlen); - /* sanity check against packet data */ if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8 || (pnt + psize) > lim) { From 90dcf2d7772977e842ab3927d2508e3401fbce45 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Sat, 24 Sep 2016 09:09:36 -0400 Subject: [PATCH 086/136] bgp debug: restore printing of memstats on exit, now prints if any debug flag set --- bgpd/bgp_debug.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_debug.h | 1 + bgpd/bgp_main.c | 2 ++ 3 files changed, 47 insertions(+) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index dc0f539847..4d9ff60e92 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -1686,6 +1686,50 @@ DEFUN (show_debugging_bgp, return CMD_SUCCESS; } +/* return count of number of debug flags set */ +int +bgp_debug_count(void) +{ + int ret = 0; + if (BGP_DEBUG (as4, AS4)) + ret++; + + if (BGP_DEBUG (as4, AS4_SEGMENT)) + ret++; + + if (BGP_DEBUG (bestpath, BESTPATH)) + ret++; + + if (BGP_DEBUG (keepalive, KEEPALIVE)) + ret++; + + if (BGP_DEBUG (neighbor_events, NEIGHBOR_EVENTS)) + ret++; + + if (BGP_DEBUG (nht, NHT)) + ret++; + + if (BGP_DEBUG (update_groups, UPDATE_GROUPS)) + ret++; + + if (BGP_DEBUG (update, UPDATE_PREFIX)) + ret++; + + if (BGP_DEBUG (update, UPDATE_IN)) + ret++; + + if (BGP_DEBUG (update, UPDATE_OUT)) + ret++; + + if (BGP_DEBUG (zebra, ZEBRA)) + ret++; + + if (BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) + ret++; + + return ret; +} + static int bgp_config_write_debug (struct vty *vty) { diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 835d585735..00fb670a47 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -150,4 +150,5 @@ extern int bgp_debug_update(struct peer *peer, struct prefix *p, extern int bgp_debug_bestpath(struct prefix *p); extern int bgp_debug_zebra(struct prefix *p); +extern int bgp_debug_count(void); #endif /* _QUAGGA_BGP_DEBUG_H */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 68d6cb7f38..404fe7d73a 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -296,6 +296,8 @@ bgp_exit (int status) if (zlog_default) closezlog (zlog_default); + if (bgp_debug_count()) + log_memstats_stderr ("bgpd"); exit (status); } From 1820090256230ac7cc32c021d4f912c9248131b2 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Tue, 12 Jan 2016 13:42:08 -0500 Subject: [PATCH 087/136] bgpd: drop machineparse / random "show" improvements Signed-off-by: Lou Berger Fix up a few changed missed in previous merge --- bgpd/bgp_route.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 41d80db605..f604b7658d 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -6036,7 +6036,7 @@ route_vty_out (struct vty *vty, struct prefix *p, if (json_paths) json_object_int_add(json_path, "med", attr->med); else - vty_out (vty, "%10u", attr->med); + vty_out (vty, "%10u ", attr->med); else if (!json_paths) vty_out (vty, " "); @@ -6046,7 +6046,7 @@ route_vty_out (struct vty *vty, struct prefix *p, if (json_paths) json_object_int_add(json_path, "localpref", attr->local_pref); else - vty_out (vty, "%7u", attr->local_pref); + vty_out (vty, "%7u ", attr->local_pref); else if (!json_paths) vty_out (vty, " "); @@ -6212,12 +6212,12 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t } #endif /* HAVE_IPV6 */ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) - vty_out (vty, "%10u", attr->med); + vty_out (vty, "%10u ", attr->med); else vty_out (vty, " "); if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) - vty_out (vty, "%7u", attr->local_pref); + vty_out (vty, "%7u ", attr->local_pref); else vty_out (vty, " "); @@ -7310,6 +7310,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, int header = 1; int display; unsigned long output_count; + unsigned long total_count; struct prefix *p; char buf[BUFSIZ]; char buf2[BUFSIZ]; @@ -7328,6 +7329,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, /* This is first entry point, so reset total line. */ output_count = 0; + total_count = 0; /* Start processing of routes. */ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) @@ -7342,6 +7344,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, for (ri = rn->info; ri; ri = ri->next) { + total_count++; if (type == bgp_show_type_flap_statistics || type == bgp_show_type_flap_address || type == bgp_show_type_flap_prefix @@ -7556,11 +7559,11 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, if (output_count == 0) { if (type == bgp_show_type_normal) - vty_out (vty, "No BGP network exists%s", VTY_NEWLINE); + vty_out (vty, "No BGP prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE); } else - vty_out (vty, "%sTotal number of prefixes %ld%s", - VTY_NEWLINE, output_count, VTY_NEWLINE); + vty_out (vty, "%sDisplayed %ld out of %ld total prefixes%s", + VTY_NEWLINE, output_count, total_count, VTY_NEWLINE); } return CMD_SUCCESS; @@ -11616,7 +11619,7 @@ bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) if (!bgp->rib[afi][safi]) { - vty_out (vty, "%% No RIB exist's for the AFI(%d)/SAFI(%d)%s", + vty_out (vty, "%% No RIB exists for the AFI(%d)/SAFI(%d)%s", afi, safi, VTY_NEWLINE); return CMD_WARNING; } @@ -11708,7 +11711,7 @@ bgp_table_stats_vty (struct vty *vty, const char *name, if (!bgp) { - vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); + vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); return CMD_WARNING; } if (strncmp (afi_str, "ipv", 3) == 0) From 520d2512db8496f9b79b917cb84e36b83465f884 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Tue, 19 Apr 2016 16:00:37 -0400 Subject: [PATCH 088/136] lib: add skiplist --- lib/Makefile.am | 7 +- lib/skiplist.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/skiplist.h | 159 +++++++++++ 3 files changed, 848 insertions(+), 3 deletions(-) create mode 100644 lib/skiplist.c create mode 100644 lib/skiplist.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 17be655a17..4401744ab2 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -15,7 +15,7 @@ libzebra_la_SOURCES = \ zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \ sigevent.c pqueue.c jhash.c workqueue.c nexthop.c json.c \ ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c \ - imsg-buffer.c imsg.c + imsg-buffer.c imsg.c skiplist.c BUILT_SOURCES = route_types.h gitversion.h @@ -32,7 +32,8 @@ pkginclude_HEADERS = \ privs.h sigevent.h pqueue.h jhash.h zassert.h \ workqueue.h route_types.h libospf.h nexthop.h json.h \ ptm_lib.h csv.h bfd.h vrf.h ns.h systemd.h bitfield.h \ - fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h + fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h \ + skiplist.h noinst_HEADERS = \ plist_int.h @@ -60,7 +61,7 @@ GITH=gitversion.h gitversion.h.tmp: $(srcdir)/../.git @PERL@ $(srcdir)/gitversion.pl $(srcdir) > ${GITH}.tmp gitversion.h: gitversion.h.tmp - { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH} + { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH} else .PHONY: gitversion.h diff --git a/lib/skiplist.c b/lib/skiplist.c new file mode 100644 index 0000000000..2a90b2c7c6 --- /dev/null +++ b/lib/skiplist.c @@ -0,0 +1,685 @@ +/* + * Copyright 1990 William Pugh + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Permission to include in quagga provide on March 31, 2016 + */ + +/* + * Skip List impementation based on code from William Pugh. + * ftp://ftp.cs.umd.edu/pub/skipLists/ + * + * Skip Lists are a probabilistic alternative to balanced trees, as + * described in the June 1990 issue of CACM and were invented by + * William Pugh in 1987. + * + * This file contains source code to implement a dictionary using + * skip lists and a test driver to test the routines. + * + * A couple of comments about this implementation: + * The routine randomLevel has been hard-coded to generate random + * levels using p=0.25. It can be easily changed. + * + * The insertion routine has been implemented so as to use the + * dirty hack described in the CACM paper: if a random level is + * generated that is more than the current maximum level, the + * current maximum level plus one is used instead. + * + * Levels start at zero and go up to MaxLevel (which is equal to + * (MaxNumberOfLevels-1). + * + * The run-time flag SKIPLIST_FLAG_ALLOW_DUPLICATES determines whether or + * not duplicates are allowed for a given list. If set, duplicates are + * allowed and act in a FIFO manner. If not set, an insertion of a value + * already in the list updates the previously existing binding. + * + * BitsInRandom is defined to be the number of bits returned by a call to + * random(). For most all machines with 32-bit integers, this is 31 bits + * as currently set. + */ + + +#include + +#include "memory.h" +#include "log.h" +#include "vty.h" +#include "skiplist.h" + +DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List") +DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node") + +#define BitsInRandom 31 + +#define MaxNumberOfLevels 16 +#define MaxLevel (MaxNumberOfLevels-1) +#define newNodeOfLevel(l) XCALLOC(MTYPE_SKIP_LIST_NODE, sizeof(struct skiplistnode)+(l)*sizeof(struct skiplistnode *)) + +static int randomsLeft; +static int randomBits; +static struct skiplist *skiplist_last_created; /* debugging hack */ + +#if 1 +#define CHECKLAST(sl) do {\ + if ((sl)->header->forward[0] && !(sl)->last) assert(0); \ + if (!(sl)->header->forward[0] && (sl)->last) assert(0); \ +} while (0) +#else +#define CHECKLAST(sl) +#endif + + +static int +randomLevel() +{ + register int level = 0; + register int b; + + do { + if (randomsLeft <= 0) { + randomBits = random(); + randomsLeft = BitsInRandom/2; + } + b = randomBits&3; + randomBits>>=2; + --randomsLeft; + + if (!b) { + level++; + if (level >= MaxLevel) + return MaxLevel; + } + } while (!b); + + return level; +} + +static int +default_cmp(void *key1, void *key2) +{ + if (key1 < key2) + return -1; + if (key1 > key2) + return 1; + return 0; +} + +unsigned int +skiplist_count(struct skiplist *l) +{ + return l->count; +} + +struct skiplist * +skiplist_new( + int flags, + int (*cmp) (void *key1, void *key2), + void (*del) (void *val)) +{ + struct skiplist *new; + + new = XCALLOC (MTYPE_SKIP_LIST, sizeof (struct skiplist)); + assert(new); + + new->level = 0; + new->count = 0; + new->header = newNodeOfLevel(MaxNumberOfLevels); + new->stats = newNodeOfLevel(MaxNumberOfLevels); + + new->flags = flags; + if (cmp) + new->cmp = cmp; + else + new->cmp = default_cmp; + + if (del) + new->del = del; + + skiplist_last_created = new; /* debug */ + + return new; +} + +void +skiplist_free(struct skiplist *l) +{ + register struct skiplistnode *p, *q; + + p = l->header; + + do { + q = p->forward[0]; + if (l->del && p != l->header) + (*l->del)(p->value); + XFREE(MTYPE_SKIP_LIST_NODE, p); + p = q; + } while (p); + + XFREE(MTYPE_SKIP_LIST_NODE, l->stats); + XFREE(MTYPE_SKIP_LIST, l); +} + + +int +skiplist_insert( + register struct skiplist *l, + register void *key, + register void *value) +{ + register int k; + struct skiplistnode *update[MaxNumberOfLevels]; + register struct skiplistnode *p, *q; + + CHECKLAST(l); + +/* DEBUG */ + if (!key) { + zlog_err("%s: key is 0, value is %p", __func__, value); + } + + p = l->header; + k = l->level; + do { + while (q = p->forward[k], q && (*l->cmp)(q->key, key) < 0) p = q; + update[k] = p; + } while (--k >= 0); + + if (!(l->flags & SKIPLIST_FLAG_ALLOW_DUPLICATES) && + q && ((*l->cmp)(q->key, key) == 0)) { + + return -1; + } + + k = randomLevel(); + if (k>l->level) { + k = ++l->level; + update[k] = l->header; + } + + q = newNodeOfLevel(k); + q->key = key; + q->value = value; +#if SKIPLIST_0TIMER_DEBUG + q->flags = SKIPLIST_NODE_FLAG_INSERTED; /* debug */ +#endif + + ++(l->stats->forward[k]); +#if SKIPLIST_DEBUG + zlog_debug("%s: incremented stats @%p:%d, now %ld", __func__, l, k, + l->stats->forward[k] - (struct skiplistnode *)NULL); +#endif + + do { + p = update[k]; + q->forward[k] = p->forward[k]; + p->forward[k] = q; + } while(--k>=0); + + /* + * If this is the last item in the list, update the "last" pointer + */ + if (!q->forward[0]) { + l->last = q; + } + + ++(l->count); + + CHECKLAST(l); + + return 0; +} + +int +skiplist_delete( + register struct skiplist *l, + register void *key, + register void *value) /* used only if duplicates allowed */ +{ + register int k,m; + struct skiplistnode *update[MaxNumberOfLevels]; + register struct skiplistnode *p, *q; + + CHECKLAST(l); + + /* to make debugging easier */ + for (k = 0; k < MaxNumberOfLevels; ++k) + update[k] = NULL; + + p = l->header; + k = m = l->level; + do { + while (q = p->forward[k], q && (*l->cmp)(q->key, key) < 0) p = q; + update[k] = p; + } while(--k>=0); + + if (l->flags & SKIPLIST_FLAG_ALLOW_DUPLICATES) { + while (q && ((*l->cmp)(q->key, key) == 0) && (q->value != value)) { + int i; + for (i = 0; i <= l->level; ++i) { + if (update[i]->forward[i] == q) + update[i] = q; + } + q = q->forward[0]; + } + } + + if (q && (*l->cmp)(q->key, key) == 0) { + if (!(l->flags & SKIPLIST_FLAG_ALLOW_DUPLICATES) || + (q->value == value)) { + + /* + * found node to delete + */ +#if SKIPLIST_0TIMER_DEBUG + q->flags &= ~SKIPLIST_NODE_FLAG_INSERTED; +#endif + /* + * If we are deleting the last element of the list, + * update the list's "last" pointer. + */ + if (l->last == q) { + if (update[0] == l->header) + l->last = NULL; + else + l->last = update[0]; + } + + for(k=0; k<=m && (p=update[k])->forward[k] == q; k++) { + p->forward[k] = q->forward[k]; + } + --(l->stats->forward[k-1]); +#if SKIPLIST_DEBUG + zlog_debug("%s: decremented stats @%p:%d, now %ld", + __func__, l, k-1, + l->stats->forward[k-1] - (struct skiplistnode *)NULL); +#endif + if (l->del) + (*l->del)(q->value); + XFREE(MTYPE_SKIP_LIST_NODE, q); + while( l->header->forward[m] == NULL && m > 0 ) + m--; + l->level = m; + CHECKLAST(l); + --(l->count); + return 0; + } + } + + CHECKLAST(l); + return -1; +} + +/* + * Obtain first value matching "key". Unless SKIPLIST_FLAG_ALLOW_DUPLICATES + * is set, this will also be the only value matching "key". + * + * Also set a cursor for use with skiplist_next_value. + */ +int +skiplist_first_value( + register struct skiplist *l, /* in */ + register void *key, /* in */ + void **valuePointer, /* out */ + void **cursor) /* out */ +{ + register int k; + register struct skiplistnode *p, *q; + + p = l->header; + k = l->level; + + do { + while (q = p->forward[k], q && (*l->cmp)(q->key, key) < 0) + p = q; + + } while (--k >= 0); + + if (!q || (*l->cmp)(q->key, key)) + return -1; + + if (valuePointer) + *valuePointer = q->value; + + if (cursor) + *cursor = q; + + return 0; +} + +int +skiplist_search( + register struct skiplist *l, + register void *key, + void **valuePointer) +{ + return skiplist_first_value(l, key, valuePointer, NULL); +} + + +/* + * Caller supplies key and value of an existing item in the list. + * Function returns the value of the next list item that has the + * same key (useful when SKIPLIST_FLAG_ALLOW_DUPLICATES is set). + * + * Returns 0 on success. If the caller-supplied key and value + * do not correspond to a list element, or if they specify the + * last element with the given key, -1 is returned. + */ +int +skiplist_next_value( + register struct skiplist *l, /* in */ + register void *key, /* in */ + void **valuePointer, /* in/out */ + void **cursor) /* in/out */ +{ + register int k,m; + register struct skiplistnode *p, *q; + + CHECKLAST(l); + + if (!(l->flags & SKIPLIST_FLAG_ALLOW_DUPLICATES)) { + return -1; + } + + if (!cursor || !*cursor) { + p = l->header; + k = m = l->level; + + /* + * Find matching key + */ + do { + while (q = p->forward[k], q && (*l->cmp)(q->key, key) < 0) + p = q; + } while(--k>=0); + + /* + * Find matching value + */ + while (q && ((*l->cmp)(q->key, key) == 0) && (q->value != *valuePointer)) { + q = q->forward[0]; + } + + if (!q || ((*l->cmp)(q->key, key) != 0) || (q->value != *valuePointer)) { + /* + * No matching value + */ + CHECKLAST(l); + return -1; + } + } else { + q = (struct skiplistnode *)*cursor; + } + + /* + * Advance cursor + */ + q = q->forward[0]; + + /* + * If we reached end-of-list or if the key is no longer the same, + * then return error + */ + if (!q || ((*l->cmp)(q->key, key) != 0)) + return -1; + + *valuePointer = q->value; + *cursor = q; + CHECKLAST(l); + return 0; +} + +int +skiplist_first( + register struct skiplist *l, + void **keyPointer, + void **valuePointer) +{ + register struct skiplistnode *p; + + CHECKLAST(l); + p = l->header->forward[0]; + if (!p) + return -1; + + if (keyPointer) + *keyPointer = p->key; + + if (valuePointer) + *valuePointer = p->value; + + CHECKLAST(l); + + return 0; +} + +int +skiplist_last( + register struct skiplist *l, + void **keyPointer, + void **valuePointer) +{ + CHECKLAST(l); + if (l->last) { + if (keyPointer) + *keyPointer = l->last->key; + if (valuePointer) + *valuePointer = l->last->value; + return 0; + } + return -1; +} + +/* + * true = empty + */ +int +skiplist_empty( + register struct skiplist *l) +{ + CHECKLAST(l); + if (l->last) + return 0; + return 1; +} + +/* + * Use this to walk the list. Caller sets *cursor to NULL to obtain + * first element. Return value of 0 indicates valid cursor/element + * returned, otherwise NULL cursor arg or EOL. + */ +int +skiplist_next( + register struct skiplist *l, /* in */ + void **keyPointer, /* out */ + void **valuePointer, /* out */ + void **cursor) /* in/out */ +{ + struct skiplistnode *p; + + if (!cursor) + return -1; + + CHECKLAST(l); + + if (!*cursor) { + p = l->header->forward[0]; + } else { + p = *cursor; + p = p->forward[0]; + } + *cursor = p; + + if (!p) + return -1; + + if (keyPointer) + *keyPointer = p->key; + + if (valuePointer) + *valuePointer = p->value; + + CHECKLAST(l); + + return 0; +} + +int +skiplist_delete_first( + register struct skiplist *l) +{ + register int k; + register struct skiplistnode *p, *q; + int nodelevel = 0; + + CHECKLAST(l); + + p = l->header; + q = l->header->forward[0]; + + if (!q) + return -1; + + for (k = l->level; k >= 0; --k) { + if (p->forward[k] == q) { + p->forward[k] = q->forward[k]; + if ((k == l->level) && (p->forward[k] == NULL) && (l->level > 0)) + --(l->level); + if (!nodelevel) + nodelevel = k; + } + } + +#if SKIPLIST_0TIMER_DEBUG + q->flags &= ~SKIPLIST_NODE_FLAG_INSERTED; +#endif + /* + * If we are deleting the last element of the list, + * update the list's "last" pointer. + */ + if (l->last == q) { + l->last = NULL; + } + + --(l->stats->forward[nodelevel]); +#if SKIPLIST_DEBUG + zlog_debug("%s: decremented stats @%p:%d, now %ld", __func__, l, nodelevel, + l->stats->forward[nodelevel] - (struct skiplistnode *)NULL); +#endif + + if (l->del) + (*l->del)(q->value); + + XFREE(MTYPE_SKIP_LIST_NODE, q); + + CHECKLAST(l); + + --(l->count); + + return 0; +} + +void +skiplist_debug(struct vty *vty, struct skiplist *l) +{ + int i; + + if (!l) + l = skiplist_last_created; + vty_out(vty, "Skiplist %p has max level %d%s", l, l->level, VTY_NEWLINE); + for (i = l->level; i >= 0; --i) + vty_out(vty, " @%d: %ld%s", + i, (long)((l->stats->forward[i]) - (struct skiplistnode *)NULL), + VTY_NEWLINE); +} + +static void * +scramble(int i) +{ + uintptr_t result; + + result = (i & 0xff) << 24; + result |= (i >> 8); + + return (void *)result; +} + +#define sampleSize 65536 +void +skiplist_test(struct vty *vty) { + struct skiplist *l; + register int i,k; + void *keys[sampleSize]; + void *v; + + zlog_debug("%s: entry", __func__); + + l= skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, NULL, NULL); + + zlog_debug("%s: skiplist_new returned %p", __func__, l); + + for (i=0; i < 4; i++) { + + for (k=0; k < sampleSize; k++) { + if (!(k%1000)) { + zlog_debug("%s: (%d:%d)", __func__, i, k); + } + //keys[k] = (void *)random(); + keys[k] = (void *)scramble(k); + if (skiplist_insert(l, keys[k], keys[k])) + zlog_debug("error in insert #%d,#%d",i,k); + } + + zlog_debug("%s: inserts done", __func__); + + for (k=0; k < sampleSize; k++) { + + if (!(k % 1000)) + zlog_debug("[%d:%d]", i, k); + if (skiplist_search(l, keys[k], &v)) + zlog_debug("error in search #%d,#%d",i,k); + + if (v != keys[k]) + zlog_debug("search returned wrong value"); + } + + + + for (k=0; k < sampleSize; k++) { + + if (!(k % 1000)) + zlog_debug("<%d:%d>", i, k); + if (skiplist_delete(l, keys[k], keys[k])) + zlog_debug("error in delete"); + keys[k] = (void *)scramble(k ^ 0xf0f0f0f0); + if (skiplist_insert(l, keys[k], keys[k])) + zlog_debug("error in insert #%d,#%d",i,k); + } + + for (k=0; k < sampleSize; k++) { + + if (!(k % 1000)) + zlog_debug("{%d:%d}", i, k); + if (skiplist_delete_first(l)) + zlog_debug("error in delete_first"); + } + } + + skiplist_free(l); +} + diff --git a/lib/skiplist.h b/lib/skiplist.h new file mode 100644 index 0000000000..25775f7543 --- /dev/null +++ b/lib/skiplist.h @@ -0,0 +1,159 @@ +/* + * Copyright 1990 William Pugh + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Permission to include in quagga provide on March 31, 2016 + */ + +/* + * Skip List impementation based on code from William Pugh. + * ftp://ftp.cs.umd.edu/pub/skipLists/ + */ + +/* skiplist.h */ + + +#ifndef _ZEBRA_SKIPLIST_H +#define _ZEBRA_SKIPLIST_H + +#define SKIPLIST_0TIMER_DEBUG 1 + +/* + * skiplistnodes must always contain data to be valid. Adding an + * empty node to a list is invalid + */ +struct skiplistnode +{ + void *key; + void *value; +#if SKIPLIST_0TIMER_DEBUG + int flags; +#define SKIPLIST_NODE_FLAG_INSERTED 0x00000001 +#endif + + struct skiplistnode *forward[1]; /* variable sized */ +}; + +struct skiplist +{ + int flags; + +#define SKIPLIST_FLAG_ALLOW_DUPLICATES 0x00000001 + + int level; /* max lvl (1 + current # of levels in list) */ + unsigned int count; + struct skiplistnode *header; + struct skiplistnode *stats; + struct skiplistnode *last; /* last real list item (NULL if empty list) */ + + /* + * Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2. + * Used as definition of sorted for listnode_add_sort + */ + int (*cmp) (void *val1, void *val2); + + /* callback to free user-owned data when listnode is deleted. supplying + * this callback is very much encouraged! + */ + void (*del) (void *val); +}; + + +/* Prototypes. */ +extern struct skiplist * +skiplist_new( /* encouraged: set list.del callback on new lists */ + int flags, + int (*cmp) (void *key1, void *key2), /* NULL => default cmp */ + void (*del) (void *val)); /* NULL => no auto val free */ + +extern void +skiplist_free (struct skiplist *); + +extern int +skiplist_insert( + register struct skiplist *l, + register void *key, + register void *value); + +extern int +skiplist_delete( + register struct skiplist *l, + register void *key, + register void *value); + +extern int +skiplist_search( + register struct skiplist *l, + register void *key, + void **valuePointer); + +extern int +skiplist_first_value( + register struct skiplist *l, /* in */ + register void *key, /* in */ + void **valuePointer, /* in/out */ + void **cursor); /* out */ + +extern int +skiplist_next_value( + register struct skiplist *l, /* in */ + register void *key, /* in */ + void **valuePointer, /* in/out */ + void **cursor); /* in/out */ + +extern int +skiplist_first( + register struct skiplist *l, + void **keyPointer, + void **valuePointer); + +extern int +skiplist_last( + register struct skiplist *l, + void **keyPointer, + void **valuePointer); + +extern int +skiplist_delete_first( + register struct skiplist *l); + +extern int +skiplist_next( + register struct skiplist *l, /* in */ + void **keyPointer, /* out */ + void **valuePointer, /* out */ + void **cursor); /* in/out */ + +extern int +skiplist_empty( + register struct skiplist *l); /* in */ + +extern unsigned int +skiplist_count( + register struct skiplist *l); /* in */ + +extern void +skiplist_debug( + struct vty *vty, + struct skiplist *l); + +extern void +skiplist_test( + struct vty *vty); + +#endif /* _ZEBRA_SKIPLIST_H */ From 65efcfce427e2abb548874ebb1a11a3b2ee7bc17 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Sat, 7 May 2016 14:18:56 -0400 Subject: [PATCH 089/136] bgpd: add L3/L2VPN Virtual Network Control feature This feature adds an L3 & L2 VPN application that makes use of the VPN and Encap SAFIs. This code is currently used to support IETF NVO3 style operation. In NVO3 terminology it provides the Network Virtualization Authority (NVA) and the ability to import/export IP prefixes and MAC addresses from Network Virtualization Edges (NVEs). The code supports per-NVE tables. The NVE-NVA protocol used to communicate routing and Ethernet / Layer 2 (L2) forwarding information between NVAs and NVEs is referred to as the Remote Forwarder Protocol (RFP). OpenFlow is an example RFP. For general background on NVO3 and RFP concepts see [1]. For information on Openflow see [2]. RFPs are integrated with BGP via the RF API contained in the new "rfapi" BGP sub-directory. Currently, only a simple example RFP is included in Quagga. Developers may use this example as a starting point to integrate Quagga with an RFP of their choosing, e.g., OpenFlow. The RFAPI code also supports the ability import/export of routing information between VNC and customer edge routers (CEs) operating within a virtual network. Import/export may take place between BGP views or to the default zebera VRF. BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VPN information between NVAs. BGP based IP VPN support is defined in RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs), and RFC4659, BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN . Use of both the Encapsulation Subsequent Address Family Identifier (SAFI) and the Tunnel Encapsulation Attribute, RFC5512, The BGP Encapsulation Subsequent Address Family Identifier (SAFI) and the BGP Tunnel Encapsulation Attribute, are supported. MAC address distribution does not follow any standard BGB encoding, although it was inspired by the early IETF EVPN concepts. The feature is conditionally compiled and disabled by default. Use the --enable-bgp-vnc configure option to enable. The majority of this code was authored by G. Paul Ziemba . [1] http://tools.ietf.org/html/draft-ietf-nvo3-nve-nva-cp-req [2] https://www.opennetworking.org/sdn-resources/technical-library Now includes changes needed to merge with cmaster-next. --- Makefile.am | 5 +- bgpd/Makefile.am | 74 +- bgpd/bgp_attr.c | 84 +- bgpd/bgp_attr.h | 19 + bgpd/bgp_ecommunity.c | 4 +- bgpd/bgp_ecommunity.h | 3 + bgpd/bgp_encap.c | 12 + bgpd/bgp_encap_tlv.c | 4 +- bgpd/bgp_encap_types.h | 2 +- bgpd/bgp_main.c | 8 +- bgpd/bgp_memory.c | 3 + bgpd/bgp_memory.h | 3 + bgpd/bgp_mplsvpn.c | 127 +- bgpd/bgp_mplsvpn.h | 41 + bgpd/bgp_nexthop.h | 2 + bgpd/bgp_route.c | 294 +- bgpd/bgp_route.h | 37 + bgpd/bgp_routemap.c | 11 + bgpd/bgp_vnc_types.h | 41 + bgpd/bgp_zebra.c | 18 + bgpd/bgpd.c | 33 +- bgpd/bgpd.conf.vnc.sample | 89 + bgpd/bgpd.h | 18 + bgpd/rfapi/bgp_rfapi_cfg.c | 4706 ++++++++++++++++++ bgpd/rfapi/bgp_rfapi_cfg.h | 312 ++ bgpd/rfapi/rfapi.c | 4412 +++++++++++++++++ bgpd/rfapi/rfapi.h | 980 ++++ bgpd/rfapi/rfapi_ap.c | 629 +++ bgpd/rfapi/rfapi_ap.h | 99 + bgpd/rfapi/rfapi_backend.h | 92 + bgpd/rfapi/rfapi_descriptor_rfp_utils.c | 131 + bgpd/rfapi/rfapi_descriptor_rfp_utils.h | 39 + bgpd/rfapi/rfapi_encap_tlv.c | 812 +++ bgpd/rfapi/rfapi_encap_tlv.h | 43 + bgpd/rfapi/rfapi_import.c | 5154 ++++++++++++++++++++ bgpd/rfapi/rfapi_import.h | 283 ++ bgpd/rfapi/rfapi_monitor.c | 1701 +++++++ bgpd/rfapi/rfapi_monitor.h | 217 + bgpd/rfapi/rfapi_nve_addr.c | 175 + bgpd/rfapi/rfapi_nve_addr.h | 43 + bgpd/rfapi/rfapi_private.h | 455 ++ bgpd/rfapi/rfapi_rib.c | 2535 ++++++++++ bgpd/rfapi/rfapi_rib.h | 154 + bgpd/rfapi/rfapi_vty.c | 5025 +++++++++++++++++++ bgpd/rfapi/rfapi_vty.h | 223 + bgpd/rfapi/vnc_debug.c | 230 + bgpd/rfapi/vnc_debug.h | 49 + bgpd/rfapi/vnc_export_bgp.c | 2177 +++++++++ bgpd/rfapi/vnc_export_bgp.h | 42 + bgpd/rfapi/vnc_export_bgp_p.h | 95 + bgpd/rfapi/vnc_export_table.c | 214 + bgpd/rfapi/vnc_export_table.h | 85 + bgpd/rfapi/vnc_import_bgp.c | 3165 ++++++++++++ bgpd/rfapi/vnc_import_bgp.h | 93 + bgpd/rfapi/vnc_import_bgp_p.h | 51 + bgpd/rfapi/vnc_zebra.c | 1121 +++++ bgpd/rfapi/vnc_zebra.h | 67 + bgpd/rfp-example/librfp/Makefile.am | 40 + bgpd/rfp-example/librfp/rfp.h | 31 + bgpd/rfp-example/librfp/rfp_example.c | 286 ++ bgpd/rfp-example/librfp/rfp_internal.h | 29 + bgpd/rfp-example/rfptest/Makefile.am | 52 + bgpd/rfp-example/rfptest/rfptest.c | 32 + bgpd/rfp-example/rfptest/rfptest.h | 26 + configure.ac | 38 + doc/Makefile.am | 28 +- doc/bgpd.texi | 4 + doc/fig-vnc-commercial-route-reflector.dia | 794 +++ doc/fig-vnc-commercial-route-reflector.png | Bin 0 -> 50984 bytes doc/fig-vnc-commercial-route-reflector.txt | 0 doc/fig-vnc-gw-rr.dia | 1155 +++++ doc/fig-vnc-gw-rr.png | Bin 0 -> 80004 bytes doc/fig-vnc-gw-rr.txt | 0 doc/fig-vnc-gw.dia | 1058 ++++ doc/fig-vnc-gw.png | Bin 0 -> 67376 bytes doc/fig-vnc-gw.txt | 0 doc/fig-vnc-mesh.dia | 1071 ++++ doc/fig-vnc-mesh.png | Bin 0 -> 61028 bytes doc/fig-vnc-mesh.txt | 0 doc/fig-vnc-quagga-route-reflector.dia | 763 +++ doc/fig-vnc-quagga-route-reflector.png | Bin 0 -> 49978 bytes doc/fig-vnc-quagga-route-reflector.txt | 0 doc/fig-vnc-redundant-route-reflectors.dia | 871 ++++ doc/fig-vnc-redundant-route-reflectors.png | Bin 0 -> 75353 bytes doc/fig-vnc-redundant-route-reflectors.txt | 0 doc/ospfd.texi | 4 + doc/quagga.texi | 8 +- doc/routemap.texi | 2 +- doc/vnc.texi | 1584 ++++++ lib/command.c | 10 + lib/command.h | 5 + lib/log.c | 52 + lib/log.h | 5 + lib/prefix.c | 2 + lib/route_types.txt | 12 + lib/vty.c | 3 + lib/zebra.h | 4 + redhat/quagga.spec.in | 6 + ripd/rip_zebra.c | 1 + ripngd/ripng_zebra.c | 1 + tests/Makefile.am | 16 +- vtysh/Makefile.am | 22 +- vtysh/extract.pl.in | 10 +- vtysh/vtysh.c | 101 + vtysh/vtysh_config.c | 30 +- zebra/zserv.c | 6 + 106 files changed, 44654 insertions(+), 49 deletions(-) create mode 100644 bgpd/bgp_vnc_types.h create mode 100644 bgpd/bgpd.conf.vnc.sample create mode 100644 bgpd/rfapi/bgp_rfapi_cfg.c create mode 100644 bgpd/rfapi/bgp_rfapi_cfg.h create mode 100644 bgpd/rfapi/rfapi.c create mode 100644 bgpd/rfapi/rfapi.h create mode 100644 bgpd/rfapi/rfapi_ap.c create mode 100644 bgpd/rfapi/rfapi_ap.h create mode 100644 bgpd/rfapi/rfapi_backend.h create mode 100644 bgpd/rfapi/rfapi_descriptor_rfp_utils.c create mode 100644 bgpd/rfapi/rfapi_descriptor_rfp_utils.h create mode 100644 bgpd/rfapi/rfapi_encap_tlv.c create mode 100644 bgpd/rfapi/rfapi_encap_tlv.h create mode 100644 bgpd/rfapi/rfapi_import.c create mode 100644 bgpd/rfapi/rfapi_import.h create mode 100644 bgpd/rfapi/rfapi_monitor.c create mode 100644 bgpd/rfapi/rfapi_monitor.h create mode 100644 bgpd/rfapi/rfapi_nve_addr.c create mode 100644 bgpd/rfapi/rfapi_nve_addr.h create mode 100644 bgpd/rfapi/rfapi_private.h create mode 100644 bgpd/rfapi/rfapi_rib.c create mode 100644 bgpd/rfapi/rfapi_rib.h create mode 100644 bgpd/rfapi/rfapi_vty.c create mode 100644 bgpd/rfapi/rfapi_vty.h create mode 100644 bgpd/rfapi/vnc_debug.c create mode 100644 bgpd/rfapi/vnc_debug.h create mode 100644 bgpd/rfapi/vnc_export_bgp.c create mode 100644 bgpd/rfapi/vnc_export_bgp.h create mode 100644 bgpd/rfapi/vnc_export_bgp_p.h create mode 100644 bgpd/rfapi/vnc_export_table.c create mode 100644 bgpd/rfapi/vnc_export_table.h create mode 100644 bgpd/rfapi/vnc_import_bgp.c create mode 100644 bgpd/rfapi/vnc_import_bgp.h create mode 100644 bgpd/rfapi/vnc_import_bgp_p.h create mode 100644 bgpd/rfapi/vnc_zebra.c create mode 100644 bgpd/rfapi/vnc_zebra.h create mode 100644 bgpd/rfp-example/librfp/Makefile.am create mode 100644 bgpd/rfp-example/librfp/rfp.h create mode 100644 bgpd/rfp-example/librfp/rfp_example.c create mode 100644 bgpd/rfp-example/librfp/rfp_internal.h create mode 100644 bgpd/rfp-example/rfptest/Makefile.am create mode 100644 bgpd/rfp-example/rfptest/rfptest.c create mode 100644 bgpd/rfp-example/rfptest/rfptest.h create mode 100644 doc/fig-vnc-commercial-route-reflector.dia create mode 100644 doc/fig-vnc-commercial-route-reflector.png create mode 100644 doc/fig-vnc-commercial-route-reflector.txt create mode 100644 doc/fig-vnc-gw-rr.dia create mode 100644 doc/fig-vnc-gw-rr.png create mode 100644 doc/fig-vnc-gw-rr.txt create mode 100644 doc/fig-vnc-gw.dia create mode 100644 doc/fig-vnc-gw.png create mode 100644 doc/fig-vnc-gw.txt create mode 100644 doc/fig-vnc-mesh.dia create mode 100644 doc/fig-vnc-mesh.png create mode 100644 doc/fig-vnc-mesh.txt create mode 100644 doc/fig-vnc-quagga-route-reflector.dia create mode 100644 doc/fig-vnc-quagga-route-reflector.png create mode 100644 doc/fig-vnc-quagga-route-reflector.txt create mode 100644 doc/fig-vnc-redundant-route-reflectors.dia create mode 100644 doc/fig-vnc-redundant-route-reflectors.png create mode 100644 doc/fig-vnc-redundant-route-reflectors.txt create mode 100644 doc/vnc.texi diff --git a/Makefile.am b/Makefile.am index 031afac9d1..d19df6f3b6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,12 +1,13 @@ ## Process this file with automake to produce Makefile.in. -SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ +SUBDIRS = lib qpb fpm @ZEBRA@ @LIBRFP@ @RFPTEST@ \ + @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \ @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ redhat @SOLARIS@ tests tools cumulus DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d ldpd \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ - solaris pimd tools cumulus + solaris pimd @LIBRFP@ @RFPTEST@ tools cumulus EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ update-autotools \ diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index fb5b2375de..ebd7932bd1 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -1,6 +1,64 @@ ## Process this file with automake to produce Makefile.in. +AUTOMAKE_OPTIONS = subdir-objects -AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +if ENABLE_BGP_VNC +#o file to keep linker happy +BGP_VNC_RFP_LIB=rfapi/rfapi_descriptor_rfp_utils.o @top_srcdir@/$(LIBRFP)/librfp.a +BGP_VNC_RFP_INC=-I@top_srcdir@/$(RFPINC) +BGP_VNC_RFP_HD=\ + @top_srcdir@/$(RFPINC)/rfp.h +BGP_VNC_RFP_LD_FLAGS_FILE=@top_srcdir@/$(LIBRFP)/rfp_ld_flags +BGP_VNC_RFP_LD_FLAGS=`if [ -e "$(BGP_VNC_RFP_LD_FLAGS_FILE)" ] ; then cat "$(BGP_VNC_RFP_LD_FLAGS_FILE)" ; fi ` + +#BGP_VNC_RFAPI_SRCDIR=rfapi +BGP_VNC_RFAPI_SRCDIR= +BGP_VNC_RFAPI_INC=-Irfapi +BGP_VNC_RFAPI_SRC=rfapi/bgp_rfapi_cfg.c \ + rfapi/rfapi_import.c \ + rfapi/rfapi.c \ + rfapi/rfapi_ap.c \ + rfapi/rfapi_descriptor_rfp_utils.c \ + rfapi/rfapi_encap_tlv.c \ + rfapi/rfapi_nve_addr.c \ + rfapi/rfapi_monitor.c \ + rfapi/rfapi_rib.c \ + rfapi/rfapi_vty.c \ + rfapi/vnc_debug.c \ + rfapi/vnc_export_bgp.c \ + rfapi/vnc_export_table.c \ + rfapi/vnc_import_bgp.c \ + rfapi/vnc_zebra.c +BGP_VNC_RFAPI_HD=rfapi/bgp_rfapi_cfg.h \ + rfapi/rfapi_import.h \ + rfapi/rfapi.h \ + rfapi/rfapi_ap.h \ + rfapi/rfapi_backend.h \ + rfapi/rfapi_descriptor_rfp_utils.h \ + rfapi/rfapi_encap_tlv.h \ + rfapi/rfapi_nve_addr.h \ + rfapi/rfapi_monitor.h \ + rfapi/rfapi_private.h \ + rfapi/rfapi_rib.h \ + rfapi/rfapi_vty.h \ + rfapi/vnc_debug.h \ + rfapi/vnc_export_bgp.h \ + rfapi/vnc_export_table.h \ + rfapi/vnc_import_bgp.h \ + rfapi/vnc_zebra.h \ + bgp_vnc_types.h $(BGP_VNC_RFP_HD) + +else +BGP_VNC_RFAPI_INC= +BGP_VNC_RFAPI_SRC= +BGP_VNC_RFAPI_HD= +BGP_VNC_RFP_LIB= +BGP_VNC_RFP_INC= +BGP_VNC_RFP_HD= +BGP_VNC_RFP_LD_FLAGS= +endif + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 @@ -18,7 +76,7 @@ libbgp_a_SOURCES = \ bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \ bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \ bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \ - bgp_encap.c bgp_encap_tlv.c + bgp_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) noinst_HEADERS = \ bgp_memory.h \ @@ -27,16 +85,20 @@ noinst_HEADERS = \ bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \ bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_nht.h \ - bgp_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h + bgp_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h \ + $(BGP_VNC_RFAPI_HD) bgpd_SOURCES = bgp_main.c -bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ +bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ @LIBM@ +bgpd_LDFLAGS = $(BGP_VNC_RFP_LD_FLAGS) bgp_btoa_SOURCES = bgp_btoa.c -bgp_btoa_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ +bgp_btoa_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ @LIBM@ +bgp_btoa_LDFLAGS = $(BGP_VNC_RFP_LD_FLAGS) examplesdir = $(exampledir) -dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 +dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 \ + bgpd.conf.vnc.sample EXTRA_DIST = BGP4-MIB.txt diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 3cb52ef911..cd92aec230 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -43,6 +43,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_encap_types.h" +#if ENABLE_BGP_VNC +# include "bgp_rfapi_cfg.h" +# include "bgp_encap_types.h" +# include "bgp_vnc_types.h" +#endif /* Attribute strings for logging. */ static const struct message attr_str [] = @@ -67,6 +72,9 @@ static const struct message attr_str [] = { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" }, { BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT" }, { BGP_ATTR_ENCAP, "ENCAP" }, +#if ENABLE_BGP_VNC + { BGP_ATTR_VNC, "VNC" }, +#endif }; static const int attr_str_max = array_size(attr_str); @@ -256,6 +264,12 @@ bgp_attr_flush_encap(struct attr *attr) encap_free(attr->extra->encap_subtlvs); attr->extra->encap_subtlvs = NULL; } +#if ENABLE_BGP_VNC + if (attr->extra->vnc_subtlvs) { + encap_free(attr->extra->vnc_subtlvs); + attr->extra->vnc_subtlvs = NULL; + } +#endif } /* @@ -421,6 +435,12 @@ bgp_attr_extra_free (struct attr *attr) encap_free(attr->extra->encap_subtlvs); attr->extra->encap_subtlvs = NULL; } +#if ENABLE_BGP_VNC + if (attr->extra->vnc_subtlvs) { + encap_free(attr->extra->vnc_subtlvs); + attr->extra->vnc_subtlvs = NULL; + } +#endif XFREE (MTYPE_ATTR_EXTRA, attr->extra); attr->extra = NULL; } @@ -461,6 +481,11 @@ bgp_attr_dup (struct attr *new, struct attr *orig) if (orig->extra->encap_subtlvs) { new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs); } +#if ENABLE_BGP_VNC + if (orig->extra->vnc_subtlvs) { + new->extra->vnc_subtlvs = encap_tlv_dup(orig->extra->vnc_subtlvs); + } +#endif } } else if (orig->extra) @@ -470,6 +495,11 @@ bgp_attr_dup (struct attr *new, struct attr *orig) if (orig->extra->encap_subtlvs) { new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs); } +#if ENABLE_BGP_VNC + if (orig->extra->vnc_subtlvs) { + new->extra->vnc_subtlvs = encap_tlv_dup(orig->extra->vnc_subtlvs); + } +#endif } } @@ -611,6 +641,9 @@ attrhash_cmp (const void *p1, const void *p2) && ae1->transit == ae2->transit && (ae1->encap_tunneltype == ae2->encap_tunneltype) && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs) +#if ENABLE_BGP_VNC + && encap_same(ae1->vnc_subtlvs, ae2->vnc_subtlvs) +#endif && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id)) return 1; else if (ae1 || ae2) @@ -669,6 +702,11 @@ bgp_attr_hash_alloc (void *p) if (attr->extra->encap_subtlvs) { attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs); } +#if ENABLE_BGP_VNC + if (attr->extra->vnc_subtlvs) { + attr->extra->vnc_subtlvs = encap_tlv_dup(attr->extra->vnc_subtlvs); + } +#endif } attr->refcnt = 0; return attr; @@ -939,6 +977,10 @@ bgp_attr_flush (struct attr *attr) transit_free (attre->transit); encap_free(attre->encap_subtlvs); attre->encap_subtlvs = NULL; +#if ENABLE_BGP_VNC + encap_free(attre->vnc_subtlvs); + attre->vnc_subtlvs = NULL; +#endif } } @@ -1956,6 +1998,12 @@ bgp_attr_encap( subtype = stream_getc (BGP_INPUT (peer)); sublength = stream_getc (BGP_INPUT (peer)); length -= 2; +#if ENABLE_BGP_VNC + } else { + subtype = stream_getw (BGP_INPUT (peer)); + sublength = stream_getw (BGP_INPUT (peer)); + length -= 4; +#endif } if (sublength > length) { @@ -1987,6 +2035,16 @@ bgp_attr_encap( } else { attre->encap_subtlvs = tlv; } +#if ENABLE_BGP_VNC + } else { + for (stlv_last = attre->vnc_subtlvs; stlv_last && stlv_last->next; + stlv_last = stlv_last->next); + if (stlv_last) { + stlv_last->next = tlv; + } else { + attre->vnc_subtlvs = tlv; + } +#endif } } else { stlv_last->next = tlv; @@ -2300,6 +2358,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_EXT_COMMUNITIES: ret = bgp_attr_ext_communities (&attr_args); break; +#if ENABLE_BGP_VNC + case BGP_ATTR_VNC: +#endif case BGP_ATTR_ENCAP: ret = bgp_attr_encap (type, peer, length, attr, flag, startp); break; @@ -2569,7 +2630,9 @@ bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p) } /* - * Encodes the tunnel encapsulation attribute + * Encodes the tunnel encapsulation attribute, + * and with ENABLE_BGP_VNC the VNC attribute which uses + * almost the same TLV format */ static void bgp_packet_mpattr_tea( @@ -2603,6 +2666,15 @@ bgp_packet_mpattr_tea( attrhdrlen = 1 + 1; /* subTLV T + L */ break; +#if ENABLE_BGP_VNC + case BGP_ATTR_VNC: + attrname = "VNC"; + subtlvs = attr->extra->vnc_subtlvs; + attrlenfield = 0; /* no outer T + L */ + attrhdrlen = 2 + 2; /* subTLV T + L */ + break; +#endif + default: assert(0); } @@ -2648,6 +2720,11 @@ bgp_packet_mpattr_tea( if (attrtype == BGP_ATTR_ENCAP) { stream_putc (s, st->type); stream_putc (s, st->length); +#if ENABLE_BGP_VNC + } else { + stream_putw (s, st->type); + stream_putw (s, st->length); +#endif } stream_put (s, st->value, st->length); } @@ -3037,6 +3114,11 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, { /* Tunnel Encap attribute */ bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP); + +#if ENABLE_BGP_VNC + /* VNC attribute */ + bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_VNC); +#endif } /* Unknown transit attribute. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 0bf8c897de..002bdfa081 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -63,6 +63,21 @@ struct bgp_attr_encap_subtlv { uint8_t value[1]; /* will be extended */ }; +#if ENABLE_BGP_VNC +/* + * old rfp<->rfapi representation + */ +struct bgp_tea_options { + struct bgp_tea_options *next; + uint8_t options_count; + uint16_t options_length; /* each TLV may be 256 in length */ + uint8_t type; + uint8_t length; + void *value; /* pointer to data */ +}; + +#endif + /* Additional/uncommon BGP attributes. * lazily allocated as and when a struct attr * requires it. @@ -107,6 +122,10 @@ struct attr_extra uint16_t encap_tunneltype; /* grr */ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ + +#if ENABLE_BGP_VNC + struct bgp_attr_encap_subtlv *vnc_subtlvs; /* VNC-specific */ +#endif }; /* BGP core attribute structure. */ diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 926e2650a2..6c72aa36d9 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -35,7 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA static struct hash *ecomhash; /* Allocate a new ecommunities. */ -static struct ecommunity * +struct ecommunity * ecommunity_new (void) { return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY, @@ -59,7 +59,7 @@ ecommunity_free (struct ecommunity **ecom) structure, we don't add the value. Newly added value is sorted by numerical order. When the value is added to the structure return 1 else return 0. */ -static int +int ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval) { u_int8_t *p; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 993fd5acfd..c5c58e4260 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -85,4 +85,7 @@ extern char *ecommunity_ecom2str (struct ecommunity *, int); extern int ecommunity_match (const struct ecommunity *, const struct ecommunity *); extern char *ecommunity_str (struct ecommunity *); +/* for vpn */ +extern struct ecommunity *ecommunity_new (void); +extern int ecommunity_add_val (struct ecommunity *, struct ecommunity_val *); #endif /* _QUAGGA_BGP_ECOMMUNITY_H */ diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c index 3f7712b24d..2cfb510295 100644 --- a/bgpd/bgp_encap.c +++ b/bgpd/bgp_encap.c @@ -45,6 +45,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" #include "bgpd/bgp_encap.h" +#if ENABLE_BGP_VNC +#include "rfapi_backend.h" +#endif + static void ecom2prd(struct ecommunity *ecom, struct prefix_rd *prd) { @@ -185,7 +189,15 @@ bgp_nlri_parse_encap( if (!withdraw) { bgp_update (peer, &p, 0, attr, afi, SAFI_ENCAP, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0); +#if ENABLE_BGP_VNC + rfapiProcessUpdate(peer, NULL, &p, &prd, attr, afi, SAFI_ENCAP, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL); +#endif } else { +#if ENABLE_BGP_VNC + rfapiProcessWithdraw(peer, NULL, &p, &prd, attr, afi, SAFI_ENCAP, + ZEBRA_ROUTE_BGP, 0); +#endif bgp_withdraw (peer, &p, 0, attr, afi, SAFI_ENCAP, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL); } diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c index 347b4b3ce7..c554ade27e 100644 --- a/bgpd/bgp_encap_tlv.c +++ b/bgpd/bgp_encap_tlv.c @@ -409,9 +409,7 @@ bgp_encap_type_mpls_to_tlv( struct bgp_encap_type_mpls *bet, /* input structure */ struct attr *attr) { - struct attr_extra *extra = bgp_attr_extra_get(attr); - - extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS; + return; /* no encap attribute for MPLS */ } void diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h index 603ff9d2d6..0985446ff2 100644 --- a/bgpd/bgp_encap_types.h +++ b/bgpd/bgp_encap_types.h @@ -32,7 +32,7 @@ typedef enum { BGP_ENCAP_TYPE_IP_IN_IP=7, BGP_ENCAP_TYPE_VXLAN=8, BGP_ENCAP_TYPE_NVGRE=9, - BGP_ENCAP_TYPE_MPLS=10, + BGP_ENCAP_TYPE_MPLS=10, /* NOTE: Encap SAFI&Attribute not used */ BGP_ENCAP_TYPE_MPLS_IN_GRE=11, BGP_ENCAP_TYPE_VXLAN_GPE=12, BGP_ENCAP_TYPE_MPLS_IN_UDP=13, diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 404fe7d73a..9ee8838fdf 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -54,6 +54,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_filter.h" #include "bgpd/bgp_zebra.h" +#ifdef ENABLE_BGP_VNC +#include "rfapi_backend.h" +#endif + /* bgpd options, we use GNU getopt library. */ static const struct option longopts[] = { @@ -282,7 +286,9 @@ bgp_exit (int status) bgp_vrf_terminate (); cmd_terminate (); vty_terminate (); - +#if ENABLE_BGP_VNC + vnc_zebra_destroy(); +#endif bgp_zebra_destroy(); if (bgp_nexthop_buf) stream_free (bgp_nexthop_buf); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 166400b745..72c0311c17 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -108,3 +108,6 @@ DEFINE_MTYPE(BGPD, BGP_REDIST, "BGP redistribution") DEFINE_MTYPE(BGPD, BGP_FILTER_NAME, "BGP Filter Information") DEFINE_MTYPE(BGPD, BGP_DUMP_STR, "BGP Dump String Information") DEFINE_MTYPE(BGPD, ENCAP_TLV, "ENCAP TLV") + +DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS, "BGP TEA Options") +DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS_VALUE, "BGP TEA Options Value") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index b2956f07ed..a4ce8b891b 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -105,4 +105,7 @@ DECLARE_MTYPE(BGP_FILTER_NAME) DECLARE_MTYPE(BGP_DUMP_STR) DECLARE_MTYPE(ENCAP_TLV) +DECLARE_MTYPE(BGP_TEA_OPTIONS) +DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE) + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 36ba65af1a..991a92b850 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -35,16 +35,35 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_attr.h" #include "bgpd/bgp_mplsvpn.h" +#if ENABLE_BGP_VNC +#include "rfapi_backend.h" +#endif + u_int16_t decode_rd_type (u_char *pnt) { u_int16_t v; v = ((u_int16_t) *pnt++ << 8); +#if ENABLE_BGP_VNC + /* + * VNC L2 stores LHI in lower byte, so omit it + */ + if (v != RD_TYPE_VNC_ETH) + v |= (u_int16_t) *pnt; +#else /* duplicate code for clarity */ v |= (u_int16_t) *pnt; +#endif + return v; } +void +encode_rd_type (u_int16_t v, u_char *pnt) +{ + *((u_int16_t *)pnt) = htons(v); +} + u_int32_t decode_label (u_char *pnt) { @@ -56,6 +75,17 @@ decode_label (u_char *pnt) return l; } +void +encode_label(u_int32_t label, + u_char *pnt) +{ + if (pnt == NULL) + return; + *pnt++ = (label>>12) & 0xff; + *pnt++ = (label>>4) & 0xff; + *pnt++ = ((label<<4)+1) & 0xff; /* S=1 */ +} + /* type == RD_TYPE_AS */ void decode_rd_as (u_char *pnt, struct rd_as *rd_as) @@ -93,6 +123,17 @@ decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) rd_ip->val |= (u_int16_t) *pnt; } +#if ENABLE_BGP_VNC +/* type == RD_TYPE_VNC_ETH */ +void +decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth) +{ + rd_vnc_eth->type = RD_TYPE_VNC_ETH; + rd_vnc_eth->local_nve_id = pnt[1]; + memcpy (rd_vnc_eth->macaddr.octet, pnt + 2, ETHER_ADDR_LEN); +} +#endif + int bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) @@ -111,6 +152,9 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, safi_t safi; int addpath_encoded; u_int32_t addpath_id; +#if ENABLE_BGP_VNC + u_int32_t label = 0; +#endif /* Check peer status. */ if (peer->status != Established) @@ -184,6 +228,10 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, } +#if ENABLE_BGP_VNC + label = decode_label (pnt); +#endif + /* Copyr label to prefix. */ tagpnt = pnt; @@ -207,21 +255,39 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, decode_rd_ip (pnt + 5, &rd_ip); break; +#if ENABLE_BGP_VNC + case RD_TYPE_VNC_ETH: + break; +#endif + default: zlog_err ("Unknown RD type %d", type); break; /* just report */ } - p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8; + p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8;/* exclude label & RD */ memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES, psize - VPN_PREFIXLEN_MIN_BYTES); if (attr) - bgp_update (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN, - ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0); + { + bgp_update (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0); +#if ENABLE_BGP_VNC + rfapiProcessUpdate(peer, NULL, &p, &prd, attr, packet->afi, + SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &label); +#endif + } else - bgp_withdraw (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN, - ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt); + { +#if ENABLE_BGP_VNC + rfapiProcessWithdraw(peer, NULL, &p, &prd, attr, packet->afi, + SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, 0); +#endif + bgp_withdraw (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt); + } } /* Packet length consistency check. */ if (pnt != lim) @@ -346,6 +412,21 @@ prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size) snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); return buf; } +#if ENABLE_BGP_VNC + else if (type == RD_TYPE_VNC_ETH) + { + snprintf(buf, size, "LHI:%d, %02x:%02x:%02x:%02x:%02x:%02x", + *(pnt+1), /* LHI */ + *(pnt+2), /* MAC[0] */ + *(pnt+3), + *(pnt+4), + *(pnt+5), + *(pnt+6), + *(pnt+7)); + + return buf; + } +#endif return NULL; } @@ -483,6 +564,9 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip = {0}; +#if ENABLE_BGP_VNC + struct rd_vnc_eth rd_vnc_eth; +#endif u_char *pnt; pnt = rn->p.u.val; @@ -496,6 +580,10 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u decode_rd_as4 (pnt + 2, &rd_as); else if (type == RD_TYPE_IP) decode_rd_ip (pnt + 2, &rd_ip); +#if ENABLE_BGP_VNC + else if (type == RD_TYPE_VNC_ETH) + decode_rd_vnc_eth (pnt, &rd_vnc_eth); +#endif if (use_json) { @@ -514,6 +602,17 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u vty_out (vty, "%u:%d", rd_as.as, rd_as.val); else if (type == RD_TYPE_IP) vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); +#if ENABLE_BGP_VNC + else if (type == RD_TYPE_VNC_ETH) + vty_out (vty, "%u:%02x:%02x:%02x:%02x:%02x:%02x", + rd_vnc_eth.local_nve_id, + rd_vnc_eth.macaddr.octet[0], + rd_vnc_eth.macaddr.octet[1], + rd_vnc_eth.macaddr.octet[2], + rd_vnc_eth.macaddr.octet[3], + rd_vnc_eth.macaddr.octet[4], + rd_vnc_eth.macaddr.octet[5]); +#endif vty_out (vty, "%s", VTY_NEWLINE); } @@ -675,6 +774,9 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip = {0}; +#if ENABLE_BGP_VNC + struct rd_vnc_eth rd_vnc_eth; +#endif u_char *pnt; pnt = rn->p.u.val; @@ -688,6 +790,10 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, decode_rd_as4 (pnt + 2, &rd_as); else if (type == RD_TYPE_IP) decode_rd_ip (pnt + 2, &rd_ip); +#if ENABLE_BGP_VNC + else if (type == RD_TYPE_VNC_ETH) + decode_rd_vnc_eth (pnt, &rd_vnc_eth); +#endif if (use_json) { @@ -706,6 +812,17 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, vty_out (vty, "%u:%d", rd_as.as, rd_as.val); else if (type == RD_TYPE_IP) vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); +#if ENABLE_BGP_VNC + else if (type == RD_TYPE_VNC_ETH) + vty_out (vty, "%u:%02x:%02x:%02x:%02x:%02x:%02x", + rd_vnc_eth.local_nve_id, + rd_vnc_eth.macaddr.octet[0], + rd_vnc_eth.macaddr.octet[1], + rd_vnc_eth.macaddr.octet[2], + rd_vnc_eth.macaddr.octet[3], + rd_vnc_eth.macaddr.octet[4], + rd_vnc_eth.macaddr.octet[5]); +#endif vty_out (vty, "%s", VTY_NEWLINE); } rd_header = 0; diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 3fbbd33540..f75b989057 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -24,9 +24,37 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define RD_TYPE_AS 0 #define RD_TYPE_IP 1 #define RD_TYPE_AS4 2 +#if ENABLE_BGP_VNC +#define RD_TYPE_VNC_ETH 0xff00 /* VNC L2VPN */ +#endif #define RD_ADDRSTRLEN 28 +typedef enum { + MPLS_LABEL_IPV4_EXPLICIT_NULL = 0, /* [RFC3032] */ + MPLS_LABEL_ROUTER_ALERT = 1, /* [RFC3032] */ + MPLS_LABEL_IPV6_EXPLICIT_NULL = 2, /* [RFC3032] */ + MPLS_LABEL_IMPLICIT_NULL = 3, /* [RFC3032] */ + MPLS_LABEL_UNASSIGNED4 = 4, + MPLS_LABEL_UNASSIGNED5 = 5, + MPLS_LABEL_UNASSIGNED6 = 6, + MPLS_LABEL_ELI = 7, /* Entropy Indicator [RFC6790] */ + MPLS_LABEL_UNASSIGNED8 = 8, + MPLS_LABEL_UNASSIGNED9 = 9, + MPLS_LABEL_UNASSIGNED10 = 10, + MPLS_LABEL_UNASSIGNED11 = 11, + MPLS_LABEL_GAL = 13, /* [RFC5586] */ + MPLS_LABEL_OAM_ALERT = 14, /* [RFC3429] */ + MPLS_LABEL_EXTENSION = 15 /* [RFC7274] */ +} mpls_special_label_t; + +#define MPLS_LABEL_IS_SPECIAL(label) \ + ((label) <= MPLS_LABEL_EXTENSION) +#define MPLS_LABEL_IS_NULL(label) \ + ((label) == MPLS_LABEL_IPV4_EXPLICIT_NULL || \ + (label) == MPLS_LABEL_IPV6_EXPLICIT_NULL || \ + (label) == MPLS_LABEL_IMPLICIT_NULL) + struct rd_as { u_int16_t type; @@ -41,7 +69,17 @@ struct rd_ip u_int16_t val; }; +#if ENABLE_BGP_VNC +struct rd_vnc_eth +{ + u_int16_t type; + uint8_t local_nve_id; + struct ethaddr macaddr; +}; +#endif + extern u_int16_t decode_rd_type (u_char *); +extern void encode_rd_type (u_int16_t, u_char *); extern void bgp_mplsvpn_init (void); extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *); extern u_int32_t decode_label (u_char *); @@ -49,6 +87,9 @@ extern void encode_label(u_int32_t, u_char *); extern void decode_rd_as (u_char *, struct rd_as *); extern void decode_rd_as4 (u_char *, struct rd_as *); extern void decode_rd_ip (u_char *, struct rd_ip *); +#if ENABLE_BGP_VNC +extern void decode_vnc_eth (u_char *, struct rd_vnc_eth *); +#endif extern int str2prefix_rd (const char *, struct prefix_rd *); extern int str2tag (const char *, u_char *); extern char *prefix_rd2str (struct prefix_rd *, char *, size_t); diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 861da5740f..652a6813ee 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -34,6 +34,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA AF_UNSPEC)) \ ) +#define BGP_MP_NEXTHOP_FAMILY NEXTHOP_FAMILY + /* BGP nexthop cache value structure. */ struct bgp_nexthop_cache { diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f604b7658d..6c97dc333b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -62,6 +62,12 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_vty.h" +#if ENABLE_BGP_VNC +#include "rfapi_backend.h" +#include "vnc_import_bgp.h" +#include "vnc_export_bgp.h" +#endif + /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; @@ -132,6 +138,13 @@ bgp_info_extra_get (struct bgp_info *ri) return ri->extra; } +/* Allocate new bgp info structure. */ +struct bgp_info * +bgp_info_new (void) +{ + return XCALLOC (MTYPE_BGP_ROUTE, sizeof (struct bgp_info)); +} + /* Free bgp route information. */ static void bgp_info_free (struct bgp_info *binfo) @@ -228,7 +241,7 @@ bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri) /* undo the effects of a previous call to bgp_info_delete; typically called when a route is deleted and then quickly re-added before the deletion has been processed */ -static void +void bgp_info_restore (struct bgp_node *rn, struct bgp_info *ri) { bgp_info_unset_flag (rn, ri, BGP_INFO_REMOVED); @@ -332,7 +345,7 @@ bgp_info_path_with_addpath_rx_str (struct bgp_info *ri, char *buf) static int bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug, - char *pfx_buf) + const char *pfx_buf) { struct attr *newattr, *existattr; struct attr_extra *newattre, *existattre; @@ -847,6 +860,31 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, return 1; } +/* Compare two bgp route entity. Return -1 if new is preferred, 1 if exist + * is preferred, or 0 if they are the same (usually will only occur if + * multipath is enabled + * This version is compatible with */ +int +bgp_info_cmp_compatible (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, + afi_t afi, safi_t safi) +{ + int paths_eq; + struct bgp_maxpaths_cfg mpath_cfg; + int ret; + ret = bgp_info_cmp (bgp, new, exist, &paths_eq, &mpath_cfg, 0, __func__); + + if (paths_eq) + ret = 0; + else + { + if (ret == 1) + ret = -1; + else + ret = 1; + } + return ret; +} + static enum filter_type bgp_input_filter (struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -1159,6 +1197,7 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp, int reflect; afi_t afi; safi_t safi; + int samepeer_safe = 0; /* for synthetic mplsvpns routes */ if (DISABLE_BGP_ANNOUNCE) return 0; @@ -1175,6 +1214,22 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp, bgp = SUBGRP_INST(subgrp); riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr; +#if ENABLE_BGP_VNC + if (((afi == AFI_IP) || (afi == AFI_IP6)) && (safi == SAFI_MPLS_VPN) && + ((ri->type == ZEBRA_ROUTE_BGP_DIRECT) || + (ri->type == ZEBRA_ROUTE_BGP_DIRECT_EXT))) { + + /* + * direct and direct_ext type routes originate internally even + * though they can have peer pointers that reference other systems + */ + char buf[BUFSIZ]; + prefix2str(p, buf, BUFSIZ); + zlog_debug("%s: pfx %s bgp_direct->vpn route peer safe", __func__, buf); + samepeer_safe = 1; + } +#endif + /* With addpath we may be asked to TX all kinds of paths so make sure * ri is valid */ if (!CHECK_FLAG (ri->flags, BGP_INFO_VALID) || @@ -1311,7 +1366,7 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp, reflect = 0; /* IBGP reflection check. */ - if (reflect) + if (reflect && !samepeer_safe) { /* A route from a Client peer. */ if (CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) @@ -1869,8 +1924,13 @@ bgp_process_main (struct work_queue *wq, void *data) !bgp->addpath_tx_used[afi][safi]) { if (bgp_zebra_has_route_changed (rn, old_select)) - bgp_zebra_announce (p, old_select, bgp, afi, safi); - + { +#if ENABLE_BGP_VNC + vnc_import_bgp_add_route(bgp, p, old_select); + vnc_import_bgp_exterior_add_route(bgp, p, old_select); +#endif + bgp_zebra_announce (p, old_select, bgp, afi, safi); + } UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG); bgp_zebra_clear_route_change_flags (rn); UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); @@ -1903,6 +1963,21 @@ bgp_process_main (struct work_queue *wq, void *data) UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG); } +#if ENABLE_BGP_VNC + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { + if (old_select != new_select) { + if (old_select) { + vnc_import_bgp_exterior_del_route(bgp, p, old_select); + vnc_import_bgp_del_route(bgp, p, old_select); + } + if (new_select) { + vnc_import_bgp_exterior_add_route(bgp, p, new_select); + vnc_import_bgp_add_route(bgp, p, new_select); + } + } + } +#endif + group_announce_route(bgp, afi, safi, rn, new_select); /* FIB update. */ @@ -2136,7 +2211,7 @@ bgp_rib_remove (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, static void bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, - afi_t afi, safi_t safi) + afi_t afi, safi_t safi, struct prefix_rd *prd) { int status = BGP_DAMP_NONE; @@ -2151,7 +2226,33 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi); return; } - + +#if ENABLE_BGP_VNC + if (safi == SAFI_MPLS_VPN) { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get(peer->bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) { + table = (struct bgp_table *)(prn->info); + + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + peer->bgp, + prd, + table, + &rn->p, + ri); + } + bgp_unlock_node(prn); + } + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) { + + vnc_import_bgp_del_route(peer->bgp, &rn->p, ri); + vnc_import_bgp_exterior_del_route(peer->bgp, &rn->p, ri); + } + } +#endif bgp_rib_remove (rn, ri, peer, afi, safi); } @@ -2254,6 +2355,9 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, char buf[SU_ADDRSTRLEN]; char buf2[30]; int connected = 0; +#if ENABLE_BGP_VNC + int vnc_implicit_withdraw = 0; +#endif bgp = peer->bgp; rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); @@ -2443,6 +2547,35 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, if (! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) bgp_damp_withdraw (ri, rn, afi, safi, 1); } +#if ENABLE_BGP_VNC + if (safi == SAFI_MPLS_VPN) { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) { + table = (struct bgp_table *)(prn->info); + + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + bgp, + prd, + table, + p, + ri); + } + bgp_unlock_node(prn); + } + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) { + /* + * Implicit withdraw case. + */ + ++vnc_implicit_withdraw; + vnc_import_bgp_del_route(bgp, p, ri); + vnc_import_bgp_exterior_del_route(bgp, p, ri); + } + } +#endif /* Update to new attribute. */ bgp_attr_unintern (&ri->attr); @@ -2452,6 +2585,25 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, if (safi == SAFI_MPLS_VPN) memcpy ((bgp_info_extra_get (ri))->tag, tag, 3); +#if ENABLE_BGP_VNC + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) + { + if (vnc_implicit_withdraw) + { + /* + * Add back the route with its new attributes (e.g., nexthop). + * The route is still selected, until the route selection + * queued by bgp_process actually runs. We have to make this + * update to the VNC side immediately to avoid racing against + * configuration changes (e.g., route-map changes) which + * trigger re-importation of the entire RIB. + */ + vnc_import_bgp_add_route(bgp, p, ri); + vnc_import_bgp_exterior_add_route(bgp, p, ri); + } + } +#endif + /* Update bgp route dampening information. */ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) && peer->sort == BGP_PEER_EBGP) @@ -2491,6 +2643,28 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, else bgp_info_set_flag (rn, ri, BGP_INFO_VALID); +#if ENABLE_BGP_VNC + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *)(prn->info); + + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + bgp, + prd, + table, + p, + ri); + } + bgp_unlock_node(prn); + } +#endif + /* Process change. */ bgp_aggregate_increment (bgp, p, ri, afi, safi); @@ -2561,6 +2735,28 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, /* route_node_get lock */ bgp_unlock_node (rn); +#if ENABLE_BGP_VNC + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *)(prn->info); + + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + bgp, + prd, + table, + p, + new); + } + bgp_unlock_node(prn); + } +#endif + /* If maximum prefix count is configured and current prefix count exeed it. */ if (bgp_maximum_prefix_overflow (peer, afi, safi, 0)) @@ -2653,7 +2849,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, u_int32_t addpath_id, /* Withdraw specified route from routing table. */ if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) - bgp_rib_withdraw (rn, ri, peer, afi, safi); + bgp_rib_withdraw (rn, ri, peer, afi, safi, prd); else if (bgp_debug_update(peer, p, NULL, 1)) zlog_debug ("%s Can't find the route %s/%d", peer->host, inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), @@ -3047,6 +3243,10 @@ bgp_clear_route_all (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) bgp_clear_route (peer, afi, safi); + +#if ENABLE_BGP_VNC + rfapiProcessPeerDown(peer); +#endif } void @@ -3117,7 +3317,13 @@ bgp_cleanup_table(struct bgp_table *table, safi_t safi) && ri->type == ZEBRA_ROUTE_BGP && (ri->sub_type == BGP_ROUTE_NORMAL || ri->sub_type == BGP_ROUTE_AGGREGATE)) - bgp_zebra_withdraw (&rn->p, ri, safi); + { +#if ENABLE_BGP_VNC + if (table->owner && table->owner->bgp) + vnc_import_bgp_del_route(table->owner->bgp, &rn->p, ri); +#endif + bgp_zebra_withdraw (&rn->p, ri, safi); + } } } @@ -3417,6 +3623,9 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, struct attr attr; struct attr *attr_new; int ret; +#if ENABLE_BGP_VNC + int vnc_implicit_withdraw = 0; +#endif assert (bgp_static); if (!bgp_static) @@ -3489,9 +3698,34 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, bgp_info_restore(rn, ri); else bgp_aggregate_decrement (bgp, p, ri, afi, safi); +#if ENABLE_BGP_VNC + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) + { + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) + { + /* + * Implicit withdraw case. + * We have to do this before ri is changed + */ + ++vnc_implicit_withdraw; + vnc_import_bgp_del_route(bgp, p, ri); + vnc_import_bgp_exterior_del_route(bgp, p, ri); + } + } +#endif bgp_attr_unintern (&ri->attr); ri->attr = attr_new; ri->uptime = bgp_clock (); +#if ENABLE_BGP_VNC + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) + { + if (vnc_implicit_withdraw) + { + vnc_import_bgp_add_route(bgp, p, ri); + vnc_import_bgp_exterior_add_route(bgp, p, ri); + } + } +#endif /* Nexthop reachability check. */ if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) @@ -3637,6 +3871,18 @@ bgp_static_withdraw_safi (struct bgp *bgp, struct prefix *p, afi_t afi, /* Withdraw static BGP route from routing table. */ if (ri) { +#if ENABLE_BGP_VNC + rfapiProcessWithdraw( + ri->peer, + NULL, + p, + prd, + ri->attr, + afi, + safi, + ri->type, + 1); /* Kill, since it is an administrative change */ +#endif bgp_aggregate_decrement (bgp, p, ri, afi, safi); bgp_info_delete (rn, ri); bgp_process (bgp, rn, afi, safi); @@ -3655,6 +3901,9 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p, struct attr *attr_new; struct attr attr = { 0 }; struct bgp_info *ri; +#if ENABLE_BGP_VNC + u_int32_t label = 0; +#endif assert (bgp_static); @@ -3731,10 +3980,19 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p, bgp_attr_unintern (&ri->attr); ri->attr = attr_new; ri->uptime = bgp_clock (); +#if ENABLE_BGP_VNC + if (ri->extra) + label = decode_label (ri->extra->tag); +#endif /* Process change. */ bgp_aggregate_increment (bgp, p, ri, afi, safi); bgp_process (bgp, rn, afi, safi); +#if ENABLE_BGP_VNC + rfapiProcessUpdate(ri->peer, NULL, p, &bgp_static->prd, + ri->attr, afi, safi, + ri->type, ri->sub_type, &label); +#endif bgp_unlock_node (rn); aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); @@ -3749,6 +4007,9 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p, SET_FLAG (new->flags, BGP_INFO_VALID); new->extra = bgp_info_extra_new(); memcpy (new->extra->tag, bgp_static->tag, 3); +#if ENABLE_BGP_VNC + label = decode_label (bgp_static->tag); +#endif /* Aggregate address increment. */ bgp_aggregate_increment (bgp, p, new, afi, safi); @@ -3762,6 +4023,12 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p, /* Process change. */ bgp_process (bgp, rn, afi, safi); +#if ENABLE_BGP_VNC + rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, + new->attr, afi, safi, + new->type, new->sub_type, &label); +#endif + /* Unintern original. */ aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); @@ -6107,7 +6374,14 @@ route_vty_out (struct vty *vty, struct prefix *p, json_object_array_add(json_paths, json_path); } else - vty_out (vty, "%s", VTY_NEWLINE); + { + vty_out (vty, "%s", VTY_NEWLINE); +#if ENABLE_BGP_VNC + /* prints an additional line, indented, with VNC info, if present */ + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_UNICAST)) + rfapi_vty_out_vncinfo(vty, p, binfo, safi); +#endif + } } /* called from terminal list command */ diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 60c406775d..3d65b4b0a9 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -49,6 +49,30 @@ struct bgp_info_extra /* MPLS label. */ u_char tag[3]; + +#if ENABLE_BGP_VNC + union { + + struct { + void *rfapi_handle; /* export: NVE advertising this route */ + struct list *local_nexthops; /* optional, for static routes */ + } export; + + struct { + void *timer; + void *hme; /* encap monitor, if this is a VPN route */ + struct prefix_rd rd; /* import: route's route-distinguisher */ + u_char un_family; /* family of cached un address, 0 if unset */ + union { + struct in_addr addr4; + struct in6_addr addr6; + } un; /* cached un address */ + time_t create_time; + struct prefix aux_prefix; /* AFI_ETHER: the IP addr, if family set */ + } import; + + } vnc; +#endif }; struct bgp_info @@ -111,6 +135,9 @@ struct bgp_info #define BGP_ROUTE_STATIC 1 #define BGP_ROUTE_AGGREGATE 2 #define BGP_ROUTE_REDISTRIBUTE 3 +#ifdef ENABLE_BGP_VNC +# define BGP_ROUTE_RFP 4 +#endif u_short instance; @@ -309,4 +336,14 @@ extern int subgroup_announce_check(struct bgp_info *ri, extern void bgp_peer_clear_node_queue_drain_immediate (struct peer *peer); extern void bgp_process_queues_drain_immediate (void); +/* for encap/vpn */ +extern struct bgp_node * +bgp_afi_node_get (struct bgp_table *, afi_t , safi_t , struct prefix *, + struct prefix_rd *); +extern struct bgp_info *bgp_info_new (void); +extern void bgp_info_restore (struct bgp_node *, struct bgp_info *); + +extern int bgp_info_cmp_compatible (struct bgp *, struct bgp_info *, + struct bgp_info *, afi_t, safi_t ); + #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index fea9ae6b18..efed71ebe9 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -58,6 +58,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" #include "bgpd/bgp_debug.h" +#if ENABLE_BGP_VNC +# include "bgp_rfapi_cfg.h" +#endif /* Memo of route-map commands. @@ -2924,6 +2927,10 @@ bgp_route_map_process_update_cb (char *rmap_name) for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) bgp_route_map_process_update(bgp, rmap_name, 1); +#if ENABLE_BGP_VNC + zlog_debug("%s: calling vnc_routemap_update", __func__); + vnc_routemap_update(bgp, __func__); +#endif return 0; } @@ -2960,6 +2967,10 @@ bgp_route_map_mark_update (const char *rmap_name) { for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) bgp_route_map_process_update(bgp, rmap_name, 0); + #if ENABLE_BGP_VNC + zlog_debug("%s: calling vnc_routemap_update", __func__); + vnc_routemap_update(bgp, __func__); +#endif } } } diff --git a/bgpd/bgp_vnc_types.h b/bgpd/bgp_vnc_types.h new file mode 100644 index 0000000000..8bc9cb6407 --- /dev/null +++ b/bgpd/bgp_vnc_types.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_VNC_TYPES_H +#define _QUAGGA_BGP_VNC_TYPES_H + +#if ENABLE_BGP_VNC +typedef enum { + BGP_VNC_SUBTLV_TYPE_LIFETIME=1, + BGP_VNC_SUBTLV_TYPE_RFPOPTION=2, /* deprecated */ +} bgp_vnc_subtlv_types; + +/* + * VNC Attribute subtlvs + */ +struct bgp_vnc_subtlv_lifetime { + uint32_t lifetime; +}; + +struct bgp_vnc_subtlv_unaddr { + struct prefix un_address; /* IPv4 or IPv6; pfx length ignored */ +}; + +#endif /* ENABLE_BGP_VNC */ +#endif /* _QUAGGA_BGP_VNC_TYPES_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 7089d140b9..cb24154061 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -46,6 +46,10 @@ Boston, MA 02111-1307, USA. */ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_nht.h" #include "bgpd/bgp_bfd.h" +#if ENABLE_BGP_VNC +# include "rfapi_backend.h" +# include "vnc_export_bgp.h" +#endif /* All information about zebra. */ struct zclient *zclient = NULL; @@ -1806,6 +1810,13 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type, u_short instance) if (vrf_bitmap_check (zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; +#if ENABLE_BGP_VNC + if (bgp->vrf_id == VRF_DEFAULT && + type == ZEBRA_ROUTE_VNC_DIRECT) { + vnc_export_bgp_enable(bgp, afi); /* only enables if mode bits cfg'd */ + } +#endif + vrf_bitmap_set (zclient->redist[afi][type], bgp->vrf_id); } @@ -1933,6 +1944,13 @@ bgp_redistribute_unreg (struct bgp *bgp, afi_t afi, int type, u_short instance) vrf_bitmap_unset (zclient->redist[afi][type], bgp->vrf_id); } +#if ENABLE_BGP_VNC + if (bgp->vrf_id == VRF_DEFAULT && + type == ZEBRA_ROUTE_VNC_DIRECT) { + vnc_export_bgp_disable(bgp, afi); + } +#endif + if (bgp_install_info_to_zebra (bgp)) { /* Send distribute delete message to zebra. */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index f4a16d6ba2..291483a866 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -63,6 +63,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_damp.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_encap.h" +#if ENABLE_BGP_VNC +#include "bgp_rfapi_cfg.h" +#include "rfapi_backend.h" +#endif #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_vty.h" @@ -1127,7 +1131,7 @@ peer_unlock_with_caller (const char *name, struct peer *peer) } /* Allocate new peer object, implicitely locked. */ -static struct peer * +struct peer * peer_new (struct bgp *bgp) { afi_t afi; @@ -2885,6 +2889,12 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type) bgp->as = *as; +#if ENABLE_BGP_VNC + bgp->rfapi = bgp_rfapi_new(bgp); + assert(bgp->rfapi); + assert(bgp->rfapi_cfg); +#endif /* ENABLE_BGP_VNC */ + if (name) { bgp->name = XSTRDUP(MTYPE_BGP, name); @@ -3165,6 +3175,11 @@ bgp_delete (struct bgp *bgp) /* TODO - Other memory may need to be freed - e.g., NHT */ +#if ENABLE_BGP_VNC + rfapi_delete(bgp); + bgp_cleanup_routes(); /* rfapi cleanup can create route entries! */ +#endif + /* Remove visibility via the master list - there may however still be * routes to be processed still referencing the struct bgp. */ @@ -5233,6 +5248,9 @@ peer_distribute_update (struct access_list *access) } } } +#if ENABLE_BGP_VNC + vnc_prefix_list_update(bgp); +#endif } } @@ -7337,6 +7355,12 @@ bgp_config_write (struct vty *vty) /* ENCAPv6 configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_ENCAP); +#if ENABLE_BGP_VNC + write += bgp_rfapi_cfg_write(vty, bgp); +#endif + + vty_out (vty, " exit%s", VTY_NEWLINE); + write++; } return write; @@ -7407,6 +7431,10 @@ bgp_init (void) /* Init zebra. */ bgp_zebra_init(bm->master); +#if ENABLE_BGP_VNC + vnc_zebra_init (bm->master); +#endif + /* BGP VTY commands installation. */ bgp_vty_init (); @@ -7419,6 +7447,9 @@ bgp_init (void) bgp_scan_vty_init(); bgp_mplsvpn_init (); bgp_encap_init (); +#if ENABLE_BGP_VNC + rfapi_init (); +#endif /* Access list initialize. */ access_list_init (); diff --git a/bgpd/bgpd.conf.vnc.sample b/bgpd/bgpd.conf.vnc.sample new file mode 100644 index 0000000000..863abde3a6 --- /dev/null +++ b/bgpd/bgpd.conf.vnc.sample @@ -0,0 +1,89 @@ +hostname H192.1.1.1 +password zebra +#enable password zebra +log stdout notifications +log monitor notifications +#debug bgp + +line vty +exec-timeout 1000 +exit + + +router bgp 64512 + + # Must set a router-id if no zebra (default 0.0.0.0) + bgp router-id 192.1.1.1 + + neighbor 192.1.1.2 remote-as 64512 + neighbor 192.1.1.2 description H192.1.1.2 + neighbor 192.1.1.2 update-source 192.1.1.1 + neighbor 192.1.1.2 advertisement-interval 1 + no neighbor 192.1.1.2 activate + + neighbor 192.1.1.3 remote-as 64512 + neighbor 192.1.1.3 description H192.1.1.3 + neighbor 192.1.1.3 update-source 192.1.1.1 + neighbor 192.1.1.3 advertisement-interval 1 + no neighbor 192.1.1.3 activate + + address-family vpnv4 + neighbor 192.1.1.2 activate + neighbor 192.1.1.3 activate + exit-address-family + + address-family vpnv6 + neighbor 192.1.1.2 activate + neighbor 192.1.1.3 activate + exit-address-family + + vnc defaults + rd auto:vn:5226 + response-lifetime 45 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.0.0/16 + exit-vnc + + vnc nve-group red + prefix vn 10.0.0.0/8 + rd auto:vn:10 + rt both 1000:10 + exit-vnc + + vnc nve-group blue + prefix vn 20.0.0.0/8 + rd auto:vn:20 + rt both 1000:20 + exit-vnc + + vnc nve-group green + prefix vn 30.0.0.0/8 + rd auto:vn:20 + rt both 1000:30 + exit-vnc + + vnc nve-group rfc4291v6c + prefix vn ::ac10:0/112 + rd auto:vn:5227 + rt both 2000:1 + exit-vnc + + vnc nve-group rfc4291v6m + prefix vn ::ffff:ac10:0/112 + rd auto:vn:5528 + rt both 3000:1 + exit-vnc + + vnc nve-group rfc6052v6 + prefix vn 64:ff9b::ac10:0/112 + rd auto:vn:5529 + rt both 4000:1 + exit-vnc + +exit + + + diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a6d3b61e55..2aa90e4877 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -351,6 +351,11 @@ struct bgp u_int32_t addpath_tx_id; int addpath_tx_used[AFI_MAX][SAFI_MAX]; + +#if ENABLE_BGP_VNC + struct rfapi_cfg *rfapi_cfg; + struct rfapi *rfapi; +#endif }; #define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold) @@ -417,6 +422,8 @@ struct bgp_rd #define RMAP_OUT 1 #define RMAP_MAX 2 +#include "filter.h" + /* BGP filter structure. */ struct bgp_filter { @@ -657,6 +664,9 @@ struct peer #define PEER_FLAG_DYNAMIC_NEIGHBOR (1 << 12) /* dynamic neighbor */ #define PEER_FLAG_CAPABILITY_ENHE (1 << 13) /* Extended next-hop (rfc 5549)*/ #define PEER_FLAG_IFPEER_V6ONLY (1 << 14) /* if-based peer is v6 only */ +#if ENABLE_BGP_VNC +#define PEER_FLAG_IS_RFAPI_HD (1 << 15) /* attached to rfapi HD */ +#endif /* NSF mode (graceful restart) */ u_char nsf[AFI_MAX][SAFI_MAX]; @@ -940,6 +950,9 @@ struct bgp_nlri #define BGP_ATTR_AS4_AGGREGATOR 18 #define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_ENCAP 23 +#if ENABLE_BGP_VNC +#define BGP_ATTR_VNC 255 +#endif /* BGP update origin. */ #define BGP_ORIGIN_IGP 0 @@ -1054,6 +1067,7 @@ struct bgp_nlri /* RFC4364 */ #define SAFI_MPLS_LABELED_VPN 128 +#define BGP_SAFI_VPN 128 /* BGP uptime string length. */ #define BGP_UPTIME_LEN 25 @@ -1506,4 +1520,8 @@ bgp_vrf_unlink (struct bgp *bgp, struct vrf *vrf) } extern void bgp_update_redist_vrf_bitmaps (struct bgp*, vrf_id_t); + +/* For benefit of rfapi */ +extern struct peer * peer_new (struct bgp *bgp); + #endif /* _QUAGGA_BGPD_H */ diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c new file mode 100644 index 0000000000..57fb04d232 --- /dev/null +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -0,0 +1,4706 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include "command.h" +#include "prefix.h" +#include "memory.h" +#include "linklist.h" +#include "table.h" +#include "plist.h" +#include "routemap.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_route.h" + +#include "bgpd/bgp_ecommunity.h" +#include "rfapi.h" +#include "bgp_rfapi_cfg.h" +#include "rfapi_backend.h" +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "vnc_zebra.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "rfapi_vty.h" +#include "vnc_import_bgp.h" + +#if ENABLE_BGP_VNC + +#undef BGP_VNC_DEBUG_MATCH_GROUP + + +DEFINE_MGROUP(RFAPI, "rfapi") +DEFINE_MTYPE(RFAPI, RFAPI_CFG, "NVE Configuration") +DEFINE_MTYPE(RFAPI, RFAPI_GROUP_CFG, "NVE Group Configuration") +DEFINE_MTYPE(RFAPI, RFAPI_L2_CFG, "RFAPI L2 Group Configuration") +DEFINE_MTYPE(RFAPI, RFAPI_RFP_GROUP_CFG, "RFAPI RFP Group Configuration") +DEFINE_MTYPE(RFAPI, RFAPI, "RFAPI Generic") +DEFINE_MTYPE(RFAPI, RFAPI_DESC, "RFAPI Descriptor") +DEFINE_MTYPE(RFAPI, RFAPI_IMPORTTABLE, "RFAPI Import Table") +DEFINE_MTYPE(RFAPI, RFAPI_MONITOR, "RFAPI Monitor VPN") +DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ENCAP, "RFAPI Monitor Encap") +DEFINE_MTYPE(RFAPI, RFAPI_NEXTHOP, "RFAPI Next Hop") +DEFINE_MTYPE(RFAPI, RFAPI_VN_OPTION, "RFAPI VN Option") +DEFINE_MTYPE(RFAPI, RFAPI_UN_OPTION, "RFAPI UN Option") +DEFINE_MTYPE(RFAPI, RFAPI_WITHDRAW, "RFAPI Withdraw") +DEFINE_MTYPE(RFAPI, RFAPI_RFG_NAME, "RFAPI RFGName") +DEFINE_MTYPE(RFAPI, RFAPI_ADB, "RFAPI Advertisement Data") +DEFINE_MTYPE(RFAPI, RFAPI_ETI, "RFAPI Export Table Info") +DEFINE_MTYPE(RFAPI, RFAPI_NVE_ADDR, "RFAPI NVE Address") +DEFINE_MTYPE(RFAPI, RFAPI_PREFIX_BAG, "RFAPI Prefix Bag") +DEFINE_MTYPE(RFAPI, RFAPI_IT_EXTRA, "RFAPI IT Extra") +DEFINE_MTYPE(RFAPI, RFAPI_INFO, "RFAPI Info") +DEFINE_MTYPE(RFAPI, RFAPI_ADDR, "RFAPI Addr") +DEFINE_MTYPE(RFAPI, RFAPI_UPDATED_RESPONSE_QUEUE, "RFAPI Updated Rsp Queue") +DEFINE_MTYPE(RFAPI, RFAPI_RECENT_DELETE, "RFAPI Recently Deleted Route") +DEFINE_MTYPE(RFAPI, RFAPI_L2ADDR_OPT, "RFAPI L2 Address Option") +DEFINE_MTYPE(RFAPI, RFAPI_AP, "RFAPI Advertised Prefix") +DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ETH, "RFAPI Monitor Ethernet") + +/*********************************************************************** + * RFAPI Support + ***********************************************************************/ + + +/* + * compaitibility to old quagga_time call + * time_t value in terms of stabilised absolute time. + * replacement for POSIX time() + */ +time_t +rfapi_time (time_t *t) +{ + time_t clock = bgp_clock(); + if (t) + *t = clock; + return clock; +} + +void +nve_group_to_nve_list ( + struct rfapi_nve_group_cfg *rfg, + struct list **nves, + uint8_t family) /* AF_INET, AF_INET6 */ +{ + struct listnode *hln; + struct rfapi_descriptor *rfd; + + /* + * loop over nves in this grp, add to list + */ + for (ALL_LIST_ELEMENTS_RO (rfg->nves, hln, rfd)) + { + if (rfd->vn_addr.addr_family == family) + { + if (!*nves) + *nves = list_new (); + listnode_add (*nves, rfd); + } + } +} + + +struct rfapi_nve_group_cfg * +bgp_rfapi_cfg_match_group ( + struct rfapi_cfg *hc, + struct prefix *vn, + struct prefix *un) +{ + struct rfapi_nve_group_cfg *rfg_vn = NULL; + struct rfapi_nve_group_cfg *rfg_un = NULL; + + struct route_table *rt_vn; + struct route_table *rt_un; + struct route_node *rn_vn; + struct route_node *rn_un; + + struct rfapi_nve_group_cfg *rfg; + struct listnode *node, *nnode; + + switch (vn->family) + { + case AF_INET: + rt_vn = &(hc->nve_groups_vn[AFI_IP]); + break; + case AF_INET6: + rt_vn = &(hc->nve_groups_vn[AFI_IP6]); + break; + default: + return NULL; + } + + switch (un->family) + { + case AF_INET: + rt_un = &(hc->nve_groups_un[AFI_IP]); + break; + case AF_INET6: + rt_un = &(hc->nve_groups_un[AFI_IP6]); + break; + default: + return NULL; + } + + rn_vn = route_node_match (rt_vn, vn); /* NB locks node */ + if (rn_vn) + { + rfg_vn = rn_vn->info; + route_unlock_node (rn_vn); + } + + rn_un = route_node_match (rt_un, un); /* NB locks node */ + if (rn_un) + { + rfg_un = rn_un->info; + route_unlock_node (rn_un); + } + +#if BGP_VNC_DEBUG_MATCH_GROUP + { + char buf[BUFSIZ]; + + prefix2str (vn, buf, BUFSIZ); + zlog_debug ("%s: vn prefix: %s", __func__, buf); + + prefix2str (un, buf, BUFSIZ); + zlog_debug ("%s: un prefix: %s", __func__, buf); + + zlog_debug ("%s: rn_vn=%p, rn_un=%p, rfg_vn=%p, rfg_un=%p", + __func__, rn_vn, rn_un, rfg_vn, rfg_un); + } +#endif + + + if (rfg_un == rfg_vn) /* same group */ + return rfg_un; + if (!rfg_un) /* un doesn't match, return vn-matched grp */ + return rfg_vn; + if (!rfg_vn) /* vn doesn't match, return un-matched grp */ + return rfg_un; + + /* + * Two different nve groups match: the group configured earlier wins. + * For now, just walk the sequential list and pick the first one. + * If this approach is too slow, then store serial numbers in the + * nve group structures as they are defined and just compare + * serial numbers. + */ + for (ALL_LIST_ELEMENTS (hc->nve_groups_sequential, node, nnode, rfg)) + { + if ((rfg == rfg_un) || (rfg == rfg_vn)) + { + return rfg; + } + } + zlog_debug ("%s: shouldn't happen, returning NULL when un and vn match", + __func__); + return NULL; /* shouldn't happen */ +} + +/*------------------------------------------ + * rfapi_get_rfp_start_val + * + * Returns value passed to rfapi on rfp_start + * + * input: + * void * bgp structure + * + * returns: + * void * + *------------------------------------------*/ +void * +rfapi_get_rfp_start_val (void *bgpv) +{ + struct bgp *bgp = bgpv; + if (bgp == NULL || bgp->rfapi == NULL) + return NULL; + return bgp->rfapi->rfp; +} + +/*------------------------------------------ + * bgp_rfapi_is_vnc_configured + * + * Returns if VNC (BGP VPN messaging /VPN & encap SAFIs) are configured + * + * input: + * bgp NULL (=use default instance) + * + * output: + * + * return value: If VNC is configured for the bgpd instance + * 0 Success + * ENXIO VNC not configured + --------------------------------------------*/ +int +bgp_rfapi_is_vnc_configured (struct bgp *bgp) +{ + if (bgp == NULL) + bgp = bgp_get_default (); + + if (bgp && bgp->rfapi_cfg) + { + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + /* if have configured VPN neighbors, assume running VNC */ + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + if (group->conf->afc[AFI_IP][SAFI_MPLS_VPN] || + group->conf->afc[AFI_IP6][SAFI_MPLS_VPN]) + return 0; + } + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->afc[AFI_IP][SAFI_MPLS_VPN] || + peer->afc[AFI_IP6][SAFI_MPLS_VPN]) + return 0; + } + } + return ENXIO; +} + +/*********************************************************************** + * VNC Configuration/CLI + ***********************************************************************/ + + +DEFUN (vnc_advertise_un_method, + vnc_advertise_un_method_cmd, + "vnc advertise-un-method (encap-safi|encap-attr)", + VNC_CONFIG_STR + "Method of advertising UN addresses\n" + "Via Encapsulation SAFI\n" + "Via Tunnel Encap attribute (in VPN SAFI)\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "VNC not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + + if (!strncmp (argv[0], "encap-safi", 7)) + { + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP; + } + else + { + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP; + } + + return CMD_SUCCESS; +} + +/*------------------------------------------------------------------------- + * RFG defaults + *-----------------------------------------------------------------------*/ + + +DEFUN (vnc_defaults, + vnc_defaults_cmd, + "vnc defaults", VNC_CONFIG_STR "Configure default NVE group\n") +{ + vty->node = BGP_VNC_DEFAULTS_NODE; + return CMD_SUCCESS; +} + +static int +set_ecom_list ( + struct vty *vty, + int argc, + const char **argv, + struct ecommunity **list) +{ + struct ecommunity *ecom = NULL; + struct ecommunity *ecomadd; + + for (; argc; --argc, ++argv) + { + + ecomadd = ecommunity_str2com (*argv, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) + { + vty_out (vty, "Malformed community-list value%s", VTY_NEWLINE); + if (ecom) + ecommunity_free (&ecom); + return CMD_WARNING; + } + + if (ecom) + { + ecommunity_merge (ecom, ecomadd); + ecommunity_free (&ecomadd); + } + else + { + ecom = ecomadd; + } + } + + if (*list) + { + ecommunity_free (&*list); + } + *list = ecom; + + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_rt_import, + vnc_defaults_rt_import_cmd, + "rt import .RTLIST", + "Specify default route targets\n" + "Import filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct bgp *bgp = vty->index; + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + return set_ecom_list (vty, argc, argv, + &bgp->rfapi_cfg->default_rt_import_list); +} + +DEFUN (vnc_defaults_rt_export, + vnc_defaults_rt_export_cmd, + "rt export .RTLIST", + "Configure default route targets\n" + "Export filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct bgp *bgp = vty->index; + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + return set_ecom_list (vty, argc, argv, + &bgp->rfapi_cfg->default_rt_export_list); +} + +DEFUN (vnc_defaults_rt_both, + vnc_defaults_rt_both_cmd, + "rt both .RTLIST", + "Configure default route targets\n" + "Export+import filters\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + int rc; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + rc = + set_ecom_list (vty, argc, argv, &bgp->rfapi_cfg->default_rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + return set_ecom_list (vty, argc, argv, + &bgp->rfapi_cfg->default_rt_export_list); +} + +DEFUN (vnc_defaults_rd, + vnc_defaults_rd_cmd, + "rd ASN:nn_or_IP-address:nn", + "Specify default route distinguisher\n" + "Route Distinguisher (: | : | auto:vn: )\n") +{ + int ret; + struct prefix_rd prd; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strncmp (argv[0], "auto:vn:", 8)) + { + /* + * use AF_UNIX to designate automatically-assigned RD + * auto:vn:nn where nn is a 2-octet quantity + */ + char *end = NULL; + uint32_t value32 = strtoul (argv[0] + 8, &end, 10); + uint16_t value = value32 & 0xffff; + + if (!*(argv[0] + 5) || *end) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (value32 > 0xffff) + { + vty_out (vty, "%% Malformed rd (must be less than %u%s", + 0x0ffff, VTY_NEWLINE); + return CMD_WARNING; + } + + memset (&prd, 0, sizeof (prd)); + prd.family = AF_UNIX; + prd.prefixlen = 64; + prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff; + prd.val[1] = RD_TYPE_IP & 0x0ff; + prd.val[6] = (value >> 8) & 0x0ff; + prd.val[7] = value & 0x0ff; + + } + else + { + + ret = str2prefix_rd (argv[0], &prd); + if (!ret) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + bgp->rfapi_cfg->default_rd = prd; + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_l2rd, + vnc_defaults_l2rd_cmd, + "l2rd (ID|auto:vn)", + "Specify default Local Nve ID value to use in RD for L2 routes\n" + "Fixed value 1-255\n" + "use the low-order octet of the NVE's VN address\n") +{ + struct bgp *bgp = vty->index; + uint8_t value = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "auto:vn")) + { + value = 0; + } + else + { + char *end = NULL; + unsigned long value_l = strtoul (argv[0], &end, 10); + + value = value_l & 0xff; + if (!*(argv[0]) || *end) + { + vty_out (vty, "%% Malformed l2 nve ID \"%s\"%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + if ((value_l < 1) || (value_l > 0xff)) + { + vty_out (vty, + "%% Malformed l2 nve id (must be greater than 0 and less than %u%s", + 0x100, VTY_NEWLINE); + return CMD_WARNING; + } + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_L2RD; + bgp->rfapi_cfg->default_l2rd = value; + + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_no_l2rd, + vnc_defaults_no_l2rd_cmd, + "no l2rd", + NO_STR + "Specify default Local Nve ID value to use in RD for L2 routes\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp->rfapi_cfg->default_l2rd = 0; + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_L2RD; + + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_responselifetime, + vnc_defaults_responselifetime_cmd, + "response-lifetime (LIFETIME|infinite)", + "Specify default response lifetime\n" + "Response lifetime in seconds\n" "Infinite response lifetime\n") +{ + uint32_t rspint; + struct bgp *bgp = vty->index; + struct rfapi *h = NULL; + struct listnode *hdnode; + struct rfapi_descriptor *rfd; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + h = bgp->rfapi; + if (!h) + return CMD_WARNING; + + if (!strcmp (argv[0], "infinite")) + { + rspint = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Response Lifetime", rspint, argv[0]); + if (rspint > INT32_MAX) + rspint = INT32_MAX; /* is really an int, not an unsigned int */ + } + + bgp->rfapi_cfg->default_response_lifetime = rspint; + + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, hdnode, rfd)) + if (rfd->rfg && !(rfd->rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME)) + rfd->response_lifetime = rfd->rfg->response_lifetime = rspint; + + return CMD_SUCCESS; +} + +static struct rfapi_nve_group_cfg * +rfapi_group_lookup_byname (struct bgp *bgp, const char *name) +{ + struct rfapi_nve_group_cfg *rfg; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS + (bgp->rfapi_cfg->nve_groups_sequential, node, nnode, rfg)) + { + if (!strcmp (rfg->name, name)) + return rfg; + } + return NULL; +} + +static struct rfapi_nve_group_cfg * +rfapi_group_new () +{ + return XCALLOC (MTYPE_RFAPI_GROUP_CFG, sizeof (struct rfapi_nve_group_cfg)); +} + +static struct rfapi_l2_group_cfg * +rfapi_l2_group_lookup_byname (struct bgp *bgp, const char *name) +{ + struct rfapi_l2_group_cfg *rfg; + struct listnode *node, *nnode; + + if (bgp->rfapi_cfg->l2_groups == NULL) /* not the best place for this */ + bgp->rfapi_cfg->l2_groups = list_new (); + + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->l2_groups, node, nnode, rfg)) + { + if (!strcmp (rfg->name, name)) + return rfg; + } + return NULL; +} + +static struct rfapi_l2_group_cfg * +rfapi_l2_group_new () +{ + return XCALLOC (MTYPE_RFAPI_L2_CFG, sizeof (struct rfapi_l2_group_cfg)); +} + +static void +rfapi_l2_group_del (struct rfapi_l2_group_cfg *rfg) +{ + XFREE (MTYPE_RFAPI_L2_CFG, rfg); +} + +static int +rfapi_str2route_type ( + const char *l3str, + const char *pstr, + afi_t *afi, + int *type) +{ + if (!l3str || !pstr) + return EINVAL; + + if (!strcmp (l3str, "ipv4")) + { + *afi = AFI_IP; + } + else + { + if (!strcmp (l3str, "ipv6")) + *afi = AFI_IP6; + else + return ENOENT; + } + + if (!strcmp (pstr, "connected")) + *type = ZEBRA_ROUTE_CONNECT; + if (!strcmp (pstr, "kernel")) + *type = ZEBRA_ROUTE_KERNEL; + if (!strcmp (pstr, "static")) + *type = ZEBRA_ROUTE_STATIC; + if (!strcmp (pstr, "bgp")) + *type = ZEBRA_ROUTE_BGP; + if (!strcmp (pstr, "bgp-direct")) + *type = ZEBRA_ROUTE_BGP_DIRECT; + if (!strcmp (pstr, "bgp-direct-to-nve-groups")) + *type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + + if (!strcmp (pstr, "rip")) + { + if (*afi == AFI_IP) + *type = ZEBRA_ROUTE_RIP; + else + *type = ZEBRA_ROUTE_RIPNG; + } + + if (!strcmp (pstr, "ripng")) + { + if (*afi == AFI_IP) + return EAFNOSUPPORT; + *type = ZEBRA_ROUTE_RIPNG; + } + + if (!strcmp (pstr, "ospf")) + { + if (*afi == AFI_IP) + *type = ZEBRA_ROUTE_OSPF; + else + *type = ZEBRA_ROUTE_OSPF6; + } + + if (!strcmp (pstr, "ospf6")) + { + if (*afi == AFI_IP) + return EAFNOSUPPORT; + *type = ZEBRA_ROUTE_OSPF6; + } + + return 0; +} + +/*------------------------------------------------------------------------- + * redistribute + *-----------------------------------------------------------------------*/ + +#define VNC_REDIST_ENABLE(bgp, afi, type) do { \ + switch (type) { \ + case ZEBRA_ROUTE_BGP_DIRECT: \ + vnc_import_bgp_redist_enable((bgp), (afi)); \ + break; \ + case ZEBRA_ROUTE_BGP_DIRECT_EXT: \ + vnc_import_bgp_exterior_redist_enable((bgp), (afi));\ + break; \ + default: \ + vnc_redistribute_set((bgp), (afi), (type)); \ + break; \ + } \ +} while (0) + +#define VNC_REDIST_DISABLE(bgp, afi, type) do { \ + switch (type) { \ + case ZEBRA_ROUTE_BGP_DIRECT: \ + vnc_import_bgp_redist_disable((bgp), (afi)); \ + break; \ + case ZEBRA_ROUTE_BGP_DIRECT_EXT: \ + vnc_import_bgp_exterior_redist_disable((bgp), (afi));\ + break; \ + default: \ + vnc_redistribute_unset((bgp), (afi), (type)); \ + break; \ + } \ +} while (0) + +static uint8_t redist_was_enabled[AFI_MAX][ZEBRA_ROUTE_MAX]; + +static void +vnc_redistribute_prechange (struct bgp *bgp) +{ + afi_t afi; + int type; + + zlog_debug ("%s: entry", __func__); + memset (redist_was_enabled, 0, sizeof (redist_was_enabled)); + + /* + * Look to see if we have any redistribution enabled. If so, flush + * the corresponding routes and turn off redistribution temporarily. + * We need to do it because the RD's used for the redistributed + * routes depend on the nve group. + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) + { + if (bgp->rfapi_cfg->redist[afi][type]) + { + redist_was_enabled[afi][type] = 1; + VNC_REDIST_DISABLE (bgp, afi, type); + } + } + } + zlog_debug ("%s: return", __func__); +} + +static void +vnc_redistribute_postchange (struct bgp *bgp) +{ + afi_t afi; + int type; + + zlog_debug ("%s: entry", __func__); + /* + * If we turned off redistribution above, turn it back on. Doing so + * will tell zebra to resend the routes to us + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) + { + if (redist_was_enabled[afi][type]) + { + VNC_REDIST_ENABLE (bgp, afi, type); + } + } + } + zlog_debug ("%s: return", __func__); +} + +DEFUN (vnc_redistribute_rh_roo_localadmin, + vnc_redistribute_rh_roo_localadmin_cmd, + "vnc redistribute resolve-nve roo-ec-local-admin <0-65535>", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "Resolve-NVE mode\n" + "Route Origin Extended Community Local Admin Field\n" "Field value\n") +{ + struct bgp *bgp = vty->index; + uint32_t localadmin; + char *endptr; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + localadmin = strtoul (argv[0], &endptr, 0); + if (!*(argv[0]) || *endptr) + { + vty_out (vty, "%% Malformed value%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (localadmin > 0xffff) + { + vty_out (vty, "%% Value out of range (0-%d)%s", 0xffff, VTY_NEWLINE); + return CMD_WARNING; + } + + if (bgp->rfapi_cfg->resolve_nve_roo_local_admin == localadmin) + return CMD_SUCCESS; + + if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) == + BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) + { + + vnc_export_bgp_prechange (bgp); + } + vnc_redistribute_prechange (bgp); + + bgp->rfapi_cfg->resolve_nve_roo_local_admin = localadmin; + + if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) == + BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) + { + + vnc_export_bgp_postchange (bgp); + } + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + + +DEFUN (vnc_redistribute_mode, + vnc_redistribute_mode_cmd, + "vnc redistribute mode (nve-group|plain|resolve-nve)", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "Redistribution mode\n" + "Based on redistribute nve-group\n" + "Unmodified\n" "Resolve each nexthop to connected NVEs\n") +{ + struct bgp *bgp = vty->index; + vnc_redist_mode_t newmode; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + + switch (*argv[0]) + { + case 'n': + newmode = VNC_REDIST_MODE_RFG; + break; + + case 'p': + newmode = VNC_REDIST_MODE_PLAIN; + break; + + case 'r': + newmode = VNC_REDIST_MODE_RESOLVE_NVE; + break; + + default: + vty_out (vty, "unknown redistribute mode%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (newmode != bgp->rfapi_cfg->redist_mode) + { + vnc_redistribute_prechange (bgp); + bgp->rfapi_cfg->redist_mode = newmode; + vnc_redistribute_postchange (bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_protocol, + vnc_redistribute_protocol_cmd, + "vnc redistribute (ipv4|ipv6) (bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static)", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "IPv4 routes\n" + "IPv6 routes\n" + "From BGP\n" + "From BGP without Zebra\n" + "From BGP without Zebra, only to configured NVE groups\n" + "Connected interfaces\n" + "From kernel routes\n" + "From Open Shortest Path First (OSPF)\n" + "From Routing Information Protocol (RIP)\n" "From Static routes\n") +{ + int type = ZEBRA_ROUTE_MAX; /* init to bogus value */ + struct bgp *bgp = vty->index; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (rfapi_str2route_type (argv[0], argv[1], &afi, &type)) + { + vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + { + if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) + { + VNC_REDIST_DISABLE (bgp, afi, type); /* disabled view implicitly */ + free (bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL; + } + bgp->rfapi_cfg->redist_bgp_exterior_view = bgp; + } + + VNC_REDIST_ENABLE (bgp, afi, type); + + return CMD_SUCCESS; +} + +DEFUN (vnc_no_redistribute_protocol, + vnc_no_redistribute_protocol_cmd, + "no vnc redistribute (ipv4|ipv6) (bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static)", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "IPv4 routes\n" + "IPv6 routes\n" + "From BGP\n" + "From BGP without Zebra\n" + "From BGP without Zebra, only to configured NVE groups\n" + "Connected interfaces\n" + "From kernel routes\n" + "From Open Shortest Path First (OSPF)\n" + "From Routing Information Protocol (RIP)\n" "From Static routes\n") +{ + int type; + struct bgp *bgp = vty->index; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (rfapi_str2route_type (argv[0], argv[1], &afi, &type)) + { + vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VNC_REDIST_DISABLE (bgp, afi, type); + + if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + { + if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) + { + free (bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL; + } + bgp->rfapi_cfg->redist_bgp_exterior_view = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_bgp_exterior, + vnc_redistribute_bgp_exterior_cmd, + "vnc redistribute (ipv4|ipv6) bgp-direct-to-nve-groups view NAME", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "IPv4 routes\n" + "IPv6 routes\n" + "From BGP without Zebra, only to configured NVE groups\n" + "From BGP view\n" "BGP view name\n") +{ + int type; + struct bgp *bgp = vty->index; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (rfapi_str2route_type (argv[0], "bgp-direct-to-nve-groups", &afi, &type)) + { + vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) + free (bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = strdup (argv[1]); + /* could be NULL if name is not defined yet */ + bgp->rfapi_cfg->redist_bgp_exterior_view = bgp_lookup_by_name (argv[1]); + + VNC_REDIST_ENABLE (bgp, afi, type); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_nvegroup, + vnc_redistribute_nvegroup_cmd, + "vnc redistribute nve-group NAME", + VNC_CONFIG_STR + "Assign a NVE group to routes redistributed from another routing protocol\n" + "NVE group\n" "Group name\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vnc_redistribute_prechange (bgp); + + /* + * OK if nve group doesn't exist yet; we'll set the pointer + * when the group is defined later + */ + bgp->rfapi_cfg->rfg_redist = rfapi_group_lookup_byname (bgp, argv[0]); + if (bgp->rfapi_cfg->rfg_redist_name) + free (bgp->rfapi_cfg->rfg_redist_name); + bgp->rfapi_cfg->rfg_redist_name = strdup (argv[0]); + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_no_nvegroup, + vnc_redistribute_no_nvegroup_cmd, + "no vnc redistribute nve-group", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "Assign a NVE group to routes redistributed from another routing protocol\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vnc_redistribute_prechange (bgp); + + bgp->rfapi_cfg->rfg_redist = NULL; + if (bgp->rfapi_cfg->rfg_redist_name) + free (bgp->rfapi_cfg->rfg_redist_name); + bgp->rfapi_cfg->rfg_redist_name = NULL; + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + + +DEFUN (vnc_redistribute_lifetime, + vnc_redistribute_lifetime_cmd, + "vnc redistribute lifetime (LIFETIME|infinite)", + VNC_CONFIG_STR + "Assign a lifetime to routes redistributed from another routing protocol\n" + "lifetime value (32 bit)\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vnc_redistribute_prechange (bgp); + + if (!strcmp (argv[0], "infinite")) + { + bgp->rfapi_cfg->redist_lifetime = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Response Lifetime", bgp->rfapi_cfg->redist_lifetime, + argv[0]); + } + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +/*-- redist policy, non-nvegroup start --*/ + +DEFUN (vnc_redist_bgpdirect_no_prefixlist, + vnc_redist_bgpdirect_no_prefixlist_cmd, + "no vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) (ipv4|ipv6) prefix-list", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "IPv4 routes\n" + "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n") +{ + struct bgp *bgp = vty->index; + afi_t afi; + struct rfapi_cfg *hc; + uint8_t route_type = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "bgp-direct")) + { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } + else + { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + if (!strcmp (argv[1], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + vnc_redistribute_prechange (bgp); + + if (hc->plist_redist_name[route_type][afi]) + free (hc->plist_redist_name[route_type][afi]); + hc->plist_redist_name[route_type][afi] = NULL; + hc->plist_redist[route_type][afi] = NULL; + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redist_bgpdirect_prefixlist, + vnc_redist_bgpdirect_prefixlist_cmd, + "vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) (ipv4|ipv6) prefix-list NAME", + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering redistributed routes\n" + "prefix list name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + afi_t afi; + uint8_t route_type = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "bgp-direct")) + { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } + else + { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + if (!strcmp (argv[1], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + vnc_redistribute_prechange (bgp); + + if (hc->plist_redist_name[route_type][afi]) + free (hc->plist_redist_name[route_type][afi]); + hc->plist_redist_name[route_type][afi] = strdup (argv[2]); + hc->plist_redist[route_type][afi] = prefix_list_lookup (afi, argv[2]); + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redist_bgpdirect_no_routemap, + vnc_redist_bgpdirect_no_routemap_cmd, + "no vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) route-map", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "Route-map for filtering redistributed routes\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + uint8_t route_type = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "bgp-direct")) + { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } + else + { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + vnc_redistribute_prechange (bgp); + + if (hc->routemap_redist_name[route_type]) + free (hc->routemap_redist_name[route_type]); + hc->routemap_redist_name[route_type] = NULL; + hc->routemap_redist[route_type] = NULL; + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redist_bgpdirect_routemap, + vnc_redist_bgpdirect_routemap_cmd, + "vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) route-map NAME", + VNC_CONFIG_STR + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + uint8_t route_type = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "bgp-direct")) + { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } + else + { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + vnc_redistribute_prechange (bgp); + + if (hc->routemap_redist_name[route_type]) + free (hc->routemap_redist_name[route_type]); + hc->routemap_redist_name[route_type] = strdup (argv[1]); + hc->routemap_redist[route_type] = route_map_lookup_by_name (argv[1]); + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +/*-- redist policy, non-nvegroup end --*/ + +/*-- redist policy, nvegroup start --*/ + +DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist, + vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd, + "no redistribute bgp-direct (ipv4|ipv6) prefix-list", + NO_STR + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "Disable redistribute filter\n" + "IPv4 routes\n" + "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + vnc_redistribute_prechange (bgp); + + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + free (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL; + rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL; + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist, + vnc_nve_group_redist_bgpdirect_prefixlist_cmd, + "redistribute bgp-direct (ipv4|ipv6) prefix-list NAME", + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering redistributed routes\n" + "prefix list name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + vnc_redistribute_prechange (bgp); + + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + free (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = strdup (argv[1]); + rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = + prefix_list_lookup (afi, argv[1]); + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap, + vnc_nve_group_redist_bgpdirect_no_routemap_cmd, + "no redistribute bgp-direct route-map", + NO_STR + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Disable redistribute filter\n" + "Route-map for filtering redistributed routes\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vnc_redistribute_prechange (bgp); + + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + free (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = NULL; + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = NULL; + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_redist_bgpdirect_routemap, + vnc_nve_group_redist_bgpdirect_routemap_cmd, + "redistribute bgp-direct route-map NAME", + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vnc_redistribute_prechange (bgp); + + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + free (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = strdup (argv[0]); + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = + route_map_lookup_by_name (argv[0]); + + vnc_redistribute_postchange (bgp); + + return CMD_SUCCESS; +} + +/*-- redist policy, nvegroup end --*/ + +/*------------------------------------------------------------------------- + * export + *-----------------------------------------------------------------------*/ + +DEFUN (vnc_export_mode, + vnc_export_mode_cmd, + "vnc export (bgp|zebra) mode (group-nve|ce|none|registering-nve)", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Select export mode\n" + "Export routes with nve-group next-hops\n" + "Export routes with NVE connected router next-hops\n" + "Disable export\n" "Export routes with registering NVE as next-hop\n") +{ + struct bgp *bgp = vty->index; + uint32_t oldmode = 0; + uint32_t newmode = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "VNC not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*argv[0] == 'b') + { + oldmode = bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS; + switch (*argv[1]) + { + case 'g': + newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP; + break; + case 'c': + newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE; + break; + case 'n': + newmode = 0; + break; + case 'r': + newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH; + break; + default: + vty_out (vty, "Invalid mode specified%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (newmode == oldmode) + { + vty_out (vty, "Mode unchanged%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + vnc_export_bgp_prechange (bgp); + + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS; + bgp->rfapi_cfg->flags |= newmode; + + vnc_export_bgp_postchange (bgp); + + + } + else + { + /* + * export to zebra with RH mode is not yet implemented + */ + vty_out (vty, "Changing modes for zebra export not implemented yet%s", + VTY_NEWLINE); + return CMD_WARNING; + + oldmode = bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS; + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS; + switch (*argv[1]) + { + case 'g': + if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH) + { + /* TBD */ + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP; + if (oldmode != BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP) + { + /* TBD */ + } + break; + case 'n': + if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH) + { + /* TBD */ + } + if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP) + { + /* TBD */ + } + break; + case 'r': + if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP) + { + /* TBD */ + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH; + if (oldmode != BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH) + { + /* TBD */ + } + break; + default: + vty_out (vty, "Invalid mode%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +static struct rfapi_rfg_name * +rfgn_new () +{ + return XCALLOC (MTYPE_RFAPI_RFG_NAME, sizeof (struct rfapi_rfg_name)); +} + +static void +rfgn_free (struct rfapi_rfg_name *rfgn) +{ + XFREE (MTYPE_RFAPI_RFG_NAME, rfgn); +} + +DEFUN (vnc_export_nvegroup, + vnc_export_nvegroup_cmd, + "vnc export (bgp|zebra) group-nve group NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "NVE group, used in 'group-nve' export mode\n" + "NVE group\n" "Group name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg_new; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rfg_new = rfapi_group_lookup_byname (bgp, argv[1]); + + if (*argv[0] == 'b') + { + + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* + * Set group for export to BGP Direct + */ + + /* see if group is already included in export list */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + if (!strcmp (rfgn->name, argv[1])) + { + /* already in the list: we're done */ + return CMD_SUCCESS; + } + } + + rfgn = rfgn_new (); + rfgn->name = strdup (argv[1]); + rfgn->rfg = rfg_new; /* OK if not set yet */ + + listnode_add (bgp->rfapi_cfg->rfg_export_direct_bgp_l, rfgn); + + zlog_debug ("%s: testing rfg_new", __func__); + if (rfg_new) + { + zlog_debug ("%s: testing bgp grp mode enabled", __func__); + if (VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + zlog_debug ("%s: calling vnc_direct_bgp_add_group", __func__); + vnc_direct_bgp_add_group (bgp, rfg_new); + } + + } + else + { + + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* + * Set group for export to Zebra + */ + + /* see if group is already included in export list */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, + node, rfgn)) + { + + if (!strcmp (rfgn->name, argv[1])) + { + /* already in the list: we're done */ + return CMD_SUCCESS; + } + } + + rfgn = rfgn_new (); + rfgn->name = strdup (argv[1]); + rfgn->rfg = rfg_new; /* OK if not set yet */ + + listnode_add (bgp->rfapi_cfg->rfg_export_zebra_l, rfgn); + + if (rfg_new) + { + if (VNC_EXPORT_ZEBRA_GRP_ENABLED (bgp->rfapi_cfg)) + vnc_zebra_add_group (bgp, rfg_new); + } + } + + return CMD_SUCCESS; +} + +/* + * This command applies to routes exported from VNC to BGP directly + * without going though zebra + */ +DEFUN (vnc_no_export_nvegroup, + vnc_no_export_nvegroup_cmd, + "vnc export (bgp|zebra) group-nve no group NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "NVE group, used in 'group-nve' export mode\n" + "Disable export of VNC routes\n" "NVE group\n" "Group name\n") +{ + struct bgp *bgp = vty->index; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*argv[0] == 'b') + { + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + + if (rfgn->name && !strcmp (rfgn->name, argv[1])) + { + zlog_debug ("%s: matched \"%s\"", __func__, rfgn->name); + if (rfgn->rfg) + vnc_direct_bgp_del_group (bgp, rfgn->rfg); + free (rfgn->name); + list_delete_node (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node); + rfgn_free (rfgn); + break; + } + } + } + else + { + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_zebra_l, + node, nnode, rfgn)) + { + + zlog_debug ("does rfg \"%s\" match?", rfgn->name); + if (rfgn->name && !strcmp (rfgn->name, argv[1])) + { + if (rfgn->rfg) + vnc_zebra_del_group (bgp, rfgn->rfg); + free (rfgn->name); + list_delete_node (bgp->rfapi_cfg->rfg_export_zebra_l, node); + rfgn_free (rfgn); + break; + } + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_export_no_prefixlist, + vnc_nve_group_export_no_prefixlist_cmd, + "no export (bgp|zebra) (ipv4|ipv6) prefix-list [NAME]", + NO_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering exported routes\n" "prefix list name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[1], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + if (*argv[0] == 'b') + { + if (((argc >= 3) && !strcmp (argv[2], rfg->plist_export_bgp_name[afi])) + || (argc < 3)) + { + + if (rfg->plist_export_bgp_name[afi]) + free (rfg->plist_export_bgp_name[afi]); + rfg->plist_export_bgp_name[afi] = NULL; + rfg->plist_export_bgp[afi] = NULL; + + vnc_direct_bgp_reexport_group_afi (bgp, rfg, afi); + } + } + else + { + if (((argc >= 3) + && !strcmp (argv[2], rfg->plist_export_zebra_name[afi])) + || (argc < 3)) + { + if (rfg->plist_export_zebra_name[afi]) + free (rfg->plist_export_zebra_name[afi]); + rfg->plist_export_zebra_name[afi] = NULL; + rfg->plist_export_zebra[afi] = NULL; + + vnc_zebra_reexport_group_afi (bgp, rfg, afi); + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_export_prefixlist, + vnc_nve_group_export_prefixlist_cmd, + "export (bgp|zebra) (ipv4|ipv6) prefix-list NAME", + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering exported routes\n" "prefix list name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[1], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + if (*argv[0] == 'b') + { + if (rfg->plist_export_bgp_name[afi]) + free (rfg->plist_export_bgp_name[afi]); + rfg->plist_export_bgp_name[afi] = strdup (argv[2]); + rfg->plist_export_bgp[afi] = prefix_list_lookup (afi, argv[2]); + + vnc_direct_bgp_reexport_group_afi (bgp, rfg, afi); + + } + else + { + if (rfg->plist_export_zebra_name[afi]) + free (rfg->plist_export_zebra_name[afi]); + rfg->plist_export_zebra_name[afi] = strdup (argv[2]); + rfg->plist_export_zebra[afi] = prefix_list_lookup (afi, argv[2]); + + vnc_zebra_reexport_group_afi (bgp, rfg, afi); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_export_no_routemap, + vnc_nve_group_export_no_routemap_cmd, + "no export (bgp|zebra) route-map [NAME]", + NO_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*argv[0] == 'b') + { + if (((argc >= 2) && !strcmp (argv[1], rfg->routemap_export_bgp_name)) || + (argc < 2)) + { + + if (rfg->routemap_export_bgp_name) + free (rfg->routemap_export_bgp_name); + rfg->routemap_export_bgp_name = NULL; + rfg->routemap_export_bgp = NULL; + + vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP); + vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP6); + } + } + else + { + if (((argc >= 2) && !strcmp (argv[1], rfg->routemap_export_zebra_name)) + || (argc < 2)) + { + if (rfg->routemap_export_zebra_name) + free (rfg->routemap_export_zebra_name); + rfg->routemap_export_zebra_name = NULL; + rfg->routemap_export_zebra = NULL; + + vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP); + vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP6); + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_export_routemap, + vnc_nve_group_export_routemap_cmd, + "export (bgp|zebra) route-map NAME", + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_nve_group_cfg *rfg; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!bgp->rfapi_cfg) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*argv[0] == 'b') + { + if (rfg->routemap_export_bgp_name) + free (rfg->routemap_export_bgp_name); + rfg->routemap_export_bgp_name = strdup (argv[1]); + rfg->routemap_export_bgp = route_map_lookup_by_name (argv[1]); + vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP); + vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP6); + } + else + { + if (rfg->routemap_export_zebra_name) + free (rfg->routemap_export_zebra_name); + rfg->routemap_export_zebra_name = strdup (argv[1]); + rfg->routemap_export_zebra = route_map_lookup_by_name (argv[1]); + vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP); + vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP6); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_no_prefixlist, + vnc_nve_export_no_prefixlist_cmd, + "no vnc export (bgp|zebra) (ipv4|ipv6) prefix-list [NAME]", + NO_STR + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 prefixes\n" + "IPv6 prefixes\n" + "Prefix-list for filtering exported routes\n" "Prefix list name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[1], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + if (*argv[0] == 'b') + { + if (((argc >= 3) && !strcmp (argv[2], hc->plist_export_bgp_name[afi])) + || (argc < 3)) + { + + if (hc->plist_export_bgp_name[afi]) + free (hc->plist_export_bgp_name[afi]); + hc->plist_export_bgp_name[afi] = NULL; + hc->plist_export_bgp[afi] = NULL; + vnc_direct_bgp_reexport (bgp, afi); + } + } + else + { + if (((argc >= 3) && !strcmp (argv[2], hc->plist_export_zebra_name[afi])) + || (argc < 3)) + { + + if (hc->plist_export_zebra_name[afi]) + free (hc->plist_export_zebra_name[afi]); + hc->plist_export_zebra_name[afi] = NULL; + hc->plist_export_zebra[afi] = NULL; + /* TBD vnc_zebra_rh_reexport(bgp, afi); */ + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_prefixlist, + vnc_nve_export_prefixlist_cmd, + "vnc export (bgp|zebra) (ipv4|ipv6) prefix-list NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Filters, used in 'registering-nve' export mode\n" + "IPv4 prefixes\n" + "IPv6 prefixes\n" + "Prefix-list for filtering exported routes\n" "Prefix list name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + afi_t afi; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[1], "ipv4")) + { + afi = AFI_IP; + } + else + { + afi = AFI_IP6; + } + + if (*argv[0] == 'b') + { + if (hc->plist_export_bgp_name[afi]) + free (hc->plist_export_bgp_name[afi]); + hc->plist_export_bgp_name[afi] = strdup (argv[2]); + hc->plist_export_bgp[afi] = prefix_list_lookup (afi, argv[2]); + vnc_direct_bgp_reexport (bgp, afi); + } + else + { + if (hc->plist_export_zebra_name[afi]) + free (hc->plist_export_zebra_name[afi]); + hc->plist_export_zebra_name[afi] = strdup (argv[2]); + hc->plist_export_zebra[afi] = prefix_list_lookup (afi, argv[2]); + /* TBD vnc_zebra_rh_reexport(bgp, afi); */ + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_no_routemap, + vnc_nve_export_no_routemap_cmd, + "no vnc export (bgp|zebra) route-map [NAME]", + NO_STR + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "Route map name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*argv[0] == 'b') + { + if (((argc >= 2) && !strcmp (argv[1], hc->routemap_export_bgp_name)) || + (argc < 2)) + { + + if (hc->routemap_export_bgp_name) + free (hc->routemap_export_bgp_name); + hc->routemap_export_bgp_name = NULL; + hc->routemap_export_bgp = NULL; + vnc_direct_bgp_reexport (bgp, AFI_IP); + vnc_direct_bgp_reexport (bgp, AFI_IP6); + } + } + else + { + if (((argc >= 2) && !strcmp (argv[1], hc->routemap_export_zebra_name)) + || (argc < 2)) + { + + if (hc->routemap_export_zebra_name) + free (hc->routemap_export_zebra_name); + hc->routemap_export_zebra_name = NULL; + hc->routemap_export_zebra = NULL; + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_routemap, + vnc_nve_export_routemap_cmd, + "vnc export (bgp|zebra) route-map NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Filters, used in 'registering-nve' export mode\n" + "Route-map for filtering exported routes\n" "Route map name\n") +{ + struct bgp *bgp = vty->index; + struct rfapi_cfg *hc; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(hc = bgp->rfapi_cfg)) + { + vty_out (vty, "rfapi not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*argv[0] == 'b') + { + if (hc->routemap_export_bgp_name) + free (hc->routemap_export_bgp_name); + hc->routemap_export_bgp_name = strdup (argv[1]); + hc->routemap_export_bgp = route_map_lookup_by_name (argv[1]); + vnc_direct_bgp_reexport (bgp, AFI_IP); + vnc_direct_bgp_reexport (bgp, AFI_IP6); + } + else + { + if (hc->routemap_export_zebra_name) + free (hc->routemap_export_zebra_name); + hc->routemap_export_zebra_name = strdup (argv[1]); + hc->routemap_export_zebra = route_map_lookup_by_name (argv[1]); + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + } + return CMD_SUCCESS; +} + + +/* + * respond to changes in the global prefix list configuration + */ +void +vnc_prefix_list_update (struct bgp *bgp) +{ + afi_t afi; + struct listnode *n; + struct rfapi_nve_group_cfg *rfg; + struct rfapi_cfg *hc; + int i; + + if (!bgp) + { + zlog_debug ("%s: No BGP process is configured", __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: rfapi not configured", __func__); + return; + } + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + { + /* + * Loop over nve groups + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->nve_groups_sequential, + n, rfg)) + { + + if (rfg->plist_export_bgp_name[afi]) + { + rfg->plist_export_bgp[afi] = + prefix_list_lookup (afi, rfg->plist_export_bgp_name[afi]); + } + if (rfg->plist_export_zebra_name[afi]) + { + rfg->plist_export_zebra[afi] = + prefix_list_lookup (afi, rfg->plist_export_zebra_name[afi]); + } + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) + { + if (rfg->plist_redist_name[i][afi]) + { + rfg->plist_redist[i][afi] = + prefix_list_lookup (afi, rfg->plist_redist_name[i][afi]); + } + } + + vnc_direct_bgp_reexport_group_afi (bgp, rfg, afi); + /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */ + } + + /* + * RH config, too + */ + if (hc->plist_export_bgp_name[afi]) + { + hc->plist_export_bgp[afi] = + prefix_list_lookup (afi, hc->plist_export_bgp_name[afi]); + } + if (hc->plist_export_zebra_name[afi]) + { + hc->plist_export_zebra[afi] = + prefix_list_lookup (afi, hc->plist_export_zebra_name[afi]); + } + + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) + { + if (hc->plist_redist_name[i][afi]) + { + hc->plist_redist[i][afi] = + prefix_list_lookup (afi, hc->plist_redist_name[i][afi]); + } + } + + } + + vnc_direct_bgp_reexport (bgp, AFI_IP); + vnc_direct_bgp_reexport (bgp, AFI_IP6); + + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + + vnc_redistribute_prechange (bgp); + vnc_redistribute_postchange (bgp); +} + +/* + * respond to changes in the global route map configuration + */ +void +vnc_routemap_update (struct bgp *bgp, const char *unused) +{ + struct listnode *n; + struct rfapi_nve_group_cfg *rfg; + struct rfapi_cfg *hc; + int i; + + zlog_debug ("%s(arg=%s)", __func__, unused); + + if (!bgp) + { + zlog_debug ("%s: No BGP process is configured", __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: rfapi not configured", __func__); + return; + } + + /* + * Loop over nve groups + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->nve_groups_sequential, n, rfg)) + { + + if (rfg->routemap_export_bgp_name) + { + rfg->routemap_export_bgp = + route_map_lookup_by_name (rfg->routemap_export_bgp_name); + } + if (rfg->routemap_export_zebra_name) + { + rfg->routemap_export_bgp = + route_map_lookup_by_name (rfg->routemap_export_zebra_name); + } + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) + { + if (rfg->routemap_redist_name[i]) + { + rfg->routemap_redist[i] = + route_map_lookup_by_name (rfg->routemap_redist_name[i]); + } + } + + vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP); + vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP6); + /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */ + } + + /* + * RH config, too + */ + if (hc->routemap_export_bgp_name) + { + hc->routemap_export_bgp = + route_map_lookup_by_name (hc->routemap_export_bgp_name); + } + if (hc->routemap_export_zebra_name) + { + hc->routemap_export_bgp = + route_map_lookup_by_name (hc->routemap_export_zebra_name); + } + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) + { + if (hc->routemap_redist_name[i]) + { + hc->routemap_redist[i] = + route_map_lookup_by_name (hc->routemap_redist_name[i]); + } + } + + vnc_direct_bgp_reexport (bgp, AFI_IP); + vnc_direct_bgp_reexport (bgp, AFI_IP6); + + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + + vnc_redistribute_prechange (bgp); + vnc_redistribute_postchange (bgp); + + zlog_debug ("%s done", __func__); +} + +static void +vnc_routemap_event (route_map_event_t type, /* ignored */ + const char *rmap_name) /* ignored */ +{ + struct listnode *mnode, *mnnode; + struct bgp *bgp; + + zlog_debug ("%s(event type=%d)", __func__, type); + if (bm->bgp == NULL) /* may be called during cleanup */ + return; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + vnc_routemap_update (bgp, rmap_name); + + zlog_debug ("%s: done", __func__); +} + +/*------------------------------------------------------------------------- + * nve-group + *-----------------------------------------------------------------------*/ + + +DEFUN (vnc_nve_group, + vnc_nve_group_cmd, + "vnc nve-group NAME", + VNC_CONFIG_STR "Configure a NVE group\n" "Group name\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Search for name */ + rfg = rfapi_group_lookup_byname (bgp, argv[0]); + + if (!rfg) + { + rfg = rfapi_group_new (); + if (!rfg) + { + /* Error out of memory */ + vty_out (vty, "Can't allocate memory for NVE group%s", VTY_NEWLINE); + return CMD_WARNING; + } + rfg->name = strdup (argv[0]); + /* add to tail of list */ + listnode_add (bgp->rfapi_cfg->nve_groups_sequential, rfg); + + /* Copy defaults from struct rfapi_cfg */ + rfg->rd = bgp->rfapi_cfg->default_rd; + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_L2RD) + { + rfg->l2rd = bgp->rfapi_cfg->default_l2rd; + rfg->flags |= RFAPI_RFG_L2RD; + } + rfg->rd = bgp->rfapi_cfg->default_rd; + rfg->response_lifetime = bgp->rfapi_cfg->default_response_lifetime; + + if (bgp->rfapi_cfg->default_rt_export_list) + { + rfg->rt_export_list = + ecommunity_dup (bgp->rfapi_cfg->default_rt_export_list); + } + + if (bgp->rfapi_cfg->default_rt_import_list) + { + rfg->rt_import_list = + ecommunity_dup (bgp->rfapi_cfg->default_rt_import_list); + rfg->rfapi_import_table = + rfapiImportTableRefAdd (bgp, rfg->rt_import_list); + } + + /* + * If a redist nve group was named but the group was not defined, + * make the linkage now + */ + if (!bgp->rfapi_cfg->rfg_redist) + { + if (bgp->rfapi_cfg->rfg_redist_name && + !strcmp (bgp->rfapi_cfg->rfg_redist_name, rfg->name)) + { + + vnc_redistribute_prechange (bgp); + bgp->rfapi_cfg->rfg_redist = rfg; + vnc_redistribute_postchange (bgp); + + } + } + + /* + * Same treatment for bgp-direct export group + */ + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + + if (!strcmp (rfgn->name, rfg->name)) + { + rfgn->rfg = rfg; + vnc_direct_bgp_add_group (bgp, rfg); + break; + } + } + + /* + * Same treatment for zebra export group + */ + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_zebra_l, + node, nnode, rfgn)) + { + + zlog_debug ("%s: ezport zebra: checking if \"%s\" == \"%s\"", + __func__, rfgn->name, rfg->name); + if (!strcmp (rfgn->name, rfg->name)) + { + rfgn->rfg = rfg; + vnc_zebra_add_group (bgp, rfg); + break; + } + } + } + + /* + * XXX subsequent calls will need to make sure this item is still + * in the linked list and has the same name + */ + vty->index_sub = rfg; + + vty->node = BGP_VNC_NVE_GROUP_NODE; + return CMD_SUCCESS; +} + +static void +bgp_rfapi_delete_nve_group ( + struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg) +{ + struct list *orphaned_nves = NULL; + struct listnode *node, *nnode; + + /* + * If there are currently-open NVEs that belong to this group, + * zero out their references to this group structure. + */ + if (rfg->nves) + { + struct rfapi_descriptor *rfd; + orphaned_nves = list_new (); + while ((rfd = listnode_head (rfg->nves))) + { + rfd->rfg = NULL; + listnode_delete (rfg->nves, rfd); + listnode_add (orphaned_nves, rfd); + } + list_delete (rfg->nves); + rfg->nves = NULL; + } + + /* delete it */ + free (rfg->name); + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); + if (rfg->rt_import_list) + ecommunity_free (&rfg->rt_import_list); + if (rfg->rt_export_list) + ecommunity_free (&rfg->rt_export_list); + + if (rfg->vn_node) + { + rfg->vn_node->info = NULL; + route_unlock_node (rfg->vn_node); /* frees */ + } + if (rfg->un_node) + { + rfg->un_node->info = NULL; + route_unlock_node (rfg->un_node); /* frees */ + } + if (rfg->rfp_cfg) + XFREE (MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg); + listnode_delete (bgp->rfapi_cfg->nve_groups_sequential, rfg); + + XFREE (MTYPE_RFAPI_GROUP_CFG, rfg); + + /* + * Attempt to reassign the orphaned nves to a new group. If + * a NVE can not be reassigned, its rfd->rfg will remain NULL + * and it will become a zombie until released by rfapi_close(). + */ + if (orphaned_nves) + { + struct rfapi_descriptor *rfd; + + for (ALL_LIST_ELEMENTS (orphaned_nves, node, nnode, rfd)) + { + /* + * 1. rfapi_close() equivalent except: + * a. don't free original descriptor + * b. remember query list + * c. remember advertised route list + * 2. rfapi_open() equivalent except: + * a. reuse original descriptor + * 3. rfapi_register() on remembered advertised route list + * 4. rfapi_query on rememebred query list + */ + + int rc; + + rc = rfapi_reopen (rfd, bgp); + + if (!rc) + { + list_delete_node (orphaned_nves, node); + if (vty) + vty_out (vty, "WARNING: reassigned NVE vn="); + rfapiPrintRfapiIpAddr (vty, &rfd->vn_addr); + if (vty) + vty_out (vty, " un="); + rfapiPrintRfapiIpAddr (vty, &rfd->un_addr); + if (vty) + vty_out (vty, " to new group \"%s\"%s", rfd->rfg->name, + VTY_NEWLINE); + + } + } + + for (ALL_LIST_ELEMENTS_RO (orphaned_nves, node, rfd)) + { + if (vty) + vty_out (vty, "WARNING: orphaned NVE vn="); + rfapiPrintRfapiIpAddr (vty, &rfd->vn_addr); + if (vty) + vty_out (vty, " un="); + rfapiPrintRfapiIpAddr (vty, &rfd->un_addr); + if (vty) + vty_out (vty, "%s", VTY_NEWLINE); + } + list_delete (orphaned_nves); + } +} + +static int +bgp_rfapi_delete_named_nve_group ( + struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + const char *rfg_name) /* NULL = any */ +{ + struct rfapi_nve_group_cfg *rfg = NULL; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + + /* Search for name */ + if (rfg_name) + { + rfg = rfapi_group_lookup_byname (bgp, rfg_name); + if (!rfg) + { + if (vty) + vty_out (vty, "No NVE group named \"%s\"%s", rfg_name, + VTY_NEWLINE); + return CMD_WARNING; + } + } + + /* + * If this group is the redist nve group, unlink it + */ + if (rfg_name == NULL || bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + bgp->rfapi_cfg->rfg_redist = NULL; + vnc_redistribute_postchange (bgp); + } + + + /* + * remove reference from bgp direct export list + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + if (rfg_name == NULL || !strcmp (rfgn->name, rfg_name)) + { + rfgn->rfg = NULL; + /* remove exported routes from this group */ + vnc_direct_bgp_del_group (bgp, rfg); + break; + } + } + + /* + * remove reference from zebra export list + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + if (rfg_name == NULL || !strcmp (rfgn->name, rfg_name)) + { + rfgn->rfg = NULL; + /* remove exported routes from this group */ + vnc_zebra_del_group (bgp, rfg); + break; + } + } + if (rfg) + bgp_rfapi_delete_nve_group (vty, bgp, rfg); + else /* must be delete all */ + for (ALL_LIST_ELEMENTS + (bgp->rfapi_cfg->nve_groups_sequential, node, nnode, rfg)) + bgp_rfapi_delete_nve_group (vty, bgp, rfg); + return CMD_SUCCESS; +} + +DEFUN (vnc_no_nve_group, + vnc_no_nve_group_cmd, + "no vnc nve-group NAME", + NO_STR + VNC_CONFIG_STR + "Configure a NVE group\n" + "Group name\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_rfapi_delete_named_nve_group (vty, bgp, argv[0]); +} + +DEFUN (vnc_nve_group_prefix, + vnc_nve_group_prefix_cmd, + "prefix (vn|un) (A.B.C.D/M|X:X::X:X/M)", + "Specify prefixes matching NVE VN or UN interfaces\n" + "VN prefix\n" + "UN prefix\n" + "IPv4 prefix\n" + "IPv6 prefix\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct prefix p; + int afi; + struct route_table *rt; + struct route_node *rn; + int is_un_prefix = 0; + + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!str2prefix (argv[1], &p)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + afi = family2afi (p.family); + if (!afi) + { + vty_out (vty, "Unsupported address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*(argv[0]) == 'u') + { + rt = &(bgp->rfapi_cfg->nve_groups_un[afi]); + is_un_prefix = 1; + } + else + { + rt = &(bgp->rfapi_cfg->nve_groups_vn[afi]); + } + + rn = route_node_get (rt, &p); /* NB locks node */ + if (rn->info) + { + /* + * There is already a group with this prefix + */ + route_unlock_node (rn); + if (rn->info != rfg) + { + /* + * different group name: fail + */ + vty_out (vty, "nve group \"%s\" already has \"%s\" prefix %s%s", + ((struct rfapi_nve_group_cfg *) (rn->info))->name, + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + else + { + /* + * same group name: it's already in the correct place + * in the table, so we're done. + * + * Implies rfg->(vn|un)_prefix is already correct. + */ + return CMD_SUCCESS; + } + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + /* New prefix, new node */ + + if (is_un_prefix) + { + + /* detach rfg from previous route table location */ + if (rfg->un_node) + { + rfg->un_node->info = NULL; + route_unlock_node (rfg->un_node); /* frees */ + } + rfg->un_node = rn; /* back ref */ + rfg->un_prefix = p; + + } + else + { + + /* detach rfg from previous route table location */ + if (rfg->vn_node) + { + rfg->vn_node->info = NULL; + route_unlock_node (rfg->vn_node); /* frees */ + } + rfg->vn_node = rn; /* back ref */ + rfg->vn_prefix = p; + } + + /* attach */ + rn->info = rfg; + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_rt_import, + vnc_nve_group_rt_import_cmd, + "rt import .RTLIST", + "Specify route targets\n" + "Import filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + int rc; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + int is_export_bgp = 0; + int is_export_zebra = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rc = set_ecom_list (vty, argc, argv, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group (bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) + vnc_zebra_del_group (bgp, rfg); + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list); + + if (is_export_bgp) + vnc_direct_bgp_add_group (bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group (bgp, rfg); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_rt_export, + vnc_nve_group_rt_export_cmd, + "rt export .RTLIST", + "Specify route targets\n" + "Export filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + int rc; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rc = set_ecom_list (vty, argc, argv, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + + return rc; +} + +DEFUN (vnc_nve_group_rt_both, + vnc_nve_group_rt_both_cmd, + "rt both .RTLIST", + "Specify route targets\n" + "Export+import filters\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + int rc; + int is_export_bgp = 0; + int is_export_zebra = 0; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rc = set_ecom_list (vty, argc, argv, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group (bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) + { + zlog_debug ("%s: is_export_zebra", __func__); + vnc_zebra_del_group (bgp, rfg); + } + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list); + + if (is_export_bgp) + vnc_direct_bgp_add_group (bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group (bgp, rfg); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rc = set_ecom_list (vty, argc, argv, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + + return rc; + +} + +DEFUN (vnc_nve_group_l2rd, + vnc_nve_group_l2rd_cmd, + "l2rd (ID|auto:vn)", + "Specify default Local Nve ID value to use in RD for L2 routes\n" + "Fixed value 1-255\n" + "use the low-order octet of the NVE's VN address\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "auto:vn")) + { + rfg->l2rd = 0; + } + else + { + char *end = NULL; + unsigned long value_l = strtoul (argv[0], &end, 10); + uint8_t value = value_l & 0xff; + + if (!*(argv[0]) || *end) + { + vty_out (vty, "%% Malformed l2 nve ID \"%s\"%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + if ((value_l < 1) || (value_l > 0xff)) + { + vty_out (vty, + "%% Malformed l2 nve id (must be greater than 0 and less than %u%s", + 0x100, VTY_NEWLINE); + return CMD_WARNING; + } + + rfg->l2rd = value; + } + rfg->flags |= RFAPI_RFG_L2RD; + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_no_l2rd, + vnc_nve_group_no_l2rd_cmd, + "no l2rd", + NO_STR + "Specify default Local Nve ID value to use in RD for L2 routes\n") +{ + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rfg->l2rd = 0; + rfg->flags &= ~RFAPI_RFG_L2RD; + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_rd, + vnc_nve_group_rd_cmd, + "rd ASN:nn_or_IP-address:nn", + "Specify route distinguisher\n" + "Route Distinguisher (: | : | auto:vn: )\n") +{ + int ret; + struct prefix_rd prd; + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strncmp (argv[0], "auto:vn:", 8)) + { + /* + * use AF_UNIX to designate automatically-assigned RD + * auto:vn:nn where nn is a 2-octet quantity + */ + char *end = NULL; + uint32_t value32 = strtoul (argv[0] + 8, &end, 10); + uint16_t value = value32 & 0xffff; + + if (!*(argv[0] + 5) || *end) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (value32 > 0xffff) + { + vty_out (vty, "%% Malformed rd (must be less than %u%s", + 0x0ffff, VTY_NEWLINE); + return CMD_WARNING; + } + + memset (&prd, 0, sizeof (prd)); + prd.family = AF_UNIX; + prd.prefixlen = 64; + prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff; + prd.val[1] = RD_TYPE_IP & 0x0ff; + prd.val[6] = (value >> 8) & 0x0ff; + prd.val[7] = value & 0x0ff; + + } + else + { + + ret = str2prefix_rd (argv[0], &prd); + if (!ret) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rfg->rd = prd; + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_responselifetime, + vnc_nve_group_responselifetime_cmd, + "response-lifetime (LIFETIME|infinite)", + "Specify response lifetime\n" + "Response lifetime in seconds\n" "Infinite response lifetime\n") +{ + unsigned int rspint; + struct rfapi_nve_group_cfg *rfg; + struct bgp *bgp = vty->index; + struct rfapi_descriptor *rfd; + struct listnode *hdnode; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "infinite")) + { + rspint = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Response Lifetime", rspint, argv[0]); + } + + rfg->response_lifetime = rspint; + rfg->flags |= RFAPI_RFG_RESPONSE_LIFETIME; + if (rfg->nves) + for (ALL_LIST_ELEMENTS_RO (rfg->nves, hdnode, rfd)) + rfd->response_lifetime = rspint; + return CMD_SUCCESS; +} + +/* + * Sigh. This command, like exit-address-family, is a hack to deal + * with the lack of rigorous level control in the command handler. + * TBD fix command handler. + */ +DEFUN (exit_vnc, + exit_vnc_cmd, + "exit-vnc", + "Exit VNC configuration mode\n") +{ + if (vty->node == BGP_VNC_DEFAULTS_NODE || + vty->node == BGP_VNC_NVE_GROUP_NODE || + vty->node == BGP_VNC_L2_GROUP_NODE) + { + + vty->node = BGP_NODE; + } + return CMD_SUCCESS; +} + +static struct cmd_node bgp_vnc_defaults_node = { + BGP_VNC_DEFAULTS_NODE, + "%s(config-router-vnc-defaults)# ", + 1 +}; + +static struct cmd_node bgp_vnc_nve_group_node = { + BGP_VNC_NVE_GROUP_NODE, + "%s(config-router-vnc-nve-group)# ", + 1 +}; + +/*------------------------------------------------------------------------- + * vnc-l2-group + *-----------------------------------------------------------------------*/ + + +DEFUN (vnc_l2_group, + vnc_l2_group_cmd, + "vnc l2-group NAME", + VNC_CONFIG_STR "Configure a L2 group\n" "Group name\n") +{ + struct rfapi_l2_group_cfg *rfg; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Search for name */ + rfg = rfapi_l2_group_lookup_byname (bgp, argv[0]); + + if (!rfg) + { + rfg = rfapi_l2_group_new (); + if (!rfg) + { + /* Error out of memory */ + vty_out (vty, "Can't allocate memory for L2 group%s", VTY_NEWLINE); + return CMD_WARNING; + } + rfg->name = strdup (argv[0]); + /* add to tail of list */ + listnode_add (bgp->rfapi_cfg->l2_groups, rfg); + } + + /* + * XXX subsequent calls will need to make sure this item is still + * in the linked list and has the same name + */ + vty->index_sub = rfg; + + vty->node = BGP_VNC_L2_GROUP_NODE; + return CMD_SUCCESS; +} + +static void +bgp_rfapi_delete_l2_group ( + struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + struct rfapi_l2_group_cfg *rfg) +{ + /* delete it */ + free (rfg->name); + if (rfg->rt_import_list) + ecommunity_free (&rfg->rt_import_list); + if (rfg->rt_export_list) + ecommunity_free (&rfg->rt_export_list); + if (rfg->labels) + list_delete (rfg->labels); + if (rfg->rfp_cfg) + XFREE (MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg); + listnode_delete (bgp->rfapi_cfg->l2_groups, rfg); + + rfapi_l2_group_del (rfg); +} + +static int +bgp_rfapi_delete_named_l2_group ( + struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + const char *rfg_name) /* NULL = any */ +{ + struct rfapi_l2_group_cfg *rfg = NULL; + struct listnode *node, *nnode; + + /* Search for name */ + if (rfg_name) + { + rfg = rfapi_l2_group_lookup_byname (bgp, rfg_name); + if (!rfg) + { + if (vty) + vty_out (vty, "No L2 group named \"%s\"%s", rfg_name, + VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (rfg) + bgp_rfapi_delete_l2_group (vty, bgp, rfg); + else /* must be delete all */ + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->l2_groups, node, nnode, rfg)) + bgp_rfapi_delete_l2_group (vty, bgp, rfg); + return CMD_SUCCESS; +} + +DEFUN (vnc_no_l2_group, + vnc_no_l2_group_cmd, + "no vnc l2-group NAME", + NO_STR + VNC_CONFIG_STR + "Configure a L2 group\n" + "Group name\n") +{ + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_rfapi_delete_named_l2_group (vty, bgp, argv[0]); +} + + +DEFUN (vnc_l2_group_lni, + vnc_l2_group_lni_cmd, + "logical-network-id <0-4294967295>", + "Specify Logical Network ID associated with group\n" + "value\n") +{ + struct rfapi_l2_group_cfg *rfg; + struct bgp *bgp = vty->index; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("logical-network-id", rfg->logical_net_id, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (vnc_l2_group_labels, + vnc_l2_group_labels_cmd, + "labels .LABELLIST", + "Specify label values associated with group\n" + "Space separated list of label values <0-1048575>\n") +{ + struct rfapi_l2_group_cfg *rfg; + struct bgp *bgp = vty->index; + struct list *ll; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ll = rfg->labels; + if (ll == NULL) + { + ll = list_new (); + rfg->labels = ll; + } + for (; argc; --argc, ++argv) + { + uint32_t label; + VTY_GET_INTEGER_RANGE ("Label value", label, argv[0], 0, 1048575); + if (!listnode_lookup (ll, (void *) (uintptr_t) label)) + listnode_add (ll, (void *) (uintptr_t) label); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_l2_group_no_labels, + vnc_l2_group_no_labels_cmd, + "no labels .LABELLIST", + NO_STR + "Remove label values associated with L2 group\n" + "Specify label values associated with L2 group\n" + "Space separated list of label values <0-1048575>\n") +{ + struct rfapi_l2_group_cfg *rfg; + struct bgp *bgp = vty->index; + struct list *ll; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ll = rfg->labels; + if (ll == NULL) + { + vty_out (vty, "Label no longer associated with group%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (; argc; --argc, ++argv) + { + uint32_t label; + VTY_GET_INTEGER_RANGE ("Label value", label, argv[0], 0, 1048575); + listnode_delete (ll, (void *) (uintptr_t) label); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_l2_group_rt, + vnc_l2_group_rt_cmd, + "rt (both|export|import) ASN:nn_or_IP-address:nn", + "Specify route targets\n" + "Export+import filters\n" + "Export filters\n" + "Import filters\n" + "A route target\n") +{ + struct rfapi_l2_group_cfg *rfg; + struct bgp *bgp = vty->index; + int rc = CMD_SUCCESS; + int do_import = 0; + int do_export = 0; + + switch (argv[0][0]) + { + case 'b': + do_export = 1; /* fall through */ + case 'i': + do_import = 1; + break; + case 'e': + do_export = 1; + break; + default: + vty_out (vty, "Unknown option, %s%s", argv[0], VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + argc--; + argv++; + if (argc < 1) + return CMD_ERR_INCOMPLETE; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* get saved pointer */ + rfg = vty->index_sub; + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (do_import) + rc = set_ecom_list (vty, argc, argv, &rfg->rt_import_list); + if (rc == CMD_SUCCESS && do_export) + rc = set_ecom_list (vty, argc, argv, &rfg->rt_export_list); + return rc; +} + + +static struct cmd_node bgp_vnc_l2_group_node = { + BGP_VNC_L2_GROUP_NODE, + "%s(config-router-vnc-l2-group)# ", + 1 +}; + +static struct rfapi_l2_group_cfg * +bgp_rfapi_get_group_by_lni_label ( + struct bgp *bgp, + uint32_t logical_net_id, + uint32_t label) +{ + struct rfapi_l2_group_cfg *rfg; + struct listnode *node; + + if (bgp->rfapi_cfg->l2_groups == NULL) /* not the best place for this */ + return NULL; + + label = label & 0xfffff; /* label is 20 bits! */ + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->l2_groups, node, rfg)) + { + if (rfg->logical_net_id == logical_net_id) + { + struct listnode *lnode; + void *data; + for (ALL_LIST_ELEMENTS_RO (rfg->labels, lnode, data)) + if (((uint32_t) ((uintptr_t) data)) == label) + { /* match! */ + return rfg; + } + } + } + return NULL; +} + +struct list * +bgp_rfapi_get_labellist_by_lni_label ( + struct bgp *bgp, + uint32_t logical_net_id, + uint32_t label) +{ + struct rfapi_l2_group_cfg *rfg; + rfg = bgp_rfapi_get_group_by_lni_label (bgp, logical_net_id, label); + if (rfg) + { + return rfg->labels; + } + return NULL; +} + +struct ecommunity * +bgp_rfapi_get_ecommunity_by_lni_label ( + struct bgp *bgp, + uint32_t is_import, + uint32_t logical_net_id, + uint32_t label) +{ + struct rfapi_l2_group_cfg *rfg; + rfg = bgp_rfapi_get_group_by_lni_label (bgp, logical_net_id, label); + if (rfg) + { + if (is_import) + return rfg->rt_import_list; + else + return rfg->rt_export_list; + } + return NULL; +} + +void +bgp_rfapi_cfg_init (void) +{ + /* main bgpd code does not use this hook, but vnc does */ + route_map_event_hook (vnc_routemap_event); + + install_node (&bgp_vnc_defaults_node, NULL); + install_node (&bgp_vnc_nve_group_node, NULL); + install_node (&bgp_vnc_l2_group_node, NULL); + install_default (BGP_VNC_DEFAULTS_NODE); + install_default (BGP_VNC_NVE_GROUP_NODE); + install_default (BGP_VNC_L2_GROUP_NODE); + + /* + * Add commands + */ + install_element (BGP_NODE, &vnc_defaults_cmd); + install_element (BGP_NODE, &vnc_nve_group_cmd); + install_element (BGP_NODE, &vnc_no_nve_group_cmd); + install_element (BGP_NODE, &vnc_l2_group_cmd); + install_element (BGP_NODE, &vnc_no_l2_group_cmd); + install_element (BGP_NODE, &vnc_advertise_un_method_cmd); + install_element (BGP_NODE, &vnc_export_mode_cmd); + + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_import_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_export_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_both_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rd_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_l2rd_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_no_l2rd_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_responselifetime_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_cmd); + + install_element (BGP_NODE, &vnc_redistribute_protocol_cmd); + install_element (BGP_NODE, &vnc_no_redistribute_protocol_cmd); + install_element (BGP_NODE, &vnc_redistribute_nvegroup_cmd); + install_element (BGP_NODE, &vnc_redistribute_no_nvegroup_cmd); + install_element (BGP_NODE, &vnc_redistribute_lifetime_cmd); + install_element (BGP_NODE, &vnc_redistribute_rh_roo_localadmin_cmd); + install_element (BGP_NODE, &vnc_redistribute_mode_cmd); + install_element (BGP_NODE, &vnc_redistribute_bgp_exterior_cmd); + + install_element (BGP_NODE, &vnc_redist_bgpdirect_no_prefixlist_cmd); + install_element (BGP_NODE, &vnc_redist_bgpdirect_prefixlist_cmd); + install_element (BGP_NODE, &vnc_redist_bgpdirect_no_routemap_cmd); + install_element (BGP_NODE, &vnc_redist_bgpdirect_routemap_cmd); + + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_prefixlist_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_no_routemap_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_routemap_cmd); + + install_element (BGP_NODE, &vnc_export_nvegroup_cmd); + install_element (BGP_NODE, &vnc_no_export_nvegroup_cmd); + install_element (BGP_NODE, &vnc_nve_export_prefixlist_cmd); + install_element (BGP_NODE, &vnc_nve_export_routemap_cmd); + install_element (BGP_NODE, &vnc_nve_export_no_prefixlist_cmd); + install_element (BGP_NODE, &vnc_nve_export_no_routemap_cmd); + + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_l2rd_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_no_l2rd_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_prefix_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_import_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_export_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_both_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rd_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_responselifetime_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_prefixlist_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_routemap_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_no_prefixlist_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_no_routemap_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_cmd); + + install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_lni_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_labels_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_no_labels_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_rt_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &exit_vnc_cmd); +} + +struct rfapi_cfg * +bgp_rfapi_cfg_new (struct rfapi_rfp_cfg *cfg) +{ + struct rfapi_cfg *h; + int afi; + + h = + (struct rfapi_cfg *) XCALLOC (MTYPE_RFAPI_CFG, sizeof (struct rfapi_cfg)); + assert (h); + + h->nve_groups_sequential = list_new (); + assert (h->nve_groups_sequential); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + { + /* ugly, to deal with addition of delegates, part of 0.99.24.1 merge */ + h->nve_groups_vn[afi].delegate = route_table_get_default_delegate (); + h->nve_groups_un[afi].delegate = route_table_get_default_delegate (); + } + h->default_response_lifetime = BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT; + h->rfg_export_direct_bgp_l = list_new (); + h->rfg_export_zebra_l = list_new (); + h->resolve_nve_roo_local_admin = + BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT; + + SET_FLAG (h->flags, BGP_VNC_CONFIG_FLAGS_DEFAULT); + + if (cfg == NULL) + { + h->rfp_cfg.download_type = RFAPI_RFP_DOWNLOAD_PARTIAL; + h->rfp_cfg.ftd_advertisement_interval = + RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL; + h->rfp_cfg.holddown_factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR; + h->rfp_cfg.use_updated_response = 0; + h->rfp_cfg.use_removes = 0; + } + else + { + h->rfp_cfg.download_type = cfg->download_type; + h->rfp_cfg.ftd_advertisement_interval = cfg->ftd_advertisement_interval; + h->rfp_cfg.holddown_factor = cfg->holddown_factor; + h->rfp_cfg.use_updated_response = cfg->use_updated_response; + h->rfp_cfg.use_removes = cfg->use_removes; + if (cfg->use_updated_response) + h->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE; + else + h->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE; + if (cfg->use_removes) + h->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; + else + h->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; + } + return h; +} + +void +bgp_rfapi_cfg_destroy (struct bgp *bgp, struct rfapi_cfg *h) +{ + if (h == NULL) + return; + + bgp_rfapi_delete_named_nve_group (NULL, bgp, NULL); + bgp_rfapi_delete_named_l2_group (NULL, bgp, NULL); + if (h->l2_groups != NULL) + list_delete (h->l2_groups); + list_delete (h->nve_groups_sequential); + list_delete (h->rfg_export_direct_bgp_l); + list_delete (h->rfg_export_zebra_l); + if (h->default_rt_export_list) + ecommunity_free (&h->default_rt_export_list); + if (h->default_rt_import_list) + ecommunity_free (&h->default_rt_import_list); + if (h->default_rfp_cfg) + XFREE (MTYPE_RFAPI_RFP_GROUP_CFG, h->default_rfp_cfg); + XFREE (MTYPE_RFAPI_CFG, h); + +} + +int +bgp_rfapi_cfg_write (struct vty *vty, struct bgp *bgp) +{ + struct listnode *node, *nnode; + struct rfapi_nve_group_cfg *rfg; + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct rfapi_rfg_name *rfgn; + int write = 0; + afi_t afi; + int type; + + if (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) + { + vty_out (vty, " vnc advertise-un-method encap-safi%s", VTY_NEWLINE); + write++; + } + + { /* was based on listen ports */ + /* for now allow both old and new */ + if (bgp->rfapi->rfp_methods.cfg_cb) + write += (bgp->rfapi->rfp_methods.cfg_cb) (vty, bgp->rfapi->rfp); + + if (write) + vty_out (vty, "!%s", VTY_NEWLINE); + + if (hc->l2_groups) + { + struct rfapi_l2_group_cfg *rfg = NULL; + struct listnode *gnode; + for (ALL_LIST_ELEMENTS_RO (hc->l2_groups, gnode, rfg)) + { + struct listnode *lnode; + void *data; + ++write; + vty_out (vty, " vnc l2-group %s%s", rfg->name, VTY_NEWLINE); + if (rfg->logical_net_id != 0) + vty_out (vty, " logical-network-id %u%s", rfg->logical_net_id, + VTY_NEWLINE); + if (rfg->labels != NULL && listhead (rfg->labels) != NULL) + { + vty_out (vty, " labels "); + for (ALL_LIST_ELEMENTS_RO (rfg->labels, lnode, data)) + { + vty_out (vty, "%hu ", (uint16_t) ((uintptr_t) data)); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (rfg->rt_import_list && rfg->rt_export_list && + ecommunity_cmp (rfg->rt_import_list, rfg->rt_export_list)) + { + char *b = ecommunity_ecom2str (rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt both %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + else + { + if (rfg->rt_import_list) + { + char *b = ecommunity_ecom2str (rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt import %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + if (rfg->rt_export_list) + { + char *b = ecommunity_ecom2str (rfg->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt export %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + } + if (bgp->rfapi->rfp_methods.cfg_group_cb) + write += + (bgp->rfapi->rfp_methods.cfg_group_cb) (vty, + bgp->rfapi->rfp, + RFAPI_RFP_CFG_GROUP_L2, + rfg->name, + rfg->rfp_cfg); + vty_out (vty, " exit-vnc%s", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + } + } + + if (hc->default_rd.family || + hc->default_response_lifetime || + hc->default_rt_import_list || + hc->default_rt_export_list || hc->nve_groups_sequential->count) + { + + + ++write; + vty_out (vty, " vnc defaults%s", VTY_NEWLINE); + + if (hc->default_rd.prefixlen) + { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + + if (AF_UNIX == hc->default_rd.family) + { + uint16_t value = 0; + + value = ((hc->default_rd.val[6] << 8) & 0x0ff00) | + (hc->default_rd.val[7] & 0x0ff); + + vty_out (vty, " rd auto:vn:%d%s", value, VTY_NEWLINE); + + } + else + { + + if (!prefix_rd2str (&hc->default_rd, buf, BUFSIZ) || + !buf[0] || buf[BUFSIZ - 1]) + { + + vty_out (vty, "!Error: Can't convert rd%s", VTY_NEWLINE); + } + else + { + vty_out (vty, " rd %s%s", buf, VTY_NEWLINE); + } + } + } + if (hc->default_response_lifetime) + { + vty_out (vty, " response-lifetime "); + if (hc->default_response_lifetime != UINT32_MAX) + vty_out (vty, "%d", hc->default_response_lifetime); + else + vty_out (vty, "infinite"); + vty_out (vty, "%s", VTY_NEWLINE); + } + if (hc->default_rt_import_list && hc->default_rt_export_list && + ecommunity_cmp (hc->default_rt_import_list, + hc->default_rt_export_list)) + { + char *b = ecommunity_ecom2str (hc->default_rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt both %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + else + { + if (hc->default_rt_import_list) + { + char *b = ecommunity_ecom2str (hc->default_rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt import %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + if (hc->default_rt_export_list) + { + char *b = ecommunity_ecom2str (hc->default_rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt export %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + } + if (bgp->rfapi->rfp_methods.cfg_group_cb) + write += + (bgp->rfapi->rfp_methods.cfg_group_cb) (vty, + bgp->rfapi->rfp, + RFAPI_RFP_CFG_GROUP_DEFAULT, + NULL, + bgp->rfapi_cfg->default_rfp_cfg); + vty_out (vty, " exit-vnc%s", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + } + + for (ALL_LIST_ELEMENTS (hc->nve_groups_sequential, node, nnode, rfg)) + { + ++write; + vty_out (vty, " vnc nve-group %s%s", rfg->name, VTY_NEWLINE); + + if (rfg->vn_prefix.family && rfg->vn_node) + { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + + prefix2str (&rfg->vn_prefix, buf, BUFSIZ); + if (!buf[0] || buf[BUFSIZ - 1]) + { + vty_out (vty, "!Error: Can't convert prefix%s", VTY_NEWLINE); + } + else + { + vty_out (vty, " prefix %s %s%s", "vn", buf, VTY_NEWLINE); + } + } + + if (rfg->un_prefix.family && rfg->un_node) + { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + prefix2str (&rfg->un_prefix, buf, BUFSIZ); + if (!buf[0] || buf[BUFSIZ - 1]) + { + vty_out (vty, "!Error: Can't convert prefix%s", VTY_NEWLINE); + } + else + { + vty_out (vty, " prefix %s %s%s", "un", buf, VTY_NEWLINE); + } + } + + + if (rfg->rd.prefixlen) + { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + + if (AF_UNIX == rfg->rd.family) + { + + uint16_t value = 0; + + value = ((rfg->rd.val[6] << 8) & 0x0ff00) | + (rfg->rd.val[7] & 0x0ff); + + vty_out (vty, " rd auto:vn:%d%s", value, VTY_NEWLINE); + + } + else + { + + if (!prefix_rd2str (&rfg->rd, buf, BUFSIZ) || + !buf[0] || buf[BUFSIZ - 1]) + { + + vty_out (vty, "!Error: Can't convert rd%s", VTY_NEWLINE); + } + else + { + vty_out (vty, " rd %s%s", buf, VTY_NEWLINE); + } + } + } + if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) + { + vty_out (vty, " response-lifetime "); + if (rfg->response_lifetime != UINT32_MAX) + vty_out (vty, "%d", rfg->response_lifetime); + else + vty_out (vty, "infinite"); + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (rfg->rt_import_list && rfg->rt_export_list && + ecommunity_cmp (rfg->rt_import_list, rfg->rt_export_list)) + { + char *b = ecommunity_ecom2str (rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt both %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + else + { + if (rfg->rt_import_list) + { + char *b = ecommunity_ecom2str (rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt import %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + if (rfg->rt_export_list) + { + char *b = ecommunity_ecom2str (rfg->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt export %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + } + + /* + * route filtering: prefix-lists and route-maps + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6"; + + if (rfg->plist_export_bgp_name[afi]) + { + vty_out (vty, " export bgp %s prefix-list %s%s", + afistr, rfg->plist_export_bgp_name[afi], + VTY_NEWLINE); + } + if (rfg->plist_export_zebra_name[afi]) + { + vty_out (vty, " export zebra %s prefix-list %s%s", + afistr, rfg->plist_export_zebra_name[afi], + VTY_NEWLINE); + } + /* + * currently we only support redist plists for bgp-direct. + * If we later add plist support for redistributing other + * protocols, we'll need to loop over protocols here + */ + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + { + vty_out (vty, " redistribute bgp-direct %s prefix-list %s%s", + afistr, + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi], + VTY_NEWLINE); + } + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT][afi]) + { + vty_out (vty, + " redistribute bgp-direct-to-nve-groups %s prefix-list %s%s", + afistr, + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT] + [afi], VTY_NEWLINE); + } + } + + if (rfg->routemap_export_bgp_name) + { + vty_out (vty, " export bgp route-map %s%s", + rfg->routemap_export_bgp_name, VTY_NEWLINE); + } + if (rfg->routemap_export_zebra_name) + { + vty_out (vty, " export zebra route-map %s%s", + rfg->routemap_export_zebra_name, VTY_NEWLINE); + } + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + { + vty_out (vty, " redistribute bgp-direct route-map %s%s", + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT], + VTY_NEWLINE); + } + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + vty_out (vty, + " redistribute bgp-direct-to-nve-groups route-map %s%s", + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT], + VTY_NEWLINE); + } + if (bgp->rfapi->rfp_methods.cfg_group_cb) + write += + (bgp->rfapi->rfp_methods.cfg_group_cb) (vty, + bgp->rfapi->rfp, + RFAPI_RFP_CFG_GROUP_NVE, + rfg->name, rfg->rfp_cfg); + vty_out (vty, " exit-vnc%s", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + } + } /* have listen ports */ + + /* + * route export to other protocols + */ + if (VNC_EXPORT_BGP_GRP_ENABLED (hc)) + { + vty_out (vty, " vnc export bgp mode group-nve%s", VTY_NEWLINE); + } + else if (VNC_EXPORT_BGP_RH_ENABLED (hc)) + { + vty_out (vty, " vnc export bgp mode registering-nve%s", VTY_NEWLINE); + } + else if (VNC_EXPORT_BGP_CE_ENABLED (hc)) + { + vty_out (vty, " vnc export bgp mode ce%s", VTY_NEWLINE); + } + + if (VNC_EXPORT_ZEBRA_GRP_ENABLED (hc)) + { + vty_out (vty, " vnc export zebra mode group-nve%s", VTY_NEWLINE); + } + else if (VNC_EXPORT_ZEBRA_RH_ENABLED (hc)) + { + vty_out (vty, " vnc export zebra mode registering-nve%s", VTY_NEWLINE); + } + + if (hc->rfg_export_direct_bgp_l) + { + for (ALL_LIST_ELEMENTS (hc->rfg_export_direct_bgp_l, node, nnode, rfgn)) + { + + vty_out (vty, " vnc export bgp group-nve group %s%s", + rfgn->name, VTY_NEWLINE); + } + } + + if (hc->rfg_export_zebra_l) + { + for (ALL_LIST_ELEMENTS (hc->rfg_export_zebra_l, node, nnode, rfgn)) + { + + vty_out (vty, " vnc export zebra group-nve group %s%s", + rfgn->name, VTY_NEWLINE); + } + } + + + if (hc->rfg_redist_name) + { + vty_out (vty, " vnc redistribute nve-group %s%s", + hc->rfg_redist_name, VTY_NEWLINE); + } + if (hc->redist_lifetime) + { + vty_out (vty, " vnc redistribute lifetime %d%s", + hc->redist_lifetime, VTY_NEWLINE); + } + if (hc->resolve_nve_roo_local_admin != + BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT) + { + + vty_out (vty, " vnc redistribute resolve-nve roo-ec-local-admin %d%s", + hc->resolve_nve_roo_local_admin, VTY_NEWLINE); + } + + if (hc->redist_mode) /* ! default */ + { + const char *s = ""; + + switch (hc->redist_mode) + { + case VNC_REDIST_MODE_PLAIN: + s = "plain"; + break; + case VNC_REDIST_MODE_RFG: + s = "nve-group"; + break; + case VNC_REDIST_MODE_RESOLVE_NVE: + s = "resolve-nve"; + break; + } + if (s) + { + vty_out (vty, " vnc redistribute mode %s%s", s, VTY_NEWLINE); + } + } + + /* + * route filtering: prefix-lists and route-maps + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6"; + + if (hc->plist_export_bgp_name[afi]) + { + vty_out (vty, " vnc export bgp %s prefix-list %s%s", + afistr, hc->plist_export_bgp_name[afi], VTY_NEWLINE); + } + if (hc->plist_export_zebra_name[afi]) + { + vty_out (vty, " vnc export zebra %s prefix-list %s%s", + afistr, hc->plist_export_zebra_name[afi], VTY_NEWLINE); + } + if (hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + { + vty_out (vty, " vnc redistribute bgp-direct %s prefix-list %s%s", + afistr, hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi], + VTY_NEWLINE); + } + } + + if (hc->routemap_export_bgp_name) + { + vty_out (vty, " vnc export bgp route-map %s%s", + hc->routemap_export_bgp_name, VTY_NEWLINE); + } + if (hc->routemap_export_zebra_name) + { + vty_out (vty, " vnc export zebra route-map %s%s", + hc->routemap_export_zebra_name, VTY_NEWLINE); + } + if (hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + { + vty_out (vty, " vnc redistribute bgp-direct route-map %s%s", + hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT], VTY_NEWLINE); + } + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) + { + if (hc->redist[afi][type]) + { + if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT && + hc->redist_bgp_exterior_view_name) + { + vty_out (vty, " vnc redistribute %s %s view %s%s", + ((afi == AFI_IP) ? "ipv4" : "ipv6"), + zebra_route_string (type), + hc->redist_bgp_exterior_view_name, VTY_NEWLINE); + } + else + { + vty_out (vty, " vnc redistribute %s %s%s", + ((afi == AFI_IP) ? "ipv4" : "ipv6"), + zebra_route_string (type), VTY_NEWLINE); + } + } + } + } + return write; +} + +void +bgp_rfapi_show_summary (struct bgp *bgp, struct vty *vty) +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + int afi, type, redist = 0; + char tmp[40]; + if (hc == NULL) + return; + + vty_out (vty, "%-39s %-19s %s%s", "VNC Advertise method:", + (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP + ? "Encapsulation SAFI" : "Tunnel Encap attribute"), + ((hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) == + (BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP & + BGP_VNC_CONFIG_FLAGS_DEFAULT) ? "(default)" : ""), VTY_NEWLINE); + /* export */ + vty_out (vty, "%-39s ", "Export from VNC:"); + /* + * route export to other protocols + */ + if (VNC_EXPORT_BGP_GRP_ENABLED (hc)) + { + redist++; + vty_out (vty, "ToBGP Groups={"); + if (hc->rfg_export_direct_bgp_l) + { + int cnt = 0; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + for (ALL_LIST_ELEMENTS (hc->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + if (cnt++ != 0) + vty_out (vty, ","); + + vty_out (vty, "%s", rfgn->name); + } + } + vty_out (vty, "}"); + } + else if (VNC_EXPORT_BGP_RH_ENABLED (hc)) + { + redist++; + vty_out (vty, "ToBGP {Registering NVE}"); + /* note filters, route-maps not shown */ + } + else if (VNC_EXPORT_BGP_CE_ENABLED (hc)) + { + redist++; + vty_out (vty, "ToBGP {NVE connected router:%d}", + hc->resolve_nve_roo_local_admin); + /* note filters, route-maps not shown */ + } + + if (VNC_EXPORT_ZEBRA_GRP_ENABLED (hc)) + { + redist++; + vty_out (vty, "%sToZebra Groups={", (redist == 1 ? "" : " ")); + if (hc->rfg_export_direct_bgp_l) + { + int cnt = 0; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + for (ALL_LIST_ELEMENTS (hc->rfg_export_zebra_l, node, nnode, rfgn)) + { + if (cnt++ != 0) + vty_out (vty, ","); + vty_out (vty, "%s", rfgn->name); + } + } + vty_out (vty, "}"); + } + else if (VNC_EXPORT_ZEBRA_RH_ENABLED (hc)) + { + redist++; + vty_out (vty, "%sToZebra {Registering NVE}", (redist == 1 ? "" : " ")); + /* note filters, route-maps not shown */ + } + vty_out (vty, "%-19s %s%s", (redist ? "" : "Off"), + (redist ? "" : "(default)"), VTY_NEWLINE); + + /* Redistribution */ + redist = 0; + vty_out (vty, "%-39s ", "Redistribution into VNC:"); + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) + { + if (hc->redist[afi][type]) + { + vty_out (vty, "{%s,%s} ", + ((afi == AFI_IP) ? "ipv4" : "ipv6"), + zebra_route_string (type)); + redist++; + } + } + } + vty_out (vty, "%-19s %s%s", (redist ? "" : "Off"), + (redist ? "" : "(default)"), VTY_NEWLINE); + + vty_out (vty, "%-39s %3u%-16s %s%s", "RFP Registration Hold-Down Factor:", + hc->rfp_cfg.holddown_factor, "%", + (hc->rfp_cfg.holddown_factor == + RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR ? "(default)" : ""), + VTY_NEWLINE); + vty_out (vty, "%-39s %-19s %s%s", "RFP Updated responses:", + (hc->rfp_cfg.use_updated_response == 0 ? "Off" : "On"), + (hc->rfp_cfg.use_updated_response == 0 ? "(default)" : ""), + VTY_NEWLINE); + vty_out (vty, "%-39s %-19s %s%s", "RFP Removal responses:", + (hc->rfp_cfg.use_removes == 0 ? "Off" : "On"), + (hc->rfp_cfg.use_removes == 0 ? "(default)" : ""), VTY_NEWLINE); + vty_out (vty, "%-39s %-19s %s%s", "RFP Full table download:", + (hc->rfp_cfg.download_type == + RFAPI_RFP_DOWNLOAD_FULL ? "On" : "Off"), + (hc->rfp_cfg.download_type == + RFAPI_RFP_DOWNLOAD_PARTIAL ? "(default)" : ""), VTY_NEWLINE); + sprintf (tmp, "%u seconds", hc->rfp_cfg.ftd_advertisement_interval); + vty_out (vty, "%-39s %-19s %s%s", " Advertisement Interval:", tmp, + (hc->rfp_cfg.ftd_advertisement_interval == + RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL + ? "(default)" : ""), VTY_NEWLINE); + vty_out (vty, "%-39s %d seconds%s", "Default RFP response lifetime:", + hc->default_response_lifetime, VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + return; +} + +struct rfapi_cfg * +bgp_rfapi_get_config (struct bgp *bgp) +{ + struct rfapi_cfg *hc = NULL; + if (bgp == NULL) + bgp = bgp_get_default (); + if (bgp != NULL) + hc = bgp->rfapi_cfg; + return hc; +} + +#endif /* ENABLE_BGP_VNC */ diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h new file mode 100644 index 0000000000..cd3a28f600 --- /dev/null +++ b/bgpd/rfapi/bgp_rfapi_cfg.h @@ -0,0 +1,312 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_CFG_H +#define _QUAGGA_BGP_RFAPI_CFG_H + +#include "table.h" +#include "routemap.h" + +#if ENABLE_BGP_VNC +#include "rfapi.h" + +struct rfapi_l2_group_cfg +{ + char *name; + uint32_t logical_net_id; + struct list *labels; /* list of uint32_t */ + struct ecommunity *rt_import_list; + struct ecommunity *rt_export_list; + void *rfp_cfg; /* rfp owned group config */ +}; + +struct rfapi_nve_group_cfg +{ + struct route_node *vn_node; /* backref */ + struct route_node *un_node; /* backref */ + + char *name; + struct prefix vn_prefix; + struct prefix un_prefix; + + struct prefix_rd rd; + uint8_t l2rd; /* 0 = VN addr LSB */ + uint32_t response_lifetime; + uint32_t flags; +#define RFAPI_RFG_RESPONSE_LIFETIME 0x1 +#define RFAPI_RFG_L2RD 0x02 + struct ecommunity *rt_import_list; + struct ecommunity *rt_export_list; + struct rfapi_import_table *rfapi_import_table; + + void *rfp_cfg; /* rfp owned group config */ + /* + * List of NVE descriptors that are assigned to this NVE group + * + * Currently (Mar 2010) this list is used only by the route + * export code to generate per-NVE nexthops for each route. + * + * The nve descriptors listed here have pointers back to + * this nve group config structure to enable them to delete + * their own list entries when they are closed. Consequently, + * if an instance of this nve group config structure is deleted, + * we must first set the nve descriptor references to it to NULL. + */ + struct list *nves; + + /* + * Route filtering + * + * Prefix lists are segregated by afi (part of the base plist code) + * Route-maps are not segregated + */ + char *plist_export_bgp_name[AFI_MAX]; + struct prefix_list *plist_export_bgp[AFI_MAX]; + + char *plist_export_zebra_name[AFI_MAX]; + struct prefix_list *plist_export_zebra[AFI_MAX]; + + char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX]; + struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX]; + + char *routemap_export_bgp_name; + struct route_map *routemap_export_bgp; + + char *routemap_export_zebra_name; + struct route_map *routemap_export_zebra; + + char *routemap_redist_name[ZEBRA_ROUTE_MAX]; + struct route_map *routemap_redist[ZEBRA_ROUTE_MAX]; +}; + +struct rfapi_rfg_name +{ + struct rfapi_nve_group_cfg *rfg; + char *name; +}; + +typedef enum +{ + VNC_REDIST_MODE_PLAIN = 0, /* 0 = default */ + VNC_REDIST_MODE_RFG, + VNC_REDIST_MODE_RESOLVE_NVE +} vnc_redist_mode_t; + +struct rfapi_cfg +{ + struct prefix_rd default_rd; + uint8_t default_l2rd; + struct ecommunity *default_rt_import_list; + struct ecommunity *default_rt_export_list; + uint32_t default_response_lifetime; +#define BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT 3600 + void *default_rfp_cfg; /* rfp owned group config */ + + struct list *l2_groups; /* rfapi_l2_group_cfg list */ + /* three views into the same collection of rfapi_nve_group_cfg */ + struct list *nve_groups_sequential; + struct route_table nve_groups_vn[AFI_MAX]; + struct route_table nve_groups_un[AFI_MAX]; + + /* + * For Single VRF export to ordinary routing protocols. This is + * the nve-group that the ordinary protocols belong to. We use it + * to set the RD when sending unicast Zebra routes to VNC + */ + uint8_t redist[AFI_MAX][ZEBRA_ROUTE_MAX]; + uint32_t redist_lifetime; + vnc_redist_mode_t redist_mode; + + /* + * view name of BGP unicast instance that holds + * exterior routes + */ + char *redist_bgp_exterior_view_name; + struct bgp *redist_bgp_exterior_view; + + /* + * nve group for redistribution of routes from zebra to VNC + * (which is probably not useful for production networks) + */ + char *rfg_redist_name; + struct rfapi_nve_group_cfg *rfg_redist; + + /* + * List of NVE groups on whose behalf we will export VNC + * routes to zebra. ((NB: it's actually a list of ) + * This list is used when BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS is + * BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP + */ + struct list *rfg_export_zebra_l; + + /* + * List of NVE groups on whose behalf we will export VNC + * routes directly to the bgp unicast RIB. (NB: it's actually + * a list of ) + * This list is used when BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS is + * BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP + */ + struct list *rfg_export_direct_bgp_l; + + /* + * Exported Route filtering + * + * Prefix lists are segregated by afi (part of the base plist code) + * Route-maps are not segregated + */ + char *plist_export_bgp_name[AFI_MAX]; + struct prefix_list *plist_export_bgp[AFI_MAX]; + + char *plist_export_zebra_name[AFI_MAX]; + struct prefix_list *plist_export_zebra[AFI_MAX]; + + char *routemap_export_bgp_name; + struct route_map *routemap_export_bgp; + + char *routemap_export_zebra_name; + struct route_map *routemap_export_zebra; + + /* + * Redistributed route filtering (routes from other + * protocols into VNC) + */ + char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX]; + struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX]; + + char *routemap_redist_name[ZEBRA_ROUTE_MAX]; + struct route_map *routemap_redist[ZEBRA_ROUTE_MAX]; + + /* + * For importing bgp unicast routes to VNC, we encode the CE + * (route nexthop) in a Route Origin extended community. The + * local part (16-bit) is user-configurable. + */ + uint16_t resolve_nve_roo_local_admin; +#define BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT 5226 + + uint32_t flags; +#define BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP 0x00000001 +#define BGP_VNC_CONFIG_CALLBACK_DISABLE 0x00000002 +#define BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE 0x00000004 + +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS 0x000000f0 +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS 0x00000f00 + +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE 0x00000000 +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP 0x00000010 +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH 0x00000020 /* registerd nve */ +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE 0x00000040 + +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_NONE 0x00000000 +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP 0x00000100 +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH 0x00000200 + +#define BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP 0x00001000 +#define BGP_VNC_CONFIG_L2RD 0x00002000 + +/* Use new NVE RIB to filter callback routes */ +/* Filter querying NVE's registrations from responses */ +/* Default to updated-responses off */ +/* Default to removal-responses off */ +#define BGP_VNC_CONFIG_FLAGS_DEFAULT \ + (BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP |\ + BGP_VNC_CONFIG_CALLBACK_DISABLE |\ + BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE) + + struct rfapi_rfp_cfg rfp_cfg; /* rfp related configuration */ +}; + +#define VNC_EXPORT_ZEBRA_GRP_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) == \ + BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP) + +#define VNC_EXPORT_ZEBRA_RH_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) == \ + BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH) + +#define VNC_EXPORT_BGP_GRP_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) == \ + BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP) + +#define VNC_EXPORT_BGP_RH_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) == \ + BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH) + +#define VNC_EXPORT_BGP_CE_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) == \ + BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) + + +void +bgp_rfapi_cfg_init (void); + +struct rfapi_cfg * +bgp_rfapi_cfg_new (struct rfapi_rfp_cfg *cfg); + +void +bgp_rfapi_cfg_destroy (struct bgp *bgp, struct rfapi_cfg *h); + +int +bgp_rfapi_cfg_write (struct vty *vty, struct bgp *bgp); + +extern int +bgp_rfapi_is_vnc_configured (struct bgp *bgp); + +extern void +nve_group_to_nve_list ( + struct rfapi_nve_group_cfg *rfg, + struct list **nves, + uint8_t family); /* AF_INET, AF_INET6 */ + +struct rfapi_nve_group_cfg * +bgp_rfapi_cfg_match_group ( + struct rfapi_cfg *hc, + struct prefix *vn, + struct prefix *un); + +extern void +vnc_prefix_list_update (struct bgp *bgp); + +extern void +vnc_routemap_update (struct bgp *bgp, const char *unused); + +extern void +bgp_rfapi_show_summary (struct bgp *bgp, struct vty *vty); + +extern struct rfapi_cfg * +bgp_rfapi_get_config (struct bgp *bgp); + +extern struct ecommunity * +bgp_rfapi_get_ecommunity_by_lni_label ( + struct bgp *bgp, + uint32_t is_import, + uint32_t logical_net_id, + uint32_t label); /* note, 20bit label! */ + +extern struct list * +bgp_rfapi_get_labellist_by_lni_label ( + struct bgp *bgp, + uint32_t logical_net_id, + uint32_t label); /* note, 20bit label! */ + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_RFAPI_CFG_H */ diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c new file mode 100644 index 0000000000..3360ad89ce --- /dev/null +++ b/bgpd/rfapi/rfapi.c @@ -0,0 +1,4412 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "routemap.h" +#include "log.h" +#include "linklist.h" +#include "command.h" +#include "stream.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_mplsvpn.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgp_route.h" +#include "bgp_aspath.h" +#include "bgp_advertise.h" +#include "bgp_vnc_types.h" +#include "bgp_zebra.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_zebra.h" +#include "vnc_import_bgp.h" +#include "rfapi_rib.h" +#include "rfapi_ap.h" +#include "rfapi_encap_tlv.h" +#include "vnc_debug.h" + +#ifdef HAVE_GLIBC_BACKTRACE +/* for backtrace and friends */ +#include +#endif /* HAVE_GLIBC_BACKTRACE */ + +struct ethaddr rfapi_ethaddr0 = { {0} }; + +#define DEBUG_RFAPI_STR "RF API debugging/testing command\n" + +const char * +rfapi_error_str (int code) +{ + switch (code) + { + case 0: + return "Success"; + case ENXIO: + return "BGP or VNC not configured"; + case ENOENT: + return "No match"; + case EEXIST: + return "Handle already open"; + case ENOMSG: + return "Incomplete configuration"; + case EAFNOSUPPORT: + return "Invalid address family"; + case EDEADLK: + return "Called from within a callback procedure"; + case EBADF: + return "Invalid handle"; + case EINVAL: + return "Invalid argument"; + case ESTALE: + return "Stale descriptor"; + default: + return "Unknown error"; + } +} + +/*------------------------------------------ + * rfapi_get_response_lifetime_default + * + * Returns the default lifetime for a response. + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * input: + * None + * + * output: + * + * return value: The bgp instance default lifetime for a response. + --------------------------------------------*/ +int +rfapi_get_response_lifetime_default (void *rfp_start_val) +{ + struct bgp *bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + if (bgp) + return bgp->rfapi_cfg->default_response_lifetime; + return BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT; +} + +/*------------------------------------------ + * rfapi_is_vnc_configured + * + * Returns if VNC (BGP VPN messaging /VPN & encap SAFIs) are configured + * + * input: + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * output: + * + * return value: If VNC is configured for the bgpd instance + * 0 Success + * ENXIO VNC not configured + --------------------------------------------*/ +int +rfapi_is_vnc_configured (void *rfp_start_val) +{ + struct bgp *bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + return bgp_rfapi_is_vnc_configured (bgp); +} + + +/*------------------------------------------ + * rfapi_get_vn_addr + * + * Get the virtual network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * vn NVE virtual network address + *------------------------------------------*/ +struct rfapi_ip_addr * +rfapi_get_vn_addr (void *rfd) +{ + struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *) rfd; + return &rrfd->vn_addr; +} + +/*------------------------------------------ + * rfapi_get_un_addr + * + * Get the underlay network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * un NVE underlay network address + *------------------------------------------*/ +struct rfapi_ip_addr * +rfapi_get_un_addr (void *rfd) +{ + struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *) rfd; + return &rrfd->un_addr; +} + +int +rfapi_ip_addr_cmp (struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2) +{ + if (a1->addr_family != a2->addr_family) + return a1->addr_family - a2->addr_family; + + if (a1->addr_family == AF_INET) + { + return IPV4_ADDR_CMP (&a1->addr.v4, &a2->addr.v4); + } + + if (a1->addr_family == AF_INET6) + { + return IPV6_ADDR_CMP (&a1->addr.v6, &a2->addr.v6); + } + + assert (1); + /* NOTREACHED */ + return 1; +} + +static int +rfapi_find_node ( + struct bgp *bgp, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + struct route_node **node) +{ + struct rfapi *h; + struct prefix p; + struct route_node *rn; + int rc; + int afi; + + if (!bgp) + { + return ENXIO; + } + + h = bgp->rfapi; + if (!h) + { + return ENXIO; + } + + afi = family2afi (un_addr->addr_family); + if (!afi) + { + return EAFNOSUPPORT; + } + + if ((rc = rfapiRaddr2Qprefix (un_addr, &p))) + return rc; + + rn = route_node_lookup (&h->un[afi], &p); + + if (!rn) + return ENOENT; + + route_unlock_node (rn); + + *node = rn; + + return 0; +} + + +int +rfapi_find_rfd ( + struct bgp *bgp, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + struct rfapi_descriptor **rfd) +{ + struct route_node *rn; + int rc; + + rc = rfapi_find_node (bgp, vn_addr, un_addr, &rn); + + if (rc) + return rc; + + for (*rfd = (struct rfapi_descriptor *) (rn->info); *rfd; + *rfd = (*rfd)->next) + { + if (!rfapi_ip_addr_cmp (&(*rfd)->vn_addr, vn_addr)) + break; + } + + if (!*rfd) + return ENOENT; + + return 0; +} + +/*------------------------------------------ + * rfapi_find_handle + * + * input: + * un underlay network address + * vn virtual network address + * + * output: + * pHandle pointer to location to store handle + * + * return value: + * 0 Success + * ENOENT no matching handle + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +static int +rfapi_find_handle ( + struct bgp *bgp, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + rfapi_handle *handle) +{ + struct rfapi_descriptor **rfd; + + rfd = (struct rfapi_descriptor **) handle; + + return rfapi_find_rfd (bgp, vn_addr, un_addr, rfd); +} + +static int +rfapi_find_handle_vty ( + struct vty *vty, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + rfapi_handle *handle) +{ + struct bgp *bgp; + struct rfapi_descriptor **rfd; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + + rfd = (struct rfapi_descriptor **) handle; + + return rfapi_find_rfd (bgp, vn_addr, un_addr, rfd); +} + +static int +is_valid_rfd (struct rfapi_descriptor *rfd) +{ + rfapi_handle hh; + + if (!rfd || rfd->bgp == NULL) + return 0; + + if (rfapi_find_handle (rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh)) + return 0; + + if (rfd != hh) + return 0; + + return 1; +} + +/* + * check status of descriptor + */ +int +rfapi_check (void *handle) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + rfapi_handle hh; + int rc; + + if (!rfd || rfd->bgp == NULL) + return EINVAL; + + if ((rc = rfapi_find_handle (rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh))) + return rc; + + if (rfd != hh) + return ENOENT; + + if (!rfd->rfg) + return ESTALE; + + return 0; +} + + + +void +del_vnc_route ( + struct rfapi_descriptor *rfd, + struct peer *peer, /* rfd->peer for RFP regs */ + struct bgp *bgp, + safi_t safi, + struct prefix *p, + struct prefix_rd *prd, + uint8_t type, + uint8_t sub_type, + struct rfapi_nexthop *lnh, + int kill) +{ + afi_t afi; /* of the VN address */ + struct bgp_node *bn; + struct bgp_info *bi; + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + struct prefix_rd prd0; + + prefix2str (p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + prefix_rd2str (prd, buf2, BUFSIZ); + buf2[BUFSIZ - 1] = 0; + + afi = family2afi (p->family); + assert (afi == AFI_IP || afi == AFI_IP6); + + if (safi == SAFI_ENCAP) + { + memset (&prd0, 0, sizeof (prd0)); + prd0.family = AF_UNSPEC; + prd0.prefixlen = 64; + prd = &prd0; + } + bn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + + zlog_debug + ("%s: peer=%p, prefix=%s, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p", + __func__, peer, buf, buf2, afi, safi, bn, (bn ? bn->info : NULL)); + + for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) + { + + zlog_debug + ("%s: trying bi=%p, bi->peer=%p, bi->type=%d, bi->sub_type=%d, bi->extra->vnc.export.rfapi_handle=%p", + __func__, bi, bi->peer, bi->type, bi->sub_type, + (bi->extra ? bi->extra->vnc.export.rfapi_handle : NULL)); + + if (bi->peer == peer && + bi->type == type && + bi->sub_type == sub_type && + bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd) + { + + zlog_debug ("%s: matched it", __func__); + + break; + } + } + + if (lnh) + { + /* + * lnh set means to JUST delete the local nexthop from this + * route. Leave the route itself in place. + * TBD add return code reporting of success/failure + */ + if (!bi || !bi->extra || !bi->extra->vnc.export.local_nexthops) + { + /* + * no local nexthops + */ + zlog_debug ("%s: lnh list already empty at prefix %s", + __func__, buf); + goto done; + } + + /* + * look for it + */ + struct listnode *node; + struct rfapi_nexthop *pLnh = NULL; + + for (ALL_LIST_ELEMENTS_RO (bi->extra->vnc.export.local_nexthops, + node, pLnh)) + { + + if (prefix_same (&pLnh->addr, &lnh->addr)) + { + break; + } + } + + if (pLnh) + { + listnode_delete (bi->extra->vnc.export.local_nexthops, pLnh); + + /* silly rabbit, listnode_delete doesn't invoke list->del on data */ + rfapi_nexthop_free (pLnh); + } + else + { + zlog_debug ("%s: desired lnh not found %s", __func__, buf); + } + goto done; + } + + /* + * loop back to import tables + * Do this before removing from BGP RIB because rfapiProcessWithdraw + * might refer to it + */ + rfapiProcessWithdraw (peer, rfd, p, prd, NULL, afi, safi, type, kill); + + if (bi) + { + char buf[BUFSIZ]; + + prefix2str (p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + zlog_debug ("%s: Found route (safi=%d) to delete at prefix %s", + __func__, safi, buf); + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve (bgp, + prd, + table, + p, bi); + } + bgp_unlock_node (prn); + } + + /* + * Delete local_nexthops list + */ + if (bi->extra && bi->extra->vnc.export.local_nexthops) + { + list_delete (bi->extra->vnc.export.local_nexthops); + } + + bgp_aggregate_decrement (bgp, p, bi, afi, safi); + bgp_info_delete (bn, bi); + bgp_process (bgp, bn, afi, safi); + } + else + { + zlog_debug ("%s: Couldn't find route (safi=%d) at prefix %s", + __func__, safi, buf); + } +done: + bgp_unlock_node (bn); +} + +struct rfapi_nexthop * +rfapi_nexthop_new (struct rfapi_nexthop *copyme) +{ + struct rfapi_nexthop *new = + XCALLOC (MTYPE_RFAPI_NEXTHOP, sizeof (struct rfapi_nexthop)); + if (copyme) + *new = *copyme; + return new; +} + +void +rfapi_nexthop_free (void *p) +{ + struct rfapi_nexthop *goner = p; + XFREE (MTYPE_RFAPI_NEXTHOP, goner); +} + +struct rfapi_vn_option * +rfapi_vn_options_dup (struct rfapi_vn_option *existing) +{ + struct rfapi_vn_option *p; + struct rfapi_vn_option *head = NULL; + struct rfapi_vn_option *tail = NULL; + + for (p = existing; p; p = p->next) + { + struct rfapi_vn_option *new; + + new = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option)); + *new = *p; + new->next = NULL; + if (tail) + (tail)->next = new; + tail = new; + if (!head) + { + head = new; + } + } + return head; +} + +void +rfapi_un_options_free (struct rfapi_un_option *p) +{ + struct rfapi_un_option *next; + + while (p) + { + next = p->next; + XFREE (MTYPE_RFAPI_UN_OPTION, p); + p = next; + } +} + +void +rfapi_vn_options_free (struct rfapi_vn_option *p) +{ + struct rfapi_vn_option *next; + + while (p) + { + next = p->next; + XFREE (MTYPE_RFAPI_VN_OPTION, p); + p = next; + } +} + +/* Based on bgp_redistribute_add() */ +void +add_vnc_route ( + struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ + struct bgp *bgp, + int safi, + struct prefix *p, + struct prefix_rd *prd, + struct rfapi_ip_addr *nexthop, + uint32_t *local_pref, + uint32_t *lifetime, /* NULL => dont send lifetime */ + struct bgp_tea_options *rfp_options, + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + struct ecommunity *rt_export_list,/* Copied, not consumed */ + uint32_t *med, /* NULL => don't set med */ + uint32_t *label, /* low order 3 bytes */ + uint8_t type, + uint8_t sub_type, /* RFP, NORMAL or REDIST */ + int flags) +{ + afi_t afi; /* of the VN address */ + struct bgp_info *new; + struct bgp_info *bi; + struct bgp_node *bn; + + struct attr attr = { 0 }; + struct attr *new_attr; + uint32_t label_val; + + struct bgp_attr_encap_subtlv *encaptlv; + char buf[BUFSIZ]; + char buf2[BUFSIZ]; +#if 0 /* unused? */ + struct prefix pfx_buf; +#endif + + struct rfapi_nexthop *lnh = NULL; /* local nexthop */ + struct rfapi_vn_option *vo; + struct rfapi_l2address_option *l2o = NULL; + struct rfapi_ip_addr *un_addr = &rfd->un_addr; + + bgp_encap_types TunnelType = BGP_ENCAP_TYPE_RESERVED; + struct bgp_redist *red; + + if (safi == SAFI_ENCAP && + !(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP)) + { + + /* + * Encap mode not enabled. UN addresses will be communicated + * via VNC Tunnel subtlv instead. + */ + zlog_debug ("%s: encap mode not enabled, not adding SAFI_ENCAP route", + __func__); + return; + } + +#if 0 /* unused? */ + if ((safi == SAFI_MPLS_VPN) && (flags & RFAPI_AHR_SET_PFX_TO_NEXTHOP)) + { + + if (rfapiRaddr2Qprefix (nexthop, &pfx_buf)) + { + zlog_debug + ("%s: can't set pfx to vn addr, not adding SAFI_MPLS_VPN route", + __func__); + return; + } + p = &pfx_buf; + } +#endif + for (vo = options_vn; vo; vo = vo->next) + { + if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) + { + l2o = &vo->v.l2addr; + if (RFAPI_0_ETHERADDR (&l2o->macaddr)) + l2o = NULL; /* not MAC resolution */ + } + if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type) + { + lnh = &vo->v.local_nexthop; + } + } + + if (label) + label_val = *label; + else + label_val = MPLS_LABEL_IMPLICIT_NULL; + + prefix_rd2str (prd, buf2, BUFSIZ); + buf2[BUFSIZ - 1] = 0; + + + afi = family2afi (p->family); + assert (afi == AFI_IP || afi == AFI_IP6); + + zlog_debug ("%s: afi=%s, safi=%s", __func__, afi2str (afi), + safi2str (safi)); + + /* Make default attribute. Produces already-interned attr.aspath */ + /* Cripes, the memory management of attributes is byzantine */ + + bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE); + assert (attr.extra); + + /* + * At this point: + * attr: static + * extra: dynamically allocated, owned by attr + * aspath: points to interned hash from aspath hash table + */ + + + /* + * Route-specific un_options get added to the VPN SAFI + * advertisement tunnel encap attribute. (the per-NVE + * "default" un_options are put into the 1-per-NVE ENCAP + * SAFI advertisement). The VPN SAFI also gets the + * default un_options if there are no route-specific options. + */ + if (options_un) + { + struct rfapi_un_option *uo; + + for (uo = options_un; uo; uo = uo->next) + { + if (RFAPI_UN_OPTION_TYPE_TUNNELTYPE == uo->type) + { + TunnelType = rfapi_tunneltype_option_to_tlv ( + bgp, un_addr, &uo->v.tunnel, &attr, l2o != NULL); + } + } + } + else + { + /* + * Add encap attr + * These are the NVE-specific "default" un_options which are + * put into the 1-per-NVE ENCAP advertisement. + */ + if (rfd->default_tunneltype_option.type) + { + TunnelType = rfapi_tunneltype_option_to_tlv ( + bgp, un_addr, &rfd->default_tunneltype_option, &attr, + l2o != NULL); + } + else + TunnelType = rfapi_tunneltype_option_to_tlv ( + bgp, un_addr, NULL, + /* create one to carry un_addr */ &attr, l2o != NULL); + } + + if (TunnelType == BGP_ENCAP_TYPE_MPLS) + { + if (safi == SAFI_ENCAP) + { + /* Encap SAFI not used with MPLS */ + zlog_debug ("%s: mpls tunnel type, encap safi omitted", __func__); + aspath_unintern (&attr.aspath); /* Unintern original. */ + bgp_attr_extra_free (&attr); + return; + } + nexthop = un_addr; /* UN used as MPLS NLRI nexthop */ + } + + if (local_pref) + { + attr.local_pref = *local_pref; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + } + + if (med) + { + attr.med = *med; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + } + + /* override default weight assigned by bgp_attr_default_set() */ + attr.extra->weight = (rfd->peer ? rfd->peer->weight : 0); + + /* + * NB: ticket 81: do not reset attr.aspath here because it would + * cause iBGP peers to drop route + */ + + /* + * Set originator ID for routes imported from BGP directly. + * These routes could be synthetic, and therefore could + * reuse the peer pointers of the routes they are derived + * from. Setting the originator ID to "us" prevents the + * wrong originator ID from being sent when this route is + * sent from a route reflector. + */ + if (type == ZEBRA_ROUTE_BGP_DIRECT || type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + { + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID); + attr.extra->originator_id = bgp->router_id; + } + + + /* Set up vnc attribute (sub-tlv for Prefix Lifetime) */ + if (lifetime && *lifetime != RFAPI_INFINITE_LIFETIME) + { + uint32_t lt; + + encaptlv = + XCALLOC (MTYPE_ENCAP_TLV, + sizeof (struct bgp_attr_encap_subtlv) - 1 + 4); + assert (encaptlv); + encaptlv->type = BGP_VNC_SUBTLV_TYPE_LIFETIME; /* prefix lifetime */ + encaptlv->length = 4; + lt = htonl (*lifetime); + memcpy (encaptlv->value, <, 4); + attr.extra->vnc_subtlvs = encaptlv; + zlog_debug ("%s: set Encap Attr Prefix Lifetime to %d", + __func__, *lifetime); + } + + /* add rfp options to vnc attr */ + if (rfp_options) + { + + if (flags & RFAPI_AHR_RFPOPT_IS_VNCTLV) + { + + /* + * this flag means we're passing a pointer to an + * existing encap tlv chain which we should copy. + * It's a hack to avoid adding yet another argument + * to add_vnc_route() + */ + encaptlv = + encap_tlv_dup ((struct bgp_attr_encap_subtlv *) rfp_options); + if (attr.extra->vnc_subtlvs) + { + attr.extra->vnc_subtlvs->next = encaptlv; + } + else + { + attr.extra->vnc_subtlvs = encaptlv; + } + + } + else + { + struct bgp_tea_options *hop; + /* XXX max of one tlv present so far from above code */ + struct bgp_attr_encap_subtlv *tail = attr.extra->vnc_subtlvs; + + for (hop = rfp_options; hop; hop = hop->next) + { + + /* + * Construct subtlv + */ + encaptlv = XCALLOC (MTYPE_ENCAP_TLV, + sizeof (struct bgp_attr_encap_subtlv) - 1 + + 2 + hop->length); + assert (encaptlv); + encaptlv->type = BGP_VNC_SUBTLV_TYPE_RFPOPTION; /* RFP option */ + encaptlv->length = 2 + hop->length; + *((uint8_t *) (encaptlv->value) + 0) = hop->type; + *((uint8_t *) (encaptlv->value) + 1) = hop->length; + memcpy (((uint8_t *) encaptlv->value) + 2, hop->value, + hop->length); + + /* + * add to end of subtlv chain + */ + if (tail) + { + tail->next = encaptlv; + } + else + { + attr.extra->vnc_subtlvs = encaptlv; + } + tail = encaptlv; + } + } + } + + /* + * At this point: + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * aspath: points to interned hash from aspath hash table + */ + + + attr.extra->ecommunity = ecommunity_new (); + assert (attr.extra->ecommunity); + + if (TunnelType != BGP_ENCAP_TYPE_MPLS && + TunnelType != BGP_ENCAP_TYPE_RESERVED) + { + /* + * Add BGP Encapsulation Extended Community. Format described in + * section 4.5 of RFC 5512. + * Always include when not MPLS type, to disambiguate this case. + */ + struct ecommunity_val beec; + + memset (&beec, 0, sizeof (beec)); + beec.val[0] = ECOMMUNITY_ENCODE_OPAQUE; + beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP; + beec.val[6] = ((TunnelType) >> 8) & 0xff; + beec.val[7] = (TunnelType) & 0xff; + ecommunity_add_val (attr.extra->ecommunity, &beec); + } + + /* + * Add extended community attributes to match rt export list + */ + if (rt_export_list) + { + attr.extra->ecommunity = + ecommunity_merge (attr.extra->ecommunity, rt_export_list); + } + + if (attr.extra->ecommunity->size) + { + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); + } + else + { + ecommunity_free (&attr.extra->ecommunity); + attr.extra->ecommunity = NULL; + } + zlog_debug ("%s: attr.extra->ecommunity=%p", __func__, + attr.extra->ecommunity); + + + /* + * At this point: + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * ecommunity: dynamic 2-part + * aspath: points to interned hash from aspath hash table + */ + + /* stuff nexthop in attr_extra; which field depends on IPv4 or IPv6 */ + switch (nexthop->addr_family) + { + case AF_INET: + /* + * set this field to prevent bgp_route.c code from setting + * mp_nexthop_global_in to self + */ + attr.nexthop.s_addr = nexthop->addr.v4.s_addr; + + attr.extra->mp_nexthop_global_in = nexthop->addr.v4; + attr.extra->mp_nexthop_len = 4; + break; + + case AF_INET6: + attr.extra->mp_nexthop_global = nexthop->addr.v6; + attr.extra->mp_nexthop_len = 16; + break; + + default: + assert (0); + } + + + prefix2str (p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + /* + * At this point: + * + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * ecommunity: dynamic 2-part + * aspath: points to interned hash from aspath hash table + */ + + red = bgp_redist_lookup(bgp, afi, type, VRF_DEFAULT); + + if (red && red->redist_metric_flag) + { + attr.med = red->redist_metric; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + } + + bn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + + /* + * bgp_attr_intern creates a new reference to a cached + * attribute, but leaves the following bits of trash: + * - old attr + * - old attr->extra (free via bgp_attr_extra_free(attr)) + * + * Note that it frees the original attr->extra->ecommunity + * but leaves the new attribute pointing to the ORIGINAL + * vnc options (which therefore we needn't free from the + * static attr) + */ + new_attr = bgp_attr_intern (&attr); + + aspath_unintern (&attr.aspath); /* Unintern original. */ + bgp_attr_extra_free (&attr); + + /* + * At this point: + * + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * ecommunity: POINTS TO INTERNED ecom, THIS REF NOT COUNTED + * + * new_attr: an attr that is part of the hash table, distinct + * from attr which is static. + * extra: dynamically allocated, owned by new_attr (in hash table) + * vnc_subtlvs: POINTS TO SAME dynamic chain AS attr + * ecommunity: POINTS TO interned/refcounted dynamic 2-part AS attr + * aspath: POINTS TO interned/refcounted hashed block + */ + for (bi = bn->info; bi; bi = bi->next) + { + /* probably only need to check bi->extra->vnc.export.rfapi_handle */ + if (bi->peer == rfd->peer && + bi->type == type && + bi->sub_type == sub_type && + bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd) + { + + break; + } + } + + if (bi) + { + + /* + * Adding new local_nexthop, which does not by itself change + * what is advertised via BGP + */ + if (lnh) + { + if (!bi->extra->vnc.export.local_nexthops) + { + /* TBD make arrangements to free when needed */ + bi->extra->vnc.export.local_nexthops = list_new (); + bi->extra->vnc.export.local_nexthops->del = rfapi_nexthop_free; + } + + /* + * already present? + */ + struct listnode *node; + struct rfapi_nexthop *pLnh = NULL; + + for (ALL_LIST_ELEMENTS_RO (bi->extra->vnc.export.local_nexthops, + node, pLnh)) + { + + if (prefix_same (&pLnh->addr, &lnh->addr)) + { + break; + } + } + + /* + * Not present, add new one + */ + if (!pLnh) + { + pLnh = rfapi_nexthop_new (lnh); + listnode_add (bi->extra->vnc.export.local_nexthops, pLnh); + } + } + + if (attrhash_cmp (bi->attr, new_attr) && + !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + bgp_attr_unintern (&new_attr); + bgp_unlock_node (bn); + + zlog_info ("%s: Found route (safi=%d) at prefix %s, no change", + __func__, safi, buf); + + goto done; + } + else + { + /* The attribute is changed. */ + bgp_info_set_flag (bn, bi, BGP_INFO_ATTR_CHANGED); + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve ( + bgp, prd, table, p, bi); + } + bgp_unlock_node (prn); + } + + /* Rewrite BGP route information. */ + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + bgp_info_restore (bn, bi); + else + bgp_aggregate_decrement (bgp, p, bi, afi, safi); + bgp_attr_unintern (&bi->attr); + bi->attr = new_attr; + bi->uptime = bgp_clock (); + + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve ( + bgp, prd, table, p, bi); + } + bgp_unlock_node (prn); + } + + /* Process change. */ + bgp_aggregate_increment (bgp, p, bi, afi, safi); + bgp_process (bgp, bn, afi, safi); + bgp_unlock_node (bn); + + zlog_info ("%s: Found route (safi=%d) at prefix %s, changed attr", + __func__, safi, buf); + + goto done; + } + } + + + new = bgp_info_new (); + new->type = type; + new->sub_type = sub_type; + new->peer = rfd->peer; + SET_FLAG (new->flags, BGP_INFO_VALID); + new->attr = new_attr; + new->uptime = bgp_clock (); + + /* save backref to rfapi handle */ + assert (bgp_info_extra_get (new)); + new->extra->vnc.export.rfapi_handle = (void *) rfd; + encode_label (label_val, new->extra->tag); + + /* debug */ + zlog_debug ("%s: printing BI", __func__); + rfapiPrintBi (NULL, new); + + bgp_aggregate_increment (bgp, p, new, afi, safi); + bgp_info_add (bn, new); + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve ( + bgp, prd, table, p, new); + } + bgp_unlock_node (prn); + } + + bgp_unlock_node (bn); + bgp_process (bgp, bn, afi, safi); + + zlog_info ("%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%s)", + __func__, safi2str (safi), buf, bn, buf2); + +done: + /* Loop back to import tables */ + rfapiProcessUpdate (rfd->peer, + rfd, + p, prd, new_attr, afi, safi, type, sub_type, &label_val); + zlog_debug ("%s: looped back import route (safi=%d)", __func__, safi); +} + +uint32_t +rfp_cost_to_localpref (uint8_t cost) +{ + return 255 - cost; +} + +static void +rfapiTunnelRouteAnnounce ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + uint32_t *pLifetime) +{ + struct prefix_rd prd; + struct prefix pfx_vn; + int rc; + uint32_t local_pref = rfp_cost_to_localpref (0); + + rc = rfapiRaddr2Qprefix (&(rfd->vn_addr), &pfx_vn); + assert (!rc); + + /* + * Construct route distinguisher = 0 + */ + memset (&prd, 0, sizeof (prd)); + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + add_vnc_route (rfd, /* rfapi descr, for export list & backref */ + bgp, /* which bgp instance */ + SAFI_ENCAP, /* which SAFI */ + &pfx_vn, /* prefix to advertise */ + &prd, /* route distinguisher to use */ + &rfd->un_addr, /* nexthop */ + &local_pref, + pLifetime, /* max lifetime of child VPN routes */ + NULL, /* no rfp options for ENCAP safi */ + NULL, /* rfp un options */ + NULL, /* rfp vn options */ + rfd->rt_export_list, + NULL, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP, + BGP_ROUTE_RFP, + 0); +} + + +/*********************************************************************** + * RFP processing behavior configuration + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_rfp_set_configuration + * + * This is used to change rfapi's processing behavior based on + * RFP requirements. + * + * input: + * rfp_start_val value returned by rfp_start + * rfapi_rfp_cfg Pointer to configuration structure + * + * output: + * none + * + * return value: + * 0 Success + * ENXIO Unabled to locate configured BGP/VNC +--------------------------------------------*/ +int +rfapi_rfp_set_configuration (void *rfp_start_val, struct rfapi_rfp_cfg *new) +{ + struct rfapi_rfp_cfg *rcfg; + struct bgp *bgp; + + bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + + if (!new || !bgp || !bgp->rfapi_cfg) + return ENXIO; + + rcfg = &bgp->rfapi_cfg->rfp_cfg; + rcfg->download_type = new->download_type; + rcfg->ftd_advertisement_interval = new->ftd_advertisement_interval; + rcfg->holddown_factor = new->holddown_factor; + + if (rcfg->use_updated_response != new->use_updated_response) + { + rcfg->use_updated_response = new->use_updated_response; + if (rcfg->use_updated_response) + rfapiMonitorCallbacksOn (bgp); + else + rfapiMonitorCallbacksOff (bgp); + } + if (rcfg->use_removes != new->use_removes) + { + rcfg->use_removes = new->use_removes; + if (rcfg->use_removes) + rfapiMonitorResponseRemovalOn (bgp); + else + rfapiMonitorResponseRemovalOff (bgp); + } + return 0; +} + +/*------------------------------------------ + * rfapi_rfp_set_cb_methods + * + * Change registered callback functions for asynchronous notifications + * from RFAPI to the RFP client. + * + * input: + * rfp_start_val value returned by rfp_start + * methods Pointer to struct rfapi_rfp_cb_methods containing + * pointers to callback methods as described above + * + * return value: + * 0 Success + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +int +rfapi_rfp_set_cb_methods (void *rfp_start_val, + struct rfapi_rfp_cb_methods *methods) +{ + struct rfapi *h; + struct bgp *bgp; + + bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + if (!bgp) + return ENXIO; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + h->rfp_methods = *methods; + + return 0; +} + +/*********************************************************************** + * NVE Sessions + ***********************************************************************/ + +/* + * Caller must supply an already-allocated rfd with the "caller" + * fields already set (vn_addr, un_addr, callback, cookie) + * The advertised_prefixes[] array elements should be NULL to + * have this function set them to newly-allocated radix trees. + */ +static int +rfapi_open_inner ( + struct rfapi_descriptor *rfd, + struct bgp *bgp, + struct rfapi *h, + struct rfapi_nve_group_cfg *rfg) +{ + int ret; + + if (h->flags & RFAPI_INCALLBACK) + return EDEADLK; + + /* + * Fill in configured fields + */ + + /* + * If group's RD is specified as "auto", then fill in based + * on NVE's VN address + */ + rfd->rd = rfg->rd; + + if (rfd->rd.family == AF_UNIX) + { + ret = rfapi_set_autord_from_vn (&rfd->rd, &rfd->vn_addr); + if (ret != 0) + return ret; + } + rfd->rt_export_list = (rfg->rt_export_list) ? + ecommunity_dup (rfg->rt_export_list) : NULL; + rfd->response_lifetime = rfg->response_lifetime; + rfd->rfg = rfg; + + /* + * Fill in BGP peer structure + */ + rfd->peer = peer_new (bgp); + rfd->peer->status = Established; /* keep bgp core happy */ + bgp_sync_delete (rfd->peer); /* don't need these */ + if (rfd->peer->ibuf) + { + stream_free (rfd->peer->ibuf); /* don't need it */ + rfd->peer->ibuf = NULL; + } + if (rfd->peer->obuf) + { + stream_fifo_free (rfd->peer->obuf); /* don't need it */ + rfd->peer->obuf = NULL; + } + if (rfd->peer->work) + { + stream_free (rfd->peer->work); /* don't need it */ + rfd->peer->work = NULL; + } + { /* base code assumes have valid host pointer */ + char buf[BUFSIZ]; + buf[0] = 0; + + if (rfd->vn_addr.addr_family == AF_INET) + { + inet_ntop (AF_INET, &rfd->vn_addr.addr.v4, buf, BUFSIZ); + } + else if (rfd->vn_addr.addr_family == AF_INET6) + { + inet_ntop (AF_INET6, &rfd->vn_addr.addr.v6, buf, BUFSIZ); + } + rfd->peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf); + } + /* Mark peer as belonging to HD */ + SET_FLAG (rfd->peer->flags, PEER_FLAG_IS_RFAPI_HD); + + /* + * Set min prefix lifetime to max value so it will get set + * upon first rfapi_register() + */ + rfd->min_prefix_lifetime = UINT32_MAX; + + /* + * Allocate response tables if needed + */ +#define RFD_RTINIT_AFI(rh, ary, afi) do {\ + if (!ary[afi]) { \ + ary[afi] = route_table_init ();\ + ary[afi]->info = rh;\ + }\ +} while (0) + +#define RFD_RTINIT(rh, ary) do {\ + RFD_RTINIT_AFI(rh, ary, AFI_IP);\ + RFD_RTINIT_AFI(rh, ary, AFI_IP6);\ + RFD_RTINIT_AFI(rh, ary, AFI_ETHER);\ +} while(0) + + RFD_RTINIT(rfd, rfd->rib); + RFD_RTINIT(rfd, rfd->rib_pending); + RFD_RTINIT(rfd, rfd->rsp_times); + + /* + * Link to Import Table + */ + rfd->import_table = rfg->rfapi_import_table; + rfd->import_table->refcount += 1; + + rfapiApInit (&rfd->advertised); + + /* + * add this NVE descriptor to the list of NVEs in the NVE group + */ + if (!rfg->nves) + { + rfg->nves = list_new (); + } + listnode_add (rfg->nves, rfd); + + vnc_direct_bgp_add_nve (bgp, rfd); + vnc_zebra_add_nve (bgp, rfd); + + return 0; +} + +struct rfapi_vn_option * +rfapiVnOptionsDup (struct rfapi_vn_option *orig) +{ + struct rfapi_vn_option *head = NULL; + struct rfapi_vn_option *tail = NULL; + struct rfapi_vn_option *vo = NULL; + + for (vo = orig; vo; vo = vo->next) + { + struct rfapi_vn_option *new; + + new = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option)); + memcpy (new, vo, sizeof (struct rfapi_vn_option)); + new->next = NULL; + + if (tail) + { + tail->next = new; + } + else + { + head = tail = new; + } + } + return head; +} + +struct rfapi_un_option * +rfapiUnOptionsDup (struct rfapi_un_option *orig) +{ + struct rfapi_un_option *head = NULL; + struct rfapi_un_option *tail = NULL; + struct rfapi_un_option *uo = NULL; + + for (uo = orig; uo; uo = uo->next) + { + struct rfapi_un_option *new; + + new = XCALLOC (MTYPE_RFAPI_UN_OPTION, sizeof (struct rfapi_un_option)); + memcpy (new, uo, sizeof (struct rfapi_un_option)); + new->next = NULL; + + if (tail) + { + tail->next = new; + } + else + { + head = tail = new; + } + } + return head; +} + +struct bgp_tea_options * +rfapiOptionsDup (struct bgp_tea_options *orig) +{ + struct bgp_tea_options *head = NULL; + struct bgp_tea_options *tail = NULL; + struct bgp_tea_options *hop = NULL; + + for (hop = orig; hop; hop = hop->next) + { + struct bgp_tea_options *new; + + new = XCALLOC (MTYPE_BGP_TEA_OPTIONS, sizeof (struct bgp_tea_options)); + memcpy (new, hop, sizeof (struct bgp_tea_options)); + new->next = NULL; + if (hop->value) + { + new->value = XCALLOC (MTYPE_BGP_TEA_OPTIONS_VALUE, hop->length); + memcpy (new->value, hop->value, hop->length); + } + if (tail) + { + tail->next = new; + } + else + { + head = tail = new; + } + } + return head; +} + +void +rfapiFreeBgpTeaOptionChain (struct bgp_tea_options *p) +{ + struct bgp_tea_options *next; + + while (p) + { + next = p->next; + + if (p->value) + { + XFREE (MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); + p->value = NULL; + } + XFREE (MTYPE_BGP_TEA_OPTIONS, p); + + p = next; + } +} + +void +rfapiAdbFree (struct rfapi_adb *adb) +{ + XFREE (MTYPE_RFAPI_ADB, adb); +} + +static int +rfapi_query_inner ( + void *handle, + struct rfapi_ip_addr *target, + struct rfapi_l2address_option *l2o, /* may be NULL */ + struct rfapi_next_hop_entry **ppNextHopEntry) +{ + afi_t afi; + struct prefix p; + struct prefix p_original; + struct route_node *rn; + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + struct bgp *bgp = rfd->bgp; + struct rfapi_next_hop_entry *pNHE = NULL; + struct rfapi_ip_addr *self_vn_addr = NULL; + int eth_is_0 = 0; + int use_eth_resolution = 0; + struct rfapi_next_hop_entry *i_nhe; + + /* preemptive */ + if (!bgp) + { + zlog_debug ("%s: No BGP instance, returning ENXIO", __func__); + return ENXIO; + } + if (!bgp->rfapi) + { + zlog_debug ("%s: No RFAPI instance, returning ENXIO", __func__); + return ENXIO; + } + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + { + zlog_debug ("%s: Called during calback, returning EDEADLK", __func__); + return EDEADLK; + } + + if (!is_valid_rfd (rfd)) + { + zlog_debug ("%s: invalid handle, returning EBADF", __func__); + return EBADF; + } + + rfd->rsp_counter++; /* dedup: identify this generation */ + rfd->rsp_time = rfapi_time (NULL); /* response content dedup */ + rfd->ftd_last_allowed_time = + bgp_clock() - bgp->rfapi_cfg->rfp_cfg.ftd_advertisement_interval; + + if (l2o) + { + if (!memcmp (l2o->macaddr.octet, rfapi_ethaddr0.octet, ETHER_ADDR_LEN)) + { + eth_is_0 = 1; + } + /* per t/c Paul/Lou 151022 */ + if (!eth_is_0 || l2o->logical_net_id) + { + use_eth_resolution = 1; + } + } + + if (ppNextHopEntry) + *ppNextHopEntry = NULL; + + /* + * Save original target in prefix form. In case of L2-based queries, + * p_original will be modified to reflect the L2 target + */ + assert(!rfapiRaddr2Qprefix (target, &p_original)); + + if (bgp->rfapi_cfg->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL) + { + /* convert query to 0/0 when full-table download is enabled */ + memset ((char *) &p, 0, sizeof (p)); + p.family = target->addr_family; + } + else + { + p = p_original; + } + + { + char buf[BUFSIZ]; + + prefix2str (&p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + zlog_debug ("%s(rfd=%p, target=%s, ppNextHop=%p)", + __func__, rfd, buf, ppNextHopEntry); + } + + afi = family2afi (p.family); + assert (afi); + + if (CHECK_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) + { + self_vn_addr = &rfd->vn_addr; + } + + if (use_eth_resolution) + { + uint32_t logical_net_id = l2o->logical_net_id; + struct ecommunity *l2com; + + /* + * fix up p_original to contain L2 address + */ + rfapiL2o2Qprefix (l2o, &p_original); + + l2com = + bgp_rfapi_get_ecommunity_by_lni_label (bgp, 1, logical_net_id, + l2o->label); + if (l2com) + { + uint8_t *v = l2com->val; + logical_net_id = (v[5] << 16) + (v[6] << 8) + (v[7]); + } + /* + * Ethernet/L2-based lookup + * + * Always returns IT node corresponding to route + */ + + if (RFAPI_RFP_DOWNLOAD_FULL == bgp->rfapi_cfg->rfp_cfg.download_type) + { + eth_is_0 = 1; + } + + rn = rfapiMonitorEthAdd (bgp, + rfd, + (eth_is_0 ? &rfapi_ethaddr0 : &l2o->macaddr), + logical_net_id); + + if (eth_is_0) + { + struct rfapi_ip_prefix rprefix; + + memset (&rprefix, 0, sizeof (rprefix)); + rprefix.prefix.addr_family = target->addr_family; + if (target->addr_family == AF_INET) + { + rprefix.length = 32; + } + else + { + rprefix.length = 128; + } + + pNHE = rfapiEthRouteTable2NextHopList (logical_net_id, &rprefix, + rfd->response_lifetime, self_vn_addr, rfd->rib[afi], &p_original); + goto done; + } + + } + else + { + + /* + * IP-based lookup + */ + + rn = rfapiMonitorAdd (bgp, rfd, &p); + + /* + * If target address is 0, this request is special: means to + * return ALL routes in the table + * + * Monitors for All-Routes queries get put on a special list, + * not in the VPN tree + */ + if (RFAPI_0_PREFIX (&p)) + { + + zlog_debug ("%s: 0-prefix", __func__); + + /* + * Generate nexthop list for caller + */ + pNHE = rfapiRouteTable2NextHopList ( + rfd->import_table->imported_vpn[afi], rfd->response_lifetime, + self_vn_addr, rfd->rib[afi], &p_original); + goto done; + } + + if (rn) + { + route_lock_node (rn); /* so we can unlock below */ + } + else + { + /* + * returns locked node. Don't unlock yet because the unlock + * might free it before we're done with it. This situation + * could occur when rfapiMonitorGetAttachNode() returns a + * newly-created default node. + */ + rn = rfapiMonitorGetAttachNode (rfd, &p); + } + } + + assert (rn); + if (!rn->info) + { + route_unlock_node (rn); + zlog_debug ("%s: VPN route not found, returning ENOENT", __func__); + return ENOENT; + } + + if (VNC_DEBUG(RFAPI_QUERY)) + { + rfapiShowImportTable (NULL, "query", rfd->import_table->imported_vpn[afi], + 1); + } + + if (use_eth_resolution) + { + + struct rfapi_ip_prefix rprefix; + + memset (&rprefix, 0, sizeof (rprefix)); + rprefix.prefix.addr_family = target->addr_family; + if (target->addr_family == AF_INET) + { + rprefix.length = 32; + } + else + { + rprefix.length = 128; + } + + pNHE = rfapiEthRouteNode2NextHopList (rn, &rprefix, + rfd->response_lifetime, self_vn_addr, rfd->rib[afi], &p_original); + + + } + else + { + /* + * Generate answer to query + */ + pNHE = rfapiRouteNode2NextHopList(rn, rfd->response_lifetime, + self_vn_addr, rfd->rib[afi], &p_original); + } + + route_unlock_node (rn); + +done: + if (ppNextHopEntry) + { + /* only count if caller gets it */ + ++bgp->rfapi->response_immediate_count; + } + + if (!pNHE) + { + zlog_debug ("%s: NO NHEs, returning ENOENT", __func__); + return ENOENT; + } + + /* + * count nexthops for statistics + */ + for (i_nhe = pNHE; i_nhe; i_nhe = i_nhe->next) + { + ++rfd->stat_count_nh_reachable; + } + + if (ppNextHopEntry) + { + *ppNextHopEntry = pNHE; + } + else + { + rfapi_free_next_hop_list (pNHE); + } + + zlog_debug ("%s: success", __func__); + return 0; +} + +/* + * support on-the-fly reassignment of an already-open nve to a new + * nve-group in the event that its original nve-group is + * administratively deleted. + */ +static int +rfapi_open_rfd (struct rfapi_descriptor *rfd, struct bgp *bgp) +{ + struct prefix pfx_vn; + struct prefix pfx_un; + struct rfapi_nve_group_cfg *rfg; + struct rfapi *h; + struct rfapi_cfg *hc; + struct prefix_rd prd; + int rc; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + hc = bgp->rfapi_cfg; + if (!hc) + return ENXIO; + + rc = rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn); + assert (!rc); + + rc = rfapiRaddr2Qprefix (&rfd->un_addr, &pfx_un); + assert (!rc); + + /* + * Find the matching nve group config block + */ + rfg = bgp_rfapi_cfg_match_group (hc, &pfx_vn, &pfx_un); + if (!rfg) + { + return ENOENT; + } + + /* + * check nve group config block for required values + */ + if (!rfg->rt_export_list || !rfg->rfapi_import_table) + { + + return ENOMSG; + } + + rc = rfapi_open_inner (rfd, bgp, h, rfg); + if (rc) + { + return rc; + } + + + /* + * Construct route distinguisher for VPN routes + */ + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * re-advertise registered routes, this time as part of new NVE-group + */ + rfapiApReadvertiseAll (bgp, rfd); + + /* + * re-attach callbacks to import table + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + rfapiMonitorAttachImportHd (rfd); + } + + return 0; +} + +/*------------------------------------------ + * rfapi_open + * + * This function initializes a NVE record and associates it with + * the specified VN and underlay network addresses + * + * input: + * rfp_start_val value returned by rfp_start + * vn NVE virtual network address + * + * un NVE underlay network address + * + * default_options Default options to use on registrations. + * For now only tunnel type is supported. + * May be overridden per-prefix in rfapi_register(). + * Caller owns (rfapi_open() does not free) + * + * response_cb Pointer to next hop list update callback function or + * NULL when no callbacks are desired. + * + * userdata Passed to subsequent response_cb invocations. + * + * output: + * response_lifetime The length of time that responses sent to this + * NVE are valid. + * + * pHandle pointer to location to store rfapi handle. The + * handle must be passed on subsequent rfapi_ calls. + * + * + * return value: + * 0 Success + * EEXIST NVE with this {vn,un} already open + * ENOENT No matching nve group config + * ENOMSG Matched nve group config was incomplete + * ENXIO BGP or VNC not configured + * EAFNOSUPPORT Matched nve group specifies auto-assignment of RD, + * but underlay network address is not IPv4 + * EDEADLK Called from within a callback procedure + *------------------------------------------*/ +int +rfapi_open ( + void *rfp_start_val, + struct rfapi_ip_addr *vn, + struct rfapi_ip_addr *un, + struct rfapi_un_option *default_options, + uint32_t *response_lifetime, + void *userdata, /* callback cookie */ + rfapi_handle *pHandle) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_descriptor *rfd; + struct rfapi_cfg *hc; + struct rfapi_nve_group_cfg *rfg; + + struct prefix pfx_vn; + struct prefix pfx_un; + + struct route_node *rn; + int rc; + rfapi_handle hh = NULL; + int reusing_provisional = 0; + + afi_t afi_vn; + afi_t afi_un; + + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug ("%s: VN=%s UN=%s", __func__, + rfapiRfapiIpAddr2Str (vn, buf[0], INET_ADDRSTRLEN), + rfapiRfapiIpAddr2Str (un, buf[1], INET_ADDRSTRLEN)); + } + + assert (pHandle); + *pHandle = NULL; + + bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + if (!bgp) + return ENXIO; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + hc = bgp->rfapi_cfg; + if (!hc) + return ENXIO; + + if (h->flags & RFAPI_INCALLBACK) + return EDEADLK; + + rc = rfapiRaddr2Qprefix (vn, &pfx_vn); + assert (!rc); + + rc = rfapiRaddr2Qprefix (un, &pfx_un); + assert (!rc); + + /* + * already have a descriptor with VN and UN? + */ + if (!rfapi_find_handle (bgp, vn, un, &hh)) + { + /* + * we might have set up a handle for static routes before + * this NVE was opened. In that case, reuse the handle + */ + rfd = hh; + if (!CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_PROVISIONAL)) + { + return EEXIST; + } + + /* + * reuse provisional descriptor + * hh is not NULL + */ + reusing_provisional = 1; + } + + /* + * Find the matching nve group config block + */ + rfg = bgp_rfapi_cfg_match_group (hc, &pfx_vn, &pfx_un); + if (!rfg) + { + ++h->stat.count_unknown_nves; + { + char buf[2][INET_ADDRSTRLEN]; + zlog_notice ("%s: no matching group VN=%s UN=%s", __func__, + rfapiRfapiIpAddr2Str (vn, buf[0], INET_ADDRSTRLEN), + rfapiRfapiIpAddr2Str (un, buf[1], INET_ADDRSTRLEN)); + } + return ENOENT; + } + + /* + * check nve group config block for required values + */ + if (!rfg->rt_export_list || !rfg->rfapi_import_table) + { + + ++h->stat.count_unknown_nves; + return ENOMSG; + } + + /* + * If group config specifies auto-rd assignment, check that + * VN address is IPv4|v6 so we don't fail in rfapi_open_inner(). + * Check here so we don't need to unwind memory allocations, &c. + */ + if ((rfg->rd.family == AF_UNIX) && (vn->addr_family != AF_INET) + && (vn->addr_family != AF_INET6)) + { + return EAFNOSUPPORT; + } + + if (hh) + { + /* + * reusing provisional rfd + */ + rfd = hh; + } + else + { + rfd = XCALLOC (MTYPE_RFAPI_DESC, sizeof (struct rfapi_descriptor)); + } + assert (rfd); + + rfd->bgp = bgp; + if (default_options) + { + struct rfapi_un_option *p; + + for (p = default_options; p; p = p->next) + { + if ((RFAPI_UN_OPTION_TYPE_PROVISIONAL == p->type)) + { + rfd->flags |= RFAPI_HD_FLAG_PROVISIONAL; + } + if ((RFAPI_UN_OPTION_TYPE_TUNNELTYPE == p->type)) + { + rfd->default_tunneltype_option = p->v.tunnel; + } + } + } + + /* + * Fill in caller fields + */ + rfd->vn_addr = *vn; + rfd->un_addr = *un; + rfd->cookie = userdata; + + if (!reusing_provisional) + { + rfapi_time (&rfd->open_time); + + { + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + + rfapiRfapiIpAddr2Str (vn, buf_vn, BUFSIZ); + rfapiRfapiIpAddr2Str (un, buf_un, BUFSIZ); + + zlog_debug ("%s: new HD with VN=%s UN=%s cookie=%p", + __func__, buf_vn, buf_un, userdata); + } + + listnode_add (&h->descriptors, rfd); + if (h->descriptors.count > h->stat.max_descriptors) + { + h->stat.max_descriptors = h->descriptors.count; + } + + /* + * attach to UN radix tree + */ + afi_vn = family2afi (rfd->vn_addr.addr_family); + afi_un = family2afi (rfd->un_addr.addr_family); + assert (afi_vn && afi_un); + assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx_un)); + + rn = route_node_get (&(h->un[afi_un]), &pfx_un); + assert (rn); + rfd->next = rn->info; + rn->info = rfd; + rfd->un_node = rn; + + rc = rfapi_open_inner (rfd, bgp, h, rfg); + /* + * This can fail only if the VN address is IPv6 and the group + * specified auto-assignment of RDs, which only works for v4, + * and the check above should catch it. + * + * Another failure possibility is that we were called + * during an rfapi callback. Also checked above. + */ + assert (!rc); + } + + if (response_lifetime) + *response_lifetime = rfd->response_lifetime; + *pHandle = rfd; + return 0; +} + +/* + * For use with debug functions + */ +static int +rfapi_set_response_cb (struct rfapi_descriptor *rfd, + rfapi_response_cb_t * response_cb) +{ + if (!is_valid_rfd (rfd)) + return EBADF; + rfd->response_cb = response_cb; + return 0; +} + +/* + * rfapi_close_inner + * + * Does almost all the work of rfapi_close, except: + * 1. preserves the descriptor (doesn't free it) + * 2. preserves the prefix query list (i.e., rfd->mon list) + * 3. preserves the advertised prefix list (rfd->advertised) + * 4. preserves the rib and rib_pending tables + * + * The purpose of organizing it this way is to support on-the-fly + * reassignment of an already-open nve to a new nve-group in the + * event that its original nve-group is administratively deleted. + */ +static int +rfapi_close_inner (struct rfapi_descriptor *rfd, struct bgp *bgp) +{ + int rc; + struct prefix pfx_vn; + struct prefix_rd prd; /* currently always 0 for VN->UN */ + + if (!is_valid_rfd (rfd)) + return EBADF; + + rc = rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn); + assert (!rc); /* should never have bad AF in stored vn address */ + + /* + * update exported routes to reflect disappearance of this NVE as nexthop + */ + vnc_direct_bgp_del_nve (bgp, rfd); + vnc_zebra_del_nve (bgp, rfd); + + /* + * unlink this HD's monitors from import table + */ + rfapiMonitorDetachImportHd (rfd); + + /* + * Unlink from Import Table + * NB rfd->import_table will be NULL if we are closing a stale descriptor + */ + if (rfd->import_table) + rfapiImportTableRefDelByIt (bgp, rfd->import_table); + rfd->import_table = NULL; + + /* + * Construct route distinguisher + */ + memset (&prd, 0, sizeof (prd)); + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * withdraw tunnel + */ + del_vnc_route ( + rfd, + rfd->peer, + bgp, + SAFI_ENCAP, + &pfx_vn, /* prefix being advertised */ + &prd, /* route distinguisher to use (0 for ENCAP) */ + ZEBRA_ROUTE_BGP, + BGP_ROUTE_RFP, + NULL, + 0); /* no kill */ + + /* + * Construct route distinguisher for VPN routes + */ + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * find all VPN routes associated with this rfd and delete them, too + */ + rfapiApWithdrawAll (bgp, rfd); + + /* + * remove this nve descriptor from the list of nves + * associated with the nve group + */ + if (rfd->rfg) + { + listnode_delete (rfd->rfg->nves, rfd); + rfd->rfg = NULL; /* XXX mark as orphaned/stale */ + } + + if (rfd->rt_export_list) + ecommunity_free (&rfd->rt_export_list); + rfd->rt_export_list = NULL; + + /* + * free peer structure (possibly delayed until its + * refcount reaches zero) + */ + if (rfd->peer) + { + zlog_debug ("%s: calling peer_delete(%p), #%d", + __func__, rfd->peer, rfd->peer->lock); + peer_delete (rfd->peer); + } + rfd->peer = NULL; + + return 0; +} + +int +rfapi_close (void *handle) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + int rc; + struct route_node *node; + struct bgp *bgp; + struct rfapi *h; + struct rfapi_cfg *hc; + + zlog_debug ("%s: rfd=%p", __func__, rfd); + +#if RFAPI_WHO_IS_CALLING_ME +#ifdef HAVE_GLIBC_BACKTRACE +#define RFAPI_DEBUG_BACKTRACE_NENTRIES 5 + { + void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES]; + char **syms; + int i; + size_t size; + + size = backtrace (buf, RFAPI_DEBUG_BACKTRACE_NENTRIES); + syms = backtrace_symbols (buf, size); + for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i) + { + zlog_debug ("backtrace[%2d]: %s", i, syms[i]); + } + free (syms); + } +#endif +#endif + + bgp = rfd->bgp; + if (!bgp) + return ENXIO; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + if (!is_valid_rfd (rfd)) + return EBADF; + + if (h->flags & RFAPI_INCALLBACK) + { + /* + * Queue these close requests for processing after callback + * is finished + */ + if (!CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) + { + work_queue_add (h->deferred_close_q, handle); + zlog_debug ("%s: added handle %p to deferred close queue", + __func__, handle); + } + return 0; + } + + hc = bgp->rfapi_cfg; + + if (CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) + { + + zlog_debug ("%s administrative close rfd=%p", __func__, rfd); + + if (h && h->rfp_methods.close_cb) + { + zlog_debug ("%s calling close callback rfd=%p", __func__, rfd); + + /* + * call the callback fairly early so that it can still lookup un/vn + * from handle, etc. + * + * NB RFAPI_INCALLBACK is tested above, so if we reach this point + * we are not already in the context of a callback. + */ + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.close_cb) (handle, EIDRM); + h->flags &= ~RFAPI_INCALLBACK; + } + } + + if (rfd->rfg) + { + /* + * Orphaned descriptors have already done this part, so do + * only for non-orphaned descriptors. + */ + if ((rc = rfapi_close_inner (rfd, bgp))) + return rc; + } + + /* + * Remove descriptor from UN index + * (remove from chain at node) + */ + rc = rfapi_find_node (bgp, &rfd->vn_addr, &rfd->un_addr, &node); + if (!rc) + { + struct rfapi_descriptor *hh; + + if (node->info == rfd) + { + node->info = rfd->next; + } + else + { + + for (hh = node->info; hh; hh = hh->next) + { + if (hh->next == rfd) + { + hh->next = rfd->next; + break; + } + } + } + route_unlock_node (node); + } + + /* + * remove from descriptor list + */ + listnode_delete (&h->descriptors, rfd); + + /* + * Delete monitor list items and free monitor structures + */ + (void) rfapiMonitorDelHd (rfd); + + /* + * release advertised prefix data + */ + rfapiApRelease (&rfd->advertised); + + /* + * Release RFP callback RIB + */ + rfapiRibFree (rfd); + + /* + * free descriptor + */ + memset (rfd, 0, sizeof (struct rfapi_descriptor)); + XFREE (MTYPE_RFAPI_DESC, rfd); + + return 0; +} + +/* + * Reopen a nve descriptor. If the descriptor's NVE-group + * does not exist (e.g., if it has been administratively removed), + * reassignment to a new NVE-group is attempted. + * + * If NVE-group reassignment fails, the descriptor becomes "stale" + * (rfd->rfg == NULL implies "stale:). The only permissible API operation + * on a stale descriptor is rfapi_close(). Any other rfapi_* API operation + * on the descriptor will return ESTALE. + * + * Reopening a descriptor is a potentially expensive operation, because + * it involves withdrawing any routes advertised by the NVE, withdrawing + * the NVE's route queries, and then re-adding them all after a new + * NVE-group is assigned. There are also possible route-export affects + * caused by deleting and then adding the NVE: advertised prefixes + * and nexthop lists for exported routes can turn over. + */ +int +rfapi_reopen (struct rfapi_descriptor *rfd, struct bgp *bgp) +{ + struct rfapi *h; + struct rfapi_cfg *hc; + int rc; + + if ((rc = rfapi_close_inner (rfd, bgp))) + { + return rc; + } + if ((rc = rfapi_open_rfd (rfd, bgp))) + { + + h = bgp->rfapi; + hc = bgp->rfapi_cfg; + + assert (!CHECK_FLAG (h->flags, RFAPI_INCALLBACK)); + + if (CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY) && + h && h->rfp_methods.close_cb) + { + + /* + * NB RFAPI_INCALLBACK is tested above, so if we reach this point + * we are not already in the context of a callback. + */ + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.close_cb) ((rfapi_handle) rfd, ESTALE); + h->flags &= ~RFAPI_INCALLBACK; + } + return rc; + } + return 0; +} + +/*********************************************************************** + * NVE Routes + ***********************************************************************/ +/* + * Announce reachability to this prefix via the NVE + */ +int +rfapi_register ( + void *handle, + struct rfapi_ip_prefix *prefix, + uint32_t lifetime, /* host byte order */ + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + rfapi_register_action action) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + struct bgp *bgp; + struct prefix p; + struct prefix *pfx_ip = NULL; + struct prefix_rd prd; + int afi; + struct prefix pfx_mac_buf; + struct prefix *pfx_mac = NULL; + struct prefix pfx_vn_buf; + const char *action_str = NULL; + uint32_t *label = NULL; + struct rfapi_vn_option *vo; + struct rfapi_l2address_option *l2o = NULL; + struct rfapi_nexthop *lnh = NULL; + struct prefix_rd *prd_override = NULL; + + switch (action) + { + case RFAPI_REGISTER_ADD: + action_str = "add"; + break; + case RFAPI_REGISTER_WITHDRAW: + action_str = "withdraw"; + break; + case RFAPI_REGISTER_KILL: + action_str = "kill"; + break; + default: + assert (0); + break; + } + + /* + * Inspect VN options + */ + for (vo = options_vn; vo; vo = vo->next) + { + if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) + { + l2o = &vo->v.l2addr; + } + if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type) + { + lnh = &vo->v.local_nexthop; + } + if (RFAPI_VN_OPTION_TYPE_INTERNAL_RD == vo->type) + { + prd_override = &vo->v.internal_rd; + } + } + + /********************************************************************* + * advertise prefix + *********************************************************************/ + + /* + * set

based on + */ + assert (!rfapiRprefix2Qprefix (prefix, &p)); + + afi = family2afi (prefix->prefix.addr_family); + assert (afi); + + + { + char buf[BUFSIZ]; + + prefix2str (&p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + zlog_debug + ("%s(rfd=%p, pfx=%s, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)", + __func__, rfd, buf, lifetime, options_un, options_vn, action_str); + } + + /* + * These tests come after the prefix conversion so that we can + * print the prefix in a debug message before failing + */ + + bgp = rfd->bgp; + if (!bgp) + { + zlog_debug ("%s: no BGP instance: returning ENXIO", __func__); + return ENXIO; + } + if (!bgp->rfapi) + { + zlog_debug ("%s: no RFAPI instance: returning ENXIO", __func__); + return ENXIO; + } + if (!rfd->rfg) + { + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + zlog_debug ("%s: rfd=%p, no RF GRP instance: returning ESTALE", + __func__, rfd); + return ESTALE; + } + + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + { + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + zlog_debug ("%s: in callback: returning EDEADLK", __func__); + return EDEADLK; + } + + if (!is_valid_rfd (rfd)) + { + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + zlog_debug ("%s: invalid handle: returning EBADF", __func__); + return EBADF; + } + + /* + * Is there a MAC address in this registration? + */ + if (l2o && !RFAPI_0_ETHERADDR (&l2o->macaddr)) + { + rfapiL2o2Qprefix (l2o, &pfx_mac_buf); + pfx_mac = &pfx_mac_buf; + } + + /* + * Is there an IP prefix in this registration? + */ + if (!(RFAPI_0_PREFIX (&p) && RFAPI_HOST_PREFIX (&p))) + { + pfx_ip = &p; + } + else + { + if (!pfx_mac) + { + zlog_debug ("%s: missing mac addr that is required for host 0 pfx", + __func__); + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + return EINVAL; + } + if (rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn_buf)) + { + zlog_debug ("%s: handle has bad vn_addr: returning EBADF", + __func__); + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + return EBADF; + } + } + + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations; + } + + /* + * Figure out if this registration is missing an IP address + * + * MAC-addr based: + * + * In RFAPI, we use prefixes in family AF_LINK to store + * the MAC addresses. These prefixes are used for the + * list of advertised prefixes and in the RFAPI import + * tables. + * + * In BGP proper, we use the prefix matching the NVE's + * VN address with a host prefix-length (i.e., 32 or 128). + * + */ + if (l2o && l2o->logical_net_id && RFAPI_0_PREFIX (&p) && + RFAPI_HOST_PREFIX (&p)) + { + + rfapiL2o2Qprefix (l2o, &pfx_mac_buf); + pfx_mac = &pfx_mac_buf; + } + + /* + * Construct route distinguisher + */ + if (prd_override) + { + prd = *prd_override; + } + else + { + memset (&prd, 0, sizeof (prd)); + if (pfx_mac) + { + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + encode_rd_type(RD_TYPE_VNC_ETH, prd.val); + if (l2o->local_nve_id || !(rfd->rfg->flags & RFAPI_RFG_L2RD)) + { + /* + * If Local NVE ID is specified in message, use it. + * (if no local default configured, also use it even if 0) + */ + prd.val[1] = l2o->local_nve_id; + } + else + { + if (rfd->rfg->l2rd) + { + /* + * locally-configured literal value + */ + prd.val[1] = rfd->rfg->l2rd; + } + else + { + /* + * 0 means auto:vn, which means use LSB of VN addr + */ + if (rfd->vn_addr.addr_family == AF_INET) + { + prd.val[1] = + *(((char *) &rfd->vn_addr.addr.v4.s_addr) + 3); + } + else + { + prd.val[1] = + *(((char *) &rfd->vn_addr.addr.v6.s6_addr) + 15); + } + } + } + memcpy (prd.val + 2, pfx_mac->u.prefix_eth.octet, 6); + } + else + { + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + } + } + + + if (action == RFAPI_REGISTER_WITHDRAW || action == RFAPI_REGISTER_KILL) + { + + int adv_tunnel = 0; + + /* + * withdraw previous advertisement + */ + del_vnc_route ( + rfd, + rfd->peer, + bgp, + SAFI_MPLS_VPN, + pfx_ip ? pfx_ip : &pfx_vn_buf, /* prefix being advertised */ + &prd, /* route distinguisher (0 for ENCAP) */ + ZEBRA_ROUTE_BGP, + BGP_ROUTE_RFP, + NULL, + action == RFAPI_REGISTER_KILL); + + if (0 == rfapiApDelete (bgp, rfd, &p, pfx_mac, &adv_tunnel)) + { + if (adv_tunnel) + rfapiTunnelRouteAnnounce (bgp, rfd, &rfd->max_prefix_lifetime); + } + + } + else + { + + int adv_tunnel = 0; + uint32_t local_pref; + struct ecommunity *rtlist = NULL; + struct ecommunity_val ecom_value; + + if (!rfapiApCount (rfd)) + { + /* + * make sure we advertise tunnel route upon adding the + * first VPN route + */ + adv_tunnel = 1; + } + + if (rfapiApAdd (bgp, rfd, &p, pfx_mac, &prd, lifetime, prefix->cost, + l2o)) + { + adv_tunnel = 1; + } + + zlog_debug ("%s: adv_tunnel = %d", __func__, adv_tunnel); + if (adv_tunnel) + { + zlog_debug ("%s: announcing tunnel route", __func__); + rfapiTunnelRouteAnnounce (bgp, rfd, &rfd->max_prefix_lifetime); + } + + zlog_debug ("%s: calling add_vnc_route", __func__); + + local_pref = rfp_cost_to_localpref (prefix->cost); + + if (l2o && l2o->label) + label = &l2o->label; + + if (pfx_mac) + { + struct ecommunity *l2com = NULL; + + if (label) + { + l2com = bgp_rfapi_get_ecommunity_by_lni_label (bgp, 1, + l2o->logical_net_id, + *label); + } + if (l2com) + { + rtlist = ecommunity_dup (l2com); + } + else + { + /* + * If mac address is set, add an RT based on the registered LNI + */ + memset ((char *) &ecom_value, 0, sizeof (ecom_value)); + ecom_value.val[1] = 0x02; + ecom_value.val[5] = (l2o->logical_net_id >> 16) & 0xff; + ecom_value.val[6] = (l2o->logical_net_id >> 8) & 0xff; + ecom_value.val[7] = (l2o->logical_net_id >> 0) & 0xff; + rtlist = ecommunity_new(); + ecommunity_add_val (rtlist, &ecom_value); + } + } + + /* + * advertise prefix via tunnel endpoint + */ + add_vnc_route ( + rfd, /* rfapi descr, for export list & backref */ + bgp, /* which bgp instance */ + SAFI_MPLS_VPN, /* which SAFI */ + (pfx_ip ? pfx_ip : &pfx_vn_buf), /* prefix being advertised */ + &prd, /* route distinguisher to use (0 for ENCAP) */ + &rfd->vn_addr, /* nexthop */ + &local_pref, + &lifetime, /* prefix lifetime -> Tunnel Encap attr */ + NULL, + options_un, /* rfapi un options */ + options_vn, /* rfapi vn options */ + (rtlist ? rtlist : rfd->rt_export_list), + NULL, /* med */ + label, /* label: default */ + ZEBRA_ROUTE_BGP, + BGP_ROUTE_RFP, + 0); + + if (rtlist) + ecommunity_free (&rtlist); /* sets rtlist = NULL */ + } + + zlog_debug ("%s: success", __func__); + return 0; +} + +int +rfapi_query ( + void *handle, + struct rfapi_ip_addr *target, + struct rfapi_l2address_option *l2o, /* may be NULL */ + struct rfapi_next_hop_entry **ppNextHopEntry) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + struct bgp *bgp = rfd->bgp; + int rc; + + assert (ppNextHopEntry); + *ppNextHopEntry = NULL; + + if (bgp && bgp->rfapi) + { + bgp->rfapi->stat.count_queries++; + } + + if (!rfd->rfg) + { + if (bgp && bgp->rfapi) + ++bgp->rfapi->stat.count_queries_failed; + return ESTALE; + } + + if ((rc = rfapi_query_inner (handle, target, l2o, ppNextHopEntry))) + { + if (bgp && bgp->rfapi) + ++bgp->rfapi->stat.count_queries_failed; + } + return rc; +} + +int +rfapi_query_done (rfapi_handle handle, struct rfapi_ip_addr *target) +{ + struct prefix p; + int rc; + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + struct bgp *bgp = rfd->bgp; + + if (!rfd->rfg) + return ESTALE; + + assert (target); + rc = rfapiRaddr2Qprefix (target, &p); + assert (!rc); + + if (!is_valid_rfd (rfd)) + return EBADF; + + /* preemptive */ + if (!bgp || !bgp->rfapi) + return ENXIO; + + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + return EDEADLK; + + rfapiMonitorDel (bgp, rfd, &p); + + return 0; +} + +int +rfapi_query_done_all (rfapi_handle handle, int *count) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + struct bgp *bgp = rfd->bgp;; + int num; + + if (!rfd->rfg) + return ESTALE; + + if (!is_valid_rfd (rfd)) + return EBADF; + + /* preemptive */ + if (!bgp || !bgp->rfapi) + return ENXIO; + + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + return EDEADLK; + + num = rfapiMonitorDelHd (rfd); + + if (count) + *count = num; + + return 0; +} + +void +rfapi_free_next_hop_list (struct rfapi_next_hop_entry *list) +{ + struct rfapi_next_hop_entry *nh; + struct rfapi_next_hop_entry *next; + + for (nh = list; nh; nh = next) + { + next = nh->next; + rfapi_un_options_free (nh->un_options); + nh->un_options = NULL; + rfapi_vn_options_free (nh->vn_options); + nh->vn_options = NULL; + XFREE (MTYPE_RFAPI_NEXTHOP, nh); + } +} + +/* + * NULL handle => return total count across all nves + */ +uint32_t +rfapi_monitor_count (void *handle) +{ + struct bgp *bgp = bgp_get_default (); + uint32_t count; + + if (handle) + { + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle; + count = rfd->monitor_count; + } + else + { + + if (!bgp || !bgp->rfapi) + return 0; + + count = bgp->rfapi->monitor_count; + } + + return count; +} + +/*********************************************************************** + * CLI/CONFIG + ***********************************************************************/ + +DEFUN (debug_rfapi_show_nves, + debug_rfapi_show_nves_cmd, + "debug rfapi-dev show nves", + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "NVE Information\n") +{ + rfapiPrintMatchingDescriptors (vty, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN ( + debug_rfapi_show_nves_vn_un, + debug_rfapi_show_nves_vn_un_cmd, + "debug rfapi-dev show nves (vn|un) (A.B.C.D|X:X::X:X)", /* prefix also ok */ + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "NVE Information\n" + "Specify virtual network or underlay network interface\n" + "IPv4 or IPv6 address\n") +{ + struct prefix pfx; + + if (!str2prefix (argv[1], &pfx)) + { + vty_out (vty, "Malformed address \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Invalid address \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (*(argv[0]) == 'c') + { + rfapiPrintMatchingDescriptors (vty, NULL, &pfx); + } + else + { + rfapiPrintMatchingDescriptors (vty, &pfx, NULL); + } + return CMD_SUCCESS; +} + +/* + * Note: this function does not flush vty output, so if it is called + * with a stream pointing to a vty, the user will have to type something + * before the callback output shows up + */ +static void +test_nexthops_callback ( +// struct rfapi_ip_addr *target, + struct rfapi_next_hop_entry *next_hops, + void *userdata) +{ + void *stream = userdata; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp (out, "Nexthops Callback, Target=("); + //rfapiPrintRfapiIpAddr(stream, target); + fp (out, ")%s", VTY_NEWLINE); + + rfapiPrintNhl (stream, next_hops); + + rfapi_free_next_hop_list (next_hops); +} + +DEFUN (debug_rfapi_open, + debug_rfapi_open_cmd, + "debug rfapi-dev open vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_open\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" "underlay network interface address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + uint32_t lifetime; + int rc; + rfapi_handle handle; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + rc = rfapi_open (rfapi_get_rfp_start_val_by_bgp (bgp_get_default ()), + &vn, &un, /*&uo */ NULL, &lifetime, NULL, &handle); + + vty_out (vty, "rfapi_open: status %d, handle %p, lifetime %d%s", + rc, handle, lifetime, VTY_NEWLINE); + + rc = rfapi_set_response_cb (handle, test_nexthops_callback); + + vty_out (vty, "rfapi_set_response_cb: status %d%s", rc, VTY_NEWLINE); + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_close_vn_un, + debug_rfapi_close_vn_un_cmd, + "debug rfapi-dev close vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_close\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" "underlay network interface address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + rc = rfapi_close (handle); + + vty_out (vty, "rfapi_close(handle=%p): status %d%s", handle, rc, + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_close_rfd, + debug_rfapi_close_rfd_cmd, + "debug rfapi-dev close rfd HANDLE", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_close\n" + "indicate handle follows\n" "rfapi handle in hexadecimal\n") +{ + rfapi_handle handle; + int rc; + char *endptr = NULL; + +#if (UINTPTR_MAX == ULONG_MAX) + handle = (void *) (uintptr_t) (strtoul (argv[0], &endptr, 16)); +#elif (UINTPTR_MAX == ULLONG_MAX) + handle = (rfapi_handle) (uintptr_t) (strtoull (argv[0], &endptr, 16)); +#else + /* give up */ + assert (0); +#endif + + if (*endptr != '\0' || (uintptr_t) handle == UINTPTR_MAX) + { + vty_out (vty, "Invalid value: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + rc = rfapi_close (handle); + + vty_out (vty, "rfapi_close(handle=%p): status %d%s", handle, rc, + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_register_vn_un, + debug_rfapi_register_vn_un_cmd, + "debug rfapi-dev register vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) lifetime SECONDS", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_register\n" + "indicate vn addr follows\n" + "virtual network IPv4 interface address\n" + "virtual network IPv6 interface address\n" + "indicate un addr follows\n" + "underlay network IPv4 interface address\n" + "underlay network IPv6 interface address\n" + "indicate prefix follows\n" + "IPv4 prefix\n" + "IPv6 prefix\n" "indicate lifetime follows\n" "lifetime\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + struct prefix pfx; + uint32_t lifetime; + struct rfapi_ip_prefix hpfx; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix (argv[2], &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &hpfx); + + if (!strcmp (argv[3], "infinite")) + { + lifetime = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Lifetime", lifetime, argv[3]); + } + + + rc = rfapi_register (handle, &hpfx, lifetime, NULL, NULL, 0); + if (rc) + { + vty_out (vty, "rfapi_register failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_register_vn_un_l2o, + debug_rfapi_register_vn_un_l2o_cmd, + "debug rfapi-dev register" + " vn (A.B.C.D|X:X::X:X)" + " un (A.B.C.D|X:X::X:X)" + " prefix (A.B.C.D/M|X:X::X:X/M)" + " lifetime SECONDS" + " macaddr YY:YY:YY:YY:YY:YY" + " lni <0-16777215>", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_register\n" + "indicate vn addr follows\n" + "virtual network IPv4 interface address\n" + "virtual network IPv6 interface address\n" + "indicate un addr follows\n" + "underlay network IPv4 interface address\n" + "underlay network IPv6 interface address\n" + "indicate prefix follows\n" + "IPv4 prefix\n" + "IPv6 prefix\n" "indicate lifetime follows\n" "lifetime\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + struct prefix pfx; + uint32_t lifetime; + struct rfapi_ip_prefix hpfx; + int rc; + struct rfapi_vn_option optary[10]; /* XXX must be big enough */ + struct rfapi_vn_option *opt = NULL; + int opt_next = 0; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix (argv[2], &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &hpfx); + + if (!strcmp (argv[3], "infinite")) + { + lifetime = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Lifetime", lifetime, argv[3]); + } + + /* L2 option parsing START */ + memset (optary, 0, sizeof (optary)); + VTY_GET_INTEGER ("Logical Network ID", + optary[opt_next].v.l2addr.logical_net_id, argv[5]); + if ((rc = rfapiStr2EthAddr (argv[4], &optary[opt_next].v.l2addr.macaddr))) + { + vty_out (vty, "Bad mac address \"%s\"%s", argv[4], VTY_NEWLINE); + return CMD_WARNING; + } + optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR; + if (opt_next) + { + optary[opt_next - 1].next = optary + opt_next; + } + else + { + opt = optary; + } + ++opt_next; + /* L2 option parsing END */ + + /* TBD fixme */ + rc = rfapi_register (handle, &hpfx, lifetime, NULL /* &uo */ , opt, 0); + if (rc) + { + vty_out (vty, "rfapi_register failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_unregister_vn_un, + debug_rfapi_unregister_vn_un_cmd, + "debug rfapi-dev unregister vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_register\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "indicate prefix follows\n" "prefix") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + struct prefix pfx; + struct rfapi_ip_prefix hpfx; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix (argv[2], &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &hpfx); + + rfapi_register (handle, &hpfx, 0, NULL, NULL, 1); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_query_vn_un, + debug_rfapi_query_vn_un_cmd, + "debug rfapi-dev query vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) target (A.B.C.D|X:X::X:X)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "indicate target follows\n" "target\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_ip_addr target; + rfapi_handle handle; + int rc; + struct rfapi_next_hop_entry *pNextHopEntry; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query (handle, &target, NULL, &pNextHopEntry); + + if (rc) + { + vty_out (vty, "rfapi_query failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + else + { + /* + * print nexthop list + */ + test_nexthops_callback ( /*&target, */ pNextHopEntry, vty); /* frees nh list! */ + } + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_query_vn_un_l2o, + debug_rfapi_query_vn_un_l2o_cmd, + "debug rfapi-dev query vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lni LNI target YY:YY:YY:YY:YY:YY", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "logical network ID follows\n" + "logical network ID\n" + "indicate target MAC addr follows\n" "target MAC addr\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_ip_addr target; + rfapi_handle handle; + int rc; + struct rfapi_next_hop_entry *pNextHopEntry; + struct rfapi_l2address_option l2o_buf; + struct bgp_tea_options hopt; + uint8_t valbuf[14]; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Set up L2 parameters + */ + memset (&l2o_buf, 0, sizeof (l2o_buf)); + if (rfapiStr2EthAddr (argv[3], &l2o_buf.macaddr)) + { + vty_out (vty, "Bad mac address \"%s\"%s", argv[3], VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("Logical Network ID", l2o_buf.logical_net_id, argv[2]); + + /* construct option chain */ + + memset (valbuf, 0, sizeof (valbuf)); + memcpy (valbuf, &l2o_buf.macaddr.octet, ETHER_ADDR_LEN); + valbuf[11] = (l2o_buf.logical_net_id >> 16) & 0xff; + valbuf[12] = (l2o_buf.logical_net_id >> 8) & 0xff; + valbuf[13] = l2o_buf.logical_net_id & 0xff; + + memset (&hopt, 0, sizeof (hopt)); + hopt.options_count = 1; + hopt.options_length = sizeof (valbuf); /* is this right? */ + hopt.type = RFAPI_VN_OPTION_TYPE_L2ADDR; + hopt.length = sizeof (valbuf); + hopt.value = valbuf; + + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query (handle, &target, &l2o_buf, &pNextHopEntry); + + if (rc) + { + vty_out (vty, "rfapi_query failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + else + { + /* + * print nexthop list + */ + /* TBD enhance to print L2 information */ + test_nexthops_callback ( /*&target, */ pNextHopEntry, vty); /* frees nh list! */ + } + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_query_done_vn_un, + debug_rfapi_query_vn_un_done_cmd, + "debug rfapi-dev query done vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) target (A.B.C.D|X:X::X:X)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query_done\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "indicate prefix follows\n" "prefix\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_ip_addr target; + rfapi_handle handle; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query_done (handle, &target); + + vty_out (vty, "rfapi_query_done returned %d%s", rc, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_show_import, + debug_rfapi_show_import_cmd, + "debug rfapi-dev show import", + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "import\n") +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + char *s; + int first_l2 = 1; + + /* + * Show all import tables + */ + + bgp = bgp_get_default (); /* assume 1 instance for now */ + if (!bgp) + { + vty_out (vty, "No BGP instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + h = bgp->rfapi; + if (!h) + { + vty_out (vty, "No RFAPI instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + + for (it = h->imports; it; it = it->next) + { + s = ecommunity_ecom2str (it->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, "Import Table %p, RTs: %s%s", it, s, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, s); + + rfapiShowImportTable (vty, "IP VPN", it->imported_vpn[AFI_IP], 1); + rfapiShowImportTable (vty, "IP ENCAP", it->imported_encap[AFI_IP], 0); + rfapiShowImportTable (vty, "IP6 VPN", it->imported_vpn[AFI_IP6], 1); + rfapiShowImportTable (vty, "IP6 ENCAP", it->imported_encap[AFI_IP6], 0); + } + + if (h->import_mac) + { + void *cursor = NULL; + uint32_t lni; + uintptr_t lni_as_ptr; + int rc; + char buf[BUFSIZ]; + + for (rc = + skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it, + &cursor); !rc; + rc = + skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it, + &cursor)) + { + + if (it->imported_vpn[AFI_ETHER]) + { + lni = lni_as_ptr; + if (first_l2) + { + vty_out (vty, "%sLNI-based Ethernet Tables:%s", + VTY_NEWLINE, VTY_NEWLINE); + first_l2 = 0; + } + snprintf (buf, BUFSIZ, "L2VPN LNI=%u", lni); + rfapiShowImportTable (vty, buf, it->imported_vpn[AFI_ETHER], 1); + } + } + } + + rfapiShowImportTable (vty, "CE IT - IP VPN", + h->it_ce->imported_vpn[AFI_IP], 1); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_show_import_vn_un, + debug_rfapi_show_import_vn_un_cmd, + "debug rfapi-dev show import vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)", + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "import\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" "underlay network interface address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + int rc; + struct rfapi_descriptor *rfd; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + rfd = (struct rfapi_descriptor *) handle; + + rfapiShowImportTable (vty, "IP VPN", + rfd->import_table->imported_vpn[AFI_IP], 1); + rfapiShowImportTable (vty, "IP ENCAP", + rfd->import_table->imported_encap[AFI_IP], 0); + rfapiShowImportTable (vty, "IP6 VPN", + rfd->import_table->imported_vpn[AFI_IP6], 1); + rfapiShowImportTable (vty, "IP6 ENCAP", + rfd->import_table->imported_encap[AFI_IP6], 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_response_omit_self, + debug_rfapi_response_omit_self_cmd, + "debug rfapi-dev response-omit-self (on|off)", + DEBUG_STR + DEBUG_RFAPI_STR + "Omit self in RFP responses\n" + "filter out self from responses\n" "leave self in responses\n") +{ + struct bgp *bgp = bgp_get_default (); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "VNC not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "on")) + SET_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP); + else + UNSET_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP); + + return CMD_SUCCESS; +} + + +#ifdef RFAPI_DEBUG_SKIPLIST_CLI + +#include "skiplist.h" +DEFUN (skiplist_test_cli, + skiplist_test_cli_cmd, + "skiplist test", + "skiplist command\n" + "test\n") +{ + skiplist_test (vty); + + return CMD_SUCCESS; +} + +DEFUN (skiplist_debug_cli, + skiplist_debug_cli_cmd, + "skiplist debug", + "skiplist command\n" + "debug\n") +{ + skiplist_debug (vty, NULL); + return CMD_SUCCESS; +} + +#endif /* RFAPI_DEBUG_SKIPLIST_CLI */ + +void +rfapi_init (void) +{ + bgp_rfapi_cfg_init (); + vnc_debug_init(); + + install_element (ENABLE_NODE, &debug_rfapi_show_import_cmd); + install_element (ENABLE_NODE, &debug_rfapi_show_import_vn_un_cmd); + + install_element (ENABLE_NODE, &debug_rfapi_open_cmd); + install_element (ENABLE_NODE, &debug_rfapi_close_vn_un_cmd); + install_element (ENABLE_NODE, &debug_rfapi_close_rfd_cmd); + install_element (ENABLE_NODE, &debug_rfapi_register_vn_un_cmd); + install_element (ENABLE_NODE, &debug_rfapi_unregister_vn_un_cmd); + install_element (ENABLE_NODE, &debug_rfapi_query_vn_un_cmd); + install_element (ENABLE_NODE, &debug_rfapi_query_vn_un_done_cmd); + install_element (ENABLE_NODE, &debug_rfapi_query_vn_un_l2o_cmd); + + install_element (ENABLE_NODE, &debug_rfapi_response_omit_self_cmd); + + /* Need the following show commands for gpz test scripts */ + install_element (ENABLE_NODE, &debug_rfapi_show_nves_cmd); + install_element (ENABLE_NODE, &debug_rfapi_show_nves_vn_un_cmd); + install_element (ENABLE_NODE, &debug_rfapi_register_vn_un_l2o_cmd); + +#ifdef RFAPI_DEBUG_SKIPLIST_CLI + install_element (ENABLE_NODE, &skiplist_test_cli_cmd); + install_element (ENABLE_NODE, &skiplist_debug_cli_cmd); +#endif + + rfapi_vty_init (); +} + +#ifdef DEBUG_RFAPI +static void +rfapi_print_exported (struct bgp *bgp) +{ + struct bgp_node *rdn; + struct bgp_node *rn; + struct bgp_info *bi; + + if (!bgp) + return; + + for (rdn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rdn; + rdn = bgp_route_next (rdn)) + { + if (!rdn->info) + continue; + fprintf (stderr, "%s: vpn rdn=%p\n", __func__, rdn); + for (rn = bgp_table_top (rdn->info); rn; rn = bgp_route_next (rn)) + { + if (!rn->info) + continue; + fprintf (stderr, "%s: rn=%p\n", __func__, rn); + for (bi = rn->info; bi; bi = bi->next) + { + rfapiPrintBi ((void *) 2, bi); /* 2 => stderr */ + } + } + } + for (rdn = bgp_table_top (bgp->rib[AFI_IP][SAFI_ENCAP]); rdn; + rdn = bgp_route_next (rdn)) + { + if (!rdn->info) + continue; + fprintf (stderr, "%s: encap rdn=%p\n", __func__, rdn); + for (rn = bgp_table_top (rdn->info); rn; rn = bgp_route_next (rn)) + { + if (!rn->info) + continue; + fprintf (stderr, "%s: rn=%p\n", __func__, rn); + for (bi = rn->info; bi; bi = bi->next) + { + rfapiPrintBi ((void *) 2, bi); /* 2 => stderr */ + } + } + } + +} +#endif /* defined(DEBUG_RFAPI) */ + +/* + * Free all memory to prepare for clean exit as seen by valgrind memcheck + */ +void +rfapi_delete (struct bgp *bgp) +{ + extern void rfp_clear_vnc_nve_all (void); /* can't fix correctly yet */ + + /* + * This clears queries and registered routes, and closes nves + */ + if (bgp->rfapi) + rfp_clear_vnc_nve_all (); + bgp_rfapi_cfg_destroy (bgp, bgp->rfapi_cfg); + bgp->rfapi_cfg = NULL; + bgp_rfapi_destroy (bgp, bgp->rfapi); + bgp->rfapi = NULL; +#ifdef DEBUG_RFAPI + /* + * show what's left in the BGP MPLSVPN RIB + */ + rfapi_print_exported (bgp); +#endif + +} + +int +rfapi_set_autord_from_vn (struct prefix_rd *rd, struct rfapi_ip_addr *vn) +{ + zlog_debug ("%s: auto-assigning RD", __func__); + if (vn->addr_family != AF_INET + && vn->addr_family != AF_INET6) + { + zlog_debug ("%s: can't auto-assign RD, VN addr family is not IPv4" + "|v6" + , __func__); + return EAFNOSUPPORT; + } + rd->family = AF_UNSPEC; + rd->prefixlen = 64; + rd->val[1] = RD_TYPE_IP; + if (vn->addr_family == AF_INET) + { + memcpy (rd->val + 2, &vn->addr.v4.s_addr, 4); + } + else + { /* is v6 */ + memcpy (rd->val + 2, &vn->addr.v6.s6_addr32[3], 4);/* low order 4 bytes */ + } + { + char buf[BUFSIZ]; + buf[0] = 0; + prefix_rd2str (rd, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: auto-RD is set to %s", __func__, buf); + } + return 0; +} + +/*------------------------------------------ + * rfapi_bgp_lookup_by_rfp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * rfp_start_val value returned by rfp_startor + * NULL (=get default instance) + * + * output: + * none + * + * return value: + * bgp bgp instance pointer + * NULL = not found + * + --------------------------------------------*/ +struct bgp * +rfapi_bgp_lookup_by_rfp (void *rfp_start_val) +{ + struct bgp *bgp = NULL; + struct listnode *node, *nnode; + + if (rfp_start_val == NULL) + bgp = bgp_get_default (); + else + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + if (bgp->rfapi != NULL && bgp->rfapi->rfp == rfp_start_val) + return bgp; + return bgp; +} + +/*------------------------------------------ + * rfapi_get_rfp_start_val_by_bgp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * bgp bgp instance pointer + * + * output: + * none + * + * return value: + * rfp_start_val + * NULL = not found + * + --------------------------------------------*/ +void * +rfapi_get_rfp_start_val_by_bgp (struct bgp *bgp) +{ + if (!bgp || !bgp->rfapi) + return NULL; + return bgp->rfapi->rfp; +} + +/*********************************************************************** + * RFP group specific configuration + ***********************************************************************/ +static void * +rfapi_rfp_get_or_init_group_config_default ( + struct rfapi_cfg *rfc, + struct vty *vty, + uint32_t size) +{ + if (rfc->default_rfp_cfg == NULL && size > 0) + { + rfc->default_rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size); + zlog_debug ("%s: allocated, size=%d", __func__, size); + + } + return rfc->default_rfp_cfg; +} + +static void * +rfapi_rfp_get_or_init_group_config_nve ( + struct rfapi_cfg *rfc, + struct vty *vty, + uint32_t size) +{ + struct rfapi_nve_group_cfg *rfg = vty->index_sub; + + /* make sure group is still in list */ + if (!listnode_lookup (rfc->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return NULL; + } + + if (rfg->rfp_cfg == NULL && size > 0) + { + rfg->rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size); + zlog_debug ("%s: allocated, size=%d", __func__, size); + + } + return rfg->rfp_cfg; +} + +static void * +rfapi_rfp_get_or_init_group_config_l2 ( + struct rfapi_cfg *rfc, + struct vty *vty, + uint32_t size) +{ + struct rfapi_l2_group_cfg *rfg = vty->index_sub; + + /* make sure group is still in list */ + if (!listnode_lookup (rfc->l2_groups, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); + return NULL; + } + if (rfg->rfp_cfg == NULL && size > 0) + { + rfg->rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size); + zlog_debug ("%s: allocated, size=%d", __func__, size); + + } + return rfg->rfp_cfg; +} + +/*------------------------------------------ + * rfapi_rfp_init_group_config_ptr_vty + * + * This is used to init or return a previously init'ed group specific + * configuration pointer. Group is identified by vty context. + * NOTE: size is ignored when a previously init'ed value is returned. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * size number of bytes to allocation + * + * output: + * none + * + * return value: + * rfp_cfg_group NULL or Pointer to configuration structure +--------------------------------------------*/ +void * +rfapi_rfp_init_group_config_ptr_vty ( + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty, + uint32_t size) +{ + struct bgp *bgp; + void *ret = NULL; + + if (rfp_start_val == NULL || vty == NULL) + return NULL; + + bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + if (!bgp || !bgp->rfapi_cfg || !vty->index_sub) + return NULL; + + switch (type) + { + case RFAPI_RFP_CFG_GROUP_DEFAULT: + ret = rfapi_rfp_get_or_init_group_config_default (bgp->rfapi_cfg, + vty, size); + break; + case RFAPI_RFP_CFG_GROUP_NVE: + ret = rfapi_rfp_get_or_init_group_config_nve (bgp->rfapi_cfg, + vty, size); + break; + case RFAPI_RFP_CFG_GROUP_L2: + ret = rfapi_rfp_get_or_init_group_config_l2 (bgp->rfapi_cfg, vty, size); + break; + default: + zlog_err ("%s: Unknown group type=%d", __func__, type); + /* should never happen */ + assert ("Unknown type" == NULL); + break; + } + return ret; +} + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_vty + * + * This is used to get group specific configuration pointer. + * Group is identified by type and vty context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +void * +rfapi_rfp_get_group_config_ptr_vty ( + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty) +{ + return rfapi_rfp_init_group_config_ptr_vty (rfp_start_val, type, vty, 0); +} + +static void * +rfapi_rfp_get_group_config_name_nve ( + struct rfapi_cfg *rfc, + const char *name, + void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct rfapi_nve_group_cfg *rfg; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (rfc->nve_groups_sequential, node, rfg)) + { + if (!strcmp (rfg->name, name) && /* name match */ + (search_cb == NULL || !search_cb (criteria, rfg->rfp_cfg))) + return rfg->rfp_cfg; + } + return NULL; +} + +static void * +rfapi_rfp_get_group_config_name_l2 ( + struct rfapi_cfg *rfc, + const char *name, + void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct rfapi_l2_group_cfg *rfg; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (rfc->l2_groups, node, rfg)) + { + if (!strcmp (rfg->name, name) && /* name match */ + (search_cb == NULL || !search_cb (criteria, rfg->rfp_cfg))) + return rfg->rfp_cfg; + } + return NULL; +} + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_name + * + * This is used to get group specific configuration pointer. + * Group is identified by type and name context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * name group name + * criteria RFAPI caller provided serach criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +void * +rfapi_rfp_get_group_config_ptr_name ( + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + const char *name, + void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct bgp *bgp; + void *ret = NULL; + + if (rfp_start_val == NULL || name == NULL) + return NULL; + + bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + if (!bgp || !bgp->rfapi_cfg) + return NULL; + + switch (type) + { + case RFAPI_RFP_CFG_GROUP_DEFAULT: + ret = bgp->rfapi_cfg->default_rfp_cfg; + break; + case RFAPI_RFP_CFG_GROUP_NVE: + ret = rfapi_rfp_get_group_config_name_nve (bgp->rfapi_cfg, + name, criteria, search_cb); + break; + case RFAPI_RFP_CFG_GROUP_L2: + ret = rfapi_rfp_get_group_config_name_l2 (bgp->rfapi_cfg, + name, criteria, search_cb); + break; + default: + zlog_err ("%s: Unknown group type=%d", __func__, type); + /* should never happen */ + assert ("Unknown type" == NULL); + break; + } + return ret; +} + +/*------------------------------------------ + * rfapi_rfp_get_l2_group_config_ptr_lni + * + * This is used to get group specific configuration pointer. + * Group is identified by type and logical network identifier. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * logical_net_id group logical network identifier + * criteria RFAPI caller provided serach criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +void * +rfapi_rfp_get_l2_group_config_ptr_lni ( + void *rfp_start_val, + uint32_t logical_net_id, + void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct bgp *bgp; + struct rfapi_l2_group_cfg *rfg; + struct listnode *node; + + if (rfp_start_val == NULL) + return NULL; + + bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); + if (!bgp || !bgp->rfapi_cfg) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->l2_groups, node, rfg)) + { + if (rfg->logical_net_id == logical_net_id && + (search_cb == NULL || !search_cb (criteria, rfg->rfp_cfg))) + { + if (rfg->rfp_cfg == NULL) + zlog_debug ("%s: returning rfp group config for lni=0", __func__); + return rfg->rfp_cfg; + } + } + return NULL; +} diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h new file mode 100644 index 0000000000..6e1baa60c1 --- /dev/null +++ b/bgpd/rfapi/rfapi.h @@ -0,0 +1,980 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_H +#define _QUAGGA_BGP_RFAPI_H + +#if ENABLE_BGP_VNC + +#include +#include +#include +#include "vty.h" +#include "prefix.h" +#include "../bgpd.h" +#include "../bgp_encap_types.h" + +/* probably ought to have a field-specific define in config.h */ +# ifndef s6_addr32 /* for solaris/bsd */ +# ifdef SOLARIS_IPV6 +# define s6_addr32 _S6_un._S6_u32 +# else +# define s6_addr32 __u6_addr.__u6_addr32 +# endif +# endif + +#define RFAPI_V4_ADDR 0x04 +#define RFAPI_V6_ADDR 0x06 +#define RFAPI_SHOW_STR "VNC information\n" + +struct rfapi_ip_addr +{ + uint8_t addr_family; /* AF_INET | AF_INET6 */ + union + { + struct in_addr v4; /* in network order */ + struct in6_addr v6; /* in network order */ + } addr; +}; + +struct rfapi_ip_prefix +{ + uint8_t length; + uint8_t cost; /* bgp local pref = 255 - cost */ + struct rfapi_ip_addr prefix; +}; + +struct rfapi_nexthop +{ + struct prefix addr; + uint8_t cost; +}; + +struct rfapi_next_hop_entry +{ + struct rfapi_next_hop_entry *next; + struct rfapi_ip_prefix prefix; + uint32_t lifetime; + struct rfapi_ip_addr un_address; + struct rfapi_ip_addr vn_address; + struct rfapi_vn_option *vn_options; + struct rfapi_un_option *un_options; +}; + +#define RFAPI_REMOVE_RESPONSE_LIFETIME 0 +#define RFAPI_INFINITE_LIFETIME 0xFFFFFFFF + +struct rfapi_l2address_option +{ + struct ethaddr macaddr; /* use 0 to assign label to IP prefix */ + uint32_t label; /* 20bit label in low bits, no TC, S, or TTL */ + uint32_t logical_net_id; /* ~= EVPN Ethernet Segment Id, + must not be zero for mac regis. */ + uint8_t local_nve_id; +}; + +typedef enum +{ + RFAPI_UN_OPTION_TYPE_PROVISIONAL, /* internal use only */ + RFAPI_UN_OPTION_TYPE_TUNNELTYPE, +} rfapi_un_option_type; + +struct rfapi_tunneltype_option +{ + bgp_encap_types type; + union + { + struct bgp_encap_type_reserved reserved; + struct bgp_encap_type_l2tpv3_over_ip l2tpv3_ip; + struct bgp_encap_type_gre gre; + struct bgp_encap_type_transmit_tunnel_endpoint transmit_tunnel_endpoint; + struct bgp_encap_type_ipsec_in_tunnel_mode ipsec_tunnel; + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode ip_ipsec; + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode mpls_ipsec; + struct bgp_encap_type_ip_in_ip ip_ip; + struct bgp_encap_type_vxlan vxlan; + struct bgp_encap_type_nvgre nvgre; + struct bgp_encap_type_mpls mpls; + struct bgp_encap_type_mpls_in_gre mpls_gre; + struct bgp_encap_type_vxlan_gpe vxlan_gpe; + struct bgp_encap_type_mpls_in_udp mpls_udp; + struct bgp_encap_type_pbb pbb; + } bgpinfo; +}; + +struct rfapi_un_option +{ + struct rfapi_un_option *next; + rfapi_un_option_type type; + union + { + struct rfapi_tunneltype_option tunnel; + } v; +}; + +typedef enum +{ + RFAPI_VN_OPTION_TYPE_L2ADDR = 3, /* Layer 2 address, 3 for legacy compatibility */ + RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP, /* for static routes */ + RFAPI_VN_OPTION_TYPE_INTERNAL_RD, /* internal use only */ +} rfapi_vn_option_type; + +struct rfapi_vn_option +{ + struct rfapi_vn_option *next; + + rfapi_vn_option_type type; + + union + { + struct rfapi_l2address_option l2addr; + + /* + * If this option is present, the next hop is local to the + * client NVE (i.e., not via a tunnel). + */ + struct rfapi_nexthop local_nexthop; + + /* + * For rfapi internal use only + */ + struct prefix_rd internal_rd; + } v; +}; + +struct rfapi_l2address_option_match +{ + struct rfapi_l2address_option o; + uint32_t flags; + +#define RFAPI_L2O_MACADDR 0x00000001 +#define RFAPI_L2O_LABEL 0x00000002 +#define RFAPI_L2O_LNI 0x00000004 +#define RFAPI_L2O_LHI 0x00000008 +}; + +#define VNC_CONFIG_STR "VNC/RFP related configuration\n" + +typedef void *rfapi_handle; + +/*********************************************************************** + * RFP Callbacks + ***********************************************************************/ +/*------------------------------------------ + * rfapi_response_cb_t (callback typedef) + * + * Callbacks of this type are used to provide asynchronous + * route updates from RFAPI to the RFP client. + * + * response_cb + * called to notify the rfp client that a next hop list + * that has previously been provided in response to an + * rfapi_query call has been updated. Deleted routes are indicated + * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME. + * + * By default, the routes an NVE receives via this callback include + * its own routes (that it has registered). However, these may be + * filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP + * flag is set. + * + * local_cb + * called to notify the rfp client that a local route + * has been added or deleted. Deleted routes are indicated + * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME. + * + * input: + * next_hops a list of possible next hops. + * This is a linked list allocated within the + * rfapi. The response_cb callback function is responsible + * for freeing this memory via rfapi_free_next_hop_list() + * in order to avoid memory leaks. + * + * userdata value (cookie) originally specified in call to + * rfapi_open() + * + *------------------------------------------*/ +typedef void (rfapi_response_cb_t) (struct rfapi_next_hop_entry * next_hops, + void *userdata); + +/*------------------------------------------ + * rfapi_nve_close_cb_t (callback typedef) + * + * Callbacks of this type are used to provide asynchronous + * notification that an rfapi_handle was invalidated + * + * input: + * pHandle Firmerly valid rfapi_handle returned to + * client via rfapi_open(). + * + * reason EIDRM handle administratively closed (clear nve ...) + * ESTALE handle invalidated by configuration change + * + *------------------------------------------*/ +typedef void (rfapi_nve_close_cb_t) (rfapi_handle pHandle, int reason); + +/*------------------------------------------ + * rfp_cfg_write_cb_t (callback typedef) + * + * This callback is used to generate output for any config parameters + * that may supported by RFP via RFP defined vty commands at the bgp + * level. See loglevel as an example. + * + * input: + * vty -- quagga vty context + * rfp_start_val -- value returned by rfp_start + * + * output: + * to vty, rfp related configuration + * + * return value: + * lines written +--------------------------------------------*/ +typedef int (rfp_cfg_write_cb_t) (struct vty * vty, void *rfp_start_val); + +/*------------------------------------------ + * rfp_cfg_group_write_cb_t (callback typedef) + * + * This callback is used to generate output for any config parameters + * that may supported by RFP via RFP defined vty commands at the + * L2 or NVE level. See loglevel as an example. + * + * input: + * vty quagga vty context + * rfp_start_val value returned by rfp_start + * type group type + * name group name + * rfp_cfg_group Pointer to configuration structure + * + * output: + * to vty, rfp related configuration + * + * return value: + * lines written +--------------------------------------------*/ +typedef enum +{ + RFAPI_RFP_CFG_GROUP_DEFAULT, + RFAPI_RFP_CFG_GROUP_NVE, + RFAPI_RFP_CFG_GROUP_L2 +} rfapi_rfp_cfg_group_type; + +typedef int (rfp_cfg_group_write_cb_t) (struct vty * vty, + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + const char *name, + void *rfp_cfg_group); + +/*********************************************************************** + * Configuration related defines and structures + ***********************************************************************/ + +struct rfapi_rfp_cb_methods +{ + rfp_cfg_write_cb_t *cfg_cb; /* show top level config */ + rfp_cfg_group_write_cb_t *cfg_group_cb; /* show group level config */ + rfapi_response_cb_t *response_cb; /* unsolicited responses */ + rfapi_response_cb_t *local_cb; /* local route add/delete */ + rfapi_nve_close_cb_t *close_cb; /* handle closed */ + +}; + +/* + * If a route with infinite lifetime is withdrawn, this is + * how long (in seconds) to wait before expiring it (because + * RFAPI_LIFETIME_MULTIPLIER_PCT * infinity is too long to wait) + */ +#define RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY (60*120) + +/* + * the factor that should be applied to a prefix's value + * before using it to expire a withdrawn prefix, expressed as a percent. + * Thus, a value of 100 means to use the exact value of , + * a value of 200 means to use twice the value of , etc. + */ +#define RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR 150 + +/* + * This is used by rfapi to determine if RFP is using/supports + * a partial (i.e., cache) or full table download approach for + * mapping information. When full table download approach is + * used all information is passed to RFP after an initial + * rfapi_query. When partial table download is used, only + * information matching a query is passed. + */ +typedef enum +{ + RFAPI_RFP_DOWNLOAD_PARTIAL = 0, + RFAPI_RFP_DOWNLOAD_FULL +} rfapi_rfp_download_type; + +#define RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL 1 + +struct rfapi_rfp_cfg +{ + /* partial or full table download */ + rfapi_rfp_download_type download_type; /* default=partial */ + /* + * When full-table-download is enabled, this is the minimum + * number of seconds between times a non-queried prefix will + * be updated to a particular NVE. + * default: RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL + */ + uint32_t ftd_advertisement_interval; + /* + * percentage of registration lifetime to continue to use information + * post soft-state refresh timeout + default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR + */ + uint32_t holddown_factor; + /* Control generation of updated RFP responses */ + uint8_t use_updated_response; /* default=0/no */ + /* when use_updated_response, also generate remove responses */ + uint8_t use_removes; /* default=0/no */ +}; + +/*********************************************************************** + * Process related functions -- MUST be provided by the RFAPI user <<=== + ***********************************************************************/ + +/*------------------------------------------ + * rfp_start + * + * This function will start the RFP code + * + * input: + * master quagga thread_master to tie into bgpd threads + * + * output: + * cfgp Pointer to rfapi_rfp_cfg (null = use defaults), + * copied by caller, updated via rfp_set_configuration + * cbmp Pointer to rfapi_rfp_cb_methods, may be null + * copied by caller, updated via rfapi_rfp_set_cb_methods + * return value: + * rfp_start_val rfp returned value passed on rfp_stop and other rfapi calls +--------------------------------------------*/ +extern void * +rfp_start ( + struct thread_master *master, + struct rfapi_rfp_cfg **cfgp, + struct rfapi_rfp_cb_methods **cbmp); + +/*------------------------------------------ + * rfp_stop + * + * This function is called on shutdown to trigger RFP cleanup + * + * input: + * rfp_start_val + * + * output: + * none + * + * return value: +--------------------------------------------*/ +extern void +rfp_stop (void *rfp_start_val); + +/*********************************************************************** + * RFP processing behavior configuration + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_rfp_set_configuration + * + * This is used to change rfapi's processing behavior based on + * RFP requirements. + * + * input: + * rfp_start_val value returned by rfp_start + * rfapi_rfp_cfg Pointer to configuration structure + * + * output: + * none + * + * return value: + * 0 Success + * ENXIO Unabled to locate configured BGP/VNC +--------------------------------------------*/ +extern int +rfapi_rfp_set_configuration ( + void *rfp_start_val, + struct rfapi_rfp_cfg *rfp_cfg); + +/*------------------------------------------ + * rfapi_rfp_set_cb_methods + * + * Change registered callback functions for asynchronous notifications + * from RFAPI to the RFP client. + * + * input: + * rfp_start_val value by rfp_start + * methods Pointer to struct rfapi_rfp_cb_methods containing + * pointers to callback methods as described above + * + * return value: + * 0 Success + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +extern int +rfapi_rfp_set_cb_methods ( + void *rfp_start_val, + struct rfapi_rfp_cb_methods *methods); + +/*********************************************************************** + * RFP group specific configuration + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_rfp_init_group_config_ptr_vty + * + * This is used to init or return a previously init'ed group specific + * configuration pointer. Group is identified by vty context. + * NOTE: size is ignored when a previously init'ed value is returned. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * size number of bytes to allocation + * + * output: + * none + * + * return value: + * rfp_cfg_group NULL or Pointer to configuration structure +--------------------------------------------*/ +extern void * +rfapi_rfp_init_group_config_ptr_vty ( + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty, + uint32_t size); + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_vty + * + * This is used to get group specific configuration pointer. + * Group is identified by type and vty context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +extern void * +rfapi_rfp_get_group_config_ptr_vty ( + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty); + +/*------------------------------------------ + * rfp_group_config_search_cb_t (callback typedef) + * + * This callback is used to called from within a + * rfapi_rfp_get_group_config_ptr to check if the rfp_cfg_group + * matches the search criteria + * + * input: + * criteria RFAPI caller provided serach criteria + * rfp_cfg_group Pointer to configuration structure | NULL + * + * output: + * + * return value: + * 0 Match/Success + * ENOENT No matching +--------------------------------------------*/ +typedef int (rfp_group_config_search_cb_t) (void *criteria, + void *rfp_cfg_group); + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_name + * + * This is used to get group specific configuration pointer. + * Group is identified by type and name context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * name group name + * criteria RFAPI caller provided serach criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +extern void * +rfapi_rfp_get_group_config_ptr_name ( + void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + const char *name, + void *criteria, + rfp_group_config_search_cb_t *search_cb); + +/*------------------------------------------ + * rfapi_rfp_get_l2_group_config_ptr_lni + * + * This is used to get group specific configuration pointer. + * Group is identified by type and logical network identifier. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * logical_net_id group logical network identifier + * criteria RFAPI caller provided serach criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +extern void * +rfapi_rfp_get_l2_group_config_ptr_lni ( + void *rfp_start_val, + uint32_t logical_net_id, + void *criteria, + rfp_group_config_search_cb_t *search_cb); + +/*********************************************************************** + * NVE Sessions + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_open + * + * This function initializes a NVE record and associates it with + * the specified VN and underlay network addresses + * + * input: + * rfp_start_val value returned by rfp_start + * vn NVE virtual network address + * + * un NVE underlay network address + * + * default_options Default options to use on registrations. + * For now only tunnel type is supported. + * May be overridden per-prefix in rfapi_register(). + * Caller owns (rfapi_open() does not free) + * + * response_cb Pointer to next hop list update callback function or + * NULL when no callbacks are desired. + * + * userdata Passed to subsequent response_cb invocations. + * + * output: + * response_lifetime The length of time that responses sent to this + * NVE are valid. + * + * pHandle pointer to location to store rfapi handle. The + * handle must be passed on subsequent rfapi_ calls. + * + * + * return value: + * 0 Success + * EEXIST NVE with this {vn,un} already open + * ENOENT No matching nve group config + * ENOMSG Matched nve group config was incomplete + * ENXIO BGP or VNC not configured + * EAFNOSUPPORT Matched nve group specifies auto-assignment of RD, + * but underlay network address is not IPv4 + * EDEADLK Called from within a callback procedure + *------------------------------------------*/ +extern int +rfapi_open ( + void *rfp_start_val, + struct rfapi_ip_addr *vn, + struct rfapi_ip_addr *un, + struct rfapi_un_option *default_options, + uint32_t *response_lifetime, + void *userdata, + rfapi_handle *pHandle); + + +/*------------------------------------------ + * rfapi_close + * + * Shut down NVE session and release associated data. Calling + * from within a rfapi callback procedure is permitted (the close + * will be completed asynchronously after the callback finishes). + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * + * output: + * + * return value: + * 0 Success + * EBADF invalid handle + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +extern int +rfapi_close (rfapi_handle rfd); + +/*------------------------------------------ + * rfapi_check + * + * Test rfapi descriptor + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * + * output: + * + * return value: + * 0 Success: handle is valid and usable + * EINVAL null argument + * ESTALE formerly valid handle invalidated by config, needs close + * EBADF invalid handle + * ENXIO BGP or VNC not configured + * EAFNOSUPPORT Internal addressing error + *------------------------------------------*/ +extern int +rfapi_check (rfapi_handle rfd); + +/*********************************************************************** + * NVE Routes + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_query + * + * This function queries the RIB for a + * particular route. Note that this call may result in subsequent + * callbacks to response_cb. Response callbacks can be cancelled + * by calling rfapi_query_done. A duplicate query using the same target + * will result in only one callback per change in next_hops. (i.e., + * cancel/replace the prior query results.) + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * target: the destination address + * l2o ptr to L2 Options struct, NULL if not present in query + * + * output: + * ppNextHopEntry pointer to a location to store a pointer + * to the returned list of nexthops. It is the + * caller's responsibility to free this list + * via rfapi_free_next_hop_list(). + * + * + * return value: + * 0 Success + * EBADF invalid handle + * ENOENT no valid route + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure +--------------------------------------------*/ +extern int +rfapi_query ( + rfapi_handle rfd, + struct rfapi_ip_addr *target, + struct rfapi_l2address_option *l2o, + struct rfapi_next_hop_entry **ppNextHopEntry); + +/*------------------------------------------ + * rfapi_query_done + * + * Notifies the rfapi that the user is no longer interested + * in the specified target. + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * target: the destination address + * + * output: + * + * return value: + * 0 Success + * EBADF invalid handle + * ENOENT no match found for target + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure +--------------------------------------------*/ +extern int +rfapi_query_done (rfapi_handle rfd, struct rfapi_ip_addr *target); + +/*------------------------------------------ + * rfapi_query_done_all + * + * Notifies the rfapi that the user is no longer interested + * in any target. + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * + * output: + * count: number of queries cleared + * + * return value: + * 0 Success + * EBADF invalid handle + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure +--------------------------------------------*/ +extern int +rfapi_query_done_all (rfapi_handle rfd, int *count); + +/*------------------------------------------ + * rfapi_register + * + * Requests that reachability to the indicated prefix via this NVE + * be advertised by BGP. If is non-zero, then the previously- + * advertised prefix should be withdrawn. + * + * (This function should NOT be called if the rfapi_open() function + * returns NULL) + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * prefix: A prefix to be registered or deregistered + * lifetime Prefix lifetime in seconds, host byte order + * options_un underlay netowrk options, may include tunnel-type + * Caller owns (rfapi_register() does not free). + * options_vn virtual network options, may include layer 2 address + * option and local-nexthop option + * Caller owns (rfapi_register() does not free). + * + * action: RFAPI_REGISTER_ADD add the route + * RFAPI_REGISTER_WITHDRAW withdraw route + * RFAPI_REGISTER_KILL withdraw without holddown + * + * return value: + * 0 Success + * EBADF invalid handle + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure + --------------------------------------------*/ + +typedef enum +{ + RFAPI_REGISTER_ADD, + RFAPI_REGISTER_WITHDRAW, + RFAPI_REGISTER_KILL +} rfapi_register_action; + +extern int +rfapi_register ( + rfapi_handle rfd, + struct rfapi_ip_prefix *prefix, + uint32_t lifetime, + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + rfapi_register_action action); + +/*********************************************************************** + * Helper / Utility functions + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_get_vn_addr + * + * Get the virtual network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * vn NVE virtual network address + *------------------------------------------*/ +extern struct rfapi_ip_addr * +rfapi_get_vn_addr (void *); + +/*------------------------------------------ + * rfapi_get_un_addr + * + * Get the underlay network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * un NVE underlay network address + *------------------------------------------*/ +extern struct rfapi_ip_addr * +rfapi_get_un_addr (void *); + +/*------------------------------------------ + * rfapi_error_str + * + * Returns a string describing the rfapi error code. + * + * input: + * + * code Error code returned by rfapi function + * + * returns: + * + * const char * String + *------------------------------------------*/ +extern const char * +rfapi_error_str (int code); + +/*------------------------------------------ + * rfapi_get_rfp_start_val + * + * Returns value passed to rfapi on rfp_start + * + * input: + * void * bgp structure + * + * returns: + * void * + *------------------------------------------*/ +extern void * +rfapi_get_rfp_start_val (void *bgpv); + +/*------------------------------------------ + * rfapi_compare_rfds + * + * Compare two generic rfapi descriptors. + * + * input: + * rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * 0 Mismatch + * 1 Match + *------------------------------------------*/ +extern int +rfapi_compare_rfds (void *rfd1, void *rfd2); + +/*------------------------------------------ + * rfapi_free_next_hop_list + * + * Frees a next_hop_list returned by a rfapi_query invocation + * + * input: + * list: a pointer to a response list (as a + * struct rfapi_next_hop_entry) to free. + * + * output: + * + * return value: None + --------------------------------------------*/ +extern void +rfapi_free_next_hop_list (struct rfapi_next_hop_entry *list); + +/*------------------------------------------ + * rfapi_get_response_lifetime_default + * + * Returns the default lifetime for a response. + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * input: + * None + * + * output: + * + * return value: The bgp instance default lifetime for a response. + --------------------------------------------*/ +extern int +rfapi_get_response_lifetime_default (void *rfp_start_val); + +/*------------------------------------------ + * rfapi_is_vnc_configured + * + * Returns if VNC (BGP VPN messaging /VPN & encap SAFIs) are configured + * + * input: + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * output: + * + * return value: If VNC is configured for the bgpd instance + * 0 Success + * ENXIO VNC not configured + --------------------------------------------*/ +extern int +rfapi_is_vnc_configured (void *rfp_start_val); + +/*------------------------------------------ + * rfapi_bgp_lookup_by_rfp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * rfp_start_val value returned by rfp_startor + * NULL (=get default instance) + * + * output: + * none + * + * return value: + * bgp bgp instance pointer + * NULL = not found + * + --------------------------------------------*/ +extern struct bgp * +rfapi_bgp_lookup_by_rfp (void *rfp_start_val); + +/*------------------------------------------ + * rfapi_get_rfp_start_val_by_bgp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * bgp bgp instance pointer + * + * output: + * none + * + * return value: + * rfp_start_val + * NULL = not found + * + --------------------------------------------*/ +extern void * +rfapi_get_rfp_start_val_by_bgp (struct bgp *bgp); + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_RFAPI_H */ diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c new file mode 100644 index 0000000000..94b6dea8dc --- /dev/null +++ b/bgpd/rfapi/rfapi_ap.c @@ -0,0 +1,629 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "routemap.h" +#include "log.h" +#include "linklist.h" +#include "command.h" +#include "stream.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_mplsvpn.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgp_route.h" +#include "bgp_aspath.h" +#include "bgp_advertise.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_zebra.h" +#include "vnc_import_bgp.h" +#include "rfapi_rib.h" + +#include "rfapi_ap.h" + +/* + * Per-NVE Advertised prefixes + * + * We maintain a list of prefixes advertised by each NVE. + * There are two indices: by prefix and by lifetime. + * + * BY-PREFIX skiplist + * + * key: ptr to struct prefix (when storing, point to prefix that + * is part of rfapi_adb). + * + * value: ptr to struct rfapi_adb + * + * BY-LIFETIME skiplist + * + * key: ptr to struct rfapi_adb + * value: ptr to struct rfapi_adb + * + */ + +/* + * Skiplist sort function that sorts first according to lifetime + * and then according to adb pointer value. The adb pointer + * is used to spread out the sort for adbs with the same lifetime + * and thereby make the skip list operations more efficient. + */ +static int +sl_adb_lifetime_cmp (void *adb1, void *adb2) +{ + struct rfapi_adb *a1 = adb1; + struct rfapi_adb *a2 = adb2; + + if (a1->lifetime < a2->lifetime) + return -1; + if (a1->lifetime > a2->lifetime) + return 1; + + if (a1 < a2) + return -1; + if (a1 > a2) + return 1; + + return 0; +} + + +void +rfapiApInit (struct rfapi_advertised_prefixes *ap) +{ + ap->ipN_by_prefix = skiplist_new (0, vnc_prefix_cmp, NULL); + ap->ip0_by_ether = skiplist_new (0, vnc_prefix_cmp, NULL); + ap->by_lifetime = skiplist_new (0, sl_adb_lifetime_cmp, NULL); +} + +void +rfapiApRelease (struct rfapi_advertised_prefixes *ap) +{ + struct rfapi_adb *adb; + + /* Free ADBs and lifetime items */ + while (0 == skiplist_first (ap->by_lifetime, NULL, (void **) &adb)) + { + rfapiAdbFree (adb); + skiplist_delete_first (ap->by_lifetime); + } + + while (0 == skiplist_delete_first (ap->ipN_by_prefix)); + while (0 == skiplist_delete_first (ap->ip0_by_ether)); + + /* Free lists */ + skiplist_free (ap->ipN_by_prefix); + skiplist_free (ap->ip0_by_ether); + skiplist_free (ap->by_lifetime); + + ap->ipN_by_prefix = NULL; + ap->ip0_by_ether = NULL; + ap->by_lifetime = NULL; +} + +int +rfapiApCount (struct rfapi_descriptor *rfd) +{ + if (!rfd->advertised.by_lifetime) + return 0; + + return skiplist_count (rfd->advertised.by_lifetime); +} + +int +rfapiApCountAll (struct bgp *bgp) +{ + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + int total = 0; + + h = bgp->rfapi; + if (h) + { + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + total += rfapiApCount (rfd); + } + } + return total; +} + + +void +rfapiApReadvertiseAll (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct rfapi_adb *adb; + void *cursor; + int rc; + + for (rc = + skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb, + &cursor); rc == 0; + rc = + skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb, + &cursor)) + { + + struct prefix_rd prd; + uint32_t local_pref = rfp_cost_to_localpref (adb->cost); + + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * TBD this is not quite right. When pfx_ip is 0/32 or 0/128, + * we need to substitute the VN address as the prefix + */ + add_vnc_route (rfd, bgp, SAFI_MPLS_VPN, &adb->prefix_ip, &prd, /* RD to use (0 for ENCAP) */ + &rfd->vn_addr, /* nexthop */ + &local_pref, &adb->lifetime, NULL, NULL, /* struct rfapi_un_option */ + NULL, /* struct rfapi_vn_option */ + rfd->rt_export_list, NULL, /* med */ + NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0); + } +} + +void +rfapiApWithdrawAll (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct rfapi_adb *adb; + void *cursor; + int rc; + + + cursor = NULL; + for (rc = + skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb, + &cursor); rc == 0; + rc = + skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb, + &cursor)) + { + + struct prefix pfx_vn_buf; + struct prefix *pfx_ip; + + if (!(RFAPI_0_PREFIX (&adb->prefix_ip) && + RFAPI_HOST_PREFIX (&adb->prefix_ip))) + { + + pfx_ip = &adb->prefix_ip; + + } + else + { + + pfx_ip = NULL; + + /* + * 0/32 or 0/128 => mac advertisement + */ + if (rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn_buf)) + { + /* + * Bad: it means we can't delete the route + */ + zlog_debug ("%s: BAD: handle has bad vn_addr: skipping", + __func__); + continue; + } + } + + del_vnc_route (rfd, rfd->peer, bgp, SAFI_MPLS_VPN, pfx_ip ? pfx_ip : &pfx_vn_buf, &adb->prd, /* RD to use (0 for ENCAP) */ + ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); + } +} + +/* + * returns nonzero if tunnel readvertisement is needed, 0 otherwise + */ +static int +rfapiApAdjustLifetimeStats ( + struct rfapi_descriptor *rfd, + uint32_t *old_lifetime, /* set if removing/replacing */ + uint32_t *new_lifetime) /* set if replacing/adding */ +{ + int advertise = 0; + int find_max = 0; + int find_min = 0; + + zlog_debug ("%s: rfd=%p, pOldLife=%p, pNewLife=%p", + __func__, rfd, old_lifetime, new_lifetime); + if (old_lifetime) + zlog_debug ("%s: OldLife=%d", __func__, *old_lifetime); + if (new_lifetime) + zlog_debug ("%s: NewLife=%d", __func__, *new_lifetime); + + if (new_lifetime) + { + /* + * Adding new lifetime + */ + if (old_lifetime) + { + /* + * replacing existing lifetime + */ + + + /* old and new are same */ + if (*old_lifetime == *new_lifetime) + return 0; + + if (*old_lifetime == rfd->min_prefix_lifetime) + { + find_min = 1; + } + if (*old_lifetime == rfd->max_prefix_lifetime) + { + find_max = 1; + } + + /* no need to search if new value is at or equals min|max */ + if (*new_lifetime <= rfd->min_prefix_lifetime) + { + rfd->min_prefix_lifetime = *new_lifetime; + find_min = 0; + } + if (*new_lifetime >= rfd->max_prefix_lifetime) + { + rfd->max_prefix_lifetime = *new_lifetime; + advertise = 1; + find_max = 0; + } + + } + else + { + /* + * Just adding new lifetime + */ + if (*new_lifetime < rfd->min_prefix_lifetime) + { + rfd->min_prefix_lifetime = *new_lifetime; + } + if (*new_lifetime > rfd->max_prefix_lifetime) + { + advertise = 1; + rfd->max_prefix_lifetime = *new_lifetime; + } + + } + } + else + { + /* + * Deleting + */ + + /* + * See if the max prefix lifetime for this NVE has decreased. + * The easy optimization: track min & max; walk the table only + * if they are different. + * The general optimization: index the advertised_prefixes + * table by lifetime. + * + * Note: for a given nve_descriptor, only one of the + * advertised_prefixes[] tables will be used: viz., the + * address family that matches the VN address. + * + */ + if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime) + { + + /* + * Common case: all lifetimes are the same. Only + * thing we need to do here is check if there are + * no exported routes left. In that case, reinitialize + * the max and min values. + */ + if (!rfapiApCount (rfd)) + { + rfd->max_prefix_lifetime = 0; + rfd->min_prefix_lifetime = UINT32_MAX; + } + + + } + else + { + if (old_lifetime) + { + if (*old_lifetime == rfd->min_prefix_lifetime) + { + find_min = 1; + } + if (*old_lifetime == rfd->max_prefix_lifetime) + { + find_max = 1; + } + } + } + } + + if (find_min || find_max) + { + uint32_t min = UINT32_MAX; + uint32_t max = 0; + + struct rfapi_adb *adb_min; + struct rfapi_adb *adb_max; + + if (!skiplist_first + (rfd->advertised.by_lifetime, (void **) &adb_min, NULL) + && !skiplist_last (rfd->advertised.by_lifetime, (void **) &adb_max, + NULL)) + { + + /* + * This should always work + */ + min = adb_min->lifetime; + max = adb_max->lifetime; + + } + else + { + + void *cursor; + struct prefix *prefix; + struct rfapi_adb *adb; + int rc; + + zlog_debug ("%s: walking to find new min/max", __func__); + + cursor = NULL; + for (rc = skiplist_next (rfd->advertised.ipN_by_prefix, + (void **) &prefix, (void **) &adb, + &cursor); !rc; + rc = + skiplist_next (rfd->advertised.ipN_by_prefix, + (void **) &prefix, (void **) &adb, &cursor)) + { + + uint32_t lt = adb->lifetime; + + if (lt > max) + max = lt; + if (lt < min) + min = lt; + } + cursor = NULL; + for (rc = skiplist_next (rfd->advertised.ip0_by_ether, + (void **) &prefix, (void **) &adb, + &cursor); !rc; + rc = + skiplist_next (rfd->advertised.ip0_by_ether, (void **) &prefix, + (void **) &adb, &cursor)) + { + + uint32_t lt = adb->lifetime; + + if (lt > max) + max = lt; + if (lt < min) + min = lt; + } + } + + /* + * trigger tunnel route update + * but only if we found a VPN route and it had + * a lifetime greater than 0 + */ + if (max && rfd->max_prefix_lifetime != max) + advertise = 1; + rfd->max_prefix_lifetime = max; + rfd->min_prefix_lifetime = min; + } + + zlog_debug ("%s: returning advertise=%d, min=%d, max=%d", + __func__, advertise, rfd->min_prefix_lifetime, + rfd->max_prefix_lifetime); + + return (advertise != 0); +} + +/* + * Return Value + * + * 0 No need to advertise tunnel route + * non-0 advertise tunnel route + */ +int +rfapiApAdd ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, + struct prefix *pfx_eth, + struct prefix_rd *prd, + uint32_t lifetime, + uint8_t cost, + struct rfapi_l2address_option *l2o) /* other options TBD */ +{ + int rc; + struct rfapi_adb *adb; + uint32_t old_lifetime = 0; + int use_ip0 = 0; + + if (RFAPI_0_PREFIX (pfx_ip) && RFAPI_HOST_PREFIX (pfx_ip)) + { + use_ip0 = 1; + assert (pfx_eth); + + rc = + skiplist_search (rfd->advertised.ip0_by_ether, pfx_eth, + (void **) &adb); + + } + else + { + + /* find prefix in advertised prefixes list */ + rc = + skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip, + (void **) &adb); + } + + + if (rc) + { + /* Not found */ + adb = XCALLOC (MTYPE_RFAPI_ADB, sizeof (struct rfapi_adb)); + assert (adb); + adb->lifetime = lifetime; + adb->prefix_ip = *pfx_ip; + if (pfx_eth) + adb->prefix_eth = *pfx_eth; + + if (use_ip0) + { + assert (pfx_eth); + skiplist_insert (rfd->advertised.ip0_by_ether, &adb->prefix_eth, + adb); + } + else + { + skiplist_insert (rfd->advertised.ipN_by_prefix, &adb->prefix_ip, + adb); + } + + skiplist_insert (rfd->advertised.by_lifetime, adb, adb); + } + else + { + old_lifetime = adb->lifetime; + if (old_lifetime != lifetime) + { + assert (!skiplist_delete (rfd->advertised.by_lifetime, adb, NULL)); + adb->lifetime = lifetime; + assert (!skiplist_insert (rfd->advertised.by_lifetime, adb, adb)); + } + + if (!use_ip0 && pfx_eth && prefix_cmp (&adb->prefix_eth, pfx_eth)) + { + /* mac address changed */ + adb->prefix_eth = *pfx_eth; + } + } + adb->cost = cost; + if (l2o) + adb->l2o = *l2o; + else + memset (&adb->l2o, 0, sizeof (struct rfapi_l2address_option)); + adb->prd = *prd; + + if (rfapiApAdjustLifetimeStats + (rfd, (rc ? NULL : &old_lifetime), &lifetime)) + return 1; + + return 0; +} + +/* + * After this function returns successfully, caller should call + * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce() + */ +int +rfapiApDelete ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, + struct prefix *pfx_eth, + int *advertise_tunnel) /* out */ +{ + int rc; + struct rfapi_adb *adb; + uint32_t old_lifetime; + int use_ip0 = 0; + + if (advertise_tunnel) + *advertise_tunnel = 0; + + /* find prefix in advertised prefixes list */ + if (RFAPI_0_PREFIX (pfx_ip) && RFAPI_HOST_PREFIX (pfx_ip)) + { + use_ip0 = 1; + assert (pfx_eth); + + rc = + skiplist_search (rfd->advertised.ip0_by_ether, pfx_eth, + (void **) &adb); + + } + else + { + + /* find prefix in advertised prefixes list */ + rc = + skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip, + (void **) &adb); + } + + if (rc) + { + return ENOENT; + } + + old_lifetime = adb->lifetime; + + if (use_ip0) + { + rc = skiplist_delete (rfd->advertised.ip0_by_ether, pfx_eth, NULL); + } + else + { + rc = skiplist_delete (rfd->advertised.ipN_by_prefix, pfx_ip, NULL); + } + assert (!rc); + + rc = skiplist_delete (rfd->advertised.by_lifetime, adb, NULL); + assert (!rc); + + rfapiAdbFree (adb); + + if (rfapiApAdjustLifetimeStats (rfd, &old_lifetime, NULL)) + { + if (advertise_tunnel) + *advertise_tunnel = 1; + } + + return 0; +} diff --git a/bgpd/rfapi/rfapi_ap.h b/bgpd/rfapi/rfapi_ap.h new file mode 100644 index 0000000000..3bb08a4b17 --- /dev/null +++ b/bgpd/rfapi/rfapi_ap.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#ifndef _QUAGGA_BGP_RFAPI_AP_H +#define _QUAGGA_BGP_RFAPI_AP_H + +/* TBD delete some of these #includes */ + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "routemap.h" +#include "log.h" +#include "linklist.h" +#include "command.h" +#include "stream.h" + +#include "bgpd.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgp_route.h" +#include "bgp_aspath.h" +#include "bgp_advertise.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_zebra.h" +#include "vnc_import_bgp.h" +#include "rfapi_rib.h" + + +extern void +rfapiApInit (struct rfapi_advertised_prefixes *ap); + +extern void +rfapiApRelease (struct rfapi_advertised_prefixes *ap); + +extern int +rfapiApCount (struct rfapi_descriptor *rfd); + + +extern int +rfapiApCountAll (struct bgp *bgp); + +extern void +rfapiApReadvertiseAll (struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void +rfapiApWithdrawAll (struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern int +rfapiApAdd ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, + struct prefix *pfx_eth, + struct prefix_rd *prd, + uint32_t lifetime, + uint8_t cost, + struct rfapi_l2address_option *l2o); /* other options TBD */ + +extern int +rfapiApDelete ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, + struct prefix *pfx_eth, + int *advertise_tunnel); /* out */ + + +#endif /* _QUAGGA_BGP_RFAPI_AP_H */ diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h new file mode 100644 index 0000000000..451f5c20c7 --- /dev/null +++ b/bgpd/rfapi/rfapi_backend.h @@ -0,0 +1,92 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_BACKEND_H +#define _QUAGGA_BGP_RFAPI_BACKEND_H + +#if ENABLE_BGP_VNC + +#include "bgp_route.h" +#include "bgp_nexthop.h" + +extern void rfapi_init (void); +extern void vnc_zebra_init (struct thread_master *master); +extern void vnc_zebra_destroy (void); + +extern void rfapi_delete (struct bgp *); + +struct rfapi *bgp_rfapi_new (struct bgp *bgp); +void bgp_rfapi_destroy (struct bgp *bgp, struct rfapi *h); + +struct rfapi_import_table *rfapiImportTableRefAdd (struct bgp *bgp, + struct ecommunity + *rt_import_list); + +void +rfapiImportTableRefDelByIt (struct bgp *bgp, + struct rfapi_import_table *it_target); + + +extern void +rfapiProcessUpdate (struct peer *peer, + void *rfd, + struct prefix *p, + struct prefix_rd *prd, + struct attr *attr, + afi_t afi, + safi_t safi, + u_char type, u_char sub_type, uint32_t * label); + + +extern void +rfapiProcessWithdraw (struct peer *peer, + void *rfd, + struct prefix *p, + struct prefix_rd *prd, + struct attr *attr, + afi_t afi, safi_t safi, u_char type, int kill); + +extern void rfapiProcessPeerDown (struct peer *peer); + +extern void +vnc_zebra_announce (struct prefix *p, + struct bgp_info *new_select, struct bgp *bgp); + +extern void +vnc_zebra_withdraw (struct prefix *p, struct bgp_info *old_select); + + +extern void +rfapi_vty_out_vncinfo (struct vty *vty, + struct prefix *p, struct bgp_info *bi, safi_t safi); + + +extern void vnc_direct_bgp_vpn_enable (struct bgp *bgp, afi_t afi); + +extern void vnc_direct_bgp_vpn_disable (struct bgp *bgp, afi_t afi); + +extern void vnc_direct_bgp_rh_vpn_enable (struct bgp *bgp, afi_t afi); + +extern void vnc_direct_bgp_rh_vpn_disable (struct bgp *bgp, afi_t afi); + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_RFAPI_BACKEND_H */ diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c new file mode 100644 index 0000000000..c2d11820e1 --- /dev/null +++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c @@ -0,0 +1,131 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "log.h" + +#include "bgpd.h" + +#include "rfapi.h" +#include "rfapi_private.h" +#include "rfapi_descriptor_rfp_utils.h" + + +void * +rfapi_create_generic (struct rfapi_ip_addr *vn, struct rfapi_ip_addr *un) +{ + struct rfapi_descriptor *rfd; + rfd = XCALLOC (MTYPE_RFAPI_DESC, sizeof (struct rfapi_descriptor)); + zlog_debug ("%s: rfd=%p", __func__, rfd); + rfd->vn_addr = *vn; + rfd->un_addr = *un; + return (void *) rfd; +} + +/*------------------------------------------ + * rfapi_free_generic + * + * Compare two generic rfapi descriptors. + * + * input: + * grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * + *------------------------------------------*/ +void +rfapi_free_generic (void *grfd) +{ + struct rfapi_descriptor *rfd; + rfd = (struct rfapi_descriptor *) grfd; + XFREE (MTYPE_RFAPI_DESC, rfd); +} + + +/*------------------------------------------ + * rfapi_compare_rfds + * + * Compare two generic rfapi descriptors. + * + * input: + * rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * 0 Mismatch + * 1 Match + *------------------------------------------*/ +int +rfapi_compare_rfds (void *rfd1, void *rfd2) +{ + struct rfapi_descriptor *rrfd1, *rrfd2; + int match = 0; + + rrfd1 = (struct rfapi_descriptor *) rfd1; + rrfd2 = (struct rfapi_descriptor *) rfd2; + + if (rrfd1->vn_addr.addr_family == rrfd2->vn_addr.addr_family) + { + if (rrfd1->vn_addr.addr_family == AF_INET) + match = IPV4_ADDR_SAME (&(rrfd1->vn_addr.addr.v4), + &(rrfd2->vn_addr.addr.v4)); + else + match = IPV6_ADDR_SAME (&(rrfd1->vn_addr.addr.v6), + &(rrfd2->vn_addr.addr.v6)); + } + + /* + * If the VN addresses don't match in all forms, + * give up. + */ + if (!match) + return 0; + + /* + * do the process again for the UN addresses. + */ + match = 0; + if (rrfd1->un_addr.addr_family == rrfd2->un_addr.addr_family) + { + /* VN addresses match + * UN address families match + * now check the actual UN addresses + */ + if (rrfd1->un_addr.addr_family == AF_INET) + match = IPV4_ADDR_SAME (&(rrfd1->un_addr.addr.v4), + &(rrfd2->un_addr.addr.v4)); + else + match = IPV6_ADDR_SAME (&(rrfd1->un_addr.addr.v6), + &(rrfd2->un_addr.addr.v6)); + } + return match; +} diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.h b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h new file mode 100644 index 0000000000..9067cdf54b --- /dev/null +++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +extern void *rfapi_create_generic (struct rfapi_ip_addr *vn, + struct rfapi_ip_addr *un); + +/*------------------------------------------ + * rfapi_free_generic + * + * Compare two generic rfapi descriptors. + * + * input: + * grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * + *------------------------------------------*/ +extern void rfapi_free_generic (void *grfd); diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c new file mode 100644 index 0000000000..17fee2cc0e --- /dev/null +++ b/bgpd/rfapi/rfapi_encap_tlv.c @@ -0,0 +1,812 @@ +/* + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include + +#include "bgpd.h" +#include "bgp_attr.h" + +#include "bgp_encap_types.h" +#include "bgp_encap_tlv.h" + +#include "rfapi.h" +#include "rfapi_encap_tlv.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "bgp_rfapi_cfg.h" + +static void +rfapi_add_endpoint_address_to_subtlv ( + struct bgp *bgp, + struct rfapi_ip_addr *ea, + struct bgp_tea_subtlv_remote_endpoint *subtlv) +{ + subtlv->family = ea->addr_family; + if (subtlv->family == AF_INET) + subtlv->ip_address.v4 = ea->addr.v4; + else + subtlv->ip_address.v6 = ea->addr.v6; + subtlv->as4 = htonl (bgp->as); +} + +bgp_encap_types +rfapi_tunneltype_option_to_tlv ( + struct bgp *bgp, + struct rfapi_ip_addr *ea, + struct rfapi_tunneltype_option *tto, + struct attr *attr, + int always_add) +{ + +#define _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ttype) \ + if ((always_add || (bgp->rfapi_cfg && \ + !CHECK_FLAG(bgp->rfapi_cfg->flags, \ + BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP))) && \ + ea && !CHECK_SUBTLV_FLAG(&tto->bgpinfo.ttype, \ + BGP_TEA_SUBTLV_REMOTE_ENDPOINT)) { \ + rfapi_add_endpoint_address_to_subtlv(bgp, ea, \ + &tto->bgpinfo.ttype.st_endpoint); \ + SET_SUBTLV_FLAG(&tto->bgpinfo.ttype, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); \ + } + + struct rfapi_tunneltype_option dto; + if (tto == NULL) + { /* create default type */ + tto = &dto; + memset (tto, 0, sizeof (dto)); + tto->type = RFAPI_BGP_ENCAP_TYPE_DEFAULT; + } + switch (tto->type) + { + case BGP_ENCAP_TYPE_L2TPV3_OVER_IP: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (l2tpv3_ip); + bgp_encap_type_l2tpv3overip_to_tlv (&tto->bgpinfo.l2tpv3_ip, attr); + break; + + case BGP_ENCAP_TYPE_GRE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (gre); + bgp_encap_type_gre_to_tlv (&tto->bgpinfo.gre, attr); + break; + + case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (transmit_tunnel_endpoint); + bgp_encap_type_transmit_tunnel_endpoint (&tto->bgpinfo.transmit_tunnel_endpoint, + attr); + break; + + case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (ipsec_tunnel); + bgp_encap_type_ipsec_in_tunnel_mode_to_tlv (&tto->bgpinfo.ipsec_tunnel, + attr); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (ip_ipsec); + bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv + (&tto->bgpinfo.ip_ipsec, attr); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls_ipsec); + bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv + (&tto->bgpinfo.mpls_ipsec, attr); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (ip_ip); + bgp_encap_type_ip_in_ip_to_tlv (&tto->bgpinfo.ip_ip, attr); + break; + + case BGP_ENCAP_TYPE_VXLAN: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (vxlan); + bgp_encap_type_vxlan_to_tlv (&tto->bgpinfo.vxlan, attr); + break; + + case BGP_ENCAP_TYPE_NVGRE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (nvgre); + bgp_encap_type_nvgre_to_tlv (&tto->bgpinfo.nvgre, attr); + break; + + case BGP_ENCAP_TYPE_MPLS: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls); + bgp_encap_type_mpls_to_tlv (&tto->bgpinfo.mpls, attr); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_GRE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls_gre); + bgp_encap_type_mpls_in_gre_to_tlv (&tto->bgpinfo.mpls_gre, attr); + break; + + case BGP_ENCAP_TYPE_VXLAN_GPE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (vxlan_gpe); + bgp_encap_type_vxlan_gpe_to_tlv (&tto->bgpinfo.vxlan_gpe, attr); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_UDP: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls_udp); + bgp_encap_type_mpls_in_udp_to_tlv (&tto->bgpinfo.mpls_udp, attr); + break; + + case BGP_ENCAP_TYPE_PBB: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (pbb); + bgp_encap_type_pbb_to_tlv (&tto->bgpinfo.pbb, attr); + break; + + default: + assert (0); + } + return tto->type; +} + +struct rfapi_un_option * +rfapi_encap_tlv_to_un_option (struct attr *attr) +{ + struct attr_extra *attre = attr->extra; + struct rfapi_un_option *uo = NULL; + struct rfapi_tunneltype_option *tto; + int rc; + struct bgp_attr_encap_subtlv *stlv; + + if (!attre) + return NULL; + + /* no tunnel encap attr stored */ + if (!attre->encap_tunneltype) + return NULL; + + stlv = attre->encap_subtlvs; + + uo = XCALLOC (MTYPE_RFAPI_UN_OPTION, sizeof (struct rfapi_un_option)); + assert (uo); + uo->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE; + uo->v.tunnel.type = attre->encap_tunneltype; + tto = &uo->v.tunnel; + + switch (attre->encap_tunneltype) + { + case BGP_ENCAP_TYPE_L2TPV3_OVER_IP: + rc = tlv_to_bgp_encap_type_l2tpv3overip (stlv, &tto->bgpinfo.l2tpv3_ip); + break; + + case BGP_ENCAP_TYPE_GRE: + rc = tlv_to_bgp_encap_type_gre (stlv, &tto->bgpinfo.gre); + break; + + case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT: + rc = tlv_to_bgp_encap_type_transmit_tunnel_endpoint (stlv, + &tto->bgpinfo.transmit_tunnel_endpoint); + break; + + case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE: + rc = tlv_to_bgp_encap_type_ipsec_in_tunnel_mode (stlv, + &tto->bgpinfo.ipsec_tunnel); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + rc = + tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode (stlv, + &tto->bgpinfo.ip_ipsec); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + rc = + tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode + (stlv, &tto->bgpinfo.mpls_ipsec); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP: + rc = tlv_to_bgp_encap_type_ip_in_ip (stlv, &tto->bgpinfo.ip_ip); + break; + + case BGP_ENCAP_TYPE_VXLAN: + rc = tlv_to_bgp_encap_type_vxlan (stlv, &tto->bgpinfo.vxlan); + break; + + case BGP_ENCAP_TYPE_NVGRE: + rc = tlv_to_bgp_encap_type_nvgre (stlv, &tto->bgpinfo.nvgre); + break; + + case BGP_ENCAP_TYPE_MPLS: + rc = tlv_to_bgp_encap_type_mpls (stlv, &tto->bgpinfo.mpls); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_GRE: + rc = tlv_to_bgp_encap_type_mpls_in_gre (stlv, &tto->bgpinfo.mpls_gre); + break; + + case BGP_ENCAP_TYPE_VXLAN_GPE: + rc = tlv_to_bgp_encap_type_vxlan_gpe (stlv, &tto->bgpinfo.vxlan_gpe); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_UDP: + rc = tlv_to_bgp_encap_type_mpls_in_udp (stlv, &tto->bgpinfo.mpls_udp); + break; + + case BGP_ENCAP_TYPE_PBB: + rc = tlv_to_bgp_encap_type_pbb (stlv, &tto->bgpinfo.pbb); + break; + + default: + zlog_debug ("%s: unknown tunnel type %d", + __func__, attre->encap_tunneltype); + rc = -1; + break; + } + if (rc) + { + XFREE (MTYPE_RFAPI_UN_OPTION, uo); + uo = NULL; + } + return uo; +} + +/*********************************************************************** + * SUBTLV PRINT + ***********************************************************************/ + +static void +subtlv_print_encap_l2tpv3_over_ip ( + void *stream, + int column_offset, + struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(L2TPv3 over IP)", + vty_newline); + fp (out, "%*s SessionID: %d%s", column_offset, "", st->sessionid, + vty_newline); + fp (out, "%*s Cookie: (length %d)%s", column_offset, "", st->cookie_length, + vty_newline); +} + +static void +subtlv_print_encap_gre ( + void *stream, + int column_offset, + struct bgp_tea_subtlv_encap_gre_key *st) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(GRE)", vty_newline); + fp (out, "%*s GRE key: %d (0x%x)%s", column_offset, "", st->gre_key, + st->gre_key, vty_newline); +} + +static void +subtlv_print_encap_pbb ( + void *stream, + int column_offset, + struct bgp_tea_subtlv_encap_pbb *st) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(PBB)", vty_newline); + if (st->flag_isid) + { + fp (out, "%*s ISID: %d (0x%x)%s", column_offset, "", st->isid, + st->isid, vty_newline); + } + if (st->flag_vid) + { + fp (out, "%*s VID: %d (0x%x)%s", column_offset, "", st->vid, st->vid, + vty_newline); + } + fp (out, "%*s MACADDR %02x:%02x:%02x:%02x:%02x:%02x%s", + column_offset, "", + st->macaddr[0], + st->macaddr[1], + st->macaddr[2], + st->macaddr[3], st->macaddr[4], st->macaddr[5], vty_newline); +} + +static void +subtlv_print_proto_type ( + void *stream, + int column_offset, + struct bgp_tea_subtlv_proto_type *st) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(Proto Type)", + vty_newline); + fp (out, "%*s Proto %d (0x%x)%s", column_offset, "", st->proto, st->proto, + vty_newline); +} + +static void +subtlv_print_color ( + void *stream, + int column_offset, + struct bgp_tea_subtlv_color *st) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp (out, "%*s%s%s", column_offset, "", "SubTLV: Color", vty_newline); + fp (out, "%*s Color: %d (0x%x)", column_offset, "", st->color, st->color, + vty_newline); +} + +static void +subtlv_print_ipsec_ta ( + void *stream, + int column_offset, + struct bgp_tea_subtlv_ipsec_ta *st) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp (out, "%*s%s%s", column_offset, "", "SubTLV: IPSEC TA", vty_newline); + fp (out, "%*s Authenticator Type: %d (0x%x)", column_offset, "", + st->authenticator_type, st->authenticator_type, vty_newline); + fp (out, "%*s Authenticator: (length %d)", column_offset, "", + st->authenticator_length, vty_newline); +} + +/*********************************************************************** + * TLV PRINT + ***********************************************************************/ + +static void +print_encap_type_l2tpv3overip ( + void *stream, + int column_offset, + struct bgp_encap_type_l2tpv3_over_ip *bet) +{ + const char *type = "L2TPv3 over IP"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_encap_l2tpv3_over_ip (stream, column_offset + 2, + &bet->st_encap); + subtlv_print_proto_type (stream, column_offset + 2, &bet->st_proto); + subtlv_print_color (stream, column_offset + 2, &bet->st_color); +} + +static void +print_encap_type_gre ( + void *stream, + int column_offset, + struct bgp_encap_type_gre *bet) +{ + const char *type = "GRE"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_encap_gre (stream, column_offset + 2, &bet->st_encap); + subtlv_print_proto_type (stream, column_offset + 2, &bet->st_proto); + subtlv_print_color (stream, column_offset + 2, &bet->st_color); +} + +static void +print_encap_type_ip_in_ip ( + void *stream, + int column_offset, + struct bgp_encap_type_ip_in_ip *bet) +{ + const char *type = "IP in IP"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_proto_type (stream, column_offset + 2, &bet->st_proto); + subtlv_print_color (stream, column_offset + 2, &bet->st_color); +} + +static void +print_encap_type_transmit_tunnel_endpoint ( + void *stream, + int column_offset, + struct bgp_encap_type_transmit_tunnel_endpoint *bet) +{ + const char *type = "Transmit Tunnel Endpoint"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void +print_encap_type_ipsec_in_tunnel_mode ( + void *stream, + int column_offset, + struct bgp_encap_type_ipsec_in_tunnel_mode *bet) +{ + const char *type = "IPSEC in Tunnel mode"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + subtlv_print_ipsec_ta (stream, column_offset + 2, &bet->st_ipsec_ta); +} + +static void +print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode ( + void *stream, + int column_offset, + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet) +{ + const char *type = "IP in IP Tunnel with IPSEC transport mode"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_ipsec_ta (stream, column_offset + 2, &bet->st_ipsec_ta); +} + +static void +print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode ( + void *stream, + int column_offset, + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet) +{ + const char *type = "MPLS in IP Tunnel with IPSEC transport mode"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_ipsec_ta (stream, column_offset + 2, &bet->st_ipsec_ta); +} + + +static void +print_encap_type_pbb ( + void *stream, + int column_offset, + struct bgp_encap_type_pbb *bet) +{ + const char *type = "PBB"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_encap_pbb (stream, column_offset + 2, &bet->st_encap); +} + + +static void +print_encap_type_vxlan ( + void *stream, + int column_offset, + struct bgp_encap_type_vxlan *bet) +{ + const char *type = "VXLAN"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + + +static void +print_encap_type_nvgre ( + void *stream, + int column_offset, + struct bgp_encap_type_nvgre *bet) +{ + const char *type = "NVGRE"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void +print_encap_type_mpls ( + void *stream, + int column_offset, + struct bgp_encap_type_mpls *bet) +{ + const char *type = "MPLS"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void +print_encap_type_mpls_in_gre ( + void *stream, + int column_offset, + struct bgp_encap_type_mpls_in_gre *bet) +{ + const char *type = "MPLS in GRE"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void +print_encap_type_vxlan_gpe ( + void *stream, + int column_offset, + struct bgp_encap_type_vxlan_gpe *bet) +{ + const char *type = "VXLAN GPE"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void +print_encap_type_mpls_in_udp ( + void *stream, + int column_offset, + struct bgp_encap_type_mpls_in_udp *bet) +{ + const char *type = "MPLS in UDP"; + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +void +rfapi_print_tunneltype_option ( + void *stream, + int column_offset, + struct rfapi_tunneltype_option *tto) +{ + switch (tto->type) + { + case BGP_ENCAP_TYPE_L2TPV3_OVER_IP: + print_encap_type_l2tpv3overip (stream, column_offset, + &tto->bgpinfo.l2tpv3_ip); + break; + + case BGP_ENCAP_TYPE_GRE: + print_encap_type_gre (stream, column_offset, &tto->bgpinfo.gre); + break; + + case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT: + print_encap_type_transmit_tunnel_endpoint (stream, column_offset, + &tto->bgpinfo.transmit_tunnel_endpoint); + break; + + case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE: + print_encap_type_ipsec_in_tunnel_mode (stream, column_offset, + &tto->bgpinfo.ipsec_tunnel); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode (stream, + column_offset, + &tto->bgpinfo.ip_ipsec); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode (stream, + column_offset, + &tto->bgpinfo.mpls_ipsec); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP: + print_encap_type_ip_in_ip (stream, column_offset, &tto->bgpinfo.ip_ip); + break; + + case BGP_ENCAP_TYPE_VXLAN: + print_encap_type_vxlan (stream, column_offset, &tto->bgpinfo.vxlan); + break; + + case BGP_ENCAP_TYPE_NVGRE: + print_encap_type_nvgre (stream, column_offset, &tto->bgpinfo.nvgre); + break; + + case BGP_ENCAP_TYPE_MPLS: + print_encap_type_mpls (stream, column_offset, &tto->bgpinfo.mpls); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_GRE: + print_encap_type_mpls_in_gre (stream, column_offset, + &tto->bgpinfo.mpls_gre); + break; + + case BGP_ENCAP_TYPE_VXLAN_GPE: + print_encap_type_vxlan_gpe (stream, column_offset, + &tto->bgpinfo.vxlan_gpe); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_UDP: + print_encap_type_mpls_in_udp (stream, column_offset, + &tto->bgpinfo.mpls_udp); + break; + + case BGP_ENCAP_TYPE_PBB: + print_encap_type_pbb (stream, column_offset, &tto->bgpinfo.pbb); + break; + + default: + assert (0); + } +} diff --git a/bgpd/rfapi/rfapi_encap_tlv.h b/bgpd/rfapi/rfapi_encap_tlv.h new file mode 100644 index 0000000000..9678655a69 --- /dev/null +++ b/bgpd/rfapi/rfapi_encap_tlv.h @@ -0,0 +1,43 @@ +/* + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_ENCAP_TLV_H +#define _QUAGGA_BGP_RFAPI_ENCAP_TLV_H + +#define RFAPI_BGP_ENCAP_TYPE_DEFAULT BGP_ENCAP_TYPE_IP_IN_IP + +extern bgp_encap_types +rfapi_tunneltype_option_to_tlv ( + struct bgp *bgp, + struct rfapi_ip_addr *ea, + struct rfapi_tunneltype_option *tto, + struct attr *attr, + int always_add); + +extern struct rfapi_un_option * +rfapi_encap_tlv_to_un_option (struct attr *attr); + +extern void +rfapi_print_tunneltype_option ( + void *stream, + int column_offset, + struct rfapi_tunneltype_option *tto); + + +#endif /* _QUAGGA_BGP_RFAPI_ENCAP_TLV_H */ diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c new file mode 100644 index 0000000000..63c1079c44 --- /dev/null +++ b/bgpd/rfapi/rfapi_import.c @@ -0,0 +1,5154 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: rfapi_import.c + * Purpose: Handle import of routes from BGP to RFAPI + */ + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "log.h" +#include "skiplist.h" +#include "thread.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_route.h" +#include "bgp_mplsvpn.h" /* prefix_rd2str() */ +#include "bgp_vnc_types.h" + +#include "rfapi.h" +#include "bgp_rfapi_cfg.h" +#include "rfapi_backend.h" +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_nve_addr.h" +#include "rfapi_vty.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_zebra.h" +#include "vnc_import_bgp.h" +#include "vnc_import_bgp_p.h" +#include "rfapi_rib.h" +#include "rfapi_encap_tlv.h" +#include "vnc_debug.h" + +#ifdef HAVE_GLIBC_BACKTRACE +/* for backtrace and friends */ +#include +#endif /* HAVE_GLIBC_BACKTRACE */ + +#undef DEBUG_MONITOR_MOVE_SHORTER +#undef DEBUG_RETURNED_NHL +#undef DEBUG_ROUTE_COUNTERS +#undef DEBUG_ENCAP_MONITOR +#undef DEBUG_L2_EXTRA +#undef DEBUG_IT_NODES +#undef DEBUG_BI_SEARCH + +/* + * Allocated for each withdraw timer instance; freed when the timer + * expires or is canceled + */ +struct rfapi_withdraw +{ + struct rfapi_import_table *import_table; + struct route_node *node; + struct bgp_info *info; + safi_t safi; /* used only for bulk operations */ + /* + * For import table node reference count checking (i.e., debugging). + * Normally when a timer expires, lockoffset should be 0. However, if + * the timer expiration function is called directly (e.g., + * rfapiExpireVpnNow), the node could be locked by a preceding + * route_top() or route_next() in a loop, so we need to pass this + * value in. + */ + int lockoffset; +}; + +/* + * DEBUG FUNCTION + * It's evil and fiendish. It's compiler-dependent. + * ? Might need LDFLAGS -rdynamic to produce all function names + */ +void +rfapiDebugBacktrace (void) +{ +#ifdef HAVE_GLIBC_BACKTRACE +#define RFAPI_DEBUG_BACKTRACE_NENTRIES 200 + void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES]; + char **syms; + size_t i; + size_t size; + + size = backtrace (buf, RFAPI_DEBUG_BACKTRACE_NENTRIES); + syms = backtrace_symbols (buf, size); + + for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i) + { + zlog_debug ("backtrace[%2lu]: %s", i, syms[i]); + } + + free (syms); +#else +#endif +} + +/* + * DEBUG FUNCTION + * Count remote routes and compare with actively-maintained values. + * Abort if they disagree. + */ +void +rfapiCheckRouteCount () +{ + struct bgp *bgp = bgp_get_default (); + struct rfapi *h; + struct rfapi_import_table *it; + afi_t afi; + + assert (bgp); + + h = bgp->rfapi; + assert (h); + + for (it = h->imports; it; it = it->next) + { + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct route_table *rt; + struct route_node *rn; + + int holddown_count = 0; + int local_count = 0; + int imported_count = 0; + int remote_count = 0; + + rt = it->imported_vpn[afi]; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + struct bgp_info *bi; + struct bgp_info *next; + + for (bi = rn->info; bi; bi = next) + { + next = bi->next; + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + ++holddown_count; + + } + else + { + if (RFAPI_LOCAL_BI (bi)) + { + ++local_count; + } + else + { + if (RFAPI_DIRECT_IMPORT_BI (bi)) + { + ++imported_count; + } + else + { + ++remote_count; + } + } + } + } + } + + if (it->holddown_count[afi] != holddown_count) + { + zlog_debug ("%s: it->holddown_count %d != holddown_count %d", + __func__, it->holddown_count[afi], holddown_count); + assert (0); + } + if (it->remote_count[afi] != remote_count) + { + zlog_debug ("%s: it->remote_count %d != remote_count %d", + __func__, it->remote_count[afi], remote_count); + assert (0); + } + if (it->imported_count[afi] != imported_count) + { + zlog_debug ("%s: it->imported_count %d != imported_count %d", + __func__, it->imported_count[afi], imported_count); + assert (0); + } + } + } +} + +#if DEBUG_ROUTE_COUNTERS +#define VNC_ITRCCK do {rfapiCheckRouteCount();} while (0) +#else +#define VNC_ITRCCK +#endif + +/* + * Validate reference count for a node in an import table + * + * Normally lockoffset is 0 for nodes in quiescent state. However, + * route_unlock_node will delete the node if it is called when + * node->lock == 1, and we have to validate the refcount before + * the node is deleted. In this case, we specify lockoffset 1. + */ +void +rfapiCheckRefcount (struct route_node *rn, safi_t safi, int lockoffset) +{ + unsigned int count_bi = 0; + unsigned int count_monitor = 0; + struct bgp_info *bi; + struct rfapi_monitor_encap *hme; + struct rfapi_monitor_vpn *hmv; + + for (bi = rn->info; bi; bi = bi->next) + ++count_bi; + + + if (rn->aggregate) + { + ++count_monitor; /* rfapi_it_extra */ + + switch (safi) + { + void *cursor; + int rc; + + case SAFI_ENCAP: + for (hme = RFAPI_MONITOR_ENCAP (rn); hme; hme = hme->next) + ++count_monitor; + break; + + case SAFI_MPLS_VPN: + + for (hmv = RFAPI_MONITOR_VPN (rn); hmv; hmv = hmv->next) + ++count_monitor; + + if (RFAPI_MONITOR_EXTERIOR (rn)->source) + { + ++count_monitor; /* sl */ + cursor = NULL; + for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn)->source, + NULL, NULL, &cursor); + !rc; + rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn)->source, + NULL, NULL, &cursor)) + { + + ++count_monitor; /* sl entry */ + } + } + break; + + default: + assert (0); + } + } + + if (count_bi + count_monitor + lockoffset != rn->lock) + { + zlog_debug + ("%s: count_bi=%d, count_monitor=%d, lockoffset=%d, rn->lock=%d", + __func__, count_bi, count_monitor, lockoffset, rn->lock); + assert (0); + } +} + +/* + * Perform deferred rfapi_close operations that were queued + * during callbacks. + */ +static wq_item_status +rfapi_deferred_close_workfunc (struct work_queue *q, void *data) +{ + struct rfapi_descriptor *rfd = data; + struct rfapi *h = q->spec.data; + + assert (!(h->flags & RFAPI_INCALLBACK)); + rfapi_close (rfd); + zlog_debug ("%s: completed deferred close on handle %p", __func__, rfd); + return WQ_SUCCESS; +} + +/* + * Extract layer 2 option from Encap TLVS in BGP attrs + */ +int +rfapiGetL2o (struct attr *attr, struct rfapi_l2address_option *l2o) +{ + if (attr && attr->extra) + { + + struct bgp_attr_encap_subtlv *pEncap; + + for (pEncap = attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next) + { + + if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) + { + if (pEncap->value[0] == RFAPI_VN_OPTION_TYPE_L2ADDR) + { + + if (pEncap->value[1] == 14) + { + memcpy (l2o->macaddr.octet, pEncap->value + 2, + ETHER_ADDR_LEN); + l2o->label = + ((pEncap->value[10] >> 4) & 0x0f) + + ((pEncap->value[9] << 4) & 0xff0) + + ((pEncap->value[8] << 12) & 0xff000); + + l2o->local_nve_id = pEncap->value[12]; + + l2o->logical_net_id = + (pEncap->value[15] & 0xff) + + ((pEncap->value[14] << 8) & 0xff00) + + ((pEncap->value[13] << 16) & 0xff0000); + } + + return 0; + } + } + } + } + + return ENOENT; +} + +/* + * Extract the lifetime from the Tunnel Encap attribute of a route in + * an import table + */ +int +rfapiGetVncLifetime (struct attr *attr, uint32_t * lifetime) +{ + struct bgp_attr_encap_subtlv *pEncap; + + *lifetime = RFAPI_INFINITE_LIFETIME; /* default to infinite */ + + if (attr && attr->extra) + { + + for (pEncap = attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next) + { + + if (pEncap->type == BGP_VNC_SUBTLV_TYPE_LIFETIME) + { /* lifetime */ + if (pEncap->length == 4) + { + memcpy (lifetime, pEncap->value, 4); + *lifetime = ntohl (*lifetime); + return 0; + } + } + } + } + + return ENOENT; +} + +/* + * Extract the tunnel type from the extended community + */ +int +rfapiGetTunnelType (struct attr *attr, + bgp_encap_types *type) +{ + *type = BGP_ENCAP_TYPE_MPLS; /* default to MPLS */ + if (attr && attr->extra && attr->extra->ecommunity) + { + struct ecommunity *ecom = attr->extra->ecommunity; + int i; + + for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) + { + uint8_t *ep; + + ep = ecom->val + i; + if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE && + ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) + { + *type = (ep[6]<<8) + ep[7]; + return 0; + } + } + } + + return ENOENT; +} + + +/* + * Look for UN address in Encap attribute + */ +int +rfapiGetVncTunnelUnAddr (struct attr *attr, struct prefix *p) +{ + struct bgp_attr_encap_subtlv *pEncap; + bgp_encap_types tun_type; + + rfapiGetTunnelType (attr, &tun_type); + if (p && tun_type == BGP_ENCAP_TYPE_MPLS) + { + /* MPLS carries UN address in next hop */ + rfapiNexthop2Prefix (attr, p); + if (p->family != 0) + return 0; + } + if (attr && attr->extra) + { + for (pEncap = attr->extra->encap_subtlvs; pEncap; pEncap = pEncap->next) + { + + if (pEncap->type == BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT) + { /* un addr */ + switch (pEncap->length) + { + case 8: + if (p) + { + p->family = AF_INET; + p->prefixlen = 32; + memcpy (p->u.val, pEncap->value, 4); + } + return 0; + + case 20: + if (p) + { + p->family = AF_INET6; + p->prefixlen = 128; + memcpy (p->u.val, pEncap->value, 16); + } + return 0; + } + } + } + } + + return ENOENT; +} + +/* + * Get UN address wherever it might be + */ +int +rfapiGetUnAddrOfVpnBi (struct bgp_info *bi, struct prefix *p) +{ + /* If it's in this route's VNC attribute, we're done */ + if (!rfapiGetVncTunnelUnAddr (bi->attr, p)) + return 0; + /* + * Otherwise, see if it's cached from a corresponding ENCAP SAFI + * advertisement + */ + if (bi->extra) + { + switch (bi->extra->vnc.import.un_family) + { + case AF_INET: + if (p) + { + p->family = bi->extra->vnc.import.un_family; + p->u.prefix4 = bi->extra->vnc.import.un.addr4; + p->prefixlen = 32; + } + return 0; + case AF_INET6: + if (p) + { + p->family = bi->extra->vnc.import.un_family; + p->u.prefix6 = bi->extra->vnc.import.un.addr6; + p->prefixlen = 128; + } + return 0; + default: + if (p) + p->family = 0; +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: bi->extra->vnc.import.un_family is 0, no UN addr", + __func__); +#endif + break; + } + } + + return ENOENT; +} + + +/* + * Make a new bgp_info from gathered parameters + */ +static struct bgp_info * +rfapiBgpInfoCreate ( + struct attr *attr, + struct peer *peer, + void *rfd, + struct prefix_rd *prd, + u_char type, + u_char sub_type, + uint32_t *label) +{ + struct bgp_info *new; + + new = bgp_info_new (); + assert (new); + + if (attr) + { + if (!new->attr) + new->attr = bgp_attr_intern (attr); + } + bgp_info_extra_get (new); + if (prd) + { + new->extra->vnc.import.rd = *prd; + rfapi_time (&new->extra->vnc.import.create_time); + } + if (label) + encode_label (*label, new->extra->tag); + new->type = type; + new->sub_type = sub_type; + new->peer = peer; + peer_lock (peer); + + return new; +} + +/* + * Frees bgp_info as used in import tables (parts are not + * allocated exactly the way they are in the main RIBs) + */ +static void +rfapiBgpInfoFree (struct bgp_info *goner) +{ + if (!goner) + return; + + if (goner->peer) + { + zlog_debug ("%s: calling peer_unlock(%p), #%d", + __func__, goner->peer, goner->peer->lock); + peer_unlock (goner->peer); + } + + if (goner->attr) + { + bgp_attr_unintern (&goner->attr); + } + if (goner->extra) + { + assert (!goner->extra->damp_info); /* Not used in import tbls */ + XFREE (MTYPE_BGP_ROUTE_EXTRA, goner->extra); + goner->extra = NULL; + } + XFREE (MTYPE_BGP_ROUTE, goner); +} + +struct rfapi_import_table * +rfapiMacImportTableGetNoAlloc (struct bgp *bgp, uint32_t lni) +{ + struct rfapi *h; + struct rfapi_import_table *it = NULL; + uintptr_t lni_as_ptr = lni; + + h = bgp->rfapi; + if (!h) + return NULL; + + if (!h->import_mac) + return NULL; + + if (skiplist_search (h->import_mac, (void *) lni_as_ptr, (void **) &it)) + return NULL; + + return it; +} + +struct rfapi_import_table * +rfapiMacImportTableGet (struct bgp *bgp, uint32_t lni) +{ + struct rfapi *h; + struct rfapi_import_table *it = NULL; + uintptr_t lni_as_ptr = lni; + + h = bgp->rfapi; + assert (h); + + if (!h->import_mac) + { + /* default cmp is good enough for LNI */ + h->import_mac = skiplist_new (0, NULL, NULL); + } + + if (skiplist_search (h->import_mac, (void *) lni_as_ptr, (void **) &it)) + { + + struct ecommunity *enew; + struct ecommunity_val eval; + afi_t afi; + + it = + XCALLOC (MTYPE_RFAPI_IMPORTTABLE, sizeof (struct rfapi_import_table)); + /* set RT list of new import table based on LNI */ + memset ((char *) &eval, 0, sizeof (eval)); + eval.val[0] = 0; /* VNC L2VPN */ + eval.val[1] = 2; /* VNC L2VPN */ + eval.val[5] = (lni >> 16) & 0xff; + eval.val[6] = (lni >> 8) & 0xff; + eval.val[7] = (lni >> 0) & 0xff; + + enew = ecommunity_new (); + ecommunity_add_val (enew, &eval); + it->rt_import_list = enew; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + it->imported_vpn[afi] = route_table_init (); + it->imported_encap[afi] = route_table_init (); + } + + it->l2_logical_net_id = lni; + + skiplist_insert (h->import_mac, (void *) lni_as_ptr, it); + } + + assert (it); + return it; +} + +/* + * Implement MONITOR_MOVE_SHORTER(original_node) from + * RFAPI-Import-Event-Handling.txt + * + * Returns pointer to the list of moved monitors + */ +static struct rfapi_monitor_vpn * +rfapiMonitorMoveShorter (struct route_node *original_vpn_node, int lockoffset) +{ + struct bgp_info *bi; + struct route_node *par; + struct rfapi_monitor_vpn *m; + struct rfapi_monitor_vpn *mlast; + struct rfapi_monitor_vpn *moved; + int movecount = 0; + int parent_already_refcounted = 0; + + RFAPI_CHECK_REFCOUNT (original_vpn_node, SAFI_MPLS_VPN, lockoffset); + +#if DEBUG_MONITOR_MOVE_SHORTER + { + char buf[BUFSIZ]; + + prefix2str (&original_vpn_node->p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: called with node pfx=%s", __func__, buf); + } +#endif + + /* + * 1. If there is at least one bi (either regular route or + * route marked as withdrawn, with a pending timer) at + * original_node with a valid UN address, we're done. Return. + */ + for (bi = original_vpn_node->info; bi; bi = bi->next) + { + struct prefix pfx; + + if (!rfapiGetUnAddrOfVpnBi (bi, &pfx)) + { +#if DEBUG_MONITOR_MOVE_SHORTER + zlog_debug ("%s: have valid UN at original node, no change", + __func__); +#endif + return NULL; + } + } + + /* + * 2. Travel up the tree (toward less-specific prefixes) from + * original_node to find the first node that has at least + * one route (even if it is only a withdrawn route) with a + * valid UN address. Call this node "Node P." + */ + for (par = original_vpn_node->parent; par; par = par->parent) + { + for (bi = par->info; bi; bi = bi->next) + { + struct prefix pfx; + if (!rfapiGetUnAddrOfVpnBi (bi, &pfx)) + { + break; + } + } + if (bi) + break; + } + + if (par) + { + RFAPI_CHECK_REFCOUNT (par, SAFI_MPLS_VPN, 0); + } + + /* + * If no less-specific routes, try to use the 0/0 node + */ + if (!par) + { + /* this isn't necessarily 0/0 */ + par = route_top (original_vpn_node->table); + + /* + * If we got the top node but it wasn't 0/0, + * ignore it + */ + if (par && par->p.prefixlen) + { + route_unlock_node (par); /* maybe free */ + par = NULL; + } + + if (par) + { + ++parent_already_refcounted; + } + } + + /* + * Create 0/0 node if it isn't there + */ + if (!par) + { + struct prefix pfx_default; + + memset (&pfx_default, 0, sizeof (pfx_default)); + pfx_default.family = original_vpn_node->p.family; + + /* creates default node if none exists */ + par = route_node_get (original_vpn_node->table, &pfx_default); + ++parent_already_refcounted; + } + + /* + * 3. Move each of the monitors found at original_node to Node P. + * These are "Moved Monitors." + * + */ + + /* + * Attach at end so that the list pointer we return points + * only to the moved routes + */ + for (m = RFAPI_MONITOR_VPN (par), mlast = NULL; m; mlast = m, m = m->next); + + if (mlast) + { + moved = mlast->next = RFAPI_MONITOR_VPN (original_vpn_node); + } + else + { + moved = RFAPI_MONITOR_VPN_W_ALLOC (par) = + RFAPI_MONITOR_VPN (original_vpn_node); + } + if (RFAPI_MONITOR_VPN (original_vpn_node)) /* check agg, so not allocated */ + RFAPI_MONITOR_VPN_W_ALLOC (original_vpn_node) = NULL; + + /* + * update the node pointers on the monitors + */ + for (m = moved; m; m = m->next) + { + ++movecount; + m->node = par; + } + + RFAPI_CHECK_REFCOUNT (par, SAFI_MPLS_VPN, + parent_already_refcounted - movecount); + while (movecount > parent_already_refcounted) + { + route_lock_node (par); + ++parent_already_refcounted; + } + while (movecount < parent_already_refcounted) + { + /* unlikely, but code defensively */ + route_unlock_node (par); + --parent_already_refcounted; + } + RFAPI_CHECK_REFCOUNT (original_vpn_node, SAFI_MPLS_VPN, + movecount + lockoffset); + while (movecount--) + { + route_unlock_node (original_vpn_node); + } + +#if DEBUG_MONITOR_MOVE_SHORTER + { + char buf[BUFSIZ]; + + prefix2str (&par->p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: moved to node pfx=%s", __func__, buf); + } +#endif + + + return moved; +} + +/* + * Implement MONITOR_MOVE_LONGER(new_node) from + * RFAPI-Import-Event-Handling.txt + */ +static void +rfapiMonitorMoveLonger (struct route_node *new_vpn_node) +{ + struct rfapi_monitor_vpn *monitor; + struct rfapi_monitor_vpn *mlast; + struct bgp_info *bi; + struct route_node *par; + + RFAPI_CHECK_REFCOUNT (new_vpn_node, SAFI_MPLS_VPN, 0); + + /* + * Make sure we have at least one valid route at the new node + */ + for (bi = new_vpn_node->info; bi; bi = bi->next) + { + struct prefix pfx; + if (!rfapiGetUnAddrOfVpnBi (bi, &pfx)) + break; + } + + if (!bi) + { + zlog_debug ("%s: no valid routes at node %p, so not attempting moves", + __func__, new_vpn_node); + return; + } + + /* + * Find first parent node that has monitors + */ + for (par = new_vpn_node->parent; par; par = par->parent) + { + if (RFAPI_MONITOR_VPN (par)) + break; + } + + if (!par) + { + zlog_debug ("%s: no parent nodes with monitors, done", __func__); + return; + } + + /* + * Check each of these monitors to see of their longest-match + * is now the updated node. Move any such monitors to the more- + * specific updated node + */ + for (mlast = NULL, monitor = RFAPI_MONITOR_VPN (par); monitor;) + { + + /* + * If new longest match for monitor prefix is the new + * route's prefix, move monitor to new route's prefix + */ + if (prefix_match (&new_vpn_node->p, &monitor->p)) + { + /* detach */ + if (mlast) + { + mlast->next = monitor->next; + } + else + { + RFAPI_MONITOR_VPN_W_ALLOC (par) = monitor->next; + } + + + /* attach */ + monitor->next = RFAPI_MONITOR_VPN (new_vpn_node); + RFAPI_MONITOR_VPN_W_ALLOC (new_vpn_node) = monitor; + monitor->node = new_vpn_node; + + route_lock_node (new_vpn_node); /* incr refcount */ + + monitor = mlast ? mlast->next : RFAPI_MONITOR_VPN (par); + + RFAPI_CHECK_REFCOUNT (par, SAFI_MPLS_VPN, 1); + /* decr refcount after we're done with par as this might free it */ + route_unlock_node (par); + + continue; + } + mlast = monitor; + monitor = monitor->next; + } + + RFAPI_CHECK_REFCOUNT (new_vpn_node, SAFI_MPLS_VPN, 0); +} + + +static void +rfapiBgpInfoChainFree (struct bgp_info *bi) +{ + struct bgp_info *next; + + while (bi) + { + + /* + * If there is a timer waiting to delete this bi, cancel + * the timer and delete immediately + */ + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && + bi->extra->vnc.import.timer) + { + + struct thread *t = (struct thread *) bi->extra->vnc.import.timer; + struct rfapi_withdraw *wcb = t->arg; + + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + thread_cancel (t); + } + + next = bi->next; + bi->next = NULL; + rfapiBgpInfoFree (bi); + bi = next; + } +} + +static void +rfapiImportTableFlush (struct rfapi_import_table *it) +{ + afi_t afi; + + /* + * Free ecommunity + */ + ecommunity_free (&it->rt_import_list); + it->rt_import_list = NULL; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct route_node *rn; + + for (rn = route_top (it->imported_vpn[afi]); rn; rn = route_next (rn)) + { + /* + * Each route_node has: + * aggregate: points to rfapi_it_extra with monitor chain(s) + * info: points to chain of bgp_info + */ + /* free bgp_info and its children */ + rfapiBgpInfoChainFree (rn->info); + rn->info = NULL; + + rfapiMonitorExtraFlush (SAFI_MPLS_VPN, rn); + } + + for (rn = route_top (it->imported_encap[afi]); rn; rn = route_next (rn)) + { + /* free bgp_info and its children */ + rfapiBgpInfoChainFree (rn->info); + rn->info = NULL; + + rfapiMonitorExtraFlush (SAFI_ENCAP, rn); + } + + route_table_finish (it->imported_vpn[afi]); + route_table_finish (it->imported_encap[afi]); + } + if (it->monitor_exterior_orphans) + { + skiplist_free (it->monitor_exterior_orphans); + } +} + +void +rfapiImportTableRefDelByIt ( + struct bgp *bgp, + struct rfapi_import_table *it_target) +{ + struct rfapi *h; + struct rfapi_import_table *it; + struct rfapi_import_table *prev = NULL; + + assert (it_target); + + h = bgp->rfapi; + assert (h); + + for (it = h->imports; it; prev = it, it = it->next) + { + if (it == it_target) + break; + } + + assert (it); + assert (it->refcount); + + it->refcount -= 1; + + if (!it->refcount) + { + if (prev) + { + prev->next = it->next; + } + else + { + h->imports = it->next; + } + rfapiImportTableFlush (it); + XFREE (MTYPE_RFAPI_IMPORTTABLE, it); + } +} + +#if RFAPI_REQUIRE_ENCAP_BEEC +/* + * Look for magic BGP Encapsulation Extended Community value + * Format in RFC 5512 Sect. 4.5 + */ +static int +rfapiEcommunitiesMatchBeec (struct ecommunity *ecom, + bgp_encap_types type) +{ + int i; + + if (!ecom) + return 0; + + for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) + { + + uint8_t *ep; + + ep = ecom->val + i; + + if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE && + ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP && + ep[6] == ((type && 0xff00)>>8) && + ep[7] == (type&0xff)) + { + + return 1; + } + } + return 0; + +} +#endif + +int +rfapiEcommunitiesIntersect (struct ecommunity *e1, struct ecommunity *e2) +{ + int i, j; + + if (!e1 || !e2) + return 0; + + { + char *s1, *s2; + s1 = ecommunity_ecom2str (e1, ECOMMUNITY_FORMAT_DISPLAY); + s2 = ecommunity_ecom2str (e2, ECOMMUNITY_FORMAT_DISPLAY); + zlog_debug ("%s: e1[%s], e2[%s]", __func__, s1, s2); + XFREE (MTYPE_ECOMMUNITY_STR, s1); + XFREE (MTYPE_ECOMMUNITY_STR, s2); + } + + for (i = 0; i < e1->size; ++i) + { + for (j = 0; j < e2->size; ++j) + { + if (!memcmp (e1->val + (i * ECOMMUNITY_SIZE), + e2->val + (j * ECOMMUNITY_SIZE), ECOMMUNITY_SIZE)) + { + + return 1; + } + } + } + return 0; +} + +int +rfapiEcommunityGetLNI (struct ecommunity *ecom, uint32_t * lni) +{ + if (ecom) + { + int i; + for (i = 0; i < ecom->size; ++i) + { + uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE); + + if ((*(p + 0) == 0x00) && (*(p + 1) == 0x02)) + { + + *lni = (*(p + 5) << 16) | (*(p + 6) << 8) | (*(p + 7)); + return 0; + } + } + } + return ENOENT; +} + +static int +rfapiVpnBiNhEqualsPt (struct bgp_info *bi, struct rfapi_ip_addr *hpt) +{ + uint8_t family; + + if (!hpt || !bi) + return 0; + + family = BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len); + + if (hpt->addr_family != family) + return 0; + + switch (family) + { + case AF_INET: + if (bi->attr->extra->mp_nexthop_global_in.s_addr != hpt->addr.v4.s_addr) + return 0; + break; + + case AF_INET6: + if (IPV6_ADDR_CMP (&bi->attr->extra->mp_nexthop_global, &hpt->addr.v6)) + return 0; + break; + + default: + return 0; + break; + } + + return 1; +} + + +/* + * Compare 2 VPN BIs. Return true if they have the same VN and UN addresses + */ +static int +rfapiVpnBiSamePtUn (struct bgp_info *bi1, struct bgp_info *bi2) +{ + struct prefix pfx_un1; + struct prefix pfx_un2; + + if (!bi1 || !bi2) + return 0; + + if (!bi1->attr || !bi2->attr) + return 0; + + if (!bi1->attr->extra || !bi2->attr->extra) + return 0; + + /* + * VN address comparisons + */ + + if (BGP_MP_NEXTHOP_FAMILY (bi1->attr->extra->mp_nexthop_len) != + BGP_MP_NEXTHOP_FAMILY (bi2->attr->extra->mp_nexthop_len)) + { + return 0; + } + + switch (BGP_MP_NEXTHOP_FAMILY (bi1->attr->extra->mp_nexthop_len)) + { + + case AF_INET: + if (bi1->attr->extra->mp_nexthop_global_in.s_addr != + bi2->attr->extra->mp_nexthop_global_in.s_addr) + return 0; + break; + + case AF_INET6: + if (IPV6_ADDR_CMP (&bi1->attr->extra->mp_nexthop_global, + &bi2->attr->extra->mp_nexthop_global)) + return 0; + break; + + default: + return 0; + break; + } + + /* + * UN address comparisons + */ + if (rfapiGetVncTunnelUnAddr (bi1->attr, &pfx_un1)) + { + if (bi1->extra) + { + pfx_un1.family = bi1->extra->vnc.import.un_family; + switch (bi1->extra->vnc.import.un_family) + { + case AF_INET: + pfx_un1.u.prefix4 = bi1->extra->vnc.import.un.addr4; + break; + case AF_INET6: + pfx_un1.u.prefix6 = bi1->extra->vnc.import.un.addr6; + break; + default: + pfx_un1.family = 0; + break; + } + } + } + + if (rfapiGetVncTunnelUnAddr (bi2->attr, &pfx_un2)) + { + if (bi2->extra) + { + pfx_un2.family = bi2->extra->vnc.import.un_family; + switch (bi2->extra->vnc.import.un_family) + { + case AF_INET: + pfx_un2.u.prefix4 = bi2->extra->vnc.import.un.addr4; + break; + case AF_INET6: + pfx_un2.u.prefix6 = bi2->extra->vnc.import.un.addr6; + break; + default: + pfx_un2.family = 0; + break; + } + } + } + + if (!pfx_un1.family || !pfx_un2.family) + return 0; + + if (pfx_un1.family != pfx_un2.family) + return 0; + + switch (pfx_un1.family) + { + case AF_INET: + if (!IPV4_ADDR_SAME + (&pfx_un1.u.prefix4.s_addr, &pfx_un2.u.prefix4.s_addr)) + return 0; + break; + case AF_INET6: + if (!IPV6_ADDR_SAME (&pfx_un1.u.prefix6, &pfx_un2.u.prefix6)) + return 0; + break; + } + + + + return 1; +} + +uint8_t +rfapiRfpCost (struct attr * attr) +{ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + { + if (attr->local_pref > 255) + { + return 0; + } + return 255 - attr->local_pref; + } + + return 255; +} + +/*------------------------------------------ + * rfapi_extract_l2o + * + * Find Layer 2 options in an option chain + * + * input: + * pHop option chain + * + * output: + * l2o layer 2 options extracted + * + * return value: + * 0 OK + * 1 no options found + * + --------------------------------------------*/ +int +rfapi_extract_l2o (struct bgp_tea_options *pHop, /* chain of options */ + struct rfapi_l2address_option *l2o) /* return extracted value */ +{ + struct bgp_tea_options *p; + + for (p = pHop; p; p = p->next) + { + if ((p->type == RFAPI_VN_OPTION_TYPE_L2ADDR) && (p->length >= 8)) + { + + char *v = p->value; + + memcpy (&l2o->macaddr, v, 6); + + l2o->label = + ((v[6] << 12) & 0xff000) + + ((v[7] << 4) & 0xff0) + ((v[8] >> 4) & 0xf); + + l2o->local_nve_id = (uint8_t) v[10]; + + l2o->logical_net_id = (v[11] << 16) + (v[12] << 8) + (v[13] << 0); + + return 0; + } + } + return 1; +} + +static struct rfapi_next_hop_entry * +rfapiRouteInfo2NextHopEntry ( + struct rfapi_ip_prefix *rprefix, + struct bgp_info *bi, /* route to encode */ + uint32_t lifetime, /* use this in nhe */ + struct route_node *rn) /* req for L2 eth addr */ +{ + struct rfapi_next_hop_entry *new; + int have_vnc_tunnel_un = 0; + +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: entry, bi %p, rn %p", __func__, bi, rn); +#endif + + new = XCALLOC (MTYPE_RFAPI_NEXTHOP, sizeof (struct rfapi_next_hop_entry)); + assert (new); + + new->prefix = *rprefix; + + if (bi->extra && + decode_rd_type(bi->extra->vnc.import.rd.val) == RD_TYPE_VNC_ETH) + { + /* ethernet */ + + struct rfapi_vn_option *vo; + + vo = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option)); + assert (vo); + + vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + + memcpy (&vo->v.l2addr.macaddr, &rn->p.u.prefix_eth.octet, + ETHER_ADDR_LEN); + /* only low 3 bytes of this are significant */ + if (bi->attr && bi->attr->extra) + { + (void) rfapiEcommunityGetLNI (bi->attr->extra->ecommunity, + &vo->v.l2addr.logical_net_id); + } + + /* local_nve_id comes from lower byte of RD type */ + vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1]; + + /* label comes from MP_REACH_NLRI label */ + vo->v.l2addr.label = decode_label (bi->extra->tag); + + new->vn_options = vo; + + /* + * If there is an auxiliary prefix (i.e., host IP address), + * use it as the nexthop prefix instead of the query prefix + */ + if (bi->extra->vnc.import.aux_prefix.family) + { + rfapiQprefix2Rprefix (&bi->extra->vnc.import.aux_prefix, + &new->prefix); + } + } + + if (bi->attr) + { + bgp_encap_types tun_type; + new->prefix.cost = rfapiRfpCost (bi->attr); + + if (bi->attr->extra) + { + + struct bgp_attr_encap_subtlv *pEncap; + + switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len)) + { + case AF_INET: + new->vn_address.addr_family = AF_INET; + new->vn_address.addr.v4 = bi->attr->extra->mp_nexthop_global_in; + break; + + case AF_INET6: + new->vn_address.addr_family = AF_INET6; + new->vn_address.addr.v6 = bi->attr->extra->mp_nexthop_global; + break; + + default: + zlog_warn ("%s: invalid vpn nexthop length: %d", + __func__, bi->attr->extra->mp_nexthop_len); + rfapi_free_next_hop_list (new); + return NULL; + } + + for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap; + pEncap = pEncap->next) + { + switch (pEncap->type) + { + case BGP_VNC_SUBTLV_TYPE_LIFETIME: + /* use configured lifetime, not attr lifetime */ + break; + + default: + zlog_warn ("%s: unknown VNC option type %d", + __func__, pEncap->type); + + + break; + } + } + + rfapiGetTunnelType (bi->attr, &tun_type); + if (tun_type == BGP_ENCAP_TYPE_MPLS) + { + struct prefix p; + /* MPLS carries UN address in next hop */ + rfapiNexthop2Prefix (bi->attr, &p); + if (p.family != 0) + { + rfapiQprefix2Raddr(&p, &new->un_address); + have_vnc_tunnel_un = 1; + } + } + + for (pEncap = bi->attr->extra->encap_subtlvs; pEncap; + pEncap = pEncap->next) + { + switch (pEncap->type) + { + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + /* + * Overrides ENCAP UN address, if any + */ + switch (pEncap->length) + { + + case 8: + new->un_address.addr_family = AF_INET; + memcpy (&new->un_address.addr.v4, pEncap->value, 4); + have_vnc_tunnel_un = 1; + break; + + case 20: + new->un_address.addr_family = AF_INET6; + memcpy (&new->un_address.addr.v6, pEncap->value, 16); + have_vnc_tunnel_un = 1; + break; + + default: + zlog_warn + ("%s: invalid tunnel subtlv UN addr length (%d) for bi %p", + __func__, pEncap->length, bi); + } + break; + + default: + zlog_warn ("%s: unknown Encap Attribute option type %d", + __func__, pEncap->type); + + + break; + } + } + + new->un_options = rfapi_encap_tlv_to_un_option (bi->attr); + +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: line %d: have_vnc_tunnel_un=%d", + __func__, __LINE__, have_vnc_tunnel_un); +#endif + + if (!have_vnc_tunnel_un && bi && bi->extra) + { + /* + * use cached UN address from ENCAP route + */ + new->un_address.addr_family = bi->extra->vnc.import.un_family; + switch (new->un_address.addr_family) + { + case AF_INET: + new->un_address.addr.v4 = bi->extra->vnc.import.un.addr4; + break; + case AF_INET6: + new->un_address.addr.v6 = bi->extra->vnc.import.un.addr6; + break; + default: + zlog_warn ("%s: invalid UN addr family (%d) for bi %p", + __func__, new->un_address.addr_family, bi); + rfapi_free_next_hop_list (new); + return NULL; + break; + } + } + } + } + new->lifetime = lifetime; + return new; +} + +int +rfapiHasNonRemovedRoutes (struct route_node *rn) +{ + struct bgp_info *bi; + + for (bi = rn->info; bi; bi = bi->next) + { + struct prefix pfx; + + if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && + (bi->extra && !rfapiGetUnAddrOfVpnBi (bi, &pfx))) + { + + return 1; + } + } + return 0; +} + +#if DEBUG_IT_NODES +/* + * DEBUG FUNCTION + */ +void +rfapiDumpNode (struct route_node *rn) +{ + struct bgp_info *bi; + + zlog_debug ("%s: rn=%p", __func__, rn); + for (bi = rn->info; bi; bi = bi->next) + { + struct prefix pfx; + int ctrc = rfapiGetUnAddrOfVpnBi (bi, &pfx); + int nr; + + if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && (bi->extra && !ctrc)) + { + + nr = 1; + } + else + { + nr = 0; + } + + zlog_debug (" bi=%p, nr=%d, flags=0x%x, extra=%p, ctrc=%d", + bi, nr, bi->flags, bi->extra, ctrc); + } +} +#endif + +static int +rfapiNhlAddNodeRoutes ( + struct route_node *rn, /* in */ + struct rfapi_ip_prefix *rprefix, /* in */ + uint32_t lifetime, /* in */ + int removed, /* in */ + struct rfapi_next_hop_entry **head, /* in/out */ + struct rfapi_next_hop_entry **tail, /* in/out */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct route_node *rfd_rib_node,/* preload this NVE rib node */ + struct prefix *pfx_target_original) /* query target */ +{ + struct bgp_info *bi; + struct rfapi_next_hop_entry *new; + struct prefix pfx_un; + struct skiplist *seen_nexthops; + int count = 0; + int is_l2 = (rn->p.family == AF_ETHERNET); + + if (rfapiRibFTDFilterRecentPrefix( + (struct rfapi_descriptor *)(rfd_rib_node->table->info), rn, + pfx_target_original)) + { + return 0; + } + + seen_nexthops = + skiplist_new (0, vnc_prefix_cmp, (void (*)(void *)) prefix_free); + + for (bi = rn->info; bi; bi = bi->next) + { + + struct prefix pfx_vn; + struct prefix *newpfx; + + if (removed && !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { +#if DEBUG_RETURNED_NHL + zlog_debug ("%s: want holddown, this route not holddown, skip", + __func__); +#endif + continue; + } + if (!removed && CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + continue; + } + + if (!bi->extra) + { + continue; + } + + /* + * Check for excluded VN address + */ + if (rfapiVpnBiNhEqualsPt (bi, exclude_vnaddr)) + continue; + + /* + * Check for VN address (nexthop) copied already + */ + if (is_l2) + { + /* L2 routes: semantic nexthop in aux_prefix; VN addr ain't it */ + pfx_vn = bi->extra->vnc.import.aux_prefix; + } + else + { + rfapiNexthop2Prefix (bi->attr, &pfx_vn); + } + if (!skiplist_search (seen_nexthops, &pfx_vn, NULL)) + { +#if DEBUG_RETURNED_NHL + char buf[BUFSIZ]; + + prefix2str (&pfx_vn, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + zlog_debug ("%s: already put VN/nexthop %s, skip", __func__, buf); +#endif + continue; + } + + if (rfapiGetUnAddrOfVpnBi (bi, &pfx_un)) + { +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: failed to get UN address of this VPN bi", + __func__); +#endif + continue; + } + + newpfx = prefix_new (); + *newpfx = pfx_vn; + skiplist_insert (seen_nexthops, newpfx, newpfx); + + new = rfapiRouteInfo2NextHopEntry(rprefix, bi, lifetime, rn); + if (new) + { + if (rfapiRibPreloadBi(rfd_rib_node, &pfx_vn, &pfx_un, lifetime, bi)) + { + /* duplicate filtered by RIB */ + rfapi_free_next_hop_list (new); + new = NULL; + } + } + + if (new) + { + if (*tail) + { + (*tail)->next = new; + } + else + { + *head = new; + } + *tail = new; + ++count; + } + } + + skiplist_free (seen_nexthops); + + return count; +} + + +/* + * Breadth-first + * + * omit_node is meant for the situation where we are adding a subtree + * of a parent of some original requested node. The response already + * contains the original requested node, and we don't want to duplicate + * its routes in the list, so we skip it if the right or left node + * matches (of course, we still travel down its child subtrees). + */ +static int +rfapiNhlAddSubtree ( + struct route_node *rn, /* in */ + uint32_t lifetime, /* in */ + struct rfapi_next_hop_entry **head, /* in/out */ + struct rfapi_next_hop_entry **tail, /* in/out */ + struct route_node *omit_node, /* in */ + struct rfapi_ip_addr *exclude_vnaddr,/* omit routes to same NVE */ + struct route_table *rfd_rib_table,/* preload here */ + struct prefix *pfx_target_original) /* query target */ +{ + struct rfapi_ip_prefix rprefix; + int rcount = 0; + + if (rn->l_left && rn->l_left != omit_node) + { + if (rn->l_left->info) + { + int count = 0; + struct route_node *rib_rn = NULL; + + rfapiQprefix2Rprefix (&rn->l_left->p, &rprefix); + if (rfd_rib_table) + { + rib_rn = route_node_get(rfd_rib_table, &rn->l_left->p); + } + + count = rfapiNhlAddNodeRoutes (rn->l_left, &rprefix, lifetime, 0, + head, tail, exclude_vnaddr, rib_rn, pfx_target_original); + if (!count) + { + count = rfapiNhlAddNodeRoutes (rn->l_left, &rprefix, lifetime, 1, + head, tail, exclude_vnaddr, rib_rn, pfx_target_original); + } + rcount += count; + if (rib_rn) + route_unlock_node(rib_rn); + } + } + + if (rn->l_right && rn->l_right != omit_node) + { + if (rn->l_right->info) + { + int count = 0; + struct route_node *rib_rn = NULL; + + rfapiQprefix2Rprefix (&rn->l_right->p, &rprefix); + if (rfd_rib_table) + { + rib_rn = route_node_get(rfd_rib_table, &rn->l_right->p); + } + count = rfapiNhlAddNodeRoutes (rn->l_right, &rprefix, lifetime, 0, + head, tail, exclude_vnaddr, rib_rn, pfx_target_original); + if (!count) + { + count = rfapiNhlAddNodeRoutes (rn->l_right, &rprefix, lifetime, 1, + head, tail, exclude_vnaddr, rib_rn, pfx_target_original); + } + rcount += count; + if (rib_rn) + route_unlock_node(rib_rn); + } + } + + if (rn->l_left) + { + rcount += rfapiNhlAddSubtree (rn->l_left, lifetime, head, tail, omit_node, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + } + if (rn->l_right) + { + rcount += rfapiNhlAddSubtree (rn->l_right, lifetime, head, tail, + omit_node, exclude_vnaddr, rfd_rib_table, pfx_target_original); + } + + return rcount; +} + +/* + * Implementation of ROUTE_LIST(node) from RFAPI-Import-Event-Handling.txt + * + * Construct an rfapi nexthop list based on the routes attached to + * the specified node. + * + * If there are any routes that do NOT have BGP_INFO_REMOVED set, + * return those only. If there are ONLY routes with BGP_INFO_REMOVED, + * then return those, and also include all the non-removed routes from the + * next less-specific node (i.e., this node's parent) at the end. + */ +struct rfapi_next_hop_entry * +rfapiRouteNode2NextHopList ( + struct route_node *rn, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr,/* omit routes to same NVE */ + struct route_table *rfd_rib_table,/* preload here */ + struct prefix *pfx_target_original) /* query target */ +{ + struct rfapi_ip_prefix rprefix; + struct rfapi_next_hop_entry *answer = NULL; + struct rfapi_next_hop_entry *last = NULL; + struct route_node *parent; + int count = 0; + struct route_node *rib_rn; + +#if DEBUG_RETURNED_NHL + { + char buf[BUFSIZ]; + + prefix2str (&rn->p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: called with node pfx=%s", __func__, buf); + } + rfapiDebugBacktrace (); +#endif + + rfapiQprefix2Rprefix (&rn->p, &rprefix); + + rib_rn = rfd_rib_table? route_node_get(rfd_rib_table, &rn->p): NULL; + + /* + * Add non-withdrawn routes at this node + */ + count = rfapiNhlAddNodeRoutes (rn, &rprefix, lifetime, 0, &answer, &last, + exclude_vnaddr, rib_rn, pfx_target_original); + + /* + * If the list has at least one entry, it's finished + */ + if (count) + { + count += rfapiNhlAddSubtree (rn, lifetime, &answer, &last, NULL, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + zlog_debug ("%s: %d nexthops, answer=%p", __func__, count, answer); +#if DEBUG_RETURNED_NHL + rfapiPrintNhl (NULL, answer); +#endif + if (rib_rn) + route_unlock_node(rib_rn); + return answer; + } + + /* + * Add withdrawn routes at this node + */ + count = rfapiNhlAddNodeRoutes (rn, &rprefix, lifetime, 1, &answer, &last, + exclude_vnaddr, rib_rn, pfx_target_original); + if (rib_rn) + route_unlock_node(rib_rn); + + // rfapiPrintNhl(NULL, answer); + + /* + * walk up the tree until we find a node with non-deleted + * routes, then add them + */ + for (parent = rn->parent; parent; parent = parent->parent) + { + if (rfapiHasNonRemovedRoutes (parent)) + { + break; + } + } + + /* + * Add non-withdrawn routes from less-specific prefix + */ + if (parent) + { + rib_rn = rfd_rib_table? route_node_get(rfd_rib_table, &parent->p): NULL; + rfapiQprefix2Rprefix (&parent->p, &rprefix); + count += rfapiNhlAddNodeRoutes (parent, &rprefix, lifetime, 0, + &answer, &last, exclude_vnaddr, rib_rn, pfx_target_original); + count += rfapiNhlAddSubtree (parent, lifetime, &answer, &last, rn, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + if (rib_rn) + route_unlock_node(rib_rn); + } + else + { + /* + * There is no parent with non-removed routes. Still need to + * add subtree of original node if it contributed routes to the + * answer. + */ + if (count) + count += rfapiNhlAddSubtree (rn, lifetime, &answer, &last, rn, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + } + + zlog_debug ("%s: %d nexthops, answer=%p", __func__, count, answer); +#if DEBUG_RETURNED_NHL + rfapiPrintNhl (NULL, answer); +#endif + return answer; +} + +/* + * Construct nexthop list of all routes in table + */ +struct rfapi_next_hop_entry * +rfapiRouteTable2NextHopList ( + struct route_table *rt, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr,/* omit routes to same NVE */ + struct route_table *rfd_rib_table, /* preload this NVE rib table */ + struct prefix *pfx_target_original) /* query target */ +{ + struct route_node *rn; + struct rfapi_next_hop_entry *biglist = NULL; + struct rfapi_next_hop_entry *nhl; + struct rfapi_next_hop_entry *tail = NULL; + int count = 0; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + nhl = rfapiRouteNode2NextHopList (rn, lifetime, exclude_vnaddr, + rfd_rib_table, pfx_target_original); + if (!tail) + { + tail = biglist = nhl; + if (tail) + count = 1; + } + else + { + tail->next = nhl; + } + if (tail) + { + while (tail->next) + { + ++count; + tail = tail->next; + } + } + } + + zlog_debug ("%s: returning %d routes", __func__, count); + return biglist; +} + +struct rfapi_next_hop_entry * +rfapiEthRouteNode2NextHopList ( + struct route_node *rn, + struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr,/* omit routes to same NVE */ + struct route_table *rfd_rib_table,/* preload NVE rib table */ + struct prefix *pfx_target_original) /* query target */ +{ + int count = 0; + struct rfapi_next_hop_entry *answer = NULL; + struct rfapi_next_hop_entry *last = NULL; + struct route_node *rib_rn; + + rib_rn = rfd_rib_table? route_node_get(rfd_rib_table, &rn->p): NULL; + + count = rfapiNhlAddNodeRoutes (rn, rprefix, lifetime, 0, &answer, &last, + NULL, rib_rn, pfx_target_original); + +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: node %p: %d non-holddown routes", __func__, rn, count); +#endif + + if (!count) + { + count = rfapiNhlAddNodeRoutes (rn, rprefix, lifetime, 1, &answer, &last, + exclude_vnaddr, rib_rn, pfx_target_original); + zlog_debug ("%s: node %p: %d holddown routes", __func__, rn, count); + } + + if (rib_rn) + route_unlock_node(rib_rn); + +#if DEBUG_RETURNED_NHL + rfapiPrintNhl (NULL, answer); +#endif + + return answer; +} + + +/* + * Construct nexthop list of all routes in table + */ +struct rfapi_next_hop_entry * +rfapiEthRouteTable2NextHopList ( + uint32_t logical_net_id, + struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr,/* omit routes to same NVE */ + struct route_table *rfd_rib_table, /* preload NVE rib node */ + struct prefix *pfx_target_original) /* query target */ +{ + struct rfapi_import_table *it; + struct bgp *bgp = bgp_get_default (); + struct route_table *rt; + struct route_node *rn; + struct rfapi_next_hop_entry *biglist = NULL; + struct rfapi_next_hop_entry *nhl; + struct rfapi_next_hop_entry *tail = NULL; + int count = 0; + + + it = rfapiMacImportTableGet (bgp, logical_net_id); + rt = it->imported_vpn[AFI_ETHER]; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + nhl = rfapiEthRouteNode2NextHopList(rn, rprefix, lifetime, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + if (!tail) + { + tail = biglist = nhl; + if (tail) + count = 1; + } + else + { + tail->next = nhl; + } + if (tail) + { + while (tail->next) + { + ++count; + tail = tail->next; + } + } + } + + zlog_debug ("%s: returning %d routes", __func__, count); + return biglist; +} + +/* + * Insert a new bi to the imported route table node, + * keeping the list of BIs sorted best route first + */ +static void +rfapiBgpInfoAttachSorted ( + struct route_node *rn, + struct bgp_info *info_new, + afi_t afi, + safi_t safi) +{ + struct bgp *bgp; + struct bgp_info *prev; + struct bgp_info *next; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + + if (VNC_DEBUG(IMPORT_BI_ATTACH)) + { + zlog_debug ("%s: info_new->peer=%p", __func__, info_new->peer); + zlog_debug ("%s: info_new->peer->su_remote=%p", __func__, + info_new->peer->su_remote); + } + + for (prev = NULL, next = rn->info; next; prev = next, next = next->next) + { + if (!bgp || + (!CHECK_FLAG (info_new->flags, BGP_INFO_REMOVED) && + CHECK_FLAG (next->flags, BGP_INFO_REMOVED)) || + bgp_info_cmp_compatible (bgp, info_new, next, afi, safi) == -1) + { /* -1 if 1st is better */ + break; + } + } + zlog_debug ("%s: prev=%p, next=%p", __func__, prev, next); + if (prev) + { + prev->next = info_new; + } + else + { + rn->info = info_new; + } + info_new->prev = prev; + info_new->next = next; + if (next) + next->prev = info_new; +} + +static void +rfapiBgpInfoDetach (struct route_node *rn, struct bgp_info *bi) +{ + /* + * Remove the route (doubly-linked) + */ + if (bi->next) + bi->next->prev = bi->prev; + if (bi->prev) + bi->prev->next = bi->next; + else + rn->info = bi->next; +} + +/* + * For L3-indexed import tables + */ +static int +rfapi_bi_peer_rd_cmp (void *b1, void *b2) +{ + struct bgp_info *bi1 = b1; + struct bgp_info *bi2 = b2; + + /* + * Compare peers + */ + if (bi1->peer < bi2->peer) + return -1; + if (bi1->peer > bi2->peer) + return 1; + + /* + * compare RDs + */ + return vnc_prefix_cmp ((struct prefix *) &bi1->extra->vnc.import.rd, + (struct prefix *) &bi2->extra->vnc.import.rd); +} + +/* + * For L2-indexed import tables + * The BIs in these tables should ALWAYS have an aux_prefix set because + * they arrive via IPv4 or IPv6 advertisements. + */ +static int +rfapi_bi_peer_rd_aux_cmp (void *b1, void *b2) +{ + struct bgp_info *bi1 = b1; + struct bgp_info *bi2 = b2; + int rc; + + /* + * Compare peers + */ + if (bi1->peer < bi2->peer) + return -1; + if (bi1->peer > bi2->peer) + return 1; + + /* + * compare RDs + */ + rc = vnc_prefix_cmp ((struct prefix *) &bi1->extra->vnc.import.rd, + (struct prefix *) &bi2->extra->vnc.import.rd); + if (rc) + { + return rc; + } + + /* + * L2 import tables can have multiple entries with the + * same MAC address, same RD, but different L3 addresses. + * + * Use presence of aux_prefix with AF=ethernet and prefixlen=1 + * as magic value to signify explicit wildcarding of the aux_prefix. + * This magic value will not appear in bona fide bi entries in + * the import table, but is allowed in the "fake" bi used to + * probe the table when searching. (We have to test both b1 and b2 + * because there is no guarantee of the order the test key and + * the real key will be passed) + */ + if ((bi1->extra->vnc.import.aux_prefix.family == AF_ETHERNET && + (bi1->extra->vnc.import.aux_prefix.prefixlen == 1)) || + (bi2->extra->vnc.import.aux_prefix.family == AF_ETHERNET && + (bi2->extra->vnc.import.aux_prefix.prefixlen == 1))) + { + + /* + * wildcard aux address specified + */ + return 0; + } + + return vnc_prefix_cmp (&bi1->extra->vnc.import.aux_prefix, + &bi2->extra->vnc.import.aux_prefix); +} + + +/* + * Index on RD and Peer + */ +static void +rfapiItBiIndexAdd ( + struct route_node *rn, /* Import table VPN node */ + struct bgp_info *bi) /* new BI */ +{ + struct skiplist *sl; + + assert (rn); + assert (bi); + assert (bi->extra); + + { + char buf[BUFSIZ]; + prefix_rd2str (&bi->extra->vnc.import.rd, buf, BUFSIZ); + zlog_debug ("%s: bi %p, peer %p, rd %s", __func__, bi, bi->peer, buf); + } + + sl = RFAPI_RDINDEX_W_ALLOC (rn); + if (!sl) + { + if (AF_ETHERNET == rn->p.family) + { + sl = skiplist_new (0, rfapi_bi_peer_rd_aux_cmp, NULL); + } + else + { + sl = skiplist_new (0, rfapi_bi_peer_rd_cmp, NULL); + } + RFAPI_IT_EXTRA_GET (rn)->u.vpn.idx_rd = sl; + route_lock_node (rn); /* for skiplist */ + } + assert (!skiplist_insert (sl, (void *) bi, (void *) bi)); + route_lock_node (rn); /* for skiplist entry */ + + /* NB: BIs in import tables are not refcounted */ +} + +static void +rfapiItBiIndexDump (struct route_node *rn) +{ + struct skiplist *sl; + void *cursor = NULL; + struct bgp_info *k; + struct bgp_info *v; + int rc; + + sl = RFAPI_RDINDEX (rn); + if (!sl) + return; + + for (rc = skiplist_next (sl, (void **) &k, (void **) &v, &cursor); + !rc; rc = skiplist_next (sl, (void **) &k, (void **) &v, &cursor)) + { + + char buf[BUFSIZ]; + char buf_aux_pfx[BUFSIZ]; + + prefix_rd2str (&k->extra->vnc.import.rd, buf, BUFSIZ); + buf_aux_pfx[0] = 0; + if (k->extra->vnc.import.aux_prefix.family) + { + prefix2str (&k->extra->vnc.import.aux_prefix, buf_aux_pfx, BUFSIZ); + } + else + { + strncpy (buf_aux_pfx, "(none)", BUFSIZ); + buf_aux_pfx[BUFSIZ - 1] = 0; + } + + zlog_debug ("bi %p, peer %p, rd %s, aux_prefix %s", k, k->peer, buf, + buf_aux_pfx); + } +} + +static struct bgp_info * +rfapiItBiIndexSearch ( + struct route_node *rn, /* Import table VPN node */ + struct prefix_rd *prd, + struct peer *peer, + struct prefix *aux_prefix) /* optional L3 addr for L2 ITs */ +{ + struct skiplist *sl; + int rc; + struct bgp_info bi_fake; + struct bgp_info_extra bi_extra; + struct bgp_info *bi_result; + + sl = RFAPI_RDINDEX (rn); + if (!sl) + return NULL; + +#if DEBUG_BI_SEARCH + { + char buf[BUFSIZ]; + char buf_aux_pfx[BUFSIZ]; + + prefix_rd2str (prd, buf, BUFSIZ); + if (aux_prefix) + { + prefix2str (aux_prefix, buf_aux_pfx, BUFSIZ); + } + else + { + strncpy (buf_aux_pfx, "(nil)", BUFSIZ - 1); + buf_aux_pfx[BUFSIZ - 1] = 0; + } + + zlog_debug ("%s want prd=%s, peer=%p, aux_prefix=%s", + __func__, buf, peer, buf_aux_pfx); + rfapiItBiIndexDump (rn); + } +#endif + + /* threshold is a WAG */ + if (sl->count < 3) + { +#if DEBUG_BI_SEARCH + zlog_debug ("%s: short list algorithm", __func__); +#endif + /* if short list, linear search might be faster */ + for (bi_result = rn->info; bi_result; bi_result = bi_result->next) + { +#if DEBUG_BI_SEARCH + { + char buf[BUFSIZ]; + prefix_rd2str (&bi_result->extra->vnc.import.rd, buf, BUFSIZ); + zlog_debug ("%s: bi has prd=%s, peer=%p", __func__, + buf, bi_result->peer); + } +#endif + if (peer == bi_result->peer && + !prefix_cmp ((struct prefix *) &bi_result->extra->vnc.import.rd, + (struct prefix *) prd)) + { + +#if DEBUG_BI_SEARCH + zlog_debug ("%s: peer and RD same, doing aux_prefix check", + __func__); +#endif + if (!aux_prefix || + !prefix_cmp (aux_prefix, + &bi_result->extra->vnc.import.aux_prefix)) + { + +#if DEBUG_BI_SEARCH + zlog_debug ("%s: match", __func__); +#endif + break; + } + + } + } + return bi_result; + } + + bi_fake.peer = peer; + bi_fake.extra = &bi_extra; + bi_fake.extra->vnc.import.rd = *(struct prefix_rd *) prd; + if (aux_prefix) + { + bi_fake.extra->vnc.import.aux_prefix = *aux_prefix; + } + else + { + /* wildcard */ + bi_fake.extra->vnc.import.aux_prefix.family = AF_ETHERNET; + bi_fake.extra->vnc.import.aux_prefix.prefixlen = 1; + } + + rc = skiplist_search (sl, (void *) &bi_fake, (void *) &bi_result); + + if (rc) + { +#if DEBUG_BI_SEARCH + zlog_debug ("%s: no match", __func__); +#endif + return NULL; + } + +#if DEBUG_BI_SEARCH + zlog_debug ("%s: matched bi=%p", __func__, bi_result); +#endif + + return bi_result; +} + +static void +rfapiItBiIndexDel ( + struct route_node *rn, /* Import table VPN node */ + struct bgp_info *bi) /* old BI */ +{ + struct skiplist *sl; + int rc; + + { + char buf[BUFSIZ]; + prefix_rd2str (&bi->extra->vnc.import.rd, buf, BUFSIZ); + zlog_debug ("%s: bi %p, peer %p, rd %s", __func__, bi, bi->peer, buf); + } + + sl = RFAPI_RDINDEX (rn); + assert (sl); + + rc = skiplist_delete (sl, (void *) (bi), (void *) bi); + if (rc) + { + rfapiItBiIndexDump (rn); + } + assert (!rc); + + route_unlock_node (rn); /* for skiplist entry */ + + /* NB: BIs in import tables are not refcounted */ +} + +/* + * Add a backreference at the ENCAP node to the VPN route that + * refers to it + */ +static void +rfapiMonitorEncapAdd ( + struct rfapi_import_table *import_table, + struct prefix *p, /* VN address */ + struct route_node *vpn_rn, /* VPN node */ + struct bgp_info *vpn_bi) /* VPN bi/route */ +{ + afi_t afi = family2afi (p->family); + struct route_node *rn; + struct rfapi_monitor_encap *m; + + assert (afi); + rn = route_node_get (import_table->imported_encap[afi], p); /* locks rn */ + assert (rn); + + m = + XCALLOC (MTYPE_RFAPI_MONITOR_ENCAP, sizeof (struct rfapi_monitor_encap)); + assert (m); + + m->node = vpn_rn; + m->bi = vpn_bi; + m->rn = rn; + + /* insert to encap node's list */ + m->next = RFAPI_MONITOR_ENCAP (rn); + if (m->next) + m->next->prev = m; + RFAPI_MONITOR_ENCAP_W_ALLOC (rn) = m; + + /* for easy lookup when deleting vpn route */ + vpn_bi->extra->vnc.import.hme = m; + + zlog_debug + ("%s: it=%p, vpn_bi=%p, afi=%d, encap rn=%p, setting vpn_bi->extra->vnc.import.hme=%p", + __func__, import_table, vpn_bi, afi, rn, m); + + RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, 0); +} + +static void +rfapiMonitorEncapDelete (struct bgp_info *vpn_bi) +{ + /* + * Remove encap monitor + */ + zlog_debug ("%s: vpn_bi=%p", __func__, vpn_bi); + if (vpn_bi->extra) + { + struct rfapi_monitor_encap *hme = vpn_bi->extra->vnc.import.hme; + + if (hme) + { + + zlog_debug ("%s: hme=%p", __func__, hme); + + /* Refcount checking takes too long here */ + //RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 0); + if (hme->next) + hme->next->prev = hme->prev; + if (hme->prev) + hme->prev->next = hme->next; + else + RFAPI_MONITOR_ENCAP_W_ALLOC (hme->rn) = hme->next; + /* Refcount checking takes too long here */ + //RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 1); + + /* see if the struct rfapi_it_extra is empty and can be freed */ + rfapiMonitorExtraPrune (SAFI_ENCAP, hme->rn); + + route_unlock_node (hme->rn); /* decr ref count */ + XFREE (MTYPE_RFAPI_MONITOR_ENCAP, hme); + vpn_bi->extra->vnc.import.hme = NULL; + } + } +} + +/* + * quagga lib/thread.h says this must return int even though + * it doesn't do anything with the return value + */ +static int +rfapiWithdrawTimerVPN (struct thread *t) +{ + struct rfapi_withdraw *wcb = t->arg; + struct bgp_info *bi = wcb->info; + struct bgp *bgp = bgp_get_default (); + + struct rfapi_monitor_vpn *moved; + afi_t afi; + + assert (wcb->node); + assert (bi); + assert (wcb->import_table); + assert (bi->extra); + + RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_MPLS_VPN, wcb->lockoffset); + + { + char buf[BUFSIZ]; + + zlog_debug ("%s: removing bi %p at prefix %s/%d", + __func__, + bi, + rfapi_ntop (wcb->node->p.family, &wcb->node->p.u.prefix, buf, + BUFSIZ), wcb->node->p.prefixlen); + } + + /* + * Remove the route (doubly-linked) + */ + if (CHECK_FLAG (bi->flags, BGP_INFO_VALID) + && VALID_INTERIOR_TYPE (bi->type)) + RFAPI_MONITOR_EXTERIOR (wcb->node)->valid_interior_count--; + + afi = family2afi (wcb->node->p.family); + wcb->import_table->holddown_count[afi] -= 1; /* keep count consistent */ + rfapiItBiIndexDel (wcb->node, bi); + rfapiBgpInfoDetach (wcb->node, bi); /* with removed bi */ + + vnc_import_bgp_exterior_del_route_interior (bgp, wcb->import_table, + wcb->node, bi); + + + /* + * If VNC is configured to send response remove messages, AND + * if the removed route had a UN address, do response removal + * processing. + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) + { + + int has_valid_duplicate = 0; + struct bgp_info *bii; + + /* + * First check if there are any OTHER routes at this node + * that have the same nexthop and a valid UN address. If + * there are (e.g., from other peers), then the route isn't + * really gone, so skip sending a response removal message. + */ + for (bii = wcb->node->info; bii; bii = bii->next) + { + if (rfapiVpnBiSamePtUn (bi, bii)) + { + has_valid_duplicate = 1; + break; + } + } + + zlog_debug ("%s: has_valid_duplicate=%d", __func__, + has_valid_duplicate); + + if (!has_valid_duplicate) + { + rfapiRibPendingDeleteRoute (bgp, wcb->import_table, afi, wcb->node); + } + } + + rfapiMonitorEncapDelete (bi); + + /* + * If there are no VPN monitors at this VPN Node A, + * we are done + */ + if (!RFAPI_MONITOR_VPN (wcb->node)) + { + zlog_debug ("%s: no VPN monitors at this node", __func__); + goto done; + } + + /* + * rfapiMonitorMoveShorter only moves monitors if there are + * no remaining valid routes at the current node + */ + moved = rfapiMonitorMoveShorter (wcb->node, 1); + + if (moved) + { + rfapiMonitorMovedUp (wcb->import_table, wcb->node, moved->node, moved); + } + +done: + /* + * Free VPN bi + */ + rfapiBgpInfoFree (bi); + wcb->info = NULL; + + /* + * If route count at this node has gone to 0, withdraw exported prefix + */ + if (!wcb->node->info) + { + /* see if the struct rfapi_it_extra is empty and can be freed */ + rfapiMonitorExtraPrune (SAFI_MPLS_VPN, wcb->node); + vnc_direct_bgp_del_prefix (bgp, wcb->import_table, wcb->node); + vnc_zebra_del_prefix (bgp, wcb->import_table, wcb->node); + } + else + { + /* + * nexthop change event + * vnc_direct_bgp_add_prefix() will recompute the VN addr ecommunity + */ + vnc_direct_bgp_add_prefix (bgp, wcb->import_table, wcb->node); + } + + RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset); + route_unlock_node (wcb->node); /* decr ref count */ + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + return 0; +} + +/* + * This works for multiprotocol extension, but not for plain ol' + * unicast IPv4 because that nexthop is stored in attr->nexthop + */ +void +rfapiNexthop2Prefix (struct attr *attr, struct prefix *p) +{ + assert (p); + assert (attr); + assert (attr->extra); + + memset (p, 0, sizeof (struct prefix)); + + switch (p->family = BGP_MP_NEXTHOP_FAMILY (attr->extra->mp_nexthop_len)) + { + case AF_INET: + p->u.prefix4 = attr->extra->mp_nexthop_global_in; + p->prefixlen = 32; + break; + + case AF_INET6: + p->u.prefix6 = attr->extra->mp_nexthop_global; + p->prefixlen = 128; + break; + + default: + zlog_debug ("%s: Family is unknown = %d", + __func__, p->family); + } +} + +void +rfapiUnicastNexthop2Prefix (afi_t afi, struct attr *attr, struct prefix *p) +{ + if (afi == AFI_IP) + { + p->family = AF_INET; + p->prefixlen = 32; + p->u.prefix4 = attr->nexthop; + } + else + { + rfapiNexthop2Prefix (attr, p); + } +} + +static int +rfapiAttrNexthopAddrDifferent (struct prefix *p1, struct prefix *p2) +{ + if (!p1 || !p2) + { + zlog_debug ("%s: p1 or p2 is NULL", __func__); + return 1; + } + + /* + * Are address families the same? + */ + if (p1->family != p2->family) + { + return 1; + } + + switch (p1->family) + { + case AF_INET: + if (IPV4_ADDR_SAME (&p1->u.prefix4, &p2->u.prefix4)) + return 0; + break; + + case AF_INET6: + if (IPV6_ADDR_SAME (&p1->u.prefix6, &p2->u.prefix6)) + return 0; + break; + + default: + assert (1); + + } + + return 1; +} + +static void +rfapiCopyUnEncap2VPN (struct bgp_info *encap_bi, struct bgp_info *vpn_bi) +{ + struct attr_extra *attre; + + if (!encap_bi->attr || !encap_bi->attr->extra) + { + zlog_warn ("%s: no encap bi attr/extra, can't copy UN address", + __func__); + return; + } + + if (!vpn_bi || !vpn_bi->extra) + { + zlog_warn ("%s: no vpn bi attr/extra, can't copy UN address", + __func__); + return; + } + + attre = encap_bi->attr->extra; + + switch (BGP_MP_NEXTHOP_FAMILY (attre->mp_nexthop_len)) + { + case AF_INET: + + /* + * instrumentation to debug segfault of 091127 + */ + zlog_debug ("%s: vpn_bi=%p", __func__, vpn_bi); + if (vpn_bi) + { + zlog_debug ("%s: vpn_bi->extra=%p", __func__, vpn_bi->extra); + } + + vpn_bi->extra->vnc.import.un_family = AF_INET; + vpn_bi->extra->vnc.import.un.addr4 = attre->mp_nexthop_global_in; + break; + + case AF_INET6: + vpn_bi->extra->vnc.import.un_family = AF_INET6; + vpn_bi->extra->vnc.import.un.addr6 = attre->mp_nexthop_global; + break; + + default: + zlog_warn ("%s: invalid encap nexthop length: %d", + __func__, attre->mp_nexthop_len); + vpn_bi->extra->vnc.import.un_family = 0; + break; + } +} + +/* + * returns 0 on success, nonzero on error + */ +static int +rfapiWithdrawEncapUpdateCachedUn ( + struct rfapi_import_table *import_table, + struct bgp_info *encap_bi, + struct route_node *vpn_rn, + struct bgp_info *vpn_bi) +{ + if (!encap_bi) + { + + /* + * clear cached UN address + */ + if (!vpn_bi || !vpn_bi->extra) + { + zlog_warn ("%s: missing VPN bi/extra, can't clear UN addr", + __func__); + return 1; + } + vpn_bi->extra->vnc.import.un_family = 0; + memset (&vpn_bi->extra->vnc.import.un, 0, + sizeof (vpn_bi->extra->vnc.import.un)); + if (CHECK_FLAG (vpn_bi->flags, BGP_INFO_VALID)) + { + if (rfapiGetVncTunnelUnAddr (vpn_bi->attr, NULL)) + { + UNSET_FLAG (vpn_bi->flags, BGP_INFO_VALID); + if (VALID_INTERIOR_TYPE (vpn_bi->type)) + RFAPI_MONITOR_EXTERIOR (vpn_rn)->valid_interior_count--; + /* signal interior route withdrawal to import-exterior */ + vnc_import_bgp_exterior_del_route_interior (bgp_get_default (), + import_table, + vpn_rn, vpn_bi); + } + } + + } + else + { + if (!vpn_bi) + { + zlog_warn ("%s: missing VPN bi, can't clear UN addr", __func__); + return 1; + } + rfapiCopyUnEncap2VPN (encap_bi, vpn_bi); + if (!CHECK_FLAG (vpn_bi->flags, BGP_INFO_VALID)) + { + SET_FLAG (vpn_bi->flags, BGP_INFO_VALID); + if (VALID_INTERIOR_TYPE (vpn_bi->type)) + RFAPI_MONITOR_EXTERIOR (vpn_rn)->valid_interior_count++; + /* signal interior route withdrawal to import-exterior */ + vnc_import_bgp_exterior_add_route_interior (bgp_get_default (), + import_table, + vpn_rn, vpn_bi); + } + } + return 0; +} + +static int +rfapiWithdrawTimerEncap (struct thread *t) +{ + struct rfapi_withdraw *wcb = t->arg; + struct bgp_info *bi = wcb->info; + int was_first_route = 0; + struct rfapi_monitor_encap *em; + struct skiplist *vpn_node_sl = skiplist_new (0, NULL, NULL); + + assert (wcb->node); + assert (bi); + assert (wcb->import_table); + + RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_ENCAP, 0); + + if (wcb->node->info == bi) + was_first_route = 1; + + /* + * Remove the route/bi and free it + */ + rfapiBgpInfoDetach (wcb->node, bi); + rfapiBgpInfoFree (bi); + + if (!was_first_route) + goto done; + + for (em = RFAPI_MONITOR_ENCAP (wcb->node); em; em = em->next) + { + + /* + * Update monitoring VPN BIs with new encap info at the + * head of the encap bi chain (which could be NULL after + * removing the expiring bi above) + */ + if (rfapiWithdrawEncapUpdateCachedUn + (wcb->import_table, wcb->node->info, em->node, em->bi)) + continue; + + /* + * Build a list of unique VPN nodes referenced by these monitors. + * Use a skiplist for speed. + */ + skiplist_insert (vpn_node_sl, em->node, em->node); + } + + + /* + * for each VPN node referenced in the ENCAP monitors: + */ + struct route_node *rn; + while (!skiplist_first (vpn_node_sl, (void **) &rn, NULL)) + { + if (!wcb->node->info) + { + struct rfapi_monitor_vpn *moved; + + moved = rfapiMonitorMoveShorter (rn, 0); + if (moved) + { + //rfapiDoRouteCallback(wcb->import_table, moved->node, moved); + rfapiMonitorMovedUp (wcb->import_table, rn, moved->node, moved); + } + } + else + { + //rfapiDoRouteCallback(wcb->import_table, rn, NULL); + rfapiMonitorItNodeChanged (wcb->import_table, rn, NULL); + } + skiplist_delete_first (vpn_node_sl); + } + +done: + RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_ENCAP, 1); + route_unlock_node (wcb->node); /* decr ref count */ + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + skiplist_free (vpn_node_sl); + return 0; +} + + +/* + * Works for both VPN and ENCAP routes; timer_service_func is different + * in each case + */ +static void +rfapiBiStartWithdrawTimer ( + struct rfapi_import_table *import_table, + struct route_node *rn, + struct bgp_info *bi, + afi_t afi, + safi_t safi, + int (*timer_service_func) (struct thread *)) +{ + uint32_t lifetime; + struct rfapi_withdraw *wcb; + + if CHECK_FLAG + (bi->flags, BGP_INFO_REMOVED) + { + /* + * Already on the path to being withdrawn, + * should already have a timer set up to + * delete it. + */ + zlog_debug ("%s: already being withdrawn, do nothing", __func__); + return; + } + + rfapiGetVncLifetime (bi->attr, &lifetime); + zlog_debug ("%s: VNC lifetime is %u", __func__, lifetime); + + /* + * withdrawn routes get to hang around for a while + */ + SET_FLAG (bi->flags, BGP_INFO_REMOVED); + + /* set timer to remove the route later */ + lifetime = rfapiGetHolddownFromLifetime (lifetime); + zlog_debug ("%s: using timeout %u", __func__, lifetime); + + /* + * Stash import_table, node, and info for use by timer + * service routine, which is supposed to free the wcb. + */ + wcb = XCALLOC (MTYPE_RFAPI_WITHDRAW, sizeof (struct rfapi_withdraw)); + assert (wcb); + wcb->node = rn; + wcb->info = bi; + wcb->import_table = import_table; + + zlog_debug + ("%s: wcb values: node=%p, info=%p, import_table=%p (bi follows)", + __func__, wcb->node, wcb->info, wcb->import_table); + rfapiPrintBi (NULL, bi); + + + assert (bi->extra); + if (lifetime > UINT32_MAX / 1001) + { + /* sub-optimal case, but will probably never happen */ + bi->extra->vnc.import.timer = thread_add_timer (bm->master, + timer_service_func, + wcb, lifetime); + } + else + { + static uint32_t jitter; + uint32_t lifetime_msec; + + /* + * the goal here is to spread out the timers so they are + * sortable in the skip list + */ + if (++jitter >= 1000) + jitter = 0; + + lifetime_msec = (lifetime * 1000) + jitter; + + bi->extra->vnc.import.timer = thread_add_background (bm->master, + timer_service_func, + wcb, + lifetime_msec); + } + + /* re-sort route list (BGP_INFO_REMOVED routes are last) */ + if (((struct bgp_info *) rn->info)->next) + { + rfapiBgpInfoDetach (rn, bi); + rfapiBgpInfoAttachSorted (rn, bi, afi, safi); + } +} + + +typedef void (rfapi_bi_filtered_import_f) (struct rfapi_import_table *, + int, + struct peer *, + void *, + struct prefix *, + struct prefix *, + afi_t, + struct prefix_rd *, + struct attr *, + u_char, u_char, uint32_t *); + + +static void +rfapiExpireEncapNow ( + struct rfapi_import_table *it, + struct route_node *rn, + struct bgp_info *bi) +{ + struct rfapi_withdraw *wcb; + struct thread t; + + /* + * pretend we're an expiring timer + */ + wcb = XCALLOC (MTYPE_RFAPI_WITHDRAW, sizeof (struct rfapi_withdraw)); + wcb->info = bi; + wcb->node = rn; + wcb->import_table = it; + memset (&t, 0, sizeof (t)); + t.arg = wcb; + rfapiWithdrawTimerEncap (&t); /* frees wcb */ +} + +static int +rfapiGetNexthop (struct attr *attr, struct prefix *prefix) +{ + switch (BGP_MP_NEXTHOP_FAMILY (attr->extra->mp_nexthop_len)) + { + case AF_INET: + prefix->family = AF_INET; + prefix->prefixlen = 32; + prefix->u.prefix4 = attr->extra->mp_nexthop_global_in; + break; + case AF_INET6: + prefix->family = AF_INET6; + prefix->prefixlen = 128; + prefix->u.prefix6 = attr->extra->mp_nexthop_global; + break; + default: + zlog_debug ("%s: unknown attr->extra->mp_nexthop_len %d", __func__, + attr->extra->mp_nexthop_len); + return EINVAL; + } + return 0; +} + +/* + * import a bgp_info if its route target list intersects with the + * import table's route target list + */ +static void +rfapiBgpInfoFilteredImportEncap ( + struct rfapi_import_table *import_table, + int action, + struct peer *peer, + void *rfd, /* set for looped back routes */ + struct prefix *p, + struct prefix *aux_prefix, /* Unused for encap routes */ + afi_t afi, + struct prefix_rd *prd, + struct attr *attr, /* part of bgp_info */ + u_char type, /* part of bgp_info */ + u_char sub_type, /* part of bgp_info */ + uint32_t *label) /* part of bgp_info */ +{ + struct route_table *rt = NULL; + struct route_node *rn; + struct bgp_info *info_new; + struct bgp_info *bi; + struct bgp_info *next; + char buf[BUFSIZ]; + + struct prefix p_firstbi_old; + struct prefix p_firstbi_new; + int replacing = 0; + const char *action_str = NULL; + struct prefix un_prefix; + + struct bgp *bgp; + bgp = bgp_get_default (); /* assume 1 instance for now */ + + switch (action) + { + case FIF_ACTION_UPDATE: + action_str = "update"; + break; + case FIF_ACTION_WITHDRAW: + action_str = "withdraw"; + break; + case FIF_ACTION_KILL: + action_str = "kill"; + break; + default: + assert (0); + break; + } + + zlog_debug ("%s: entry: %s: prefix %s/%d", __func__, + action_str, + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); + + memset (&p_firstbi_old, 0, sizeof (p_firstbi_old)); + memset (&p_firstbi_new, 0, sizeof (p_firstbi_new)); + + if (action == FIF_ACTION_UPDATE) + { + /* + * Compare rt lists. If no intersection, don't import this route + * On a withdraw, peer and RD are sufficient to determine if + * we should act. + */ + if (!attr || !attr->extra || !attr->extra->ecommunity) + { + + zlog_debug ("%s: attr, extra, or ecommunity missing, not importing", + __func__); + return; + } +#if RFAPI_REQUIRE_ENCAP_BEEC + if (!rfapiEcommunitiesMatchBeec (attr->extra->ecommunity)) + { + zlog_debug ("%s: it=%p: no match for BGP Encapsulation ecommunity", + __func__, import_table); + return; + } +#endif + if (!rfapiEcommunitiesIntersect (import_table->rt_import_list, + attr->extra->ecommunity)) + { + + zlog_debug ("%s: it=%p: no ecommunity intersection", + __func__, import_table); + return; + } + + /* + * Updates must also have a nexthop address + */ + memset (&un_prefix, 0, sizeof (un_prefix)); /* keep valgrind happy */ + if (rfapiGetNexthop (attr, &un_prefix)) + { + zlog_debug ("%s: missing nexthop address", __func__); + return; + } + } + + /* + * Figure out which radix tree the route would go into + */ + switch (afi) + { + case AFI_IP: + case AFI_IP6: + rt = import_table->imported_encap[afi]; + break; + + default: + zlog_err ("%s: bad afi %d", __func__, afi); + return; + } + + /* + * route_node_lookup returns a node only if there is at least + * one route attached. + */ + rn = route_node_lookup (rt, p); + +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: initial encap lookup (it=%p) rn=%p", + __func__, import_table, rn); +#endif + + if (rn) + { + + RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, 1); + route_unlock_node (rn); /* undo lock in route_node_lookup */ + + + /* + * capture nexthop of first bi + */ + if (rn->info) + { + rfapiNexthop2Prefix (((struct bgp_info *) (rn->info))->attr, + &p_firstbi_old); + } + + for (bi = rn->info; bi; bi = bi->next) + { + + /* + * Does this bgp_info refer to the same route + * as we are trying to add? + */ + zlog_debug ("%s: comparing BI %p", __func__, bi); + + + /* + * Compare RDs + * + * RD of import table bi is in bi->extra->vnc.import.rd + * RD of info_orig is in prd + */ + if (!bi->extra) + { + zlog_debug ("%s: no bi->extra", __func__); + continue; + } + if (prefix_cmp ((struct prefix *) &bi->extra->vnc.import.rd, + (struct prefix *) prd)) + { + + zlog_debug ("%s: prd does not match", __func__); + continue; + } + + /* + * Compare peers + */ + if (bi->peer != peer) + { + zlog_debug ("%s: peer does not match", __func__); + continue; + } + + zlog_debug ("%s: found matching bi", __func__); + + /* Same route. Delete this bi, replace with new one */ + + if (action == FIF_ACTION_WITHDRAW) + { + + zlog_debug ("%s: withdrawing at prefix %s/%d", + __func__, + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, + BUFSIZ), rn->p.prefixlen); + + rfapiBiStartWithdrawTimer (import_table, rn, bi, + afi, SAFI_ENCAP, + rfapiWithdrawTimerEncap); + + } + else + { + zlog_debug ("%s: %s at prefix %s/%d", + __func__, + ((action == + FIF_ACTION_KILL) ? "killing" : "replacing"), + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, + BUFSIZ), rn->p.prefixlen); + + /* + * If this route is waiting to be deleted because of + * a previous withdraw, we must cancel its timer. + */ + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) + && bi->extra->vnc.import.timer) + { + + struct thread *t = + (struct thread *) bi->extra->vnc.import.timer; + struct rfapi_withdraw *wcb = t->arg; + + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + thread_cancel (t); + } + + if (action == FIF_ACTION_UPDATE) + { + rfapiBgpInfoDetach (rn, bi); + rfapiBgpInfoFree (bi); + replacing = 1; + } + else + { + /* + * Kill: do export stuff when removing bi + */ + struct rfapi_withdraw *wcb; + struct thread t; + + /* + * pretend we're an expiring timer + */ + wcb = + XCALLOC (MTYPE_RFAPI_WITHDRAW, + sizeof (struct rfapi_withdraw)); + wcb->info = bi; + wcb->node = rn; + wcb->import_table = import_table; + memset (&t, 0, sizeof (t)); + t.arg = wcb; + rfapiWithdrawTimerEncap (&t); /* frees wcb */ + } + } + + break; + } + } + + if (rn) + RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, replacing ? 1 : 0); + + if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL) + return; + + info_new = rfapiBgpInfoCreate (attr, peer, rfd, prd, type, sub_type, NULL); + + if (rn) + { + if (!replacing) + route_lock_node (rn); /* incr ref count for new BI */ + } + else + { + rn = route_node_get (rt, p); + } + + zlog_debug ("%s: (afi=%d, rn=%p) inserting at prefix %s/%d", + __func__, + afi, + rn, + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), + rn->p.prefixlen); + + rfapiBgpInfoAttachSorted (rn, info_new, afi, SAFI_ENCAP); + + /* + * Delete holddown routes from same NVE. See details in + * rfapiBgpInfoFilteredImportVPN() + */ + for (bi = info_new->next; bi; bi = next) + { + + struct prefix pfx_un; + int un_match = 0; + + next = bi->next; + if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + /* + * We already match the VN address (it is the prefix + * of the route node) + */ + + if (!rfapiGetNexthop (bi->attr, &pfx_un) && + prefix_same (&pfx_un, &un_prefix)) + { + + un_match = 1; + } + + if (!un_match) + continue; + + zlog_debug ("%s: removing holddown bi matching NVE of new route", + __func__); + if (bi->extra->vnc.import.timer) + { + struct thread *t = (struct thread *) bi->extra->vnc.import.timer; + struct rfapi_withdraw *wcb = t->arg; + + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + thread_cancel (t); + } + rfapiExpireEncapNow (import_table, rn, bi); + } + + rfapiNexthop2Prefix (((struct bgp_info *) (rn->info))->attr, + &p_firstbi_new); + + /* + * If the nexthop address of the selected Encap route (i.e., + * the UN address) has changed, then we must update the VPN + * routes that refer to this Encap route and possibly force + * rfapi callbacks. + */ + if (rfapiAttrNexthopAddrDifferent (&p_firstbi_old, &p_firstbi_new)) + { + + struct rfapi_monitor_encap *m; + struct rfapi_monitor_encap *mnext; + + struct route_node *referenced_vpn_prefix; + + /* + * Optimized approach: build radix tree on the fly to + * hold list of VPN nodes referenced by the ENCAP monitors + * + * The nodes in this table correspond to prefixes of VPN routes. + * The "info" pointer of the node points to a chain of + * struct rfapi_monitor_encap, each of which refers to a + * specific VPN node. + */ + struct route_table *referenced_vpn_table; + + referenced_vpn_table = route_table_init (); + assert (referenced_vpn_table); + + /* + * iterate over the set of monitors at this ENCAP node. + */ +#if DEBUG_ENCAP_MONITOR + zlog_debug ("%s: examining monitors at rn=%p", __func__, rn); +#endif + for (m = RFAPI_MONITOR_ENCAP (rn); m; m = m->next) + { + + /* + * For each referenced bi/route, copy the ENCAP route's + * nexthop to the VPN route's cached UN address field and set + * the address family of the cached UN address field. + */ + rfapiCopyUnEncap2VPN (info_new, m->bi); + if (!CHECK_FLAG (m->bi->flags, BGP_INFO_VALID)) + { + SET_FLAG (m->bi->flags, BGP_INFO_VALID); + if (VALID_INTERIOR_TYPE (m->bi->type)) + RFAPI_MONITOR_EXTERIOR (m->node)->valid_interior_count++; + vnc_import_bgp_exterior_add_route_interior (bgp, + import_table, + m->node, m->bi); + } + + /* + * Build a list of unique VPN nodes referenced by these monitors + * + * There could be more than one VPN node here with a given + * prefix. Those are currently in an unsorted linear list + * per prefix. + */ + + referenced_vpn_prefix = + route_node_get (referenced_vpn_table, &m->node->p); + assert (referenced_vpn_prefix); + for (mnext = referenced_vpn_prefix->info; mnext; + mnext = mnext->next) + { + + if (mnext->node == m->node) + break; + } + + if (mnext) + { + /* + * already have an entry for this VPN node + */ + route_unlock_node (referenced_vpn_prefix); + } + else + { + mnext = XCALLOC (MTYPE_RFAPI_MONITOR_ENCAP, + sizeof (struct rfapi_monitor_encap)); + assert (mnext); + mnext->node = m->node; + mnext->next = referenced_vpn_prefix->info; + referenced_vpn_prefix->info = mnext; + } + + } + + /* + * for each VPN node referenced in the ENCAP monitors: + */ + for (referenced_vpn_prefix = route_top (referenced_vpn_table); + referenced_vpn_prefix; + referenced_vpn_prefix = route_next (referenced_vpn_prefix)) + { + + while ((m = referenced_vpn_prefix->info)) + { + + struct route_node *n; + + rfapiMonitorMoveLonger (m->node); + for (n = m->node; n; n = n->parent) + { + //rfapiDoRouteCallback(import_table, n, NULL); + } + rfapiMonitorItNodeChanged (import_table, m->node, NULL); + + referenced_vpn_prefix->info = m->next; + route_unlock_node (referenced_vpn_prefix); + XFREE (MTYPE_RFAPI_MONITOR_ENCAP, m); + } + + } + route_table_finish (referenced_vpn_table); + } + + RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, 0); +} + +static void +rfapiExpireVpnNow ( + struct rfapi_import_table *it, + struct route_node *rn, + struct bgp_info *bi, + int lockoffset) +{ + struct rfapi_withdraw *wcb; + struct thread t; + + /* + * pretend we're an expiring timer + */ + wcb = XCALLOC (MTYPE_RFAPI_WITHDRAW, sizeof (struct rfapi_withdraw)); + wcb->info = bi; + wcb->node = rn; + wcb->import_table = it; + wcb->lockoffset = lockoffset; + memset (&t, 0, sizeof (t)); + t.arg = wcb; + rfapiWithdrawTimerVPN (&t); /* frees wcb */ +} + + +/* + * import a bgp_info if its route target list intersects with the + * import table's route target list + */ +void +rfapiBgpInfoFilteredImportVPN ( + struct rfapi_import_table *import_table, + int action, + struct peer *peer, + void *rfd, /* set for looped back routes */ + struct prefix *p, + struct prefix *aux_prefix, /* AFI_ETHER: optional IP */ + afi_t afi, + struct prefix_rd *prd, + struct attr *attr, /* part of bgp_info */ + u_char type, /* part of bgp_info */ + u_char sub_type, /* part of bgp_info */ + uint32_t *label) /* part of bgp_info */ +{ + struct route_table *rt = NULL; + struct route_node *rn; + struct route_node *n; + struct bgp_info *info_new; + struct bgp_info *bi; + struct bgp_info *next; + char buf[BUFSIZ]; + struct prefix vn_prefix; + struct prefix un_prefix; + int un_prefix_valid = 0; + struct route_node *ern; + int replacing = 0; + int original_had_routes = 0; + struct prefix original_nexthop; + const char *action_str = NULL; + int is_it_ce = 0; + + struct bgp *bgp; + bgp = bgp_get_default (); /* assume 1 instance for now */ + + switch (action) + { + case FIF_ACTION_UPDATE: + action_str = "update"; + break; + case FIF_ACTION_WITHDRAW: + action_str = "withdraw"; + break; + case FIF_ACTION_KILL: + action_str = "kill"; + break; + default: + assert (0); + break; + } + + if (import_table == bgp->rfapi->it_ce) + is_it_ce = 1; + + zlog_debug ("%s: entry: %s%s: prefix %s/%d: it %p, afi %s", __func__, + (is_it_ce ? "CE-IT " : ""), + action_str, + rfapi_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen, import_table, afi2str (afi)); + + VNC_ITRCCK; + + /* + * Compare rt lists. If no intersection, don't import this route + * On a withdraw, peer and RD are sufficient to determine if + * we should act. + */ + if (action == FIF_ACTION_UPDATE) + { + if (!attr || !attr->extra || !attr->extra->ecommunity) + { + + zlog_debug ("%s: attr, extra, or ecommunity missing, not importing", + __func__); + return; + } + if ((import_table != bgp->rfapi->it_ce) && + !rfapiEcommunitiesIntersect (import_table->rt_import_list, + attr->extra->ecommunity)) + { + + zlog_debug ("%s: it=%p: no ecommunity intersection", + __func__, import_table); + return; + } + + memset (&vn_prefix, 0, sizeof (vn_prefix)); /* keep valgrind happy */ + if (rfapiGetNexthop (attr, &vn_prefix)) + { + /* missing nexthop address would be a bad, bad thing */ + zlog_debug ("%s: missing nexthop", __func__); + return; + } + } + + /* + * Figure out which radix tree the route would go into + */ + switch (afi) + { + case AFI_IP: + case AFI_IP6: + case AFI_ETHER: + rt = import_table->imported_vpn[afi]; + break; + + default: + zlog_err ("%s: bad afi %d", __func__, afi); + return; + } + + /* clear it */ + memset (&original_nexthop, 0, sizeof (original_nexthop)); + + /* + * route_node_lookup returns a node only if there is at least + * one route attached. + */ + rn = route_node_lookup (rt, p); + + zlog_debug ("%s: rn=%p", __func__, rn); + + if (rn) + { + + RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 1); + route_unlock_node (rn); /* undo lock in route_node_lookup */ + + if (rn->info) + original_had_routes = 1; + + /* + * Look for same route (will have same RD and peer) + */ + bi = rfapiItBiIndexSearch (rn, prd, peer, aux_prefix); + + if (bi) + { + + /* + * This was an old test when we iterated over the + * BIs linearly. Since we're now looking up with + * RD and peer, comparing types should not be + * needed. Changed to assertion. + * + * Compare types. Doing so prevents a RFP-originated + * route from matching an imported route, for example. + */ + assert (bi->type == type); + + zlog_debug ("%s: found matching bi", __func__); + + /* + * In the special CE table, withdrawals occur without holddown + */ + if (import_table == bgp->rfapi->it_ce) + { + vnc_direct_bgp_del_route_ce (bgp, rn, bi); + if (action == FIF_ACTION_WITHDRAW) + action = FIF_ACTION_KILL; + } + + if (action == FIF_ACTION_WITHDRAW) + { + + int washolddown = CHECK_FLAG (bi->flags, BGP_INFO_REMOVED); + + zlog_debug ("%s: withdrawing at prefix %s/%d%s", + __func__, + rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf, + BUFSIZ), rn->p.prefixlen, + (washolddown ? " (already being withdrawn)" : "")); + + VNC_ITRCCK; + if (!washolddown) + { + rfapiBiStartWithdrawTimer (import_table, rn, bi, + afi, SAFI_MPLS_VPN, + rfapiWithdrawTimerVPN); + + RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, -1); + import_table->holddown_count[afi] += 1; + } + VNC_ITRCCK; + } + else + { + int washolddown = 0; + + zlog_debug ("%s: %s at prefix %s/%d", + __func__, + ((action == + FIF_ACTION_KILL) ? "killing" : "replacing"), + rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf, + BUFSIZ), rn->p.prefixlen); + + /* + * If this route is waiting to be deleted because of + * a previous withdraw, we must cancel its timer. + */ + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && + bi->extra->vnc.import.timer) + { + + struct thread *t = + (struct thread *) bi->extra->vnc.import.timer; + struct rfapi_withdraw *wcb = t->arg; + + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + thread_cancel (t); + + import_table->holddown_count[afi] -= 1; + RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, 1); + + washolddown = 1; + } + /* + * decrement remote count (if route is remote) because + * we are going to remove it below + */ + RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, -1); + if (action == FIF_ACTION_UPDATE) + { + replacing = 1; + + /* + * make copy of original nexthop so we can see if it changed + */ + rfapiGetNexthop (bi->attr, &original_nexthop); + + /* + * remove bi without doing any export processing + */ + if (CHECK_FLAG (bi->flags, BGP_INFO_VALID) + && VALID_INTERIOR_TYPE (bi->type)) + RFAPI_MONITOR_EXTERIOR (rn)->valid_interior_count--; + rfapiItBiIndexDel (rn, bi); + rfapiBgpInfoDetach (rn, bi); + rfapiMonitorEncapDelete (bi); + vnc_import_bgp_exterior_del_route_interior (bgp, + import_table, + rn, bi); + rfapiBgpInfoFree (bi); + } + else + { + /* Kill */ + /* + * remove bi and do export processing + */ + import_table->holddown_count[afi] += 1; + rfapiExpireVpnNow (import_table, rn, bi, 0); + } + + } + } + + } + + if (rn) + RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, replacing ? 1 : 0); + + if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL) + { + VNC_ITRCCK; + return; + } + + info_new = rfapiBgpInfoCreate (attr, peer, rfd, prd, type, sub_type, label); + + /* + * lookup un address in encap table + */ + ern = route_node_match (import_table->imported_encap[afi], &vn_prefix); + if (ern) + { + rfapiCopyUnEncap2VPN (ern->info, info_new); + route_unlock_node (ern); /* undo lock in route_note_match */ + } + else + { + char buf[BUFSIZ]; + prefix2str (&vn_prefix, buf, sizeof (buf)); + buf[BUFSIZ - 1] = 0; + /* Not a big deal, just means VPN route got here first */ + zlog_debug ("%s: no encap route for vn addr %s", __func__, buf); + info_new->extra->vnc.import.un_family = 0; + } + + if (rn) + { + if (!replacing) + route_lock_node (rn); + } + else + { + /* + * No need to increment reference count, so only "get" + * if the node is not there already + */ + rn = route_node_get (rt, p); + } + + /* + * For ethernet routes, if there is an accompanying IP address, + * save it in the bi + */ + if ((AFI_ETHER == afi) && aux_prefix) + { + + zlog_debug ("%s: setting BI's aux_prefix", __func__); + info_new->extra->vnc.import.aux_prefix = *aux_prefix; + } + + zlog_debug ("%s: inserting bi %p at prefix %s/%d #%d", + __func__, + info_new, + rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), + rn->p.prefixlen, rn->lock); + + rfapiBgpInfoAttachSorted (rn, info_new, afi, SAFI_MPLS_VPN); + rfapiItBiIndexAdd (rn, info_new); + if (!rfapiGetUnAddrOfVpnBi (info_new, NULL)) + { + if (VALID_INTERIOR_TYPE (info_new->type)) + RFAPI_MONITOR_EXTERIOR (rn)->valid_interior_count++; + SET_FLAG (info_new->flags, BGP_INFO_VALID); + } + RFAPI_UPDATE_ITABLE_COUNT (info_new, import_table, afi, 1); + vnc_import_bgp_exterior_add_route_interior (bgp, import_table, rn, + info_new); + + if (import_table == bgp->rfapi->it_ce) + vnc_direct_bgp_add_route_ce (bgp, rn, info_new); + + zlog_debug ("%s: showing IT node", __func__); + rfapiShowItNode (NULL, rn); /* debug */ + + rfapiMonitorEncapAdd (import_table, &vn_prefix, rn, info_new); + + if (!rfapiGetUnAddrOfVpnBi (info_new, &un_prefix)) + { + + /* + * if we have a valid UN address (either via Encap route + * or via tunnel attribute), then we should attempt + * to move any monitors at less-specific nodes to this node + */ + rfapiMonitorMoveLonger (rn); + + un_prefix_valid = 1; + + } + + /* + * 101129 Enhancement: if we add a route (implication: it is not + * in holddown), delete all other routes from this nve at this + * node that are in holddown, regardless of peer. + * + * Reasons it's OK to do that: + * + * - if the holddown route being deleted originally came from BGP VPN, + * it is already gone from BGP (implication of holddown), so there + * won't be any added inconsistency with the BGP RIB. + * + * - once a fresh route is added at a prefix, any routes in holddown + * at that prefix will not show up in RFP responses, so deleting + * the holddown routes won't affect the contents of responses. + * + * - lifetimes are supposed to be consistent, so there should not + * be a case where the fresh route has a shorter lifetime than + * the holddown route, so we don't expect the fresh route to + * disappear and complete its holddown time before the existing + * holddown routes time out. Therefore, we won't have a situation + * where we expect the existing holddown routes to be hidden and + * then to reappear sometime later (as holddown routes) in a + * RFP response. + * + * Among other things, this would enable us to skirt the problem + * of local holddown routes that refer to NVE descriptors that + * have already been closed (if the same NVE triggers a subsequent + * rfapi_open(), the new peer is different and doesn't match the + * peer of the holddown route, so the stale holddown route still + * hangs around until it times out instead of just being replaced + * by the fresh route). + */ + /* + * We know that the new bi will have been inserted before any routes + * in holddown, so we can skip any that came before it + */ + for (bi = info_new->next; bi; bi = next) + { + + struct prefix pfx_vn; + struct prefix pfx_un; + int un_match = 0; + int remote_peer_match = 0; + + next = bi->next; + + /* + * Must be holddown + */ + if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + /* + * Must match VN address (nexthop of VPN route) + */ + if (rfapiGetNexthop (bi->attr, &pfx_vn)) + continue; + if (!prefix_same (&pfx_vn, &vn_prefix)) + continue; + + if (un_prefix_valid && /* new route UN addr */ + !rfapiGetUnAddrOfVpnBi (bi, &pfx_un) && /* old route UN addr */ + prefix_same (&pfx_un, &un_prefix)) + { /* compare */ + un_match = 1; + } + if (!RFAPI_LOCAL_BI (bi) && !RFAPI_LOCAL_BI (info_new) && + sockunion_same (&bi->peer->su, &info_new->peer->su)) + { + /* old & new are both remote, same peer */ + remote_peer_match = 1; + } + + if (!un_match & !remote_peer_match) + continue; + + zlog_debug ("%s: removing holddown bi matching NVE of new route", + __func__); + if (bi->extra->vnc.import.timer) + { + struct thread *t = (struct thread *) bi->extra->vnc.import.timer; + struct rfapi_withdraw *wcb = t->arg; + + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + thread_cancel (t); + } + rfapiExpireVpnNow (import_table, rn, bi, 0); + } + + if (!original_had_routes) + { + /* + * We went from 0 usable routes to 1 usable route. Perform the + * "Adding a Route" export process. + */ + vnc_direct_bgp_add_prefix (bgp, import_table, rn); + vnc_zebra_add_prefix (bgp, import_table, rn); + } + else + { + /* + * Check for nexthop change event + * Note: the prefix_same() test below detects two situations: + * 1. route is replaced, new route has different nexthop + * 2. new route is added (original_nexthop is 0) + */ + struct prefix new_nexthop; + + rfapiGetNexthop (attr, &new_nexthop); + if (!prefix_same (&original_nexthop, &new_nexthop)) + { + /* + * nexthop change event + * vnc_direct_bgp_add_prefix() will recompute VN addr ecommunity + */ + vnc_direct_bgp_add_prefix (bgp, import_table, rn); + } + } + + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + for (n = rn; n; n = n->parent) + { + //rfapiDoRouteCallback(import_table, n, NULL); + } + rfapiMonitorItNodeChanged (import_table, rn, NULL); + } + RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 0); + VNC_ITRCCK; +} + +static rfapi_bi_filtered_import_f * +rfapiBgpInfoFilteredImportFunction (safi_t safi) +{ + switch (safi) + { + case SAFI_MPLS_VPN: + case BGP_SAFI_VPN: + return rfapiBgpInfoFilteredImportVPN; + + case SAFI_ENCAP: + return rfapiBgpInfoFilteredImportEncap; + } + zlog_err ("%s: bad safi %d", __func__, safi); + return NULL; +} + +void +rfapiProcessUpdate ( + struct peer *peer, + void *rfd, /* set when looped from RFP/RFAPI */ + struct prefix *p, + struct prefix_rd *prd, + struct attr *attr, + afi_t afi, + safi_t safi, + u_char type, + u_char sub_type, + uint32_t *label) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + int has_ip_route = 1; + uint32_t lni = 0; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + assert (bgp); + + h = bgp->rfapi; + assert (h); + + /* + * look at high-order byte of RD. FF means MAC + * address is present (VNC L2VPN) + */ + if ((safi == SAFI_MPLS_VPN) && + (decode_rd_type(prd->val) == RD_TYPE_VNC_ETH)) + { + struct prefix pfx_mac_buf; + struct prefix pfx_nexthop_buf; + int rc; + + /* + * Set flag if prefix and nexthop are the same - don't + * add the route to normal IP-based import tables + */ + if (!rfapiGetNexthop (attr, &pfx_nexthop_buf)) + { + if (!prefix_cmp (&pfx_nexthop_buf, p)) + { + has_ip_route = 0; + } + } + + memset (&pfx_mac_buf, 0, sizeof (pfx_mac_buf)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + memcpy (&pfx_mac_buf.u.prefix_eth.octet, prd->val + 2, 6); + + /* + * Find rt containing LNI (Logical Network ID), which + * _should_ always be present when mac address is present + */ + rc = rfapiEcommunityGetLNI (attr->extra->ecommunity, &lni); + + zlog_debug + ("%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p, attr->extra=%p", + __func__, rc, lni, attr, attr->extra); + if (attr && attr->extra && !rc) + { + it = rfapiMacImportTableGet (bgp, lni); + + rfapiBgpInfoFilteredImportVPN ( + it, + FIF_ACTION_UPDATE, + peer, + rfd, + &pfx_mac_buf, /* prefix */ + p, /* aux prefix: IP addr */ + AFI_ETHER, + prd, + attr, + type, + sub_type, + label); + } + + } + + if (!has_ip_route) + return; + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + for (it = h->imports; it; it = it->next) + { + (*rfapiBgpInfoFilteredImportFunction (safi)) ( + it, + FIF_ACTION_UPDATE, + peer, + rfd, + p, /* prefix */ + NULL, + afi, + prd, + attr, + type, + sub_type, + label); + } + + if (safi == SAFI_MPLS_VPN || safi == BGP_SAFI_VPN) + { + vnc_direct_bgp_rh_add_route (bgp, afi, p, peer, attr); + } + + if (safi == SAFI_MPLS_VPN) + { + rfapiBgpInfoFilteredImportVPN ( + bgp->rfapi->it_ce, + FIF_ACTION_UPDATE, + peer, + rfd, + p, /* prefix */ + NULL, + afi, + prd, + attr, + type, + sub_type, + label); + } +} + + +void +rfapiProcessWithdraw ( + struct peer *peer, + void *rfd, + struct prefix *p, + struct prefix_rd *prd, + struct attr *attr, + afi_t afi, + safi_t safi, + u_char type, + int kill) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + assert (bgp); + + h = bgp->rfapi; + assert (h); + + /* + * look at high-order byte of RD. FF means MAC + * address is present (VNC L2VPN) + */ + if (h->import_mac != NULL && safi == SAFI_MPLS_VPN && + decode_rd_type(prd->val) == RD_TYPE_VNC_ETH) + { + struct prefix pfx_mac_buf; + void *cursor = NULL; + int rc; + + memset (&pfx_mac_buf, 0, sizeof (pfx_mac_buf)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + memcpy (&pfx_mac_buf.u.prefix_eth, prd->val + 2, 6); + + /* + * withdraw does not contain attrs, so we don't have + * access to the route's LNI, which would ordinarily + * select the specific mac-based import table. Instead, + * we must iterate over all mac-based tables and rely + * on the RD to match. + * + * If this approach is too slow, add an index where + * key is {RD, peer} and value is the import table + */ + for (rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor); + rc == 0; + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor)) + { + +#if DEBUG_L2_EXTRA + zlog_debug + ("%s: calling rfapiBgpInfoFilteredImportVPN(it=%p, afi=AFI_ETHER)", + __func__, it); +#endif + + rfapiBgpInfoFilteredImportVPN ( + it, + (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), + peer, + rfd, + &pfx_mac_buf, /* prefix */ + p, /* aux_prefix: IP */ + AFI_ETHER, + prd, + attr, + type, + 0, + NULL); /* sub_type & label unused for withdraw */ + } + } + + /* + * XXX For the case where the withdraw involves an L2 + * route with no IP information, we rely on the lack + * of RT-list intersection to filter out the withdraw + * from the IP-based import tables below + */ + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + for (it = h->imports; it; it = it->next) + { + (*rfapiBgpInfoFilteredImportFunction (safi)) ( + it, + (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), + peer, + rfd, + p, /* prefix */ + NULL, + afi, + prd, + attr, + type, + 0, + NULL); /* sub_type & label unused for withdraw */ + } + + /* TBD the deletion should happen after the lifetime expires */ + if (safi == SAFI_MPLS_VPN || safi == BGP_SAFI_VPN) + vnc_direct_bgp_rh_del_route (bgp, afi, p, peer); + + if (safi == SAFI_MPLS_VPN) + { + rfapiBgpInfoFilteredImportVPN ( + bgp->rfapi->it_ce, + (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), + peer, + rfd, + p, /* prefix */ + NULL, + afi, + prd, + attr, + type, + 0, + NULL); /* sub_type & label unused for withdraw */ + } +} + +/* + * TBD optimized withdraw timer algorithm for case of many + * routes expiring at the same time due to peer drop. + */ +/* + * 1. Visit all BIs in all ENCAP import tables. + * + * a. If a bi's peer is the failed peer, remove the bi. + * b. If the removed ENCAP bi was first in the list of + * BIs at this ENCAP node, loop over all monitors + * at this node: + * + * (1) for each ENCAP monitor, loop over all its + * VPN node monitors and set their RFAPI_MON_FLAG_NEEDCALLBACK + * flags. + * + * 2. Visit all BIs in all VPN import tables. + * a. If a bi's peer is the failed peer, remove the bi. + * b. loop over all the VPN node monitors and set their + * RFAPI_MON_FLAG_NEEDCALLBACK flags + * c. If there are no BIs left at this VPN node, + * + */ + + +/* surprise, this gets called from peer_delete(), from rfapi_close() */ +static void +rfapiProcessPeerDownRt ( + struct peer *peer, + struct rfapi_import_table *import_table, + afi_t afi, + safi_t safi) +{ + struct route_node *rn; + struct bgp_info *bi; + struct route_table *rt; + int (*timer_service_func) (struct thread *); + + assert (afi == AFI_IP || afi == AFI_IP6); + + VNC_ITRCCK; + + switch (safi) + { + case SAFI_MPLS_VPN: + rt = import_table->imported_vpn[afi]; + timer_service_func = rfapiWithdrawTimerVPN; + break; + case SAFI_ENCAP: + rt = import_table->imported_encap[afi]; + timer_service_func = rfapiWithdrawTimerEncap; + break; + default: + assert (0); + } + + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + for (bi = rn->info; bi; bi = bi->next) + { + if (bi->peer == peer) + { + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + /* already in holddown, skip */ + continue; + } + + if (safi == SAFI_MPLS_VPN) + { + RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, -1); + import_table->holddown_count[afi] += 1; + } + rfapiBiStartWithdrawTimer (import_table, rn, bi, + afi, safi, + timer_service_func); + } + } + } + VNC_ITRCCK; +} + +/* + * This gets called when a peer connection drops. We have to remove + * all the routes from this peer. + * + * Current approach is crude. TBD Optimize by setting fewer timers and + * grouping withdrawn routes so we can generate callbacks more + * efficiently. + */ +void +rfapiProcessPeerDown (struct peer *peer) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + + /* + * If this peer is a "dummy" peer structure atached to a RFAPI + * nve_descriptor, we don't need to walk the import tables + * because the routes are already withdrawn by rfapi_close() + */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_IS_RFAPI_HD)) + return; + + /* + * 1. Visit all BIs in all ENCAP import tables. + * Start withdraw timer on the BIs that match peer. + * + * 2. Visit All BIs in all VPN import tables. + * Start withdraw timer on the BIs that match peer. + */ + + bgp = bgp_get_default (); /* assume 1 instance for now */ + assert (bgp); + + h = bgp->rfapi; + assert (h); + + for (it = h->imports; it; it = it->next) + { + rfapiProcessPeerDownRt (peer, it, AFI_IP, SAFI_ENCAP); + rfapiProcessPeerDownRt (peer, it, AFI_IP6, SAFI_ENCAP); + rfapiProcessPeerDownRt (peer, it, AFI_IP, SAFI_MPLS_VPN); + rfapiProcessPeerDownRt (peer, it, AFI_IP6, SAFI_MPLS_VPN); + } + + if (h->it_ce) + { + rfapiProcessPeerDownRt (peer, h->it_ce, AFI_IP, SAFI_MPLS_VPN); + rfapiProcessPeerDownRt (peer, h->it_ce, AFI_IP6, SAFI_MPLS_VPN); + } +} + +/* + * Import an entire RIB (for an afi/safi) to an import table RIB, + * filtered according to the import table's RT list + * + * TBD: does this function need additions to match rfapiProcessUpdate() + * for, e.g., L2 handling? + */ +static void +rfapiBgpTableFilteredImport ( + struct bgp *bgp, + struct rfapi_import_table *it, + afi_t afi, + safi_t safi) +{ + struct bgp_node *rn1; + struct bgp_node *rn2; + + /* Only these SAFIs have 2-level RIBS */ + assert (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP); + + /* + * Now visit all the rd nodes and the nodes of all the + * route tables attached to them, and import the routes + * if they have matching route targets + */ + for (rn1 = bgp_table_top (bgp->rib[afi][safi]); + rn1; rn1 = bgp_route_next (rn1)) + { + + if (rn1->info) + { + for (rn2 = bgp_table_top (rn1->info); + rn2; rn2 = bgp_route_next (rn2)) + { + + struct bgp_info *bi; + + for (bi = rn2->info; bi; bi = bi->next) + { + u_int32_t label = 0; + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + if (bi->extra) + label = decode_label (bi->extra->tag); + (*rfapiBgpInfoFilteredImportFunction (safi)) ( + it, /* which import table */ + FIF_ACTION_UPDATE, + bi->peer, + NULL, + &rn2->p, /* prefix */ + NULL, + afi, + (struct prefix_rd *) &rn1->p, + bi->attr, + bi->type, + bi->sub_type, + &label); + } + } + } + } +} + + +/* per-bgp-instance rfapi data */ +struct rfapi * +bgp_rfapi_new (struct bgp *bgp) +{ + struct rfapi *h; + int afi; + struct rfapi_rfp_cfg *cfg = NULL; + struct rfapi_rfp_cb_methods *cbm = NULL; + + assert (bgp->rfapi_cfg == NULL); + + h = (struct rfapi *) XCALLOC (MTYPE_RFAPI, sizeof (struct rfapi)); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + { + /* ugly, to deal with addition of delegates, part of 0.99.24.1 merge */ + h->un[afi].delegate = route_table_get_default_delegate (); + } + + /* + * initialize the ce import table + */ + h->it_ce = + XCALLOC (MTYPE_RFAPI_IMPORTTABLE, sizeof (struct rfapi_import_table)); + h->it_ce->imported_vpn[AFI_IP] = route_table_init (); + h->it_ce->imported_vpn[AFI_IP6] = route_table_init (); + h->it_ce->imported_encap[AFI_IP] = route_table_init (); + h->it_ce->imported_encap[AFI_IP6] = route_table_init (); + rfapiBgpTableFilteredImport (bgp, h->it_ce, AFI_IP, SAFI_MPLS_VPN); + rfapiBgpTableFilteredImport (bgp, h->it_ce, AFI_IP6, SAFI_MPLS_VPN); + + /* + * Set up work queue for deferred rfapi_close operations + */ + h->deferred_close_q = work_queue_new (bm->master, "rfapi deferred close"); + h->deferred_close_q->spec.workfunc = rfapi_deferred_close_workfunc; + h->deferred_close_q->spec.data = h; + + h->rfp = rfp_start (bm->master, &cfg, &cbm); + bgp->rfapi_cfg = bgp_rfapi_cfg_new (cfg); + if (cbm != NULL) + { + h->rfp_methods = *cbm; + } + return h; +} + +void +bgp_rfapi_destroy (struct bgp *bgp, struct rfapi *h) +{ + if (bgp == NULL || h == NULL) + return; + + if (h->resolve_nve_nexthop) + { + skiplist_free (h->resolve_nve_nexthop); + h->resolve_nve_nexthop = NULL; + } + + route_table_finish (h->it_ce->imported_vpn[AFI_IP]); + route_table_finish (h->it_ce->imported_vpn[AFI_IP6]); + route_table_finish (h->it_ce->imported_encap[AFI_IP]); + route_table_finish (h->it_ce->imported_encap[AFI_IP6]); + + if (h->import_mac) + { + struct rfapi_import_table *it; + void *cursor; + int rc; + + for (cursor = NULL, + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor); + !rc; + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor)) + { + + rfapiImportTableFlush (it); + XFREE (MTYPE_RFAPI_IMPORTTABLE, it); + } + skiplist_free (h->import_mac); + h->import_mac = NULL; + } + + work_queue_free (h->deferred_close_q); + + if (h->rfp != NULL) + rfp_stop (h->rfp); + XFREE (MTYPE_RFAPI_IMPORTTABLE, h->it_ce); + XFREE (MTYPE_RFAPI, h); +} + +struct rfapi_import_table * +rfapiImportTableRefAdd (struct bgp *bgp, struct ecommunity *rt_import_list) +{ + struct rfapi *h; + struct rfapi_import_table *it; + afi_t afi; + + h = bgp->rfapi; + assert (h); + + for (it = h->imports; it; it = it->next) + { + if (ecommunity_cmp (it->rt_import_list, rt_import_list)) + break; + } + + zlog_debug ("%s: matched it=%p", __func__, it); + + if (!it) + { + it = + XCALLOC (MTYPE_RFAPI_IMPORTTABLE, sizeof (struct rfapi_import_table)); + assert (it); + it->next = h->imports; + h->imports = it; + + it->rt_import_list = ecommunity_dup (rt_import_list); + it->monitor_exterior_orphans = + skiplist_new (0, NULL, (void (*)(void *)) prefix_free); + + /* + * fill import route tables from RIBs + * + * Potential area for optimization. If this occurs when + * tables are large (e.g., the operator adds a nve group + * with a new RT list to a running system), it could take + * a while. + * + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + it->imported_vpn[afi] = route_table_init (); + it->imported_encap[afi] = route_table_init (); + + rfapiBgpTableFilteredImport (bgp, it, afi, SAFI_MPLS_VPN); + rfapiBgpTableFilteredImport (bgp, it, afi, SAFI_ENCAP); + + vnc_import_bgp_exterior_redist_enable_it (bgp, afi, it); + } + } + + it->refcount += 1; + + return it; +} + +/* + * skiplist element free function + */ +static void +delete_rem_pfx_na_free (void *na) +{ + uint32_t *pCounter = ((struct rfapi_nve_addr *) na)->info; + + *pCounter += 1; + XFREE (MTYPE_RFAPI_NVE_ADDR, na); +} + +/* + * Common deleter for IP and MAC import tables + */ +static void +rfapiDeleteRemotePrefixesIt ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct prefix *un, + struct prefix *vn, + struct prefix *p, + int delete_active, + int delete_holddown, + uint32_t *pARcount, + uint32_t *pAHcount, + uint32_t *pHRcount, + uint32_t *pHHcount, + struct skiplist *uniq_active_nves, + struct skiplist *uniq_holddown_nves) +{ + afi_t afi; + +#if DEBUG_L2_EXTRA + { + char buf_pfx[BUFSIZ]; + + if (p) + { + prefix2str (p, buf_pfx, BUFSIZ); + } + else + { + buf_pfx[0] = '*'; + buf_pfx[1] = 0; + } + + zlog_debug ("%s: entry, p=%s, delete_active=%d, delete_holddown=%d", + __func__, buf_pfx, delete_active, delete_holddown); + } +#endif + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct route_table *rt; + struct route_node *rn; + + if (p && (family2afi (p->family) != afi)) + { + continue; + } + + rt = it->imported_vpn[afi]; + if (!rt) + continue; + + zlog_debug ("%s: scanning rt for afi=%d", __func__, afi); + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + struct bgp_info *bi; + struct bgp_info *next; + + if (VNC_DEBUG(IMPORT_DEL_REMOTE)) + { + char p1line[BUFSIZ]; + char p2line[BUFSIZ]; + + prefix2str (p, p1line, BUFSIZ); + prefix2str (&rn->p, p2line, BUFSIZ); + zlog_debug ("%s: want %s, have %s", __func__, p1line, p2line); + } + + if (p && prefix_cmp (p, &rn->p)) + continue; + + { + char buf_pfx[BUFSIZ]; + prefix2str (&rn->p, buf_pfx, BUFSIZ); + zlog_debug ("%s: rn pfx=%s", __func__, buf_pfx); + } + + /* TBD is this valid for afi == AFI_ETHER? */ + RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 1); + + for (bi = rn->info; bi; bi = next) + { + next = bi->next; + + struct prefix qpt; + struct prefix qct; + int qpt_valid = 0; + int qct_valid = 0; + int is_active = 0; + + zlog_debug ("%s: examining bi %p", __func__, bi); + + if (bi->attr) + { + if (!rfapiGetNexthop (bi->attr, &qpt)) + qpt_valid = 1; + } + if (vn) + { + if (!qpt_valid || !prefix_match (vn, &qpt)) + { +#if DEBUG_L2_EXTRA + zlog_debug + ("%s: continue at vn && !qpt_valid || !prefix_match(vn, &qpt)", + __func__); +#endif + continue; + } + } + + if (!rfapiGetUnAddrOfVpnBi (bi, &qct)) + qct_valid = 1; + + if (un) + { + if (!qct_valid || !prefix_match (un, &qct)) + { +#if DEBUG_L2_EXTRA + zlog_debug + ("%s: continue at un && !qct_valid || !prefix_match(un, &qct)", + __func__); +#endif + continue; + } + } + + + /* + * Blow bi away + */ + /* + * If this route is waiting to be deleted because of + * a previous withdraw, we must cancel its timer. + */ + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + if (!delete_holddown) + continue; + if (bi->extra->vnc.import.timer) + { + + struct thread *t = + (struct thread *) bi->extra->vnc.import.timer; + struct rfapi_withdraw *wcb = t->arg; + + wcb->import_table->holddown_count[afi] -= 1; + RFAPI_UPDATE_ITABLE_COUNT (bi, wcb->import_table, afi, + 1); + XFREE (MTYPE_RFAPI_WITHDRAW, wcb); + thread_cancel (t); + } + } + else + { + if (!delete_active) + continue; + is_active = 1; + } + + zlog_debug + ("%s: deleting bi %p (qct_valid=%d, qpt_valid=%d, delete_holddown=%d, delete_active=%d)", + __func__, bi, qct_valid, qpt_valid, delete_holddown, + delete_active); + + + /* + * add nve to list + */ + if (qct_valid && qpt_valid) + { + + struct rfapi_nve_addr na; + struct rfapi_nve_addr *nap; + + memset (&na, 0, sizeof (na)); + assert (!rfapiQprefix2Raddr (&qct, &na.un)); + assert (!rfapiQprefix2Raddr (&qpt, &na.vn)); + + if (skiplist_search ((is_active ? uniq_active_nves : + uniq_holddown_nves), &na, + (void **) &nap)) + { + char line[BUFSIZ]; + + nap = XCALLOC (MTYPE_RFAPI_NVE_ADDR, + sizeof (struct rfapi_nve_addr)); + assert (nap); + *nap = na; + nap->info = is_active ? pAHcount : pHHcount; + skiplist_insert ((is_active ? uniq_active_nves : + uniq_holddown_nves), nap, nap); + + rfapiNveAddr2Str (nap, line, BUFSIZ); + } + } + + vnc_direct_bgp_rh_del_route (bgp, afi, &rn->p, bi->peer); + + RFAPI_UPDATE_ITABLE_COUNT (bi, it, afi, -1); + it->holddown_count[afi] += 1; + rfapiExpireVpnNow (it, rn, bi, 1); + + zlog_debug ("%s: incrementing count (is_active=%d)", + __func__, is_active); + + if (is_active) + ++ * pARcount; + else + ++ * pHRcount; + } + } + } +} + + +/* + * For use by the "clear vnc prefixes" command + */ +/*------------------------------------------ + * rfapiDeleteRemotePrefixes + * + * UI helper: For use by the "clear vnc prefixes" command + * + * input: + * un if set, tunnel must match this prefix + * vn if set, nexthop prefix must match this prefix + * p if set, prefix must match this prefix + * + * output + * pARcount number of active routes deleted + * pAHcount number of active nves deleted + * pHRcount number of holddown routes deleted + * pHHcount number of holddown nves deleted + * + * return value: + * void + --------------------------------------------*/ +void +rfapiDeleteRemotePrefixes ( + struct prefix *un, + struct prefix *vn, + struct prefix *p, + int delete_active, + int delete_holddown, + uint32_t *pARcount, + uint32_t *pAHcount, + uint32_t *pHRcount, + uint32_t *pHHcount) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + uint32_t deleted_holddown_route_count = 0; + uint32_t deleted_active_route_count = 0; + uint32_t deleted_holddown_nve_count = 0; + uint32_t deleted_active_nve_count = 0; + struct skiplist *uniq_holddown_nves; + struct skiplist *uniq_active_nves; + + VNC_ITRCCK; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + /* If no bgp instantiated yet, no vnc prefixes exist */ + if (!bgp) + return; + + h = bgp->rfapi; + assert (h); + + uniq_holddown_nves = + skiplist_new (0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free); + uniq_active_nves = + skiplist_new (0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free); + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + for (it = h->imports; it; it = it->next) + { + + zlog_debug + ("%s: calling rfapiDeleteRemotePrefixesIt() on (IP) import %p", + __func__, it); + + rfapiDeleteRemotePrefixesIt ( + bgp, + it, + un, + vn, + p, + delete_active, + delete_holddown, + &deleted_active_route_count, + &deleted_active_nve_count, + &deleted_holddown_route_count, + &deleted_holddown_nve_count, + uniq_active_nves, + uniq_holddown_nves); + } + + /* + * Now iterate over L2 import tables + */ + if (h->import_mac && !(p && (p->family != AF_ETHERNET))) + { + + void *cursor = NULL; + int rc; + + for (cursor = NULL, + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor); + !rc; + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor)) + { + + zlog_debug + ("%s: calling rfapiDeleteRemotePrefixesIt() on import_mac %p", + __func__, it); + + rfapiDeleteRemotePrefixesIt ( + bgp, + it, + un, + vn, + p, + delete_active, + delete_holddown, + &deleted_active_route_count, + &deleted_active_nve_count, + &deleted_holddown_route_count, + &deleted_holddown_nve_count, + uniq_active_nves, + uniq_holddown_nves); + } + } + + /* + * our custom element freeing function above counts as it deletes + */ + skiplist_free (uniq_holddown_nves); + skiplist_free (uniq_active_nves); + + if (pARcount) + *pARcount = deleted_active_route_count; + if (pAHcount) + *pAHcount = deleted_active_nve_count; + if (pHRcount) + *pHRcount = deleted_holddown_route_count; + if (pHHcount) + *pHHcount = deleted_holddown_nve_count; + + VNC_ITRCCK; +} + +/*------------------------------------------ + * rfapiCountRemoteRoutes + * + * UI helper: count VRF routes from BGP side + * + * input: + * + * output + * pALRcount count of active local routes + * pARRcount count of active remote routes + * pHRcount count of holddown routes + * pIRcount count of direct imported routes + * + * return value: + * void + --------------------------------------------*/ +void +rfapiCountAllItRoutes (int *pALRcount, /* active local routes */ + int *pARRcount, /* active remote routes */ + int *pHRcount, /* holddown routes */ + int *pIRcount) /* imported routes */ +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + afi_t afi; + + int total_active_local = 0; + int total_active_remote = 0; + int total_holddown = 0; + int total_imported = 0; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + assert (bgp); + + h = bgp->rfapi; + assert (h); + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + for (it = h->imports; it; it = it->next) + { + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + total_active_local += it->local_count[afi]; + total_active_remote += it->remote_count[afi]; + total_holddown += it->holddown_count[afi]; + total_imported += it->imported_count[afi]; + + } + } + + void *cursor; + int rc; + + if (h->import_mac) + { + for (cursor = NULL, + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor); + !rc; + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor)) + { + + total_active_local += it->local_count[AFI_ETHER]; + total_active_remote += it->remote_count[AFI_ETHER]; + total_holddown += it->holddown_count[AFI_ETHER]; + total_imported += it->imported_count[AFI_ETHER]; + + } + } + + + if (pALRcount) + { + *pALRcount = total_active_local; + } + if (pARRcount) + { + *pARRcount = total_active_remote; + } + if (pHRcount) + { + *pHRcount = total_holddown; + } + if (pIRcount) + { + *pIRcount = total_imported; + } +} + +/*------------------------------------------ + * rfapiGetHolddownFromLifetime + * + * calculate holddown value based on lifetime + * + * input: + * lifetime lifetime + * + * return value: + * Holddown value based on lifetime, holddown_factor, + * and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY + * + --------------------------------------------*/ +/* hold down time maxes out at RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY */ +uint32_t +rfapiGetHolddownFromLifetime (uint32_t lifetime) +{ + uint32_t factor; + struct bgp *bgp; + + bgp = bgp_get_default (); + if (bgp && bgp->rfapi_cfg) + factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor; + else + factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR; + + if (factor < 100 || lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY) + lifetime = lifetime * factor / 100; + if (lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY) + return lifetime; + else + return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY; +} diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h new file mode 100644 index 0000000000..9e88b52f52 --- /dev/null +++ b/bgpd/rfapi/rfapi_import.h @@ -0,0 +1,283 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: rfapi_import.h + * Purpose: Handle import of routes from BGP to RFAPI + */ + +#ifndef QUAGGA_HGP_RFAPI_IMPORT_H +#define QUAGGA_HGP_RFAPI_IMPORT_H + +#include "thread.h" + +/* + * These are per-rt-import-list + * + * routes are not segregated by RD - the RD is stored in bgp_info_extra + * and is needed to determine if two prefixes are the same. + */ +struct rfapi_import_table +{ + struct rfapi_import_table *next; + struct ecommunity *rt_import_list; /* copied from nve grp */ + int refcount; /* nve grps and nves */ + uint32_t l2_logical_net_id; /* L2 only: EVPN Eth Seg Id */ + struct route_table *imported_vpn[AFI_MAX]; + struct rfapi_monitor_vpn *vpn0_queries[AFI_MAX]; + struct rfapi_monitor_eth *eth0_queries; + struct route_table *imported_encap[AFI_MAX]; + struct skiplist *monitor_exterior_orphans; + int local_count[AFI_MAX]; + int remote_count[AFI_MAX]; + int holddown_count[AFI_MAX]; + int imported_count[AFI_MAX]; +}; + +#define RFAPI_LOCAL_BI(bi) \ + (((bi)->type == ZEBRA_ROUTE_BGP) && ((bi)->sub_type == BGP_ROUTE_RFP)) + +#define RFAPI_DIRECT_IMPORT_BI(bi) \ + (((bi)->type == ZEBRA_ROUTE_BGP_DIRECT) || ((bi)->type == ZEBRA_ROUTE_BGP_DIRECT_EXT)) + +#define RFAPI_UPDATE_ITABLE_COUNT(bi, itable, afi, cnt) \ + if (RFAPI_LOCAL_BI(bi)) { \ + (itable)->local_count[(afi)] += (cnt); \ + } else { \ + if (RFAPI_DIRECT_IMPORT_BI(bi)) \ + (itable)->imported_count[(afi)] += (cnt); \ + else \ + (itable)->remote_count[(afi)] += (cnt); \ + } + +extern uint8_t +rfapiRfpCost (struct attr *attr); + +extern void +rfapiDebugBacktrace (void); + +extern void +rfapiCheckRouteCount (void); + +/* + * Print BI in an Import Table + */ +extern void +rfapiPrintBi (void *stream, struct bgp_info *bi); + +extern void +rfapiShowImportTable ( + void *stream, + const char *label, + struct route_table *rt, + int isvpn); + + +extern void +rfapiImportTableRefDelByIt ( + struct bgp *bgp, + struct rfapi_import_table *it_target); + + +/* + * Construct an rfapi nexthop list based on the routes attached to + * the specified node. + * + * If there are any routes that do NOT have BGP_INFO_REMOVED set, + * return those only. If there are ONLY routes with BGP_INFO_REMOVED, + * then return those, and also include all the non-removed routes from the + * next less-specific node (i.e., this node's parent) at the end. + */ +extern struct rfapi_next_hop_entry * +rfapiRouteNode2NextHopList ( + struct route_node *rn, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct route_table *rfd_rib_table, /* preload this NVE rib table */ + struct prefix *pfx_target_original); /* query target */ + +extern struct rfapi_next_hop_entry * +rfapiRouteTable2NextHopList ( + struct route_table *rt, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct route_table *rfd_rib_table, /* preload this NVE rib table */ + struct prefix *pfx_target_original); /* query target */ + +extern struct rfapi_next_hop_entry * +rfapiEthRouteTable2NextHopList ( + uint32_t logical_net_id, + struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct route_table *rib_route_table,/* preload NVE rib node */ + struct prefix *pfx_target_original); /* query target */ + +extern int +rfapiEcommunitiesIntersect (struct ecommunity *e1, struct ecommunity *e2); + +extern void +rfapiCheckRefcount (struct route_node *rn, safi_t safi, int lockoffset); + +extern int +rfapiHasNonRemovedRoutes (struct route_node *rn); + +extern int +rfapiProcessDeferredClose (struct thread *t); + +extern int +rfapiGetUnAddrOfVpnBi (struct bgp_info *bi, struct prefix *p); + +extern void +rfapiNexthop2Prefix (struct attr *attr, struct prefix *p); + +extern void +rfapiUnicastNexthop2Prefix ( + afi_t afi, + struct attr *attr, + struct prefix *p); + +/* Filtered Import Function actions */ +#define FIF_ACTION_UPDATE 0 +#define FIF_ACTION_WITHDRAW 1 +#define FIF_ACTION_KILL 2 + +extern void +rfapiBgpInfoFilteredImportVPN ( + struct rfapi_import_table *import_table, + int action, + struct peer *peer, + void *rfd, /* set for looped back routes */ + struct prefix *p, + struct prefix *aux_prefix, /* AFI_ETHER: optional IP */ + afi_t afi, + struct prefix_rd *prd, + struct attr *attr, /* part of bgp_info */ + u_char type, /* part of bgp_info */ + u_char sub_type, /* part of bgp_info */ + uint32_t *label); /* part of bgp_info */ + +extern struct rfapi_next_hop_entry * +rfapiEthRouteNode2NextHopList ( + struct route_node *rn, + struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct route_table *rib_route_table,/* preload NVE rib table */ + struct prefix *pfx_target_original); /* query target */ + +extern struct rfapi_import_table * +rfapiMacImportTableGetNoAlloc ( + struct bgp *bgp, + uint32_t lni); + +extern struct rfapi_import_table * +rfapiMacImportTableGet ( + struct bgp *bgp, + uint32_t lni); + +extern int +rfapiGetL2o ( + struct attr *attr, + struct rfapi_l2address_option *l2o); + +extern int rfapiEcommunityGetLNI ( + struct ecommunity *ecom, + uint32_t *lni); + + +/* enable for debugging; disable for performance */ +#if 0 +#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) rfapiCheckRefcount((rn),(safi),(lo)) +#else +#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) {} +#endif + +/*------------------------------------------ + * rfapiDeleteRemotePrefixes + * + * UI helper: For use by the "clear vnc prefixes" command + * + * input: + * un if set, tunnel must match this prefix + * vn if set, nexthop prefix must match this prefix + * p if set, prefix must match this prefix + * + * output + * pARcount number of active routes deleted + * pAHcount number of active nves deleted + * pHRcount number of holddown routes deleted + * pHHcount number of holddown nves deleted + * + * return value: + * void + --------------------------------------------*/ +extern void +rfapiDeleteRemotePrefixes ( + struct prefix *un, + struct prefix *vn, + struct prefix *p, + int delete_active, + int delete_holddown, + uint32_t *pARcount, /* active routes */ + uint32_t *pAHcount, /* active nves */ + uint32_t *pHRcount, /* holddown routes */ + uint32_t *pHHcount); /* holddown nves */ + +/*------------------------------------------ + * rfapiCountAllItRoutes + * + * UI helper: count VRF routes from BGP side + * + * input: + * + * output + * pARcount count of active routes + * pHRcount count of holddown routes + * pIRcount count of holddown routes + * + * return value: + * void + --------------------------------------------*/ +extern void +rfapiCountAllItRoutes ( + int *pALRcount, /* active local routes */ + int *pARRcount, /* active remote routes */ + int *pHRcount, /* holddown routes */ + int *pIRcount); /* direct imported routes */ + +/*------------------------------------------ + * rfapiGetHolddownFromLifetime + * + * calculate holddown value based on lifetime + * + * input: + * lifetime lifetime + * + * return value: + * Holddown value based on lifetime, holddown_factor, + * and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY + * + --------------------------------------------*/ +extern uint32_t +rfapiGetHolddownFromLifetime (uint32_t lifetime); + +#endif /* QUAGGA_HGP_RFAPI_IMPORT_H */ diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c new file mode 100644 index 0000000000..4677d1fa68 --- /dev/null +++ b/bgpd/rfapi/rfapi_monitor.c @@ -0,0 +1,1701 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: rfapi_monitor.c + */ + +/* TBD remove unneeded includes */ + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "log.h" +#include "table.h" +#include "skiplist.h" + +#include "bgpd.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "rfapi.h" +#include "rfapi_import.h" +#include "vnc_import_bgp.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "rfapi_rib.h" + +#define DEBUG_L2_EXTRA 0 +#define DEBUG_DUP_CHECK 0 +#define DEBUG_ETH_SL 0 + +static void +rfapiMonitorTimerRestart (struct rfapi_monitor_vpn *m); + +static void +rfapiMonitorEthTimerRestart (struct rfapi_monitor_eth *m); + +/* + * Forward declarations + */ +static void +rfapiMonitorEthDetachImport (struct bgp *bgp, struct rfapi_monitor_eth *mon); + +#if DEBUG_ETH_SL +/* + * Debug function, special case + */ +void +rfapiMonitorEthSlCheck( + struct route_node *rn, + const char *tag1, + const char *tag2) +{ + struct route_node *rn_saved = NULL; + static struct skiplist *sl_saved = NULL; + struct skiplist *sl; + + if (!rn) + return; + + if (rn_saved && (rn != rn_saved)) + return; + + if (!rn_saved) + rn_saved = rn; + + sl = RFAPI_MONITOR_ETH(rn); + if (sl || sl_saved) + { + zlog_debug("%s[%s%s]: rn=%p, rn->lock=%d, old sl=%p, new sl=%p", + __func__, (tag1? tag1: ""), (tag2? tag2: ""), rn, rn->lock, + sl_saved, sl); + sl_saved = sl; + } +} +#endif + +/* + * Debugging function that aborts when it finds monitors whose + * "next" pointer * references themselves + */ +void +rfapiMonitorLoopCheck (struct rfapi_monitor_vpn *mchain) +{ + struct rfapi_monitor_vpn *m; + + for (m = mchain; m; m = m->next) + assert (m != m->next); +} + +#if DEBUG_DUP_CHECK +/* + * Debugging code: see if a monitor is mentioned more than once + * in a HD's monitor list + */ +void +rfapiMonitorDupCheck (struct bgp *bgp) +{ + struct listnode *hnode; + struct rfapi_descriptor *rfd; + + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd)) + { + struct route_node *mrn; + + if (!rfd->mon) + continue; + + for (mrn = route_top (rfd->mon); mrn; mrn = route_next (mrn)) + { + struct rfapi_monitor_vpn *m; + for (m = (struct rfapi_monitor_vpn *) (mrn->info); m; m = m->next) + m->dcount = 0; + } + } + + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd)) + { + struct route_node *mrn; + + if (!rfd->mon) + continue; + + for (mrn = route_top (rfd->mon); mrn; mrn = route_next (mrn)) + { + struct rfapi_monitor_vpn *m; + + for (m = (struct rfapi_monitor_vpn *) (mrn->info); m; m = m->next) + assert (++m->dcount == 1); + } + } +} +#endif + +/* debug */ +void +rfapiMonitorCleanCheck (struct bgp *bgp) +{ + struct listnode *hnode; + struct rfapi_descriptor *rfd; + + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd)) + { + assert (!rfd->import_table->vpn0_queries[AFI_IP]); + assert (!rfd->import_table->vpn0_queries[AFI_IP6]); + + struct route_node *rn; + + for (rn = route_top (rfd->import_table->imported_vpn[AFI_IP]); rn; + rn = route_next (rn)) + { + + assert (!RFAPI_MONITOR_VPN (rn)); + } + for (rn = route_top (rfd->import_table->imported_vpn[AFI_IP6]); rn; + rn = route_next (rn)) + { + + assert (!RFAPI_MONITOR_VPN (rn)); + } + } +} + +/* debug */ +void +rfapiMonitorCheckAttachAllowed (void) +{ + struct bgp *bgp = bgp_get_default (); + assert (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)); +} + +void +rfapiMonitorExtraFlush (safi_t safi, struct route_node *rn) +{ + struct rfapi_it_extra *hie; + struct rfapi_monitor_vpn *v; + struct rfapi_monitor_vpn *v_next; + struct rfapi_monitor_encap *e = NULL; + struct rfapi_monitor_encap *e_next = NULL; + + if (!rn) + return; + + if (!rn->aggregate) + return; + + hie = (struct rfapi_it_extra *) (rn->aggregate); + + switch (safi) + { + case SAFI_ENCAP: + for (e = hie->u.encap.e; e; e = e_next) + { + e_next = e->next; + e->next = NULL; + XFREE (MTYPE_RFAPI_MONITOR_ENCAP, e); + route_unlock_node (rn); + } + hie->u.encap.e = NULL; + break; + + case SAFI_MPLS_VPN: + for (v = hie->u.vpn.v; v; v = v_next) + { + v_next = v->next; + v->next = NULL; + XFREE (MTYPE_RFAPI_MONITOR, e); + route_unlock_node (rn); + } + hie->u.vpn.v = NULL; + if (hie->u.vpn.e.source) + { + while (!skiplist_delete_first (hie->u.vpn.e.source)) + { + route_unlock_node (rn); + } + skiplist_free (hie->u.vpn.e.source); + hie->u.vpn.e.source = NULL; + route_unlock_node (rn); + } + if (hie->u.vpn.idx_rd) + { + /* looping through bi->extra->vnc.import.rd is tbd */ + while (!skiplist_delete_first (hie->u.vpn.idx_rd)) + { + route_unlock_node (rn); + } + skiplist_free (hie->u.vpn.idx_rd); + hie->u.vpn.idx_rd = NULL; + route_unlock_node (rn); + } + if (hie->u.vpn.mon_eth) + { + while (!skiplist_delete_first (hie->u.vpn.mon_eth)) + { + route_unlock_node (rn); + } + skiplist_free (hie->u.vpn.mon_eth); + hie->u.vpn.mon_eth = NULL; + route_unlock_node (rn); + } + break; + + default: + assert (0); + } + XFREE (MTYPE_RFAPI_IT_EXTRA, hie); + rn->aggregate = NULL; + route_unlock_node (rn); +} + +/* + * If the child lists are empty, release the rfapi_it_extra struct + */ +void +rfapiMonitorExtraPrune (safi_t safi, struct route_node *rn) +{ + struct rfapi_it_extra *hie; + + if (!rn) + return; + + if (!rn->aggregate) + return; + + hie = (struct rfapi_it_extra *) (rn->aggregate); + + switch (safi) + { + case SAFI_ENCAP: + if (hie->u.encap.e) + return; + break; + + case SAFI_MPLS_VPN: + if (hie->u.vpn.v) + return; + if (hie->u.vpn.mon_eth) + { + if (skiplist_count (hie->u.vpn.mon_eth)) + return; + skiplist_free (hie->u.vpn.mon_eth); + hie->u.vpn.mon_eth = NULL; + route_unlock_node (rn); /* uncount skiplist */ + } + if (hie->u.vpn.e.source) + { + if (skiplist_count (hie->u.vpn.e.source)) + return; + skiplist_free (hie->u.vpn.e.source); + hie->u.vpn.e.source = NULL; + route_unlock_node (rn); + } + if (hie->u.vpn.idx_rd) + { + if (skiplist_count (hie->u.vpn.idx_rd)) + return; + skiplist_free (hie->u.vpn.idx_rd); + hie->u.vpn.idx_rd = NULL; + route_unlock_node (rn); + } + if (hie->u.vpn.mon_eth) + { + if (skiplist_count (hie->u.vpn.mon_eth)) + return; + skiplist_free (hie->u.vpn.mon_eth); + hie->u.vpn.mon_eth = NULL; + route_unlock_node (rn); + } + break; + + default: + assert (0); + } + XFREE (MTYPE_RFAPI_IT_EXTRA, hie); + rn->aggregate = NULL; + route_unlock_node (rn); +} + +/* + * returns locked node + */ +struct route_node * +rfapiMonitorGetAttachNode (struct rfapi_descriptor *rfd, struct prefix *p) +{ + afi_t afi; + struct route_node *rn; + + if (RFAPI_0_PREFIX (p)) + { + assert (1); + } + + afi = family2afi (p->family); + assert (afi); + + /* + * It's possible that even though there is a route at this node, + * there are no routes with valid UN addresses (i.e,. with no + * valid tunnel routes). Check for that and walk back up the + * tree if necessary. + * + * When the outer loop completes, the matched node, if any, is + * locked (i.e., its reference count has been incremented) to + * account for the VPN monitor we are about to attach. + * + * if a monitor is moved to another node, there must be + * corresponding unlock/locks + */ + for (rn = route_node_match (rfd->import_table->imported_vpn[afi], p); rn;) + { + + struct bgp_info *bi; + struct prefix pfx_dummy; + + /* TBD update this code to use new valid_interior_count */ + for (bi = rn->info; bi; bi = bi->next) + { + /* + * If there is a cached ENCAP UN address, it's a usable + * VPN route + */ + if (bi->extra && bi->extra->vnc.import.un_family) + { + break; + } + + /* + * Or if there is a valid Encap Attribute tunnel subtlv address, + * it's a usable VPN route. + */ + if (!rfapiGetVncTunnelUnAddr (bi->attr, &pfx_dummy)) + { + break; + } + } + if (bi) + break; + + route_unlock_node (rn); + if ((rn = rn->parent)) + { + route_lock_node (rn); + } + } + + if (!rn) + { + struct prefix pfx_default; + + memset (&pfx_default, 0, sizeof (pfx_default)); + pfx_default.family = p->family; + + /* creates default node if none exists, and increments ref count */ + rn = + route_node_get (rfd->import_table->imported_vpn[afi], &pfx_default); + } + + return rn; +} + +/* + * If this function happens to attach the monitor to a radix tree + * node (as opposed to the 0-prefix list), the node pointer is + * returned (for the benefit of caller which might like to use it + * to generate an immediate query response). + */ +static struct route_node * +rfapiMonitorAttachImport (struct rfapi_descriptor *rfd, + struct rfapi_monitor_vpn *m) +{ + struct route_node *rn; + + rfapiMonitorCheckAttachAllowed (); + + if (RFAPI_0_PREFIX (&m->p)) + { + /* + * Add new monitor entry to vpn0 list + */ + afi_t afi; + + afi = family2afi (m->p.family); + assert (afi); + + m->next = rfd->import_table->vpn0_queries[afi]; + rfd->import_table->vpn0_queries[afi] = m; + zlog_debug ("%s: attached monitor %p to vpn0 list", __func__, m); + return NULL; + } + + /* + * Attach new monitor entry to import table node + */ + rn = rfapiMonitorGetAttachNode (rfd, &m->p); /* returns locked rn */ + m->node = rn; + m->next = RFAPI_MONITOR_VPN (rn); + RFAPI_MONITOR_VPN_W_ALLOC (rn) = m; + RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 0); + zlog_debug ("%s: attached monitor %p to rn %p", __func__, m, rn); + return rn; +} + + +/* + * reattach monitors for this HD to import table + */ +void +rfapiMonitorAttachImportHd (struct rfapi_descriptor *rfd) +{ + struct route_node *mrn; + + if (!rfd->mon) + { + /* + * No monitors for this HD + */ + return; + } + + for (mrn = route_top (rfd->mon); mrn; mrn = route_next (mrn)) + { + + if (!mrn->info) + continue; + + (void) rfapiMonitorAttachImport (rfd, + (struct rfapi_monitor_vpn + *) (mrn->info)); + } +} + +/* + * Adds a monitor for a query to the NVE descriptor's list + * and, if callbacks are enabled, attaches it to the import table. + * + * If we happened to locate the import table radix tree attachment + * point, return it so the caller can use it to generate a query + * response without repeating the lookup. Note that when callbacks + * are disabled, this function will not perform a lookup, and the + * caller will have to do its own lookup. + */ +struct route_node * +rfapiMonitorAdd ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *p) +{ + struct rfapi_monitor_vpn *m; + struct route_node *rn; + + /* + * Initialize nve's monitor list if needed + * NB use the same radix tree for IPv4 and IPv6 targets. + * The prefix will always have full-length mask (/32, /128) + * or be 0/0 so they won't get mixed up. + */ + if (!rfd->mon) + { + rfd->mon = route_table_init (); + } + rn = route_node_get (rfd->mon, p); + if (rn->info) + { + /* + * received this query before, no further action needed + */ + rfapiMonitorTimerRestart ((struct rfapi_monitor_vpn *) rn->info); + route_unlock_node (rn); + return NULL; + } + + /* + * New query for this nve, record it in the HD + */ + rn->info = XCALLOC (MTYPE_RFAPI_MONITOR, sizeof (struct rfapi_monitor_vpn)); + m = (struct rfapi_monitor_vpn *) (rn->info); + m->rfd = rfd; + prefix_copy (&m->p, p); + + ++rfd->monitor_count; + ++bgp->rfapi->monitor_count; + + rfapiMonitorTimerRestart (m); + + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) + { + /* + * callbacks turned off, so don't attach monitor to import table + */ + return NULL; + } + + + /* + * attach to import table + */ + return rfapiMonitorAttachImport (rfd, m); +} + +/* + * returns monitor pointer if found, NULL if not + */ +static struct rfapi_monitor_vpn * +rfapiMonitorDetachImport (struct rfapi_monitor_vpn *m) +{ + struct rfapi_monitor_vpn *prev; + struct rfapi_monitor_vpn *this = NULL; + + if (RFAPI_0_PREFIX (&m->p)) + { + afi_t afi; + + /* + * 0-prefix monitors are stored in a special list and not + * in the import VPN tree + */ + + afi = family2afi (m->p.family); + assert (afi); + + if (m->rfd->import_table) + { + for (prev = NULL, this = m->rfd->import_table->vpn0_queries[afi]; + this; prev = this, this = this->next) + { + + if (this == m) + break; + } + if (this) + { + if (!prev) + { + m->rfd->import_table->vpn0_queries[afi] = this->next; + } + else + { + prev->next = this->next; + } + } + } + } + else + { + + if (m->node) + { + for (prev = NULL, + this = RFAPI_MONITOR_VPN (m->node); + this; prev = this, this = this->next) + { + + if (this == m) + break; + } + if (this) + { + if (prev) + { + prev->next = this->next; + } + else + { + RFAPI_MONITOR_VPN_W_ALLOC (m->node) = this->next; + } + RFAPI_CHECK_REFCOUNT (m->node, SAFI_MPLS_VPN, 1); + route_unlock_node (m->node); + } + m->node = NULL; + } + } + return this; +} + + +void +rfapiMonitorDetachImportHd (struct rfapi_descriptor *rfd) +{ + struct route_node *rn; + + if (!rfd->mon) + return; + + for (rn = route_top (rfd->mon); rn; rn = route_next (rn)) + { + if (rn->info) + { + rfapiMonitorDetachImport ((struct rfapi_monitor_vpn *) (rn->info)); + } + } +} + +void +rfapiMonitorDel ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *p) +{ + struct route_node *rn; + struct rfapi_monitor_vpn *m; + + assert (rfd->mon); + rn = route_node_get (rfd->mon, p); /* locks node */ + m = rn->info; + + assert (m); + + /* + * remove from import table + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + rfapiMonitorDetachImport (m); + } + + if (m->timer) + { + thread_cancel (m->timer); + m->timer = NULL; + } + + /* + * remove from rfd list + */ + XFREE (MTYPE_RFAPI_MONITOR, m); + rn->info = NULL; + route_unlock_node (rn); /* undo original lock when created */ + route_unlock_node (rn); /* undo lock in route_node_get */ + + --rfd->monitor_count; + --bgp->rfapi->monitor_count; +} + +/* + * returns count of monitors deleted + */ +int +rfapiMonitorDelHd (struct rfapi_descriptor *rfd) +{ + struct route_node *rn; + struct bgp *bgp; + int count = 0; + + zlog_debug ("%s: entry rfd=%p", __func__, rfd); + + bgp = bgp_get_default (); + + if (rfd->mon) + { + for (rn = route_top (rfd->mon); rn; rn = route_next (rn)) + { + struct rfapi_monitor_vpn *m; + if ((m = rn->info)) + { + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + rfapiMonitorDetachImport (m); + } + + if (m->timer) + { + thread_cancel (m->timer); + m->timer = NULL; + } + + XFREE (MTYPE_RFAPI_MONITOR, m); + rn->info = NULL; + route_unlock_node (rn); /* undo original lock when created */ + ++count; + --rfd->monitor_count; + --bgp->rfapi->monitor_count; + } + } + route_table_finish (rfd->mon); + rfd->mon = NULL; + } + + if (rfd->mon_eth) + { + + struct rfapi_monitor_eth *mon_eth; + + while (!skiplist_first (rfd->mon_eth, NULL, (void **) &mon_eth)) + { + + int rc; + + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + rfapiMonitorEthDetachImport (bgp, mon_eth); + } + else + { +#if DEBUG_L2_EXTRA + zlog_debug + ("%s: callbacks disabled, not attempting to detach mon_eth %p", + __func__, mon_eth); +#endif + } + + if (mon_eth->timer) + { + thread_cancel (mon_eth->timer); + mon_eth->timer = NULL; + } + + /* + * remove from rfd list + */ + rc = skiplist_delete (rfd->mon_eth, mon_eth, mon_eth); + assert (!rc); + + zlog_debug ("%s: freeing mon_eth %p", __func__, mon_eth); + XFREE (MTYPE_RFAPI_MONITOR_ETH, mon_eth); + + ++count; + --rfd->monitor_count; + --bgp->rfapi->monitor_count; + } + skiplist_free (rfd->mon_eth); + rfd->mon_eth = NULL; + + } + + return count; +} + +void +rfapiMonitorResponseRemovalOff (struct bgp *bgp) +{ + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE) + { + return; + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; +} + +void +rfapiMonitorResponseRemovalOn (struct bgp *bgp) +{ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) + { + return; + } + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; +} + +static int +rfapiMonitorTimerExpire (struct thread *t) +{ + struct rfapi_monitor_vpn *m = t->arg; + + /* forget reference to thread, it's gone */ + m->timer = NULL; + + /* delete the monitor */ + rfapiMonitorDel (bgp_get_default (), m->rfd, &m->p); + + return 0; +} + +static void +rfapiMonitorTimerRestart (struct rfapi_monitor_vpn *m) +{ + if (m->timer) + { + unsigned long remain = thread_timer_remain_second (m->timer); + + /* unexpected case, but avoid wraparound problems below */ + if (remain > m->rfd->response_lifetime) + return; + + /* don't restart if we just restarted recently */ + if (m->rfd->response_lifetime - remain < 2) + return; + + thread_cancel (m->timer); + m->timer = NULL; + } + + { + char buf[BUFSIZ]; + + zlog_debug ("%s: target %s life %u", __func__, + rfapi_ntop (m->p.family, m->p.u.val, buf, BUFSIZ), + m->rfd->response_lifetime); + } + m->timer = thread_add_timer (bm->master, rfapiMonitorTimerExpire, m, + m->rfd->response_lifetime); +} + +/* + * called when an updated response is sent to the NVE. Per + * ticket 255, restart timers for any monitors that could have + * been responsible for the response, i.e., any monitors for + * the exact prefix or a parent of it. + */ +void +rfapiMonitorTimersRestart (struct rfapi_descriptor *rfd, struct prefix *p) +{ + struct route_node *rn; + + if (AF_ETHERNET == p->family) + { + struct rfapi_monitor_eth *mon_eth; + int rc; + void *cursor; + + /* + * XXX match any LNI + */ + for (cursor = NULL, + rc = + skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth, &cursor); + rc == 0; + rc = + skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth, &cursor)) + { + + if (!memcmp (mon_eth->macaddr.octet, p->u.prefix_eth.octet, + ETHER_ADDR_LEN)) + { + + rfapiMonitorEthTimerRestart (mon_eth); + + } + } + + } + else + { + for (rn = route_top (rfd->mon); rn; rn = route_next (rn)) + { + struct rfapi_monitor_vpn *m; + + if (!((m = rn->info))) + continue; + + /* NB order of test is significant ! */ + if (!m->node || prefix_match (&m->node->p, p)) + { + rfapiMonitorTimerRestart (m); + } + } + } +} + +/* + * Find monitors at this node and all its parents. Call + * rfapiRibUpdatePendingNode with this node and all corresponding NVEs. + */ +void +rfapiMonitorItNodeChanged ( + struct rfapi_import_table *import_table, + struct route_node *it_node, + struct rfapi_monitor_vpn *monitor_list) /* for base it node, NULL=all */ +{ + struct skiplist *nves_seen; + struct route_node *rn = it_node; + struct bgp *bgp = bgp_get_default (); + afi_t afi = family2afi (rn->p.family); +#if DEBUG_L2_EXTRA + char buf_prefix[BUFSIZ]; +#endif + + assert (bgp); + assert (import_table); + + nves_seen = skiplist_new (0, NULL, NULL); + +#if DEBUG_L2_EXTRA + prefix2str (&it_node->p, buf_prefix, BUFSIZ); + zlog_debug ("%s: it=%p, it_node=%p, it_node->prefix=%s", + __func__, import_table, it_node, buf_prefix); +#endif + + if (AFI_ETHER == afi) + { + struct rfapi_monitor_eth *m; + struct skiplist *sl; + void *cursor; + int rc; + + if ((sl = RFAPI_MONITOR_ETH (rn))) + { + + for (cursor = NULL, + rc = skiplist_next (sl, NULL, (void **) &m, (void **) &cursor); + !rc; + rc = skiplist_next (sl, NULL, (void **) &m, (void **) &cursor)) + { + + if (skiplist_search (nves_seen, m->rfd, NULL)) + { + /* + * Haven't done this NVE yet. Add to "seen" list. + */ + assert (!skiplist_insert (nves_seen, m->rfd, NULL)); + + /* + * update its RIB + */ + rfapiRibUpdatePendingNode(bgp, m->rfd, import_table, + it_node, m->rfd->response_lifetime); + } + } + } + + } + else + { + + struct rfapi_monitor_vpn *m; + + if (monitor_list) + { + m = monitor_list; + } + else + { + m = RFAPI_MONITOR_VPN (rn); + } + + do + { + /* + * If we have reached the root node (parent==NULL) and there + * are no routes here (info==NULL), and the IT node that + * changed was not the root node (it_node->parent != NULL), + * then any monitors at this node are here because they had + * no match at all. Therefore, do not send route updates to them + * because we haven't sent them an initial route. + */ + if (!rn->parent && !rn->info && it_node->parent) + break; + + for (; m; m = m->next) + { + + if (RFAPI_0_PREFIX (&m->p)) + { + /* shouldn't happen, but be safe */ + continue; + } + if (skiplist_search (nves_seen, m->rfd, NULL)) + { + /* + * Haven't done this NVE yet. Add to "seen" list. + */ + assert (!skiplist_insert (nves_seen, m->rfd, NULL)); + + { + char buf_attach_pfx[BUFSIZ]; + char buf_target_pfx[BUFSIZ]; + + prefix2str (&m->node->p, buf_attach_pfx, BUFSIZ); + prefix2str (&m->p, buf_target_pfx, BUFSIZ); + zlog_debug + ("%s: update rfd %p attached to pfx %s (targ=%s)", + __func__, m->rfd, buf_attach_pfx, buf_target_pfx); + } + + /* + * update its RIB + */ + rfapiRibUpdatePendingNode(bgp, m->rfd, import_table, + it_node, m->rfd->response_lifetime); + } + } + rn = rn->parent; + if (rn) + m = RFAPI_MONITOR_VPN (rn); + } + while (rn); + } + + /* + * All-routes L2 monitors + */ + if (AFI_ETHER == afi) + { + struct rfapi_monitor_eth *e; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: checking L2 all-routes monitors", __func__); +#endif + + for (e = import_table->eth0_queries; e; e = e->next) + { +#if DEBUG_L2_EXTRA + zlog_debug ("%s: checking eth0 mon=%p", __func__, e); +#endif + if (skiplist_search (nves_seen, e->rfd, NULL)) + { + /* + * Haven't done this NVE yet. Add to "seen" list. + */ + assert (!skiplist_insert (nves_seen, e->rfd, NULL)); + + /* + * update its RIB + */ +#if DEBUG_L2_EXTRA + zlog_debug ("%s: found L2 all-routes monitor %p", __func__, e); +#endif + rfapiRibUpdatePendingNode (bgp, e->rfd, import_table, it_node, + e->rfd->response_lifetime); + } + } + } + else + { + struct rfapi_monitor_vpn *m; + + /* + * All-routes IPv4. IPv6 monitors + */ + for (m = import_table->vpn0_queries[afi]; m; m = m->next) + { + if (skiplist_search (nves_seen, m->rfd, NULL)) + { + /* + * Haven't done this NVE yet. Add to "seen" list. + */ + assert (!skiplist_insert (nves_seen, m->rfd, NULL)); + + /* + * update its RIB + */ + rfapiRibUpdatePendingNode (bgp, m->rfd, import_table, it_node, + m->rfd->response_lifetime); + } + } + } + + skiplist_free (nves_seen); +} + +/* + * For the listed monitors, update new node and its subtree, but + * omit old node and its subtree + */ +void +rfapiMonitorMovedUp ( + struct rfapi_import_table *import_table, + struct route_node *old_node, + struct route_node *new_node, + struct rfapi_monitor_vpn *monitor_list) +{ + struct bgp *bgp = bgp_get_default (); + struct rfapi_monitor_vpn *m; + + assert (new_node); + assert (old_node); + assert (new_node != old_node); + + /* + * If new node is 0/0 and there is no route there, don't + * generate an update because it will not contain any + * routes including the target. + */ + if (!new_node->parent && !new_node->info) + { + zlog_debug ("%s: new monitor at 0/0 and no routes, no updates", + __func__); + return; + } + + for (m = monitor_list; m; m = m->next) + { + rfapiRibUpdatePendingNode (bgp, m->rfd, import_table, new_node, + m->rfd->response_lifetime); + rfapiRibUpdatePendingNodeSubtree (bgp, m->rfd, import_table, new_node, + old_node, m->rfd->response_lifetime); + } +} + +static int +rfapiMonitorEthTimerExpire (struct thread *t) +{ + struct rfapi_monitor_eth *m = t->arg; + + /* forget reference to thread, it's gone */ + m->timer = NULL; + + /* delete the monitor */ + rfapiMonitorEthDel (bgp_get_default (), m->rfd, &m->macaddr, + m->logical_net_id); + + return 0; +} + +static void +rfapiMonitorEthTimerRestart (struct rfapi_monitor_eth *m) +{ + if (m->timer) + { + unsigned long remain = thread_timer_remain_second (m->timer); + + /* unexpected case, but avoid wraparound problems below */ + if (remain > m->rfd->response_lifetime) + return; + + /* don't restart if we just restarted recently */ + if (m->rfd->response_lifetime - remain < 2) + return; + + thread_cancel (m->timer); + m->timer = NULL; + } + + { + char buf[BUFSIZ]; + + zlog_debug ("%s: target %s life %u", __func__, + rfapiEthAddr2Str (&m->macaddr, buf, BUFSIZ), + m->rfd->response_lifetime); + } + m->timer = thread_add_timer (bm->master, rfapiMonitorEthTimerExpire, m, + m->rfd->response_lifetime); +} + +static int +mon_eth_cmp (void *a, void *b) +{ + struct rfapi_monitor_eth *m1; + struct rfapi_monitor_eth *m2; + + int i; + + m1 = (struct rfapi_monitor_eth *) a; + m2 = (struct rfapi_monitor_eth *) b; + + /* + * compare ethernet addresses + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + { + if (m1->macaddr.octet[i] != m2->macaddr.octet[i]) + return (m1->macaddr.octet[i] - m2->macaddr.octet[i]); + } + + /* + * compare LNIs + */ + return (m1->logical_net_id - m2->logical_net_id); +} + +static void +rfapiMonitorEthAttachImport ( + struct rfapi_import_table *it, + struct route_node *rn, /* it node attach point if non-0 */ + struct rfapi_monitor_eth *mon) /* monitor struct to attach */ +{ + struct skiplist *sl; + int rc; + + zlog_debug ("%s: it=%p", __func__, it); + + rfapiMonitorCheckAttachAllowed (); + + if (RFAPI_0_ETHERADDR (&mon->macaddr)) + { + /* + * These go on a different list + */ + mon->next = it->eth0_queries; + it->eth0_queries = mon; +#if DEBUG_L2_EXTRA + zlog_debug ("%s: attached monitor %p to eth0 list", __func__, mon); +#endif + return; + } + + if (rn == NULL) + { +#if DEBUG_L2_EXTRA + zlog_debug ("%s: rn is null!", __func__); +#endif + return; + } + + /* + * Get sl to attach to + */ + sl = RFAPI_MONITOR_ETH_W_ALLOC (rn); + if (!sl) + { + sl = RFAPI_MONITOR_ETH_W_ALLOC (rn) = skiplist_new (0, NULL, NULL); + route_lock_node(rn); /* count skiplist mon_eth */ + } + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: rn=%p, rn->lock=%d, sl=%p, attaching eth mon %p", + __func__, rn, rn->lock, sl, mon); +#endif + + rc = skiplist_insert (sl, (void *) mon, (void *) mon); + assert (!rc); + + /* count eth monitor */ + route_lock_node(rn); +} + +/* + * reattach monitors for this HD to import table + */ +static void +rfapiMonitorEthAttachImportHd (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + void *cursor; + struct rfapi_monitor_eth *mon; + int rc; + + if (!rfd->mon_eth) + { + /* + * No monitors for this HD + */ + return; + } + + for (cursor = NULL, + rc = skiplist_next (rfd->mon_eth, NULL, (void **) &mon, &cursor); + rc == 0; + rc = skiplist_next (rfd->mon_eth, NULL, (void **) &mon, &cursor)) + { + + struct rfapi_import_table *it; + struct prefix pfx_mac_buf; + struct route_node *rn; + + it = rfapiMacImportTableGet (bgp, mon->logical_net_id); + assert (it); + + memset ((void *) &pfx_mac_buf, 0, sizeof (struct prefix)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + pfx_mac_buf.u.prefix_eth = mon->macaddr; + + rn = route_node_get (it->imported_vpn[AFI_ETHER], &pfx_mac_buf); + assert (rn); + + (void) rfapiMonitorEthAttachImport (it, rn, mon); + } +} + +static void +rfapiMonitorEthDetachImport ( + struct bgp *bgp, + struct rfapi_monitor_eth *mon) /* monitor struct to detach */ +{ + struct rfapi_import_table *it; + struct prefix pfx_mac_buf; + struct skiplist *sl; + struct route_node *rn; + int rc; + + it = rfapiMacImportTableGet (bgp, mon->logical_net_id); + assert (it); + + if (RFAPI_0_ETHERADDR (&mon->macaddr)) + { + struct rfapi_monitor_eth *prev; + struct rfapi_monitor_eth *this = NULL; + + for (prev = NULL, + this = it->eth0_queries; this; prev = this, this = this->next) + { + + if (this == mon) + break; + } + if (this) + { + if (!prev) + { + it->eth0_queries = this->next; + } + else + { + prev->next = this->next; + } + } +#if DEBUG_L2_EXTRA + zlog_debug ("%s: it=%p, LNI=%d, detached eth0 mon %p", + __func__, it, mon->logical_net_id, mon); +#endif + return; + } + + memset ((void *) &pfx_mac_buf, 0, sizeof (struct prefix)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + pfx_mac_buf.u.prefix_eth = mon->macaddr; + + rn = route_node_get (it->imported_vpn[AFI_ETHER], &pfx_mac_buf); + assert (rn); + +#if DEBUG_L2_EXTRA + char buf_prefix[BUFSIZ]; + prefix2str (&rn->p, buf_prefix, BUFSIZ); +#endif + + /* + * Get sl to detach from + */ + sl = RFAPI_MONITOR_ETH (rn); +#if DEBUG_L2_EXTRA + zlog_debug ("%s: it=%p, rn=%p, rn->lock=%d, sl=%p, pfx=%s, LNI=%d, detaching eth mon %p", + __func__, it, rn, rn->lock, sl, buf_prefix, mon->logical_net_id, mon); +#endif + assert (sl); + + + rc = skiplist_delete (sl, (void *) mon, (void *) mon); + assert (!rc); + + /* uncount eth monitor */ + route_unlock_node(rn); +} + +struct route_node * +rfapiMonitorEthAdd ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id) +{ + int rc; + struct rfapi_monitor_eth mon_buf; + struct rfapi_monitor_eth *val; + struct rfapi_import_table *it; + struct route_node *rn = NULL; + struct prefix pfx_mac_buf; + + if (!rfd->mon_eth) + { + rfd->mon_eth = skiplist_new (0, mon_eth_cmp, NULL); + } + + it = rfapiMacImportTableGet (bgp, logical_net_id); + assert (it); + + /* + * Get route node in import table. Here is where we attach the + * monitor. + * + * Look it up now because we return it to caller regardless of + * whether we create a new monitor or not. + */ + memset ((void *) &pfx_mac_buf, 0, sizeof (struct prefix)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + pfx_mac_buf.u.prefix_eth = *macaddr; + + if (!RFAPI_0_ETHERADDR (macaddr)) + { + rn = route_node_get (it->imported_vpn[AFI_ETHER], &pfx_mac_buf); + assert (rn); + } + + memset ((void *) &mon_buf, 0, sizeof (mon_buf)); + mon_buf.rfd = rfd; + mon_buf.macaddr = *macaddr; + mon_buf.logical_net_id = logical_net_id; + + { + char buf[BUFSIZ]; + + zlog_debug ("%s: LNI=%d: rfd=%p, pfx=%s", + __func__, logical_net_id, rfd, + rfapi_ntop (pfx_mac_buf.family, pfx_mac_buf.u.val, buf, + BUFSIZ)); + } + + + /* + * look up query + */ + rc = skiplist_search (rfd->mon_eth, (void *) &mon_buf, (void **) &val); + if (!rc) + { + /* + * Found monitor - we have seen this query before + * restart timer + */ + zlog_debug ("%s: already present in rfd->mon_eth, not adding", + __func__); + rfapiMonitorEthTimerRestart (val); + return rn; + } + + /* + * New query + */ + val = XCALLOC (MTYPE_RFAPI_MONITOR_ETH, sizeof (struct rfapi_monitor_eth)); + assert (val); + *val = mon_buf; + + ++rfd->monitor_count; + ++bgp->rfapi->monitor_count; + + rc = skiplist_insert (rfd->mon_eth, val, val); + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: inserted rfd=%p mon_eth=%p, rc=%d", __func__, rfd, val, + rc); +#endif + + /* + * start timer + */ + rfapiMonitorEthTimerRestart (val); + + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) + { + /* + * callbacks turned off, so don't attach monitor to import table + */ +#if DEBUG_L2_EXTRA + zlog_debug + ("%s: callbacks turned off, not attaching mon_eth %p to import table", + __func__, val); +#endif + return rn; + } + + /* + * attach to import table + */ + rfapiMonitorEthAttachImport (it, rn, val); + + return rn; +} + +void +rfapiMonitorEthDel ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id) +{ + struct rfapi_monitor_eth *val; + struct rfapi_monitor_eth mon_buf; + int rc; + + zlog_debug ("%s: entry rfd=%p", __func__, rfd); + + assert (rfd->mon_eth); + + memset ((void *) &mon_buf, 0, sizeof (mon_buf)); + mon_buf.macaddr = *macaddr; + mon_buf.logical_net_id = logical_net_id; + + rc = skiplist_search (rfd->mon_eth, (void *) &mon_buf, (void **) &val); + assert (!rc); + + /* + * remove from import table + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + rfapiMonitorEthDetachImport (bgp, val); + } + + if (val->timer) + { + thread_cancel (val->timer); + val->timer = NULL; + } + + /* + * remove from rfd list + */ + rc = skiplist_delete (rfd->mon_eth, val, val); + assert (!rc); + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: freeing mon_eth %p", __func__, val); +#endif + XFREE (MTYPE_RFAPI_MONITOR_ETH, val); + + --rfd->monitor_count; + --bgp->rfapi->monitor_count; +} + + +void +rfapiMonitorCallbacksOff (struct bgp *bgp) +{ + struct rfapi_import_table *it; + afi_t afi; + struct route_table *rt; + struct route_node *rn; + void *cursor; + int rc; + struct rfapi *h = bgp->rfapi; + + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) + { + /* + * Already off. + */ + return; + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: turned off callbacks", __func__); +#endif + + if (h == NULL) + return; + /* + * detach monitors from import VPN tables. The monitors + * will still be linked in per-nve monitor lists. + */ + for (it = h->imports; it; it = it->next) + { + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct rfapi_monitor_vpn *m; + struct rfapi_monitor_vpn *next; + + rt = it->imported_vpn[afi]; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + m = RFAPI_MONITOR_VPN (rn); + if (RFAPI_MONITOR_VPN (rn)) + RFAPI_MONITOR_VPN_W_ALLOC (rn) = NULL; + for (; m; m = next) + { + next = m->next; + m->next = NULL; /* gratuitous safeness */ + m->node = NULL; + route_unlock_node (rn); /* uncount */ + } + } + + for (m = it->vpn0_queries[afi]; m; m = next) + { + next = m->next; + m->next = NULL; /* gratuitous safeness */ + m->node = NULL; + } + it->vpn0_queries[afi] = NULL; /* detach first monitor */ + } + } + + /* + * detach monitors from import Eth tables. The monitors + * will still be linked in per-nve monitor lists. + */ + + /* + * Loop over ethernet import tables + */ + for (cursor = NULL, + rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor); + !rc; rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor)) + { + struct rfapi_monitor_eth *e; + struct rfapi_monitor_eth *enext; + + /* + * The actual route table + */ + rt = it->imported_vpn[AFI_ETHER]; + + /* + * Find non-0 monitors (i.e., actual addresses, not FTD monitors) + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + struct skiplist *sl; + + sl = RFAPI_MONITOR_ETH (rn); + while (!skiplist_delete_first(sl)) + { + route_unlock_node (rn); /* uncount monitor */ + } + } + + /* + * Find 0-monitors (FTD queries) + */ + for (e = it->eth0_queries; e; e = enext) + { +#if DEBUG_L2_EXTRA + zlog_debug ("%s: detaching eth0 mon %p", __func__, e); +#endif + enext = e->next; + e->next = NULL; /* gratuitous safeness */ + } + it->eth0_queries = NULL; /* detach first monitor */ + } +} + +void +rfapiMonitorCallbacksOn (struct bgp *bgp) +{ + struct listnode *hnode; + struct rfapi_descriptor *rfd; + + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) + { + /* + * Already on. It's important that we don't try to reattach + * monitors that are already attached because, in the interest + * of performance, there is no checking at the lower level + * whether a monitor is already attached. It leads to + * corrupted chains (e.g., looped pointers) + */ + return; + } + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE; +#if DEBUG_L2_EXTRA + zlog_debug ("%s: turned on callbacks", __func__); +#endif + if (bgp->rfapi == NULL) + return; + + /* + * reattach monitors + */ + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd)) + { + + rfapiMonitorAttachImportHd (rfd); + rfapiMonitorEthAttachImportHd (bgp, rfd); + } +} diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h new file mode 100644 index 0000000000..b08a6e60c6 --- /dev/null +++ b/bgpd/rfapi/rfapi_monitor.h @@ -0,0 +1,217 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef QUAGGA_HGP_RFAPI_MONITOR_H +#define QUAGGA_HGP_RFAPI_MONITOR_H + +#include "zebra.h" +#include "prefix.h" +#include "table.h" + +/* + * These get attached to the nodes in an import table (using "aggregate" ptr) + * to indicate which nves are interested in a prefix/target + */ +struct rfapi_monitor_vpn +{ + struct rfapi_monitor_vpn *next; /* chain from struct route_node */ + struct rfapi_descriptor *rfd; /* which NVE requested the route */ + struct prefix p; /* constant: pfx in original request */ + struct route_node *node; /* node we're currently attached to */ + uint32_t flags; +#define RFAPI_MON_FLAG_NEEDCALLBACK 0x00000001 /* deferred callback */ + + //int dcount; /* debugging counter */ + void *timer; +}; + +struct rfapi_monitor_encap +{ + struct rfapi_monitor_encap *next; + struct rfapi_monitor_encap *prev; + struct route_node *node; /* VPN node */ + struct bgp_info *bi; /* VPN bi */ + struct route_node *rn; /* parent node */ +}; + +struct rfapi_monitor_eth +{ + struct rfapi_monitor_eth *next; /* for use in vpn0_queries list */ + struct rfapi_descriptor *rfd; /* which NVE requested the route */ + struct ethaddr macaddr; + uint32_t logical_net_id; + void *timer; +}; + +/* + * This is referenced by the "aggregate" field of a route node + * in an RFAPI import table. + * + * node lock/unlock: + * - one lock increment for this structure itself + * - one lock per chained struct rfapi_monitor_vpn + * - one lock for the mon_eth skiplist itself + * - one lock per mon_eth skiplist entry + * - one lock for the ext skiplist itself + * - one lock for each ext skiplist entry + * remember to free skiplist when freeing rfapi_it_extra + * - one lock per chained struct rfapi_monitor_encap + * + */ +struct rfapi_it_extra +{ + union + { + struct + { + struct rfapi_monitor_vpn *v; + struct skiplist *idx_rd; /* RD index */ + struct skiplist *mon_eth; /* ether queries */ + struct + { + /* routes with UN addrs, either cached encap or Encap TLV */ + int valid_interior_count; + + /* unicast exterior routes, key=bi, val=allocated prefix */ + struct skiplist *source; + } e; + } vpn; + struct + { + struct rfapi_monitor_encap *e; + } encap; + } u; +}; + +#define RFAPI_IT_EXTRA_GET(rn) ((struct rfapi_it_extra *)( \ + (rn)->aggregate? (rn)->aggregate: \ + (route_lock_node(rn), (rn)->aggregate = \ + XCALLOC(MTYPE_RFAPI_IT_EXTRA,sizeof(struct rfapi_it_extra))))) + +#define RFAPI_RDINDEX(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd : NULL) + +#define RFAPI_RDINDEX_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd) + +#define RFAPI_MONITOR_ETH(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth : NULL) + +#define RFAPI_MONITOR_ETH_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth) + +#define RFAPI_MONITOR_VPN(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.v : NULL) + +#define RFAPI_MONITOR_VPN_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.v) + +#define RFAPI_MONITOR_ENCAP(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.encap.e : NULL) + +#define RFAPI_MONITOR_ENCAP_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.encap.e) + +#define RFAPI_MONITOR_EXTERIOR(rn) (&(RFAPI_IT_EXTRA_GET(rn)->u.vpn.e)) + +#define RFAPI_HAS_MONITOR_EXTERIOR(rn) (rn && rn->aggregate && \ + ((struct rfapi_it_extra *)(rn->aggregate))->u.vpn.e.source && \ + !skiplist_first(((struct rfapi_it_extra *)(rn->aggregate))-> \ + u.vpn.e.source, NULL, NULL)) + +extern void +rfapiMonitorLoopCheck (struct rfapi_monitor_vpn *mchain); + +extern void +rfapiMonitorCleanCheck (struct bgp *bgp); + +extern void +rfapiMonitorCheckAttachAllowed (void); + +extern void +rfapiMonitorExtraFlush (safi_t safi, struct route_node *rn); + +extern struct route_node * +rfapiMonitorGetAttachNode (struct rfapi_descriptor *rfd, struct prefix *p); + +extern void +rfapiMonitorAttachImportHd (struct rfapi_descriptor *rfd); + +extern struct route_node * +rfapiMonitorAdd ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *p); + +extern void +rfapiMonitorDetachImportHd (struct rfapi_descriptor *rfd); + +extern void +rfapiMonitorDel ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *p); + +extern int +rfapiMonitorDelHd (struct rfapi_descriptor *rfd); + +extern void +rfapiMonitorCallbacksOff (struct bgp *bgp); + +extern void +rfapiMonitorCallbacksOn (struct bgp *bgp); + +extern void +rfapiMonitorResponseRemovalOff (struct bgp *bgp); + +extern void +rfapiMonitorResponseRemovalOn (struct bgp *bgp); + +extern void +rfapiMonitorExtraPrune (safi_t safi, struct route_node *rn); + +extern void +rfapiMonitorTimersRestart (struct rfapi_descriptor *rfd, struct prefix *p); + +extern void +rfapiMonitorItNodeChanged ( + struct rfapi_import_table *import_table, + struct route_node *it_node, + struct rfapi_monitor_vpn *monitor_list); + +extern void +rfapiMonitorMovedUp ( + struct rfapi_import_table *import_table, + struct route_node *old_node, + struct route_node *new_node, + struct rfapi_monitor_vpn *monitor_list); + +extern struct route_node * +rfapiMonitorEthAdd ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id); + +extern void +rfapiMonitorEthDel ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id); + +#endif /* QUAGGA_HGP_RFAPI_MONITOR_H */ diff --git a/bgpd/rfapi/rfapi_nve_addr.c b/bgpd/rfapi/rfapi_nve_addr.c new file mode 100644 index 0000000000..835c2d2fae --- /dev/null +++ b/bgpd/rfapi/rfapi_nve_addr.c @@ -0,0 +1,175 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "skiplist.h" + + +#include "bgpd.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_nve_addr.h" +#include "rfapi_vty.h" + +#define DEBUG_NVE_ADDR 0 + +void rfapiNveAddr2Str (struct rfapi_nve_addr *, char *, int); + + +#if DEBUG_NVE_ADDR +static void +logdifferent (const char *tag, + struct rfapi_nve_addr *a, struct rfapi_nve_addr *b) +{ + char a_str[BUFSIZ]; + char b_str[BUFSIZ]; + + rfapiNveAddr2Str (a, a_str, BUFSIZ); + rfapiNveAddr2Str (b, b_str, BUFSIZ); + zlog_debug ("%s: [%s] [%s]", tag, a_str, b_str); +} +#endif + + +int +rfapi_nve_addr_cmp (void *k1, void *k2) +{ + struct rfapi_nve_addr *a = (struct rfapi_nve_addr *) k1; + struct rfapi_nve_addr *b = (struct rfapi_nve_addr *) k2; + int ret = 0; + + if (!a || !b) + { +#if DEBUG_NVE_ADDR + zlog_debug ("%s: missing address a=%p b=%p", __func__, a, b); +#endif + return (a - b); + } + if (a->un.addr_family != b->un.addr_family) + { +#if DEBUG_NVE_ADDR + zlog_debug ("diff: UN addr fam a->un.af=%d, b->un.af=%d", + a->un.addr_family, b->un.addr_family); +#endif + return (a->un.addr_family - b->un.addr_family); + } + if (a->un.addr_family == AF_INET) + { + ret = IPV4_ADDR_CMP (&a->un.addr.v4, &b->un.addr.v4); + if (ret != 0) + { +#if DEBUG_NVE_ADDR + logdifferent ("diff: UN addr", a, b); +#endif + return ret; + } + } + else if (a->un.addr_family == AF_INET6) + { + ret = IPV6_ADDR_CMP (&a->un.addr.v6, &b->un.addr.v6); + if (ret == 0) + { +#if DEBUG_NVE_ADDR + logdifferent ("diff: UN addr", a, b); +#endif + return ret; + } + } + else + { + assert (0); + } + if (a->vn.addr_family != b->vn.addr_family) + { +#if DEBUG_NVE_ADDR + zlog_debug ("diff: pT addr fam a->vn.af=%d, b->vn.af=%d", + a->vn.addr_family, b->vn.addr_family); +#endif + return (a->vn.addr_family - b->vn.addr_family); + } + if (a->vn.addr_family == AF_INET) + { + ret = IPV4_ADDR_CMP (&a->vn.addr.v4, &b->vn.addr.v4); + if (ret != 0) + { +#if DEBUG_NVE_ADDR + logdifferent ("diff: VN addr", a, b); +#endif + return ret; + } + } + else if (a->vn.addr_family == AF_INET6) + { + ret = IPV6_ADDR_CMP (&a->vn.addr.v6, &b->vn.addr.v6); + if (ret == 0) + { +#if DEBUG_NVE_ADDR + logdifferent ("diff: VN addr", a, b); +#endif + return ret; + } + } + else + { + assert (0); + } + return 0; +} + +void +rfapiNveAddr2Str (struct rfapi_nve_addr *na, char *buf, int bufsize) +{ + char *p = buf; + int r; + +#define REMAIN (bufsize - (p-buf)) +#define INCP {p += (r > REMAIN)? REMAIN: r;} + + if (bufsize < 1) + return; + + r = snprintf (p, REMAIN, "VN="); + INCP; + + if (!rfapiRfapiIpAddr2Str (&na->vn, p, REMAIN)) + goto done; + + buf[bufsize - 1] = 0; + p = buf + strlen (buf); + + r = snprintf (p, REMAIN, ", UN="); + INCP; + + rfapiRfapiIpAddr2Str (&na->un, p, REMAIN); + +done: + buf[bufsize - 1] = 0; +} diff --git a/bgpd/rfapi/rfapi_nve_addr.h b/bgpd/rfapi/rfapi_nve_addr.h new file mode 100644 index 0000000000..2b2d2b50d4 --- /dev/null +++ b/bgpd/rfapi/rfapi_nve_addr.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_NVE_ADDR_H +#define _QUAGGA_BGP_RFAPI_NVE_ADDR_H + +#include "rfapi.h" + +struct rfapi_nve_addr +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + void *info; +}; + + +extern int +rfapi_nve_addr_cmp (void *k1, void *k2); + +extern void +rfapiNveAddr2Str (struct rfapi_nve_addr *na, char *buf, int bufsize); + + + +#endif /* _QUAGGA_BGP_RFAPI_NVE_ADDR_H */ diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h new file mode 100644 index 0000000000..aca034b572 --- /dev/null +++ b/bgpd/rfapi/rfapi_private.h @@ -0,0 +1,455 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * Internal definitions for RFAPI. Not for use by other code + */ + +#ifndef _QUAGGA_BGP_RFAPI_PRIVATE_H +#define _QUAGGA_BGP_RFAPI_PRIVATE_H + +#include "linklist.h" +#include "skiplist.h" +#include "workqueue.h" + +#include "bgp_attr.h" + +#include "rfapi.h" + +/* + * RFAPI Advertisement Data Block + * + * Holds NVE prefix advertisement information + */ +struct rfapi_adb +{ + struct prefix prefix_ip; + struct prefix prefix_eth; /* now redundant with l2o */ + struct prefix_rd prd; + uint32_t lifetime; + uint8_t cost; + struct rfapi_l2address_option l2o; +}; + +/* + * Lists of rfapi_adb. Each rfapi_adb is referenced twice: + * + * 1. each is referenced in by_lifetime + * 2. each is referenced by exactly one of: ipN_by_prefix, ip0_by_ether + */ +struct rfapi_advertised_prefixes +{ + struct skiplist *ipN_by_prefix; /* all except 0/32, 0/128 */ + struct skiplist *ip0_by_ether; /* ip prefix 0/32, 0/128 */ + struct skiplist *by_lifetime; /* all */ +}; + + +struct rfapi_descriptor +{ + struct route_node *un_node; /* backref to un table */ + + struct rfapi_descriptor *next; /* next vn_addr */ + + /* supplied by client */ + struct bgp *bgp; /* from rfp_start_val */ + struct rfapi_ip_addr vn_addr; + struct rfapi_ip_addr un_addr; + rfapi_response_cb_t *response_cb; /* override per-bgp response_cb */ + void *cookie; /* for callbacks */ + struct rfapi_tunneltype_option default_tunneltype_option; + + /* supplied by matched configuration */ + struct prefix_rd rd; + struct ecommunity *rt_export_list; + uint32_t response_lifetime; + + /* list of prefixes currently being advertised by this nve */ + struct rfapi_advertised_prefixes advertised; + + time_t open_time; + + uint32_t max_prefix_lifetime; + uint32_t min_prefix_lifetime; + + /* reference to this nve's import table */ + struct rfapi_import_table *import_table; + + uint32_t monitor_count; + struct route_table *mon; /* rfapi_monitors */ + struct skiplist *mon_eth; /* ethernet monitors */ + + /* + * rib RIB as seen by NVE + * rib_pending RIB containing nodes with updated info chains + * rsp_times last time we sent response containing pfx + */ + uint32_t rib_prefix_count; /* pfxes with routes */ + struct route_table *rib[AFI_MAX]; + struct route_table *rib_pending[AFI_MAX]; + struct work_queue *updated_responses_queue; + struct route_table *rsp_times[AFI_MAX]; + + uint32_t rsp_counter; /* dedup initial rsp */ + time_t rsp_time; /* dedup initial rsp */ + time_t ftd_last_allowed_time; /* FTD filter */ + + unsigned int stat_count_nh_reachable; + unsigned int stat_count_nh_removal; + + /* + * points to the original nve group structure that matched + * when this nve_descriptor was created. We use this pointer + * in rfapi_close() to find the nve group structure and + * delete its reference back to us. + * + * If the nve group structure is deleted (via configuration + * change) while this nve_descriptor exists, this rfg pointer + * will be set to NULL. + */ + struct rfapi_nve_group_cfg *rfg; + + /* + * This ~7kB structure is here to permit multiple routes for + * a prefix to be injected to BGP. There are at least two + * situations where such conditions obtain: + * + * When an VNC route is exported to BGP on behalf of the set of + * NVEs that belong to the export NVE group, it is replicated + * so that there is one route per NVE (and the route's nexthop + * is the NVE's VN address). + * + * Each of these routes being injected to BGP must have a distinct + * peer pointer (otherwise, if they have the same peer pointer, each + * route will be considered an implicit waithdraw of the previous + * route injected from that peer, and the new route will replace + * rather than augment the old one(s)). + */ + struct peer *peer; + + uint32_t flags; +#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP 0x00000001 +#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6 0x00000002 +#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_ETHER 0x00000004 +#define RFAPI_HD_FLAG_PROVISIONAL 0x00000008 +#define RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY 0x00000010 +}; + +#define RFAPI_QUEUED_FLAG(afi) ( \ + ((afi) == AFI_IP)? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP: \ + (((afi) == AFI_IP6)? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6: \ + (((afi) == AFI_ETHER)? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_ETHER: \ + (assert(0), 0) ))) + + +struct rfapi_global_stats +{ + time_t last_reset; + unsigned int max_descriptors; + + unsigned int count_unknown_nves; + + unsigned int count_queries; + unsigned int count_queries_failed; + + unsigned int max_responses; /* semantics? */ + + unsigned int count_registrations; + unsigned int count_registrations_failed; + + unsigned int count_updated_response_updates; + unsigned int count_updated_response_deletes; +}; + +/* + * There is one of these per BGP instance. + * + * Radix tree is indexed by un address; follow chain and + * check vn address to get exact match. + */ +struct rfapi +{ + struct route_table un[AFI_MAX]; + struct rfapi_import_table *imports; /* IPv4, IPv6 */ + struct list descriptors;/* debug & resolve-nve imports */ + + struct rfapi_global_stats stat; + + /* + * callbacks into RFP, set at startup time (bgp_rfapi_new() gets + * values from rfp_start()) or via rfapi_rfp_set_cb_methods() + * (otherwise NULL). Note that the response_cb method can also + * be overridden per-rfd (currently used only for debug/test scenarios) + */ + struct rfapi_rfp_cb_methods rfp_methods; + + /* + * Import tables for Ethernet over IPSEC + * + * The skiplist keys are LNIs. Values are pointers + * to struct rfapi_import_table. + */ + struct skiplist *import_mac; /* L2 */ + + /* + * when exporting plain routes ("registered-nve" mode) to + * bgp unicast or zebra, we need to keep track of information + * related to expiring the routes according to the VNC lifetime + */ + struct route_table *rt_export_bgp[AFI_MAX]; + struct route_table *rt_export_zebra[AFI_MAX]; + + /* + * For VNC->BGP unicast exports in CE mode, we need a + * routing table that collects all of the VPN routes + * in a single tree. The VPN rib is split up according + * to RD first, so we can't use that. This is an import + * table that matches all RTs. + */ + struct rfapi_import_table *it_ce; + + /* + * when importing bgp-direct routes in resolve-nve mode, + * this list maps unicast route nexthops to their bgp_infos + * in the unicast table + */ + struct skiplist *resolve_nve_nexthop; + + /* + * Descriptors for which rfapi_close() was called during a callback. + * They will be closed after the callback finishes. + */ + struct work_queue *deferred_close_q; + + /* + * For "show vnc responses" + */ + uint32_t response_immediate_count; + uint32_t response_updated_count; + uint32_t monitor_count; + + uint32_t rib_prefix_count_total; + uint32_t rib_prefix_count_total_max; + + uint32_t flags; +#define RFAPI_INCALLBACK 0x00000001 + void *rfp; /* from rfp_start */ +}; + +#define RFAPI_RIB_PREFIX_COUNT_INCR(rfd, rfapi) do { \ + ++(rfd)->rib_prefix_count; \ + ++(rfapi)->rib_prefix_count_total; \ + if ((rfapi)->rib_prefix_count_total > (rfapi)->rib_prefix_count_total_max) \ + ++(rfapi)->rib_prefix_count_total_max; \ + } while (0) + +#define RFAPI_RIB_PREFIX_COUNT_DECR(rfd, rfapi) do { \ + --(rfd)->rib_prefix_count; \ + --(rfapi)->rib_prefix_count_total; \ + } while (0) + +#define RFAPI_0_PREFIX(prefix) ( \ + (((prefix)->family == AF_INET)? (prefix)->u.prefix4.s_addr == 0: \ + (((prefix)->family == AF_INET6)? \ + (IN6_IS_ADDR_UNSPECIFIED(&(prefix)->u.prefix6)) : 0)) \ +) + +#define RFAPI_0_ETHERADDR(ea) ( \ + ((ea)->octet[0] | (ea)->octet[1] | (ea)->octet[2] | \ + (ea)->octet[3] | (ea)->octet[4] | (ea)->octet[5]) == 0) + +#define RFAPI_HOST_PREFIX(prefix) ( \ + ((prefix)->family == AF_INET)? ((prefix)->prefixlen == 32): \ + (((prefix)->family == AF_INET6)? ((prefix)->prefixlen == 128): 0) ) + +extern void +rfapiQprefix2Rprefix ( + struct prefix *qprefix, + struct rfapi_ip_prefix *rprefix); + +extern int +rfapi_find_rfd ( + struct bgp *bgp, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + struct rfapi_descriptor **rfd); + +extern void +add_vnc_route ( + struct rfapi_descriptor *rfd, /* cookie + UN addr for VPN */ + struct bgp *bgp, + int safi, + struct prefix *p, + struct prefix_rd *prd, + struct rfapi_ip_addr *nexthop, + uint32_t *local_pref, /* host byte order */ + uint32_t *lifetime, /* host byte order */ + struct bgp_tea_options *rfp_options, + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + struct ecommunity *rt_export_list, + uint32_t *med, + uint32_t *label, + uint8_t type, + uint8_t sub_type, + int flags); +#define RFAPI_AHR_NO_TUNNEL_SUBTLV 0x00000001 +#define RFAPI_AHR_RFPOPT_IS_VNCTLV 0x00000002 /* hack! */ +#if 0 /* unused? */ +# define RFAPI_AHR_SET_PFX_TO_NEXTHOP 0x00000004 +#endif + +extern void +del_vnc_route ( + struct rfapi_descriptor *rfd, + struct peer *peer, + struct bgp *bgp, + safi_t safi, + struct prefix *p, + struct prefix_rd *prd, + uint8_t type, + uint8_t sub_type, + struct rfapi_nexthop *lnh, + int kill); + +extern int +rfapiCliGetPrefixAddr (struct vty *vty, const char *str, struct prefix *p); + +extern int +rfapiGetVncLifetime (struct attr *attr, uint32_t * lifetime); + +extern int +rfapiGetTunnelType (struct attr *attr, bgp_encap_types *type); + +extern int +rfapiGetVncTunnelUnAddr (struct attr *attr, struct prefix *p); + +extern int +rfapi_reopen (struct rfapi_descriptor *rfd, struct bgp *bgp); + +extern void +vnc_import_bgp_add_rfp_host_route_mode_resolve_nve ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *prefix); + +extern void +vnc_import_bgp_del_rfp_host_route_mode_resolve_nve ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *prefix); + +extern void +rfapiFreeBgpTeaOptionChain (struct bgp_tea_options *p); + +extern struct rfapi_vn_option * +rfapiVnOptionsDup (struct rfapi_vn_option *orig); + +extern struct rfapi_un_option * +rfapiUnOptionsDup (struct rfapi_un_option *orig); + +extern struct bgp_tea_options * +rfapiOptionsDup (struct bgp_tea_options *orig); + +extern int +rfapi_ip_addr_cmp (struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2); + +extern uint32_t +rfp_cost_to_localpref (uint8_t cost); + +extern int +rfapi_set_autord_from_vn (struct prefix_rd *rd, struct rfapi_ip_addr *vn); + +extern void +rfapiAdbFree (struct rfapi_adb *adb); + +extern struct rfapi_nexthop * +rfapi_nexthop_new (struct rfapi_nexthop *copyme); + +extern void +rfapi_nexthop_free (void *goner); + +extern struct rfapi_vn_option * +rfapi_vn_options_dup (struct rfapi_vn_option *existing); + +extern void +rfapi_un_options_free (struct rfapi_un_option *goner); + +extern void +rfapi_vn_options_free (struct rfapi_vn_option *goner); + +/*------------------------------------------ + * rfapi_extract_l2o + * + * Find Layer 2 options in an option chain + * + * input: + * pHop option chain + * + * output: + * l2o layer 2 options extracted + * + * return value: + * 0 OK + * 1 no options found + * + --------------------------------------------*/ +extern int +rfapi_extract_l2o ( + struct bgp_tea_options *pHop, /* chain of options */ + struct rfapi_l2address_option *l2o); /* return extracted value */ + +/* + * compaitibility to old quagga_time call + * time_t value in terms of stabilised absolute time. + * replacement for POSIX time() + */ +extern time_t rfapi_time (time_t *t); + +DECLARE_MGROUP(RFAPI) +DECLARE_MTYPE(RFAPI_CFG) +DECLARE_MTYPE(RFAPI_GROUP_CFG) +DECLARE_MTYPE(RFAPI_L2_CFG) +DECLARE_MTYPE(RFAPI_RFP_GROUP_CFG) +DECLARE_MTYPE(RFAPI) +DECLARE_MTYPE(RFAPI_DESC) +DECLARE_MTYPE(RFAPI_IMPORTTABLE) +DECLARE_MTYPE(RFAPI_MONITOR) +DECLARE_MTYPE(RFAPI_MONITOR_ENCAP) +DECLARE_MTYPE(RFAPI_NEXTHOP) +DECLARE_MTYPE(RFAPI_VN_OPTION) +DECLARE_MTYPE(RFAPI_UN_OPTION) +DECLARE_MTYPE(RFAPI_WITHDRAW) +DECLARE_MTYPE(RFAPI_RFG_NAME) +DECLARE_MTYPE(RFAPI_ADB) +DECLARE_MTYPE(RFAPI_ETI) +DECLARE_MTYPE(RFAPI_NVE_ADDR) +DECLARE_MTYPE(RFAPI_PREFIX_BAG) +DECLARE_MTYPE(RFAPI_IT_EXTRA) +DECLARE_MTYPE(RFAPI_INFO) +DECLARE_MTYPE(RFAPI_ADDR) +DECLARE_MTYPE(RFAPI_UPDATED_RESPONSE_QUEUE) +DECLARE_MTYPE(RFAPI_RECENT_DELETE) +DECLARE_MTYPE(RFAPI_L2ADDR_OPT) +DECLARE_MTYPE(RFAPI_AP) +DECLARE_MTYPE(RFAPI_MONITOR_ETH) + +#endif /* _QUAGGA_BGP_RFAPI_PRIVATE_H */ diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c new file mode 100644 index 0000000000..70acc14d3e --- /dev/null +++ b/bgpd/rfapi/rfapi_rib.c @@ -0,0 +1,2535 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: rfapi_rib.c + * Purpose: maintain per-nve ribs and generate change lists + */ + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "log.h" +#include "skiplist.h" +#include "workqueue.h" + +#include "bgpd.h" +#include "bgp_route.h" +#include "bgp_ecommunity.h" +#include "bgp_mplsvpn.h" +#include "bgp_vnc_types.h" + +#include "rfapi.h" +#include "bgp_rfapi_cfg.h" +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_vty.h" +#include "vnc_import_bgp.h" +#include "rfapi_rib.h" +#include "rfapi_monitor.h" +#include "rfapi_encap_tlv.h" + +#define DEBUG_PROCESS_PENDING_NODE 0 +#define DEBUG_PENDING_DELETE_ROUTE 0 +#define DEBUG_NHL 0 +#define DEBUG_RIB_SL_RD 0 + +/* forward decl */ +#if DEBUG_NHL +static void +rfapiRibShowRibSl (void *stream, struct prefix *pfx, struct skiplist *sl); +#endif + +/* + * RIB + * --- + * Model of the set of routes currently in the NVE's RIB. + * + * node->info ptr to "struct skiplist". + * MUST be NULL if there are no routes. + * key = ptr to struct prefix {vn} + * val = ptr to struct rfapi_info + * skiplist.del = NULL + * skiplist.cmp = vnc_prefix_cmp + * + * node->aggregate ptr to "struct skiplist". + * key = ptr to struct prefix {vn} + * val = ptr to struct rfapi_info + * skiplist.del = rfapi_info_free + * skiplist.cmp = vnc_prefix_cmp + * + * This skiplist at "aggregate" + * contains the routes recently + * deleted + * + * + * Pending RIB + * ----------- + * Sparse list of prefixes that need to be updated. Each node + * will have the complete set of routes for the prefix. + * + * node->info ptr to "struct list" (lib/linklist.h) + * "Cost List" + * List of routes sorted lowest cost first. + * This list is how the new complete set + * of routes should look. + * Set if there are updates to the prefix; + * MUST be NULL if there are no updates. + * + * .data = ptr to struct rfapi_info + * list.cmp = NULL (sorted manually) + * list.del = rfapi_info_free + * + * Special case: if node->info is 1, it means + * "delete all routes at this prefix". + * + * node->aggregate ptr to struct skiplist + * key = ptr to struct prefix {vn} (part of ri) + * val = struct rfapi_info + * skiplist.cmp = vnc_prefix_cmp + * skiplist.del = NULL + * + * ptlist is rewritten anew each time + * rfapiRibUpdatePendingNode() is called + * + * THE ptlist VALUES ARE REFERENCES TO THE + * rfapi_info STRUCTS IN THE node->info LIST. + */ + +/* + * iterate over RIB to count responses, compare with running counters + */ +void +rfapiRibCheckCounts ( + int checkstats, /* validate rfd & global counts */ + unsigned int offset) /* number of ri's held separately */ +{ + struct rfapi_descriptor *rfd; + struct listnode *node; + + struct bgp *bgp = bgp_get_default (); + + uint32_t t_pfx_active = 0; + uint32_t t_pfx_deleted = 0; + + uint32_t t_ri_active = 0; + uint32_t t_ri_deleted = 0; + uint32_t t_ri_pend = 0; + + unsigned int alloc_count; + + /* + * loop over NVEs + */ + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd)) + { + + afi_t afi; + uint32_t pfx_active = 0; + uint32_t pfx_deleted = 0; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct route_node *rn; + + for (rn = route_top (rfd->rib[afi]); rn; rn = route_next (rn)) + { + + struct skiplist *sl = rn->info; + struct skiplist *dsl = rn->aggregate; + uint32_t ri_active = 0; + uint32_t ri_deleted = 0; + + if (sl) + { + ri_active = skiplist_count (sl); + assert (ri_active); + t_ri_active += ri_active; + ++pfx_active; + ++t_pfx_active; + } + + if (dsl) + { + ri_deleted = skiplist_count (dsl); + t_ri_deleted += ri_deleted; + ++pfx_deleted; + ++t_pfx_deleted; + } + } + for (rn = route_top (rfd->rib_pending[afi]); rn; + rn = route_next (rn)) + { + + struct list *l = rn->info; /* sorted by cost */ + struct skiplist *sl = rn->aggregate; + uint32_t ri_pend_cost = 0; + uint32_t ri_pend_uniq = 0; + + if (sl) + { + ri_pend_uniq = skiplist_count (sl); + } + + if (l && (l != (void *) 1)) + { + ri_pend_cost = l->count; + t_ri_pend += l->count; + } + + assert (ri_pend_uniq == ri_pend_cost); + } + } + + if (checkstats) + { + if (pfx_active != rfd->rib_prefix_count) + { + zlog_debug ("%s: rfd %p actual pfx count %u != running %u", + __func__, rfd, pfx_active, rfd->rib_prefix_count); + assert (0); + } + } + } + + if (checkstats && bgp && bgp->rfapi) + { + if (t_pfx_active != bgp->rfapi->rib_prefix_count_total) + { + zlog_debug ("%s: actual total pfx count %u != running %u", + __func__, t_pfx_active, + bgp->rfapi->rib_prefix_count_total); + assert (0); + } + } + + /* + * Check against memory allocation count + */ + alloc_count = mtype_stats_alloc (MTYPE_RFAPI_INFO); + assert (t_ri_active + t_ri_deleted + t_ri_pend + offset == alloc_count); +} + +static struct rfapi_info * +rfapi_info_new () +{ + return XCALLOC (MTYPE_RFAPI_INFO, sizeof (struct rfapi_info)); +} + +void +rfapiFreeRfapiUnOptionChain (struct rfapi_un_option *p) +{ + while (p) + { + struct rfapi_un_option *next; + + next = p->next; + XFREE (MTYPE_RFAPI_UN_OPTION, p); + p = next; + } +} + +void +rfapiFreeRfapiVnOptionChain (struct rfapi_vn_option *p) +{ + while (p) + { + struct rfapi_vn_option *next; + + next = p->next; + XFREE (MTYPE_RFAPI_VN_OPTION, p); + p = next; + } +} + + +static void +rfapi_info_free (struct rfapi_info *goner) +{ + if (goner) + { + if (goner->tea_options) + { + rfapiFreeBgpTeaOptionChain (goner->tea_options); + goner->tea_options = NULL; + } + if (goner->un_options) + { + rfapiFreeRfapiUnOptionChain (goner->un_options); + goner->un_options = NULL; + } + if (goner->vn_options) + { + rfapiFreeRfapiVnOptionChain (goner->vn_options); + goner->vn_options = NULL; + } + if (goner->timer) + { + struct rfapi_rib_tcb *tcb; + + tcb = ((struct thread *) goner->timer)->arg; + thread_cancel ((struct thread *) goner->timer); + XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb); + goner->timer = NULL; + } + XFREE (MTYPE_RFAPI_INFO, goner); + } +} + +/* + * Timer control block for recently-deleted and expired routes + */ +struct rfapi_rib_tcb +{ + struct rfapi_descriptor *rfd; + struct skiplist *sl; + struct rfapi_info *ri; + struct route_node *rn; + int flags; +#define RFAPI_RIB_TCB_FLAG_DELETED 0x00000001 +}; + +/* + * remove route from rib + */ +static int +rfapiRibExpireTimer (struct thread *t) +{ + struct rfapi_rib_tcb *tcb = t->arg; + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + /* + * Forget reference to thread. Otherwise rfapi_info_free() will + * attempt to free thread pointer as an option chain + */ + tcb->ri->timer = NULL; + + /* "deleted" skiplist frees ri, "active" doesn't */ + assert (!skiplist_delete (tcb->sl, &tcb->ri->rk, NULL)); + if (!tcb->sl->del) + { + /* + * XXX in this case, skiplist has no delete function: we must + * therefore delete rfapi_info explicitly. + */ + rfapi_info_free (tcb->ri); + } + + if (skiplist_empty (tcb->sl)) + { + if (CHECK_FLAG (tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED)) + tcb->rn->aggregate = NULL; + else + { + struct bgp *bgp = bgp_get_default (); + tcb->rn->info = NULL; + RFAPI_RIB_PREFIX_COUNT_DECR (tcb->rfd, bgp->rfapi); + } + skiplist_free (tcb->sl); + route_unlock_node (tcb->rn); + } + + XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb); + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + return 0; +} + +static void +rfapiRibStartTimer ( + struct rfapi_descriptor *rfd, + struct rfapi_info *ri, + struct route_node *rn, /* route node attached to */ + int deleted) +{ + struct thread *t = ri->timer; + struct rfapi_rib_tcb *tcb = NULL; + char buf_prefix[BUFSIZ]; + + if (t) + { + tcb = t->arg; + thread_cancel (t); + ri->timer = NULL; + } + else + { + tcb = + XCALLOC (MTYPE_RFAPI_RECENT_DELETE, sizeof (struct rfapi_rib_tcb)); + } + tcb->rfd = rfd; + tcb->ri = ri; + tcb->rn = rn; + if (deleted) + { + tcb->sl = (struct skiplist *) rn->aggregate; + SET_FLAG (tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED); + } + else + { + tcb->sl = (struct skiplist *) rn->info; + UNSET_FLAG (tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED); + } + + prefix2str (&rn->p, buf_prefix, BUFSIZ); + zlog_debug ("%s: rfd %p pfx %s life %u", __func__, rfd, buf_prefix, + ri->lifetime); + ri->timer = thread_add_timer (bm->master, rfapiRibExpireTimer, + tcb, ri->lifetime); + assert (ri->timer); +} + +/* + * Compares two s + */ +static int +rfapi_rib_key_cmp (void *k1, void *k2) +{ + struct rfapi_rib_key *a = (struct rfapi_rib_key *) k1; + struct rfapi_rib_key *b = (struct rfapi_rib_key *) k2; + int ret; + + if (!a || !b) + return (a - b); + + ret = vnc_prefix_cmp (&a->vn, &b->vn); + if (ret) + return ret; + + ret = vnc_prefix_cmp(&a->rd, &b->rd); + if (ret) + return ret; + + ret = vnc_prefix_cmp (&a->aux_prefix, &b->aux_prefix); + + return ret; +} + + +/* + * Note: this function will claim that two option chains are + * different unless their option items are in identical order. + * The consequence is that RFP updated responses can be sent + * unnecessarily, or that they might contain nexthop items + * that are not strictly needed. + * + * This function could be modified to compare option chains more + * thoroughly, but it's not clear that the extra compuation would + * be worth it. + */ +static int +bgp_tea_options_cmp (struct bgp_tea_options *a, struct bgp_tea_options *b) +{ + int rc; + + if (!a || !b) + { + return (a - b); + } + + if (a->type != b->type) + return (a->type - b->type); + if (a->length != b->length) + return (a->length = b->length); + if ((rc = memcmp (a->value, b->value, a->length))) + return rc; + if (!a->next != !b->next) + { /* logical xor */ + return (a->next - b->next); + } + if (a->next) + return bgp_tea_options_cmp (a->next, b->next); + return 0; + +} + +static int +rfapi_info_cmp (struct rfapi_info *a, struct rfapi_info *b) +{ + int rc; + + if (!a || !b) + return (a - b); + + if ((rc = rfapi_rib_key_cmp (&a->rk, &b->rk))) + return rc; + + if ((rc = vnc_prefix_cmp (&a->un, &b->un))) + return rc; + + if (a->cost != b->cost) + return (a->cost - b->cost); + + if (a->lifetime != b->lifetime) + return (a->lifetime - b->lifetime); + + if ((rc = bgp_tea_options_cmp (a->tea_options, b->tea_options))) + return rc; + + return 0; +} + +void +rfapiRibClear (struct rfapi_descriptor *rfd) +{ + struct bgp *bgp = bgp_get_default (); + afi_t afi; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: rfd=%p", __func__, rfd); +#endif + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + struct route_node *pn; + struct route_node *rn; + + if (rfd->rib_pending[afi]) + { + for (pn = route_top (rfd->rib_pending[afi]); pn; + pn = route_next (pn)) + { + if (pn->aggregate) + { + /* + * free references into the rfapi_info structures before + * freeing the structures themselves + */ + skiplist_free ((struct skiplist *) (pn->aggregate)); + pn->aggregate = NULL; + route_unlock_node (pn); /* skiplist deleted */ + } + /* + * free the rfapi_info structures + */ + if (pn->info) + { + if (pn->info != (void *) 1) + { + list_delete ((struct list *) (pn->info)); + } + pn->info = NULL; + route_unlock_node (pn); /* linklist or 1 deleted */ + } + } + } + if (rfd->rib[afi]) + { + for (rn = route_top (rfd->rib[afi]); rn; rn = route_next (rn)) + { + if (rn->info) + { + + struct rfapi_info *ri; + + while (0 == + skiplist_first ((struct skiplist *) rn->info, NULL, + (void **) &ri)) + { + + rfapi_info_free (ri); + skiplist_delete_first ((struct skiplist *) rn->info); + } + skiplist_free ((struct skiplist *) rn->info); + rn->info = NULL; + route_unlock_node (rn); + RFAPI_RIB_PREFIX_COUNT_DECR (rfd, bgp->rfapi); + } + if (rn->aggregate) + { + + struct rfapi_info *ri_del; + + /* delete skiplist & contents */ + while (!skiplist_first ((struct skiplist *) (rn->aggregate), + NULL, (void **) &ri_del)) + { + + /* sl->del takes care of ri_del */ + skiplist_delete_first ( + (struct skiplist *) (rn->aggregate)); + } + skiplist_free ((struct skiplist *) (rn->aggregate)); + + rn->aggregate = NULL; + route_unlock_node (rn); + } + } + } + } + if (rfd->updated_responses_queue) + { + work_queue_free (rfd->updated_responses_queue); + rfd->updated_responses_queue = NULL; + } +} + +/* + * Release all dynamically-allocated memory that is part of an HD's RIB + */ +void +rfapiRibFree (struct rfapi_descriptor *rfd) +{ + afi_t afi; + + + /* + * NB rfd is typically detached from master list, so is not included + * in the count performed by RFAPI_RIB_CHECK_COUNTS + */ + + /* + * Free routes attached to radix trees + */ + rfapiRibClear (rfd); + + /* Now the uncounted rfapi_info's are freed, so the check should succeed */ + RFAPI_RIB_CHECK_COUNTS (1, 0); + + /* + * Free radix trees + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + route_table_finish (rfd->rib_pending[afi]); + rfd->rib_pending[afi] = NULL; + + route_table_finish (rfd->rib[afi]); + rfd->rib[afi] = NULL; + + /* NB route_table_finish frees only prefix nodes, not chained info */ + route_table_finish (rfd->rsp_times[afi]); + rfd->rib[afi] = NULL; + } +} + +/* + * Copies struct bgp_info to struct rfapi_info, except for rk fields and un + */ +static void +rfapiRibBi2Ri( + struct bgp_info *bi, + struct rfapi_info *ri, + uint32_t lifetime) +{ + struct bgp_attr_encap_subtlv *pEncap; + + ri->cost = rfapiRfpCost (bi->attr); + ri->lifetime = lifetime; + + /* This loop based on rfapiRouteInfo2NextHopEntry() */ + for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next) + { + struct bgp_tea_options *hop; + + switch (pEncap->type) + { + case BGP_VNC_SUBTLV_TYPE_LIFETIME: + /* use configured lifetime, not attr lifetime */ + break; + + case BGP_VNC_SUBTLV_TYPE_RFPOPTION: + hop = XCALLOC (MTYPE_BGP_TEA_OPTIONS, + sizeof (struct bgp_tea_options)); + assert (hop); + hop->type = pEncap->value[0]; + hop->length = pEncap->value[1]; + hop->value = XCALLOC (MTYPE_BGP_TEA_OPTIONS_VALUE, + pEncap->length - 2); + assert (hop->value); + memcpy (hop->value, pEncap->value + 2, pEncap->length - 2); + if (hop->length > pEncap->length - 2) + { + zlog_warn ("%s: VNC subtlv length mismatch: " + "RFP option says %d, attr says %d " + "(shrinking)", + __func__, hop->length, pEncap->length - 2); + hop->length = pEncap->length - 2; + } + hop->next = ri->tea_options; + ri->tea_options = hop; + break; + + default: + break; + } + } + + rfapi_un_options_free (ri->un_options); /* maybe free old version */ + ri->un_options = rfapi_encap_tlv_to_un_option (bi->attr); + + /* + * VN options + */ + if (bi->extra && + decode_rd_type(bi->extra->vnc.import.rd.val) == RD_TYPE_VNC_ETH) + { + /* ethernet route */ + + struct rfapi_vn_option *vo; + + vo = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option)); + assert (vo); + + vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + + /* copy from RD already stored in bi, so we don't need it_node */ + memcpy (&vo->v.l2addr.macaddr, bi->extra->vnc.import.rd.val+2, + ETHER_ADDR_LEN); + + if (bi->attr && bi->attr->extra) + { + (void) rfapiEcommunityGetLNI (bi->attr->extra->ecommunity, + &vo->v.l2addr.logical_net_id); + } + + /* local_nve_id comes from RD */ + vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1]; + + /* label comes from MP_REACH_NLRI label */ + vo->v.l2addr.label = decode_label (bi->extra->tag); + + rfapi_vn_options_free (ri->vn_options); /* maybe free old version */ + ri->vn_options = vo; + } + + /* + * If there is an auxiliary IP address (L2 can have it), copy it + */ + if (bi && bi->extra && bi->extra->vnc.import.aux_prefix.family) + { + ri->rk.aux_prefix = bi->extra->vnc.import.aux_prefix; + } +} + +/* + * rfapiRibPreloadBi + * + * Install route into NVE RIB model so as to be consistent with + * caller's response to rfapi_query(). + * + * Also: return indication to caller whether this specific route + * should be included in the response to the NVE according to + * the following tests: + * + * 1. If there were prior duplicates of this route in this same + * query response, don't include the route. + * + * RETURN VALUE: + * + * 0 OK to include route in response + * !0 do not include route in response + */ +int +rfapiRibPreloadBi( + struct route_node *rfd_rib_node, /* NULL = don't preload or filter */ + struct prefix *pfx_vn, + struct prefix *pfx_un, + uint32_t lifetime, + struct bgp_info *bi) +{ + struct rfapi_descriptor *rfd; + struct skiplist *slRibPt = NULL; + struct rfapi_info *ori = NULL; + struct rfapi_rib_key rk; + struct route_node *trn; + afi_t afi; + + if (!rfd_rib_node) + return 0; + + afi = family2afi(rfd_rib_node->p.family); + + rfd = (struct rfapi_descriptor *)(rfd_rib_node->table->info); + + memset((void *)&rk, 0, sizeof(rk)); + rk.vn = *pfx_vn; + rk.rd = bi->extra->vnc.import.rd; + + /* + * If there is an auxiliary IP address (L2 can have it), copy it + */ + if (bi->extra->vnc.import.aux_prefix.family) + { + rk.aux_prefix = bi->extra->vnc.import.aux_prefix; + } + + /* + * is this route already in NVE's RIB? + */ + slRibPt = (struct skiplist *) rfd_rib_node->info; + + if (slRibPt && !skiplist_search (slRibPt, &rk, (void **) &ori)) + { + + if ((ori->rsp_counter == rfd->rsp_counter) && + (ori->last_sent_time == rfd->rsp_time)) + { + return -1; /* duplicate in this response */ + } + + /* found: update contents of existing route in RIB */ + ori->un = *pfx_un; + rfapiRibBi2Ri(bi, ori, lifetime); + } + else + { + /* not found: add new route to RIB */ + ori = rfapi_info_new (); + ori->rk = rk; + ori->un = *pfx_un; + rfapiRibBi2Ri(bi, ori, lifetime); + + if (!slRibPt) + { + slRibPt = skiplist_new (0, rfapi_rib_key_cmp, NULL); + rfd_rib_node->info = slRibPt; + route_lock_node (rfd_rib_node); + RFAPI_RIB_PREFIX_COUNT_INCR (rfd, rfd->bgp->rfapi); + } + skiplist_insert (slRibPt, &ori->rk, ori); + } + + ori->last_sent_time = rfapi_time (NULL); + + /* + * poke timer + */ + RFAPI_RIB_CHECK_COUNTS (0, 0); + rfapiRibStartTimer (rfd, ori, rfd_rib_node, 0); + RFAPI_RIB_CHECK_COUNTS (0, 0); + + /* + * Update last sent time for prefix + */ + trn = route_node_get (rfd->rsp_times[afi], &rfd_rib_node->p); /* locks trn */ + trn->info = (void *) (uintptr_t) bgp_clock (); + if (trn->lock > 1) + route_unlock_node (trn); + + return 0; +} + +/* + * Frees rfapi_info items at node + * + * Adjust 'rib' and 'rib_pending' as follows: + * + * If rib_pending node->info is 1 (magic value): + * callback: NHL = RIB NHL with lifetime = withdraw_lifetime_value + * RIB = remove all routes at the node + * DONE + * + * For each item at rib node: + * if not present in pending node, move RIB item to "delete list" + * + * For each item at pending rib node: + * if present (same vn/un) in rib node with same lifetime & options, drop + * matching item from pending node + * + * For each remaining item at pending rib node, add or replace item + * at rib node. + * + * Construct NHL as concatenation of pending list + delete list + * + * Clear pending node + */ +static void +process_pending_node ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + afi_t afi, + struct route_node *pn, /* pending node */ + struct rfapi_next_hop_entry **head, + struct rfapi_next_hop_entry **tail) +{ + struct listnode *node = NULL; + struct listnode *nnode = NULL; + struct rfapi_info *ri = NULL; /* happy valgrind */ + struct rfapi_ip_prefix hp = { 0 }; /* pfx to put in NHE */ + struct route_node *rn = NULL; + struct skiplist *slRibPt = NULL; /* rib list */ + struct skiplist *slPendPt = NULL; + struct list *lPendCost = NULL; + struct list *delete_list = NULL; + int printedprefix = 0; + char buf_prefix[BUFSIZ]; + int rib_node_started_nonempty = 0; + int sendingsomeroutes = 0; + +#if DEBUG_PROCESS_PENDING_NODE + unsigned int count_rib_initial = 0; + unsigned int count_pend_vn_initial = 0; + unsigned int count_pend_cost_initial = 0; +#endif + + assert (pn); + prefix2str (&pn->p, buf_prefix, BUFSIZ); + zlog_debug ("%s: afi=%d, %s pn->info=%p", + __func__, afi, buf_prefix, pn->info); + + if (AFI_ETHER != afi) + { + rfapiQprefix2Rprefix (&pn->p, &hp); + } + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + /* + * Find corresponding RIB node + */ + rn = route_node_get (rfd->rib[afi], &pn->p); /* locks rn */ + + /* + * RIB skiplist has key=rfapi_addr={vn,un}, val = rfapi_info, + * skiplist.del = NULL + */ + slRibPt = (struct skiplist *) rn->info; + if (slRibPt) + rib_node_started_nonempty = 1; + + slPendPt = (struct skiplist *) (pn->aggregate); + lPendCost = (struct list *) (pn->info); + +#if DEBUG_PROCESS_PENDING_NODE + /* debugging */ + if (slRibPt) + count_rib_initial = skiplist_count (slRibPt); + + if (slPendPt) + count_pend_vn_initial = skiplist_count (slPendPt); + + if (lPendCost && lPendCost != (struct list *) 1) + count_pend_cost_initial = lPendCost->count; +#endif + + + /* + * Handle special case: delete all routes at prefix + */ + if (lPendCost == (struct list *) 1) + { + zlog_debug ("%s: lPendCost=1 => delete all", __func__); + if (slRibPt && !skiplist_empty (slRibPt)) + { + delete_list = list_new (); + while (0 == skiplist_first (slRibPt, NULL, (void **) &ri)) + { + + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + + listnode_add (delete_list, ri); + zlog_debug ("%s: after listnode_add, delete_list->count=%d", + __func__, delete_list->count); + rfapiFreeBgpTeaOptionChain (ri->tea_options); + ri->tea_options = NULL; + + if (ri->timer) + { + struct rfapi_rib_tcb *tcb; + + tcb = ((struct thread *) ri->timer)->arg; + thread_cancel (ri->timer); + XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb); + ri->timer = NULL; + } + + prefix2str (&ri->rk.vn, buf, BUFSIZ); + prefix2str (&ri->un, buf2, BUFSIZ); + zlog_debug + ("%s: put dl pfx=%s vn=%s un=%s cost=%d life=%d vn_options=%p", + __func__, buf_prefix, buf, buf2, ri->cost, ri->lifetime, + ri->vn_options); + + skiplist_delete_first (slRibPt); + } + + assert (skiplist_empty (slRibPt)); + + skiplist_free (slRibPt); + rn->info = slRibPt = NULL; + route_unlock_node (rn); + + lPendCost = pn->info = NULL; + route_unlock_node (pn); + + goto callback; + } + if (slRibPt) + { + skiplist_free (slRibPt); + rn->info = NULL; + route_unlock_node (rn); + } + + assert (!slPendPt); + if (slPendPt) + { /* TBD I think we can toss this block */ + skiplist_free (slPendPt); + pn->aggregate = NULL; + route_unlock_node (pn); + } + + pn->info = NULL; + route_unlock_node (pn); + + route_unlock_node (rn); /* route_node_get() */ + + if (rib_node_started_nonempty) + { + RFAPI_RIB_PREFIX_COUNT_DECR (rfd, bgp->rfapi); + } + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + return; + } + + zlog_debug ("%s: lPendCost->count=%d, slRibPt->count=%d", + __func__, + (lPendCost ? lPendCost->count : -1), + (slRibPt ? slRibPt->count : -1)); + + /* + * Iterate over routes at RIB Node. + * If not found at Pending Node, delete from RIB Node and add to deletelist + * If found at Pending Node + * If identical rfapi_info, delete from Pending Node + */ + if (slRibPt) + { + void *cursor = NULL; + struct rfapi_info *ori; + + /* + * Iterate over RIB List + * + */ + while (!skiplist_next (slRibPt, NULL, (void **) &ori, &cursor)) + { + + if (skiplist_search (slPendPt, &ori->rk, (void **) &ri)) + { + /* + * Not in Pending list, so it should be deleted + */ + if (!delete_list) + delete_list = list_new (); + listnode_add (delete_list, ori); + rfapiFreeBgpTeaOptionChain (ori->tea_options); + ori->tea_options = NULL; + if (ori->timer) + { + struct rfapi_rib_tcb *tcb; + + tcb = ((struct thread *) ori->timer)->arg; + thread_cancel (ori->timer); + XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb); + ori->timer = NULL; + } + +#if DEBUG_PROCESS_PENDING_NODE + /* deleted from slRibPt below, after we're done iterating */ + zlog_debug + ("%s: slRibPt ri %p not matched in pending list, delete", + __func__, ori); +#endif + + } + else + { + /* + * Found in pending list. If same lifetime, cost, options, + * then remove from pending list because the route + * hasn't changed. + */ + int same = 0; + if (!rfapi_info_cmp (ori, ri)) + { + /* same: delete from pending list */ + same = 1; + + skiplist_delete (slPendPt, &ri->rk, NULL); + assert (lPendCost); + if (lPendCost) + { + /* linear walk: might need optimization */ + listnode_delete (lPendCost, ri); /* XXX doesn't free data! bug? */ + rfapi_info_free (ri); /* grr... */ + } + } +#if DEBUG_PROCESS_PENDING_NODE + zlog_debug ("%s: slRibPt ri %p matched in pending list, %s", + __func__, ori, + (same ? "same info" : "different info")); +#endif + } + } + /* + * Go back and delete items from RIB + */ + if (delete_list) + { + for (ALL_LIST_ELEMENTS_RO (delete_list, node, ri)) + { + zlog_debug ("%s: deleting ri %p from slRibPt", __func__, ri); + assert (!skiplist_delete (slRibPt, &ri->rk, NULL)); + } + if (skiplist_empty (slRibPt)) + { + skiplist_free (slRibPt); + slRibPt = rn->info = NULL; + route_unlock_node (rn); + } + } + } + + RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0)); + + /* + * Iterate over routes at Pending Node + * + * If {vn} found at RIB Node, update RIB Node route contents to match PN + * If {vn} NOT found at RIB Node, add copy to RIB Node + */ + if (lPendCost) + { + for (ALL_LIST_ELEMENTS_RO (lPendCost, node, ri)) + { + + struct rfapi_info *ori; + + if (slRibPt && !skiplist_search (slRibPt, &ri->rk, (void **) &ori)) + { + + /* found: update contents of existing route in RIB */ + ori->un = ri->un; + ori->cost = ri->cost; + ori->lifetime = ri->lifetime; + rfapiFreeBgpTeaOptionChain (ori->tea_options); + ori->tea_options = rfapiOptionsDup (ri->tea_options); + ori->last_sent_time = rfapi_time (NULL); + + rfapiFreeRfapiVnOptionChain (ori->vn_options); + ori->vn_options = rfapiVnOptionsDup (ri->vn_options); + + rfapiFreeRfapiUnOptionChain (ori->un_options); + ori->un_options = rfapiUnOptionsDup (ri->un_options); + + zlog_debug + ("%s: matched lPendCost item %p in slRibPt, rewrote", + __func__, ri); + + } + else + { + + char buf_rd[BUFSIZ]; + + /* not found: add new route to RIB */ + ori = rfapi_info_new (); + ori->rk = ri->rk; + ori->un = ri->un; + ori->cost = ri->cost; + ori->lifetime = ri->lifetime; + ori->tea_options = rfapiOptionsDup (ri->tea_options); + ori->last_sent_time = rfapi_time (NULL); + ori->vn_options = rfapiVnOptionsDup (ri->vn_options); + ori->un_options = rfapiUnOptionsDup (ri->un_options); + + if (!slRibPt) + { + slRibPt = skiplist_new (0, rfapi_rib_key_cmp, NULL); + rn->info = slRibPt; + route_lock_node (rn); + } + skiplist_insert (slRibPt, &ori->rk, ori); + +#if DEBUG_RIB_SL_RD + prefix_rd2str(&ori->rk.rd, buf_rd, sizeof(buf_rd)); +#else + buf_rd[0] = 0; +#endif + + zlog_debug ("%s: nomatch lPendCost item %p in slRibPt, added (rd=%s)", + __func__, ri, buf_rd); + } + + /* + * poke timer + */ + RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0)); + rfapiRibStartTimer (rfd, ori, rn, 0); + RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0)); + } + } + + +callback: + /* + * Construct NHL as concatenation of pending list + delete list + */ + + + RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0)); + + if (lPendCost) + { + + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + + zlog_debug ("%s: lPendCost->count now %d", __func__, lPendCost->count); + zlog_debug ("%s: For prefix %s (a)", __func__, buf_prefix); + printedprefix = 1; + + for (ALL_LIST_ELEMENTS (lPendCost, node, nnode, ri)) + { + + struct rfapi_next_hop_entry *new; + struct route_node *trn; + + new = + XCALLOC (MTYPE_RFAPI_NEXTHOP, + sizeof (struct rfapi_next_hop_entry)); + assert (new); + + if (ri->rk.aux_prefix.family) + { + rfapiQprefix2Rprefix (&ri->rk.aux_prefix, &new->prefix); + } + else + { + new->prefix = hp; + if (AFI_ETHER == afi) + { + /* hp is 0; need to set length to match AF of vn */ + new->prefix.length = + (ri->rk.vn.family == AF_INET) ? 32 : 128; + } + } + new->prefix.cost = ri->cost; + new->lifetime = ri->lifetime; + rfapiQprefix2Raddr (&ri->rk.vn, &new->vn_address); + rfapiQprefix2Raddr (&ri->un, &new->un_address); + /* free option chain from ri */ + rfapiFreeBgpTeaOptionChain (ri->tea_options); + + ri->tea_options = NULL; /* option chain was transferred to NHL */ + + new->vn_options = ri->vn_options; + ri->vn_options = NULL; /* option chain was transferred to NHL */ + + new->un_options = ri->un_options; + ri->un_options = NULL; /* option chain was transferred to NHL */ + + if (*tail) + (*tail)->next = new; + *tail = new; + if (!*head) + { + *head = new; + } + sendingsomeroutes = 1; + + ++rfd->stat_count_nh_reachable; + ++bgp->rfapi->stat.count_updated_response_updates; + + /* + * update this NVE's timestamp for this prefix + */ + trn = route_node_get (rfd->rsp_times[afi], &pn->p); /* locks trn */ + trn->info = (void *) (uintptr_t) bgp_clock (); + if (trn->lock > 1) + route_unlock_node (trn); + + rfapiRfapiIpAddr2Str (&new->vn_address, buf, BUFSIZ); + rfapiRfapiIpAddr2Str (&new->un_address, buf2, BUFSIZ); + zlog_debug ("%s: add vn=%s un=%s cost=%d life=%d", __func__, + buf, buf2, new->prefix.cost, new->lifetime); + } + } + + RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0)); + + if (delete_list) + { + + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + + if (!printedprefix) + { + zlog_debug ("%s: For prefix %s (d)", __func__, buf_prefix); + printedprefix = 1; + } + zlog_debug ("%s: delete_list has %d elements", + __func__, delete_list->count); + + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + if (!CHECK_FLAG (bgp->rfapi_cfg->flags, + BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) + { + + for (ALL_LIST_ELEMENTS (delete_list, node, nnode, ri)) + { + + struct rfapi_next_hop_entry *new; + struct rfapi_info *ri_del; + + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + new = XCALLOC (MTYPE_RFAPI_NEXTHOP, + sizeof (struct rfapi_next_hop_entry)); + assert (new); + + if (ri->rk.aux_prefix.family) + { + rfapiQprefix2Rprefix (&ri->rk.aux_prefix, &new->prefix); + } + else + { + new->prefix = hp; + if (AFI_ETHER == afi) + { + /* hp is 0; need to set length to match AF of vn */ + new->prefix.length = + (ri->rk.vn.family == AF_INET) ? 32 : 128; + } + } + + new->prefix.cost = ri->cost; + new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME; + rfapiQprefix2Raddr (&ri->rk.vn, &new->vn_address); + rfapiQprefix2Raddr (&ri->un, &new->un_address); + + new->vn_options = ri->vn_options; + ri->vn_options = NULL; /* option chain was transferred to NHL */ + + new->un_options = ri->un_options; + ri->un_options = NULL; /* option chain was transferred to NHL */ + + if (*tail) + (*tail)->next = new; + *tail = new; + if (!*head) + { + *head = new; + } + ++rfd->stat_count_nh_removal; + ++bgp->rfapi->stat.count_updated_response_deletes; + + rfapiRfapiIpAddr2Str (&new->vn_address, buf, BUFSIZ); + rfapiRfapiIpAddr2Str (&new->un_address, buf2, BUFSIZ); + zlog_debug ("%s: DEL vn=%s un=%s cost=%d life=%d", __func__, + buf, buf2, new->prefix.cost, new->lifetime); + + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + /* + * Update/add to list of recent deletions at this prefix + */ + if (!rn->aggregate) + { + rn->aggregate = skiplist_new (0, rfapi_rib_key_cmp, + (void (*)(void *)) + rfapi_info_free); + route_lock_node (rn); + } + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + + /* sanity check lifetime */ + if (ri->lifetime > RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY) + ri->lifetime = RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY; + + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + /* cancel normal expire timer */ + if (ri->timer) + { + struct rfapi_rib_tcb *tcb; + + tcb = ((struct thread *) ri->timer)->arg; + thread_cancel ((struct thread *) ri->timer); + XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb); + ri->timer = NULL; + } + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + + /* + * Look in "recently-deleted" list + */ + if (skiplist_search ((struct skiplist *) (rn->aggregate), + &ri->rk, (void **) &ri_del)) + { + + int rc; + + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + /* + * NOT in "recently-deleted" list + */ + list_delete_node (delete_list, node); /* does not free ri */ + rc = skiplist_insert ((struct skiplist *) (rn->aggregate), + &ri->rk, ri); + assert (!rc); + + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + rfapiRibStartTimer (rfd, ri, rn, 1); + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + ri->last_sent_time = rfapi_time (NULL); +#if DEBUG_RIB_SL_RD + { + char buf_rd[BUFSIZ]; + prefix_rd2str(&ri->rk.rd, buf_rd, sizeof(buf_rd)); + zlog_debug("%s: move route to recently deleted list, rd=%s", + __func__, buf_rd); + } +#endif + + } + else + { + /* + * IN "recently-deleted" list + */ + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + rfapiRibStartTimer (rfd, ri_del, rn, 1); + RFAPI_RIB_CHECK_COUNTS (0, delete_list->count); + ri->last_sent_time = rfapi_time (NULL); + + } + } + } + else + { + zlog_debug ("%s: response removal disabled, omitting removals", + __func__); + } + + delete_list->del = (void (*)(void *)) rfapi_info_free; + list_delete (delete_list); + } + + RFAPI_RIB_CHECK_COUNTS (0, 0); + + /* + * Reset pending lists. The final route_unlock_node() will probably + * cause the pending node to be released. + */ + if (slPendPt) + { + skiplist_free (slPendPt); + pn->aggregate = NULL; + route_unlock_node (pn); + } + if (lPendCost) + { + list_delete (lPendCost); + pn->info = NULL; + route_unlock_node (pn); + } + RFAPI_RIB_CHECK_COUNTS (0, 0); + + if (rib_node_started_nonempty) + { + if (!rn->info) + { + RFAPI_RIB_PREFIX_COUNT_DECR (rfd, bgp->rfapi); + } + } + else + { + if (rn->info) + { + RFAPI_RIB_PREFIX_COUNT_INCR (rfd, bgp->rfapi); + } + } + + if (sendingsomeroutes) + rfapiMonitorTimersRestart (rfd, &pn->p); + + route_unlock_node (rn); /* route_node_get() */ + + RFAPI_RIB_CHECK_COUNTS (1, 0); +} + +/* + * regardless of targets, construct a single callback by doing + * only one traversal of the pending RIB + * + * + * Do callback + * + */ +static void +rib_do_callback_onepass (struct rfapi_descriptor *rfd, afi_t afi) +{ + struct bgp *bgp = bgp_get_default (); + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct route_node *rn; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: rfd=%p, afi=%d", __func__, rfd, afi); +#endif + + if (!rfd->rib_pending[afi]) + return; + + assert (bgp->rfapi); + + for (rn = route_top (rfd->rib_pending[afi]); rn; rn = route_next (rn)) + { + process_pending_node (bgp, rfd, afi, rn, &head, &tail); + } + + if (head) + { + rfapi_response_cb_t *f; + +#if DEBUG_NHL + zlog_debug ("%s: response callback NHL follows:", __func__); + rfapiPrintNhl (NULL, head); +#endif + + if (rfd->response_cb) + f = rfd->response_cb; + else + f = bgp->rfapi->rfp_methods.response_cb; + + bgp->rfapi->flags |= RFAPI_INCALLBACK; + zlog_debug ("%s: invoking updated response callback", __func__); + (*f) (head, rfd->cookie); + bgp->rfapi->flags &= ~RFAPI_INCALLBACK; + ++bgp->rfapi->response_updated_count; + } +} + +static wq_item_status +rfapiRibDoQueuedCallback (struct work_queue *wq, void *data) +{ + struct rfapi_descriptor *rfd; + afi_t afi; + uint32_t queued_flag; + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + rfd = ((struct rfapi_updated_responses_queue *) data)->rfd; + afi = ((struct rfapi_updated_responses_queue *) data)->afi; + + /* Make sure the HD wasn't closed after the work item was scheduled */ + if (rfapi_check (rfd)) + return WQ_SUCCESS; + + rib_do_callback_onepass (rfd, afi); + + queued_flag = RFAPI_QUEUED_FLAG (afi); + + UNSET_FLAG (rfd->flags, queued_flag); + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + return WQ_SUCCESS; +} + +static void +rfapiRibQueueItemDelete (struct work_queue *wq, void *data) +{ + XFREE (MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE, data); +} + +static void +updated_responses_queue_init (struct rfapi_descriptor *rfd) +{ + if (rfd->updated_responses_queue) + return; + + rfd->updated_responses_queue = work_queue_new (bm->master, + "rfapi updated responses"); + assert (rfd->updated_responses_queue); + + rfd->updated_responses_queue->spec.workfunc = rfapiRibDoQueuedCallback; + rfd->updated_responses_queue->spec.del_item_data = rfapiRibQueueItemDelete; + rfd->updated_responses_queue->spec.max_retries = 0; + rfd->updated_responses_queue->spec.hold = 1; +} + +/* + * Called when an import table node is modified. Construct a + * new complete nexthop list, sorted by cost (lowest first), + * based on the import table node. + * + * Filter out duplicate nexthops (vn address). There should be + * only one UN address per VN address from the point of view of + * a given import table, so we can probably ignore UN addresses + * while filtering. + * + * Based on rfapiNhlAddNodeRoutes() + */ +void +rfapiRibUpdatePendingNode ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, /* needed for L2 */ + struct route_node *it_node, + uint32_t lifetime) +{ + struct prefix *prefix; + struct bgp_info *bi; + struct route_node *pn; + afi_t afi; + uint32_t queued_flag; + int count = 0; + char buf[BUFSIZ]; + + zlog_debug ("%s: entry", __func__); + + if (CHECK_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_CALLBACK_DISABLE)) + return; + + zlog_debug ("%s: callbacks are not disabled", __func__); + + RFAPI_RIB_CHECK_COUNTS (1, 0); + + prefix = &it_node->p; + afi = family2afi (prefix->family); + prefix2str (prefix, buf, BUFSIZ); + zlog_debug ("%s: prefix=%s", __func__, buf); + + pn = route_node_get (rfd->rib_pending[afi], prefix); + assert (pn); + + zlog_debug ("%s: pn->info=%p, pn->aggregate=%p", __func__, pn->info, + pn->aggregate); + + if (pn->aggregate) + { + /* + * free references into the rfapi_info structures before + * freeing the structures themselves + */ + skiplist_free ((struct skiplist *) (pn->aggregate)); + pn->aggregate = NULL; + route_unlock_node (pn); /* skiplist deleted */ + } + + + /* + * free the rfapi_info structures + */ + if (pn->info) + { + if (pn->info != (void *) 1) + { + list_delete ((struct list *) (pn->info)); + } + pn->info = NULL; + route_unlock_node (pn); /* linklist or 1 deleted */ + } + + /* + * The BIs in the import table are already sorted by cost + */ + for (bi = it_node->info; bi; bi = bi->next) + { + + struct rfapi_info *ri; + struct prefix pfx_nh; + + if (!bi->attr) + { + /* shouldn't happen */ + /* TBD increment error stats counter */ + continue; + } + if (!bi->extra) + { + /* shouldn't happen */ + /* TBD increment error stats counter */ + continue; + } + + rfapiNexthop2Prefix (bi->attr, &pfx_nh); + + /* + * Omit route if nexthop is self + */ + if (CHECK_FLAG + (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) + { + + struct prefix pfx_vn; + + rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn); + if (prefix_same (&pfx_vn, &pfx_nh)) + continue; + } + + ri = rfapi_info_new (); + ri->rk.vn = pfx_nh; + ri->rk.rd = bi->extra->vnc.import.rd; + /* + * If there is an auxiliary IP address (L2 can have it), copy it + */ + if (bi->extra->vnc.import.aux_prefix.family) + { + ri->rk.aux_prefix = bi->extra->vnc.import.aux_prefix; + } + + if (rfapiGetUnAddrOfVpnBi (bi, &ri->un)) + { + rfapi_info_free (ri); + continue; + } + + if (!pn->aggregate) + { + pn->aggregate = skiplist_new (0, rfapi_rib_key_cmp, NULL); + route_lock_node (pn); + } + + /* + * If we have already added this nexthop, the insert will fail. + * Note that the skiplist key is a pointer INTO the rfapi_info + * structure which will be added to the "info" list. + * The skiplist entry VALUE is not used for anything but + * might be useful during debugging. + */ + if (skiplist_insert ((struct skiplist *) pn->aggregate, &ri->rk, ri)) + { + + /* + * duplicate + */ + rfapi_info_free (ri); + continue; + } + + rfapiRibBi2Ri(bi, ri, lifetime); + + if (!pn->info) + { + pn->info = list_new (); + ((struct list *)(pn->info))->del = (void (*)(void *))rfapi_info_free; + route_lock_node (pn); + } + + listnode_add ((struct list *) (pn->info), ri); + } + + if (pn->info) + { + count = ((struct list *) (pn->info))->count; + } + + if (!count) + { + assert (!pn->info); + assert (!pn->aggregate); + pn->info = (void *) 1; /* magic value means this node has no routes */ + route_lock_node (pn); + } + + route_unlock_node (pn); /* route_node_get */ + + queued_flag = RFAPI_QUEUED_FLAG (afi); + + if (!CHECK_FLAG (rfd->flags, queued_flag)) + { + + struct rfapi_updated_responses_queue *urq; + + urq = XCALLOC (MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE, + sizeof (struct rfapi_updated_responses_queue)); + assert (urq); + if (!rfd->updated_responses_queue) + updated_responses_queue_init (rfd); + + SET_FLAG (rfd->flags, queued_flag); + urq->rfd = rfd; + urq->afi = afi; + work_queue_add (rfd->updated_responses_queue, urq); + } + RFAPI_RIB_CHECK_COUNTS (1, 0); +} + +void +rfapiRibUpdatePendingNodeSubtree ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, + struct route_node *it_node, + struct route_node *omit_subtree, /* may be NULL */ + uint32_t lifetime) +{ + if (it_node->l_left && (it_node->l_left != omit_subtree)) + { + if (it_node->l_left->info) + rfapiRibUpdatePendingNode (bgp, rfd, it, it_node->l_left, lifetime); + rfapiRibUpdatePendingNodeSubtree (bgp, rfd, it, it_node->l_left, + omit_subtree, lifetime); + } + + if (it_node->l_right && (it_node->l_right != omit_subtree)) + { + if (it_node->l_right->info) + rfapiRibUpdatePendingNode (bgp, rfd, it, it_node->l_right, lifetime); + rfapiRibUpdatePendingNodeSubtree (bgp, rfd, it, it_node->l_right, + omit_subtree, lifetime); + } +} + +/* + * RETURN VALUE + * + * 0 allow prefix to be included in response + * !0 don't allow prefix to be included in response + */ +int +rfapiRibFTDFilterRecentPrefix( + struct rfapi_descriptor *rfd, + struct route_node *it_rn, /* import table node */ + struct prefix *pfx_target_original) /* query target */ +{ + struct bgp *bgp = rfd->bgp; + afi_t afi = family2afi(it_rn->p.family); + time_t prefix_time; + struct route_node *trn; + + /* + * Not in FTD mode, so allow prefix + */ + if (bgp->rfapi_cfg->rfp_cfg.download_type != RFAPI_RFP_DOWNLOAD_FULL) + return 0; + + /* + * TBD + * This matches behavior of now-obsolete rfapiRibFTDFilterRecent(), + * but we need to decide if that is correct. + */ + if (it_rn->p.family == AF_ETHERNET) + return 0; + +#if DEBUG_FTD_FILTER_RECENT + { + char buf_pfx[BUFSIZ]; + + prefix2str(&it_rn->p, buf_pfx, BUFSIZ); + zlog_debug("%s: prefix %s", __func__, buf_pfx); + } +#endif + + /* + * prefix covers target address, so allow prefix + */ + if (prefix_match (&it_rn->p, pfx_target_original)) + { +#if DEBUG_FTD_FILTER_RECENT + zlog_debug("%s: prefix covers target, allowed", __func__); +#endif + return 0; + } + + /* + * check this NVE's timestamp for this prefix + */ + trn = route_node_get (rfd->rsp_times[afi], &it_rn->p); /* locks trn */ + prefix_time = (time_t) trn->info; + if (trn->lock > 1) + route_unlock_node (trn); + +#if DEBUG_FTD_FILTER_RECENT + zlog_debug("%s: last sent time %lu, last allowed time %lu", + __func__, prefix_time, rfd->ftd_last_allowed_time); +#endif + + /* + * haven't sent this prefix, which doesn't cover target address, + * to NVE since ftd_advertisement_interval, so OK to send now. + */ + if (prefix_time <= rfd->ftd_last_allowed_time) + return 0; + + return 1; +} + +/* + * Call when rfapi returns from rfapi_query() so the RIB reflects + * the routes sent to the NVE before the first updated response + * + * Also: remove duplicates from response. Caller should use returned + * value of nexthop chain. + */ +struct rfapi_next_hop_entry * +rfapiRibPreload ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_next_hop_entry *response, + int use_eth_resolution) +{ + struct rfapi_next_hop_entry *nhp; + struct rfapi_next_hop_entry *nhp_next; + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + time_t new_last_sent_time; + + zlog_debug ("%s: loading response=%p, use_eth_resolution=%d", + __func__, response, use_eth_resolution); + + new_last_sent_time = rfapi_time (NULL); + + for (nhp = response; nhp; nhp = nhp_next) + { + + struct prefix pfx; + struct rfapi_rib_key rk; + afi_t afi; + struct rfapi_info *ri; + int need_insert; + struct route_node *rn; + int rib_node_started_nonempty = 0; + struct route_node *trn; + int allowed = 0; + + /* save in case we delete nhp */ + nhp_next = nhp->next; + + if (nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME) + { + /* + * weird, shouldn't happen + */ + zlog_debug + ("%s: got nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME", + __func__); + continue; + } + + + if (use_eth_resolution) + { + /* get the prefix of the ethernet address in the L2 option */ + struct rfapi_l2address_option *pL2o; + struct rfapi_vn_option *vo; + + /* + * Look for VN option of type RFAPI_VN_OPTION_TYPE_L2ADDR + */ + for (pL2o = NULL, vo = nhp->vn_options; vo; vo = vo->next) + { + if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) + { + pL2o = &vo->v.l2addr; + break; + } + } + + if (!pL2o) + { + /* + * not supposed to happen + */ + zlog_debug ("%s: missing L2 info", __func__); + continue; + } + + afi = AFI_ETHER; + rfapiL2o2Qprefix (pL2o, &pfx); + } + else + { + rfapiRprefix2Qprefix (&nhp->prefix, &pfx); + afi = family2afi (pfx.family); + } + + /* + * TBD for ethernet, rib must know the right way to distinguish + * duplicate routes + * + * Current approach: prefix is key to radix tree; then + * each prefix has a set of routes with unique VN addrs + */ + + /* + * Look up prefix in RIB + */ + rn = route_node_get (rfd->rib[afi], &pfx); /* locks rn */ + + if (rn->info) + { + rib_node_started_nonempty = 1; + } + else + { + rn->info = skiplist_new (0, rfapi_rib_key_cmp, NULL); + route_lock_node (rn); + } + + /* + * Look up route at prefix + */ + need_insert = 0; + memset ((void *) &rk, 0, sizeof (rk)); + assert (!rfapiRaddr2Qprefix (&nhp->vn_address, &rk.vn)); + + if (use_eth_resolution) + { + /* copy what came from aux_prefix to rk.aux_prefix */ + rfapiRprefix2Qprefix (&nhp->prefix, &rk.aux_prefix); + if (RFAPI_0_PREFIX (&rk.aux_prefix) + && RFAPI_HOST_PREFIX (&rk.aux_prefix)) + { + /* mark as "none" if nhp->prefix is 0/32 or 0/128 */ + rk.aux_prefix.family = 0; + } + } + +#if DEBUG_NHL + { + char str_vn[BUFSIZ]; + char str_aux_prefix[BUFSIZ]; + + str_vn[0] = 0; + str_aux_prefix[0] = 0; + + prefix2str (&rk.vn, str_vn, BUFSIZ); + prefix2str (&rk.aux_prefix, str_aux_prefix, BUFSIZ); + + if (!rk.aux_prefix.family) + { + + } + zlog_debug ("%s: rk.vn=%s rk.aux_prefix=%s", + __func__, str_vn, + (rk.aux_prefix.family ? str_aux_prefix : "-")); + } + zlog_debug ("%s: RIB skiplist for this prefix follows", __func__); + rfapiRibShowRibSl (NULL, &rn->p, (struct skiplist *) rn->info); +#endif + + + if (!skiplist_search ((struct skiplist *) rn->info, &rk, (void **) &ri)) + { + /* + * Already have this route; make values match + */ + rfapiFreeRfapiUnOptionChain (ri->un_options); + ri->un_options = NULL; + rfapiFreeRfapiVnOptionChain (ri->vn_options); + ri->vn_options = NULL; + +#if DEBUG_NHL + zlog_debug ("%s: found in RIB", __func__); +#endif + + /* + * Filter duplicate routes from initial response. + * Check timestamps to avoid wraparound problems + */ + if ((ri->rsp_counter != rfd->rsp_counter) || + (ri->last_sent_time != new_last_sent_time)) + { + +#if DEBUG_NHL + zlog_debug ("%s: allowed due to counter/timestamp diff", + __func__); +#endif + allowed = 1; + } + + } + else + { + +#if DEBUG_NHL + zlog_debug ("%s: allowed due to not yet in RIB", __func__); +#endif + /* not found: add new route to RIB */ + ri = rfapi_info_new (); + need_insert = 1; + allowed = 1; + } + + ri->rk = rk; + assert (!rfapiRaddr2Qprefix (&nhp->un_address, &ri->un)); + ri->cost = nhp->prefix.cost; + ri->lifetime = nhp->lifetime; + ri->vn_options = rfapiVnOptionsDup (nhp->vn_options); + ri->rsp_counter = rfd->rsp_counter; + ri->last_sent_time = rfapi_time (NULL); + + if (need_insert) + { + int rc; + rc = skiplist_insert ((struct skiplist *) rn->info, &ri->rk, ri); + assert (!rc); + } + + if (!rib_node_started_nonempty) + { + RFAPI_RIB_PREFIX_COUNT_INCR (rfd, bgp->rfapi); + } + + RFAPI_RIB_CHECK_COUNTS (0, 0); + rfapiRibStartTimer (rfd, ri, rn, 0); + RFAPI_RIB_CHECK_COUNTS (0, 0); + + route_unlock_node (rn); + + /* + * update this NVE's timestamp for this prefix + */ + trn = route_node_get (rfd->rsp_times[afi], &pfx); /* locks trn */ + trn->info = (void *) (uintptr_t) bgp_clock (); + if (trn->lock > 1) + route_unlock_node (trn); + + { + char str_pfx[BUFSIZ]; + char str_pfx_vn[BUFSIZ]; + + prefix2str (&pfx, str_pfx, BUFSIZ); + prefix2str (&rk.vn, str_pfx_vn, BUFSIZ); + zlog_debug + ("%s: added pfx=%s nh[vn]=%s, cost=%u, lifetime=%u, allowed=%d", + __func__, str_pfx, str_pfx_vn, nhp->prefix.cost, nhp->lifetime, + allowed); + } + + if (allowed) + { + if (tail) + (tail)->next = nhp; + tail = nhp; + if (!head) + { + head = nhp; + } + } + else + { + rfapi_un_options_free (nhp->un_options); + nhp->un_options = NULL; + rfapi_vn_options_free (nhp->vn_options); + nhp->vn_options = NULL; + + XFREE (MTYPE_RFAPI_NEXTHOP, nhp); + nhp = NULL; + } + } + + if (tail) + tail->next = NULL; + return head; +} + +void +rfapiRibPendingDeleteRoute ( + struct bgp *bgp, + struct rfapi_import_table *it, + afi_t afi, + struct route_node *it_node) +{ + struct rfapi_descriptor *rfd; + struct listnode *node; + char buf[BUFSIZ]; + + prefix2str (&it_node->p, buf, BUFSIZ); + zlog_debug ("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%s", + __func__, it, afi, it_node, buf); + + if (AFI_ETHER == afi) + { + /* + * ethernet import tables are per-LNI and each ethernet monitor + * identifies the rfd that owns it. + */ + struct rfapi_monitor_eth *m; + struct route_node *rn; + struct skiplist *sl; + void *cursor; + int rc; + + /* + * route-specific monitors + */ + if ((sl = RFAPI_MONITOR_ETH (it_node))) + { + + zlog_debug ("%s: route-specific skiplist: %p", __func__, sl); + + for (cursor = NULL, rc = + skiplist_next (sl, NULL, (void **) &m, (void **) &cursor); !rc; + rc = skiplist_next (sl, NULL, (void **) &m, (void **) &cursor)) + { + +#if DEBUG_PENDING_DELETE_ROUTE + zlog_debug ("%s: eth monitor rfd=%p", __func__, m->rfd); +#endif + /* + * If we have already sent a route with this prefix to this + * NVE, it's OK to send an update with the delete + */ + if ((rn = route_node_lookup (m->rfd->rib[afi], &it_node->p))) + { + rfapiRibUpdatePendingNode (bgp, m->rfd, it, it_node, + m->rfd->response_lifetime); + route_unlock_node (rn); + } + } + } + + /* + * all-routes/FTD monitors + */ + for (m = it->eth0_queries; m; m = m->next) + { +#if DEBUG_PENDING_DELETE_ROUTE + zlog_debug ("%s: eth0 monitor rfd=%p", __func__, m->rfd); +#endif + /* + * If we have already sent a route with this prefix to this + * NVE, it's OK to send an update with the delete + */ + if ((rn = route_node_lookup (m->rfd->rib[afi], &it_node->p))) + { + rfapiRibUpdatePendingNode (bgp, m->rfd, it, it_node, + m->rfd->response_lifetime); + } + } + + } + else + { + /* + * Find RFDs that reference this import table + */ + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd)) + { + + struct route_node *rn; + + zlog_debug ("%s: comparing rfd(%p)->import_table=%p to it=%p", + __func__, rfd, rfd->import_table, it); + + if (rfd->import_table != it) + continue; + + zlog_debug ("%s: matched rfd %p", __func__, rfd); + + /* + * If we have sent a response to this NVE with this prefix + * previously, we should send an updated response. + */ + if ((rn = route_node_lookup (rfd->rib[afi], &it_node->p))) + { + rfapiRibUpdatePendingNode (bgp, rfd, it, it_node, + rfd->response_lifetime); + route_unlock_node (rn); + } + } + } +} + +void +rfapiRibShowResponsesSummary (void *stream) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + struct bgp *bgp = bgp_get_default (); + + int nves = 0; + int nves_with_nonempty_ribs = 0; + struct rfapi_descriptor *rfd; + struct listnode *node; + + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp (out, "%-24s ", "Responses: (Prefixes)"); + fp (out, "%-8s %-8u ", "Active:", bgp->rfapi->rib_prefix_count_total); + fp (out, "%-8s %-8u", "Maximum:", bgp->rfapi->rib_prefix_count_total_max); + fp (out, "%s", VTY_NEWLINE); + + fp (out, "%-24s ", " (Updated)"); + fp (out, "%-8s %-8u ", "Update:", + bgp->rfapi->stat.count_updated_response_updates); + fp (out, "%-8s %-8u", "Remove:", + bgp->rfapi->stat.count_updated_response_deletes); + fp (out, "%-8s %-8u", "Total:", + bgp->rfapi->stat.count_updated_response_updates + + bgp->rfapi->stat.count_updated_response_deletes); + fp (out, "%s", VTY_NEWLINE); + + fp (out, "%-24s ", " (NVEs)"); + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd)) + { + ++nves; + if (rfd->rib_prefix_count) + ++nves_with_nonempty_ribs; + } + fp (out, "%-8s %-8u ", "Active:", nves_with_nonempty_ribs); + fp (out, "%-8s %-8u", "Total:", nves); + fp (out, "%s", VTY_NEWLINE); + +} + +void +rfapiRibShowResponsesSummaryClear (void) +{ + struct bgp *bgp = bgp_get_default (); + + bgp->rfapi->rib_prefix_count_total_max = bgp->rfapi->rib_prefix_count_total; +} + +static int +print_rib_sl ( + int (*fp) (void *, const char *, ...), + struct vty *vty, + void *out, + struct skiplist *sl, + int deleted, + char *str_pfx, + int *printedprefix) +{ + struct rfapi_info *ri; + int rc; + void *cursor; + int routes_displayed = 0; + + cursor = NULL; + for (rc = skiplist_next (sl, NULL, (void **) &ri, &cursor); + !rc; rc = skiplist_next (sl, NULL, (void **) &ri, &cursor)) + { + + char str_vn[BUFSIZ]; + char str_un[BUFSIZ]; + char str_lifetime[BUFSIZ]; + char str_age[BUFSIZ]; + char *p; + char str_rd[BUFSIZ]; + + ++routes_displayed; + + prefix2str (&ri->rk.vn, str_vn, BUFSIZ); + p = index (str_vn, '/'); + if (p) + *p = 0; + + prefix2str (&ri->un, str_un, BUFSIZ); + p = index (str_un, '/'); + if (p) + *p = 0; + + rfapiFormatSeconds (ri->lifetime, str_lifetime, BUFSIZ); +#if RFAPI_REGISTRATIONS_REPORT_AGE + rfapiFormatAge (ri->last_sent_time, str_age, BUFSIZ); +#else + { + time_t now = rfapi_time (NULL); + time_t expire = ri->last_sent_time + (time_t) ri->lifetime; + /* allow for delayed/async removal */ + rfapiFormatSeconds ((expire > now ? expire - now : 1), + str_age, BUFSIZ); + } +#endif + + str_rd[0] = 0; /* start empty */ +#if DEBUG_RIB_SL_RD + str_rd[0] = ' '; + prefix_rd2str(&ri->rk.rd, str_rd+1, BUFSIZ-1); +#endif + + fp (out, " %c %-20s %-15s %-15s %-4u %-8s %-8s%s%s", + deleted ? 'r' : ' ', + *printedprefix ? "" : str_pfx, + str_vn, str_un, ri->cost, str_lifetime, str_age, str_rd, VTY_NEWLINE); + + if (!*printedprefix) + *printedprefix = 1; + } + return routes_displayed; +} + +#if DEBUG_NHL +/* + * This one is for debugging (set stream to NULL to send output to log) + */ +static void +rfapiRibShowRibSl (void *stream, struct prefix *pfx, struct skiplist *sl) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + int nhs_displayed = 0; + char str_pfx[BUFSIZ]; + int printedprefix = 0; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + prefix2str (pfx, str_pfx, BUFSIZ); + + nhs_displayed += print_rib_sl (fp, vty, out, sl, + 0, str_pfx, &printedprefix); +} +#endif + +void +rfapiRibShowResponses ( + void *stream, + struct prefix *pfx_match, + int show_removed) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + struct rfapi_descriptor *rfd; + struct listnode *node; + + struct bgp *bgp = bgp_get_default (); + int printedheader = 0; + int routes_total = 0; + int nhs_total = 0; + int prefixes_total = 0; + int prefixes_displayed = 0; + int nves_total = 0; + int nves_with_routes = 0; + int nves_displayed = 0; + int routes_displayed = 0; + int nhs_displayed = 0; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + /* + * loop over NVEs + */ + for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd)) + { + + int printednve = 0; + afi_t afi; + + ++nves_total; + if (rfd->rib_prefix_count) + ++nves_with_routes; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct route_node *rn; + + if (!rfd->rib[afi]) + continue; + + for (rn = route_top (rfd->rib[afi]); rn; rn = route_next (rn)) + { + + struct skiplist *sl; + char str_pfx[BUFSIZ]; + int printedprefix = 0; + + if (!show_removed) + sl = rn->info; + else + sl = rn->aggregate; + + if (!sl) + continue; + + routes_total++; + nhs_total += skiplist_count (sl); + ++prefixes_total; + + if (pfx_match && !prefix_match (pfx_match, &rn->p) && + !prefix_match (&rn->p, pfx_match)) + continue; + + ++prefixes_displayed; + + if (!printedheader) + { + ++printedheader; + + fp (out, "%s[%s]%s", + VTY_NEWLINE, + show_removed ? "Removed" : "Active", VTY_NEWLINE); + fp (out, "%-15s %-15s%s", "Querying VN", "Querying UN", + VTY_NEWLINE); + fp (out, " %-20s %-15s %-15s %4s %-8s %-8s%s", + "Prefix", "Registered VN", "Registered UN", "Cost", + "Lifetime", +#if RFAPI_REGISTRATIONS_REPORT_AGE + "Age", +#else + "Remaining", +#endif + VTY_NEWLINE); + } + if (!printednve) + { + char str_vn[BUFSIZ]; + char str_un[BUFSIZ]; + + ++printednve; + ++nves_displayed; + + fp (out, "%-15s %-15s%s", + rfapiRfapiIpAddr2Str (&rfd->vn_addr, str_vn, BUFSIZ), + rfapiRfapiIpAddr2Str (&rfd->un_addr, str_un, BUFSIZ), + VTY_NEWLINE); + + } + prefix2str (&rn->p, str_pfx, BUFSIZ); + //fp(out, " %s%s", buf, VTY_NEWLINE); /* prefix */ + + routes_displayed++; + nhs_displayed += print_rib_sl (fp, vty, out, sl, + show_removed, str_pfx, + &printedprefix); + } + } + } + + if (routes_total) + { + fp (out, "%s", VTY_NEWLINE); + fp (out, "Displayed %u NVEs, and %u out of %u %s prefixes", + nves_displayed, routes_displayed, + routes_total, show_removed ? "removed" : "active"); + if (nhs_displayed != routes_displayed || nhs_total != routes_total) + fp (out, " with %u out of %u next hops", nhs_displayed, nhs_total); + fp (out, "%s", VTY_NEWLINE); + } +} diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h new file mode 100644 index 0000000000..2a111946f7 --- /dev/null +++ b/bgpd/rfapi/rfapi_rib.h @@ -0,0 +1,154 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: rfapi_rib.h + * Purpose: per-nve rib + */ + +#ifndef QUAGGA_HGP_RFAPI_RIB_H +#define QUAGGA_HGP_RFAPI_RIB_H + +/* + * Key for indexing RIB and Pending RIB skiplists. For L3 RIBs, + * the VN address is sufficient because it represents the actual next hop. + * + * For L2 RIBs, it is possible to have multiple routes to a given L2 + * prefix via a given VN address, but each route having a unique aux_prefix. + */ +struct rfapi_rib_key +{ + struct prefix vn; + struct prefix_rd rd; + + /* + * for L2 routes: optional IP addr + * .family == 0 means "none" + */ + struct prefix aux_prefix; +}; + +struct rfapi_info +{ + struct rfapi_rib_key rk; /* NVE VN addr + aux addr */ + struct prefix un; + uint8_t cost; + uint32_t lifetime; + time_t last_sent_time; + uint32_t rsp_counter; /* dedup initial responses */ + struct bgp_tea_options *tea_options; + struct rfapi_un_option *un_options; + struct rfapi_vn_option *vn_options; + void *timer; +}; + +/* + * Work item for updated responses queue + */ +struct rfapi_updated_responses_queue +{ + struct rfapi_descriptor *rfd; + afi_t afi; +}; + + +extern void +rfapiRibClear (struct rfapi_descriptor *rfd); + +extern void +rfapiRibFree (struct rfapi_descriptor *rfd); + +extern void +rfapiRibUpdatePendingNode ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, + struct route_node *it_node, + uint32_t lifetime); + +extern void +rfapiRibUpdatePendingNodeSubtree ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, + struct route_node *it_node, + struct route_node *omit_subtree, + uint32_t lifetime); + +extern int +rfapiRibPreloadBi( + struct route_node *rfd_rib_node, + struct prefix *pfx_vn, + struct prefix *pfx_un, + uint32_t lifetime, + struct bgp_info *bi); + +extern struct rfapi_next_hop_entry * +rfapiRibPreload ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_next_hop_entry *response, + int use_eth_resolution); + +extern void +rfapiRibPendingDeleteRoute ( + struct bgp *bgp, + struct rfapi_import_table *it, + afi_t afi, + struct route_node *it_node); + +extern void +rfapiRibShowResponsesSummary (void *stream); + +extern void +rfapiRibShowResponsesSummaryClear (void); + +extern void +rfapiRibShowResponses ( + void *stream, + struct prefix *pfx_match, + int show_removed); + +extern int +rfapiRibFTDFilterRecentPrefix( + struct rfapi_descriptor *rfd, + struct route_node *it_rn, /* import table node */ + struct prefix *pfx_target_original); /* query target */ + +extern void +rfapiFreeRfapiUnOptionChain (struct rfapi_un_option *p); + +extern void +rfapiFreeRfapiVnOptionChain (struct rfapi_vn_option *p); + +extern void +rfapiRibCheckCounts ( + int checkstats, /* validate rfd & global counts */ + unsigned int offset); /* number of ri's held separately */ + +/* enable for debugging; disable for performance */ +#if 0 +#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset) rfapiRibCheckCounts(checkstats, offset) +#else +#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset) +#endif + +#endif /* QUAGGA_HGP_RFAPI_RIB_H */ diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c new file mode 100644 index 0000000000..315bac4fe6 --- /dev/null +++ b/bgpd/rfapi/rfapi_vty.c @@ -0,0 +1,5025 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "routemap.h" +#include "log.h" +#include "linklist.h" +#include "command.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_mplsvpn.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgp_route.h" +#include "bgp_aspath.h" +#include "bgp_community.h" +#include "bgp_vnc_types.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_rib.h" +#include "rfapi_vty.h" +#include "rfapi_ap.h" +#include "rfapi_encap_tlv.h" +#include "vnc_debug.h" + +#define DEBUG_L2_EXTRA 0 + +#define VNC_SHOW_STR "VNC information\n" + +/* format related utilies */ + + +#define FMT_MIN 60 /* seconds */ +#define FMT_HOUR (60 * FMT_MIN) +#define FMT_DAY (24 * FMT_HOUR) +#define FMT_YEAR (365 * FMT_DAY) + +char * +rfapiFormatSeconds (uint32_t seconds, char *buf, size_t len) +{ + int year, day, hour, min; + + if (seconds >= FMT_YEAR) + { + year = seconds / FMT_YEAR; + seconds -= year * FMT_YEAR; + } + else + year = 0; + + if (seconds >= FMT_DAY) + { + day = seconds / FMT_DAY; + seconds -= day * FMT_DAY; + } + else + day = 0; + + if (seconds >= FMT_HOUR) + { + hour = seconds / FMT_HOUR; + seconds -= hour * FMT_HOUR; + } + else + hour = 0; + + if (seconds >= FMT_MIN) + { + min = seconds / FMT_MIN; + seconds -= min * FMT_MIN; + } + else + min = 0; + + if (year > 0) + { + snprintf (buf, len, "%dy%dd%dh", year, day, hour); + } + else if (day > 0) + { + snprintf (buf, len, "%dd%dh%dm", day, hour, min); + } + else + { + snprintf (buf, len, "%02d:%02d:%02d", hour, min, seconds); + } + + return buf; +} + +char * +rfapiFormatAge (time_t age, char *buf, size_t len) +{ + time_t now, age_adjusted; + + now = rfapi_time (NULL); + age_adjusted = now - age; + + return rfapiFormatSeconds (age_adjusted, buf, len); +} + + +/* + * Reimplementation of quagga/lib/prefix.c function, but + * for RFAPI-style prefixes + */ +void +rfapiRprefixApplyMask (struct rfapi_ip_prefix *rprefix) +{ + uint8_t *pnt; + int index; + int offset; + + static uint8_t maskbit[] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + + switch (rprefix->prefix.addr_family) + { + case AF_INET: + index = rprefix->length / 8; + if (index < 4) + { + pnt = (uint8_t *) & rprefix->prefix.addr.v4; + offset = rprefix->length % 8; + pnt[index] &= maskbit[offset]; + index++; + while (index < 4) + pnt[index++] = 0; + } + break; + + case AF_INET6: + index = rprefix->length / 8; + if (index < 16) + { + pnt = (uint8_t *) & rprefix->prefix.addr.v6; + offset = rprefix->length % 8; + pnt[index] &= maskbit[offset]; + index++; + while (index < 16) + pnt[index++] = 0; + } + break; + + default: + assert (0); + } +} + +/* + * translate a quagga prefix into a rfapi IP address. The + * prefix is REQUIRED to be 32 bits for IPv4 and 128 bits for IPv6 + * + * RETURNS: + * + * 0 Success + * <0 Error + */ +int +rfapiQprefix2Raddr (struct prefix *qprefix, struct rfapi_ip_addr *raddr) +{ + memset (raddr, 0, sizeof (struct rfapi_ip_addr)); + raddr->addr_family = qprefix->family; + switch (qprefix->family) + { + case AF_INET: + if (qprefix->prefixlen != 32) + return -1; + raddr->addr.v4 = qprefix->u.prefix4; + break; + case AF_INET6: + if (qprefix->prefixlen != 128) + return -1; + raddr->addr.v6 = qprefix->u.prefix6; + break; + default: + return -1; + } + return 0; +} + +/* + * Translate Quagga prefix to RFAPI prefix + */ +/* rprefix->cost set to 0 */ +void +rfapiQprefix2Rprefix (struct prefix *qprefix, struct rfapi_ip_prefix *rprefix) +{ + memset (rprefix, 0, sizeof (struct rfapi_ip_prefix)); + rprefix->length = qprefix->prefixlen; + rprefix->prefix.addr_family = qprefix->family; + switch (qprefix->family) + { + case AF_INET: + rprefix->prefix.addr.v4 = qprefix->u.prefix4; + break; + case AF_INET6: + rprefix->prefix.addr.v6 = qprefix->u.prefix6; + break; + default: + assert (0); + } +} + +int +rfapiRprefix2Qprefix (struct rfapi_ip_prefix *rprefix, struct prefix *qprefix) +{ + memset (qprefix, 0, sizeof (struct prefix)); + qprefix->prefixlen = rprefix->length; + qprefix->family = rprefix->prefix.addr_family; + + switch (rprefix->prefix.addr_family) + { + case AF_INET: + qprefix->u.prefix4 = rprefix->prefix.addr.v4; + break; + case AF_INET6: + qprefix->u.prefix6 = rprefix->prefix.addr.v6; + break; + default: + return EAFNOSUPPORT; + } + return 0; +} + +/* + * returns 1 if prefixes have same addr family, prefix len, and address + * Note that host bits matter in this comparison! + * + * For paralellism with quagga/lib/prefix.c. if we need a comparison + * where host bits are ignored, call that function rfapiRprefixCmp. + */ +int +rfapiRprefixSame (struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2) +{ + if (hp1->prefix.addr_family != hp2->prefix.addr_family) + return 0; + if (hp1->length != hp2->length) + return 0; + if (hp1->prefix.addr_family == AF_INET) + if (IPV4_ADDR_SAME (&hp1->prefix.addr.v4, &hp2->prefix.addr.v4)) + return 1; + if (hp1->prefix.addr_family == AF_INET6) + if (IPV6_ADDR_SAME (&hp1->prefix.addr.v6, &hp2->prefix.addr.v6)) + return 1; + return 0; +} + +int +rfapiRaddr2Qprefix (struct rfapi_ip_addr *hia, struct prefix *pfx) +{ + memset (pfx, 0, sizeof (struct prefix)); + pfx->family = hia->addr_family; + + switch (hia->addr_family) + { + case AF_INET: + pfx->prefixlen = 32; + pfx->u.prefix4 = hia->addr.v4; + break; + case AF_INET6: + pfx->prefixlen = 128; + pfx->u.prefix6 = hia->addr.v6; + break; + default: + return EAFNOSUPPORT; + } + return 0; +} + +void +rfapiL2o2Qprefix (struct rfapi_l2address_option *l2o, struct prefix *pfx) +{ + memset (pfx, 0, sizeof (struct prefix)); + pfx->family = AF_ETHERNET; + pfx->prefixlen = 48; + pfx->u.prefix_eth = l2o->macaddr; +} + +char * +rfapiEthAddr2Str (const struct ethaddr *ea, char *buf, int bufsize) +{ + int i; + char *p = buf; + + assert (bufsize > (3 * ETHER_ADDR_LEN)); + + for (i = 0; i <= ETHER_ADDR_LEN; ++i) + { + sprintf (p, "%02x", ea->octet[i]); + if (i < (ETHER_ADDR_LEN - 1)) + *(p + 2) = ':'; + p += 3; + } + return buf; +} + +int +rfapiStr2EthAddr (const char *str, struct ethaddr *ea) +{ + unsigned int a[6]; + int i; + + if (sscanf (str, "%2x:%2x:%2x:%2x:%2x:%2x", + a + 0, a + 1, a + 2, a + 3, a + 4, a + 5) != 6) + { + + return EINVAL; + } + + for (i = 0; i < 6; ++i) + ea->octet[i] = a[i] & 0xff; + + return 0; +} + +const char * +rfapi_ntop (int af, const void *src, char *buf, socklen_t size) +{ + if (af == AF_ETHERNET) + { + return rfapiEthAddr2Str ((const struct ethaddr *) src, buf, size); + } + + return inet_ntop (af, src, buf, size); +} + +int +rfapiDebugPrintf (void *dummy, const char *format, ...) +{ + va_list args; + va_start (args, format); + vzlog (NULL, LOG_DEBUG, format, args); + va_end (args); + return 0; +} + +static int +rfapiStdioPrintf (void *stream, const char *format, ...) +{ + FILE *file = NULL; + + va_list args; + va_start (args, format); + + switch ((uintptr_t) stream) + { + case 1: + file = stdout; + break; + case 2: + file = stderr; + break; + default: + assert (0); + } + + vfprintf (file, format, args); + va_end (args); + return 0; +} + +/* Fake out for debug logging */ +static struct vty vty_dummy_zlog; +static struct vty vty_dummy_stdio; +#define HVTY_NEWLINE ((vty == &vty_dummy_zlog)? "": VTY_NEWLINE) + +static const char * +str_vty_newline (struct vty *vty) +{ + if (vty == &vty_dummy_zlog) + return ""; + return VTY_NEWLINE; +} + +int +rfapiStream2Vty ( + void *stream, /* input */ + int (**fp) (void *, const char *, ...), /* output */ + struct vty **vty, /* output */ + void **outstream, /* output */ + const char **vty_newline) /* output */ +{ + + if (!stream) + { + vty_dummy_zlog.type = VTY_SHELL; /* for VTY_NEWLINE */ + *vty = &vty_dummy_zlog; + *fp = (int (*)(void *, const char *,...)) rfapiDebugPrintf; + *outstream = NULL; + *vty_newline = str_vty_newline (*vty); + return (vzlog_test (NULL, LOG_DEBUG)); + } + + if (((uintptr_t) stream == (uintptr_t) 1) || + ((uintptr_t) stream == (uintptr_t) 2)) + { + + vty_dummy_stdio.type = VTY_SHELL; /* for VTY_NEWLINE */ + *vty = &vty_dummy_stdio; + *fp = (int (*)(void *, const char *,...)) rfapiStdioPrintf; + *outstream = stream; + *vty_newline = str_vty_newline (*vty); + return 1; + } + + if (stream) + { + *vty = stream; /* VTY_NEWLINE requires vty to be legit */ + *fp = (int (*)(void *, const char *,...)) vty_out; + *outstream = stream; + *vty_newline = str_vty_newline (*vty); + return 1; + } + + return 0; +} + +/* called from bgpd/bgp_vty.c'route_vty_out() */ +void +rfapi_vty_out_vncinfo ( + struct vty *vty, + struct prefix *p, + struct bgp_info *bi, + safi_t safi) +{ + char *s; + uint32_t lifetime; + + /* + * Print, on an indented line: + * UN address [if VPN route and VNC UN addr subtlv] + * EC list + * VNC lifetime + */ + vty_out (vty, " "); + + if (safi == SAFI_MPLS_VPN) + { + struct prefix pfx_un; + + if (!rfapiGetVncTunnelUnAddr (bi->attr, &pfx_un)) + { + char buf[BUFSIZ]; + vty_out (vty, "UN=%s", inet_ntop (pfx_un.family, + pfx_un.u.val, buf, BUFSIZ)); + } + } + + if (bi->attr && bi->attr->extra && bi->attr->extra->ecommunity) + { + s = ecommunity_ecom2str (bi->attr->extra->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " EC{%s}", s); + XFREE (MTYPE_ECOMMUNITY_STR, s); + } + + if (bi->extra != NULL && bi->extra->tag != NULL) + vty_out (vty, " label=%u", decode_label (bi->extra->tag)); + + if (rfapiGetVncLifetime (bi->attr, &lifetime)) + { + if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) + { + vty_out (vty, " life=none"); + } + } + else + { + vty_out (vty, " life=%d", lifetime); + } + + vty_out (vty, " type=%s, subtype=%d", + zebra_route_string (bi->type), bi->sub_type); + + vty_out (vty, "%s", HVTY_NEWLINE); +} + +void +rfapiPrintAttrPtrs (void *stream, struct attr *attr) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + struct attr_extra *ae; + char buf[BUFSIZ]; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp (out, "Attr[%p]:%s", attr, HVTY_NEWLINE); + if (!attr) + return; + + /* IPv4 Nexthop */ + inet_ntop (AF_INET, &attr->nexthop, buf, BUFSIZ); + fp (out, " nexthop=%s%s", buf, HVTY_NEWLINE); + + fp (out, " aspath=%p, refcnt=%d%s", attr->aspath, + (attr->aspath ? attr->aspath->refcnt : 0), HVTY_NEWLINE); + fp (out, " community=%p, refcnt=%d%s", attr->community, + (attr->community ? attr->community->refcnt : 0), HVTY_NEWLINE); + + if ((ae = attr->extra)) + { + fp (out, " ecommunity=%p, refcnt=%d%s", ae->ecommunity, + (ae->ecommunity ? ae->ecommunity->refcnt : 0), HVTY_NEWLINE); + fp (out, " cluster=%p, refcnt=%d%s", ae->cluster, + (ae->cluster ? ae->cluster->refcnt : 0), HVTY_NEWLINE); + fp (out, " transit=%p, refcnt=%d%s", ae->transit, + (ae->transit ? ae->transit->refcnt : 0), HVTY_NEWLINE); + } +} + +/* + * Print BI in an Import Table + */ +void +rfapiPrintBi (void *stream, struct bgp_info *bi) +{ + char buf[BUFSIZ]; + char *s; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + char line[BUFSIZ]; + char *p = line; + int r; + int has_macaddr = 0; + struct ethaddr macaddr; + struct rfapi_l2address_option l2o_buf; + uint8_t l2hid; /* valid if has_macaddr */ + +#define REMAIN (BUFSIZ - (p-line)) +#define INCP {p += (r > REMAIN)? REMAIN: r;} + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + if (!bi) + return; + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && bi->extra + && bi->extra->vnc.import.timer) + { + struct thread *t = (struct thread *) bi->extra->vnc.import.timer; + r = snprintf (p, REMAIN, " [%4lu] ", thread_timer_remain_second (t)); + INCP; + + } + else + { + r = snprintf (p, REMAIN, " "); + INCP; + } + + if (bi->extra) + { + /* TBD This valid only for SAFI_MPLS_VPN, but not for encap */ + if (decode_rd_type(bi->extra->vnc.import.rd.val) == RD_TYPE_VNC_ETH) + { + has_macaddr = 1; + memcpy (macaddr.octet, bi->extra->vnc.import.rd.val + 2, 6); + l2hid = bi->extra->vnc.import.rd.val[1]; + } + } + + /* + * Print these items: + * type/subtype + * nexthop address + * lifetime + * RFP option sizes (they are opaque values) + * extended communities (RTs) + */ + if (bi->attr && bi->attr->extra) + { + uint32_t lifetime; + int printed_1st_gol = 0; + struct bgp_attr_encap_subtlv *pEncap; + struct prefix pfx_un; + int af = BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len); + + /* Nexthop */ + if (af == AF_INET) + { + r = snprintf (p, REMAIN, "%s", inet_ntop (AF_INET, + &bi->attr->extra->mp_nexthop_global_in, + buf, BUFSIZ)); + INCP; + } + else if (af == AF_INET6) + { + r = snprintf (p, REMAIN, "%s", inet_ntop (AF_INET6, + &bi->attr->extra->mp_nexthop_global, + buf, BUFSIZ)); + INCP; + } + else + { + r = snprintf (p, REMAIN, "?"); + INCP; + } + + /* + * VNC tunnel subtlv, if present, contains UN address + */ + if (!rfapiGetVncTunnelUnAddr (bi->attr, &pfx_un)) + { + r = snprintf (p, REMAIN, " un=%s", inet_ntop (pfx_un.family, + pfx_un.u.val, buf, + BUFSIZ)); + INCP; + + } + + /* Lifetime */ + if (rfapiGetVncLifetime (bi->attr, &lifetime)) + { + r = snprintf (p, REMAIN, " nolife"); + INCP; + } + else + { + if (lifetime == 0xffffffff) + r = snprintf (p, REMAIN, " %6s", "infini"); + else + r = snprintf (p, REMAIN, " %6u", lifetime); + INCP; + } + + /* RFP option lengths */ + for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap; + pEncap = pEncap->next) + { + + if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) + { + if (printed_1st_gol) + { + r = snprintf (p, REMAIN, ","); + INCP; + } + else + { + r = snprintf (p, REMAIN, " "); /* leading space */ + INCP; + } + r = snprintf (p, REMAIN, "%d", pEncap->length); + INCP; + printed_1st_gol = 1; + } + } + + /* RT list */ + if (bi->attr->extra->ecommunity) + { + s = ecommunity_ecom2str (bi->attr->extra->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP); + r = snprintf (p, REMAIN, " %s", s); + INCP; + XFREE (MTYPE_ECOMMUNITY_STR, s); + } + + } + + r = snprintf (p, REMAIN, " bi@%p", bi); + INCP; + + r = snprintf (p, REMAIN, " p@%p", bi->peer); + INCP; + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + r = snprintf (p, REMAIN, " HD=yes"); + INCP; + } + else + { + r = snprintf (p, REMAIN, " HD=no"); + INCP; + } + + if (bi->attr) + { + + if (bi->attr->extra) + { + r = snprintf (p, REMAIN, " W=%d", bi->attr->extra->weight); + INCP; + } + + if (bi->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + { + r = snprintf (p, REMAIN, " LP=%d", bi->attr->local_pref); + INCP; + } + else + { + r = snprintf (p, REMAIN, " LP=unset"); + INCP; + } + } + + r = + snprintf (p, REMAIN, " %c:%u", zebra_route_char (bi->type), bi->sub_type); + INCP; + + fp (out, "%s%s", line, HVTY_NEWLINE); + + if (has_macaddr) + { + fp (out, " RD HID=%d ETH=%02x:%02x:%02x:%02x:%02x:%02x%s", + l2hid, + macaddr.octet[0], + macaddr.octet[1], + macaddr.octet[2], + macaddr.octet[3], macaddr.octet[4], macaddr.octet[5], HVTY_NEWLINE); + } + + if (!rfapiGetL2o (bi->attr, &l2o_buf)) + { + fp (out, + " L2O ETH=%02x:%02x:%02x:%02x:%02x:%02x LBL=%d LNI=%d LHI=%hhu%s", + l2o_buf.macaddr.octet[0], l2o_buf.macaddr.octet[1], + l2o_buf.macaddr.octet[2], l2o_buf.macaddr.octet[3], + l2o_buf.macaddr.octet[4], l2o_buf.macaddr.octet[5], l2o_buf.label, + l2o_buf.logical_net_id, l2o_buf.local_nve_id, HVTY_NEWLINE); + } + if (bi->extra && bi->extra->vnc.import.aux_prefix.family) + { + char buf[BUFSIZ]; + const char *sp; + + sp = rfapi_ntop (bi->extra->vnc.import.aux_prefix.family, + &bi->extra->vnc.import.aux_prefix.u.prefix, + buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + if (sp) + { + fp (out, " IP: %s%s", sp, HVTY_NEWLINE); + } + } + { + struct rfapi_un_option *uo = rfapi_encap_tlv_to_un_option (bi->attr); + if (uo) + { + rfapi_print_tunneltype_option (stream, 8, &uo->v.tunnel); + rfapi_un_options_free (uo); + } + } +} + +char * +rfapiMonitorVpn2Str (struct rfapi_monitor_vpn *m, char *buf, int size) +{ + char buf_pfx[BUFSIZ]; + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + int rc; + + rfapiRfapiIpAddr2Str (&m->rfd->un_addr, buf_vn, BUFSIZ); + rfapiRfapiIpAddr2Str (&m->rfd->vn_addr, buf_un, BUFSIZ); + + rc = snprintf (buf, size, + "m=%p, next=%p, rfd=%p(vn=%s un=%s), p=%s/%d, node=%p", + m, m->next, m->rfd, buf_vn, buf_un, + inet_ntop (m->p.family, &m->p.u.prefix, buf_pfx, BUFSIZ), + m->p.prefixlen, m->node); + buf[size - 1] = 0; + if (rc >= size) + return NULL; + return buf; +} + +static void +rfapiDebugPrintMonitorVpn (void *stream, struct rfapi_monitor_vpn *m) +{ + char buf[BUFSIZ]; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + rfapiMonitorVpn2Str (m, buf, BUFSIZ); + fp (out, " Mon %s%s", buf, HVTY_NEWLINE); +} + +static void +rfapiDebugPrintMonitorEncap (void *stream, struct rfapi_monitor_encap *m) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out = NULL; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp (out, " Mon m=%p, next=%p, node=%p, bi=%p%s", + m, m->next, m->node, m->bi, HVTY_NEWLINE); +} + +void +rfapiShowItNode (void *stream, struct route_node *rn) +{ + struct bgp_info *bi; + char buf[BUFSIZ]; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp (out, "%s/%d @%p #%d%s", + rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), + rn->p.prefixlen, rn, rn->lock, HVTY_NEWLINE); + + for (bi = rn->info; bi; bi = bi->next) + { + rfapiPrintBi (stream, bi); + } + + /* doesn't show montors */ +} + +void +rfapiShowImportTable ( + void *stream, + const char *label, + struct route_table *rt, + int isvpn) +{ + struct route_node *rn; + char buf[BUFSIZ]; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp (out, "Import Table [%s]%s", label, HVTY_NEWLINE); + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + struct bgp_info *bi; + + if (rn->p.family == AF_ETHERNET) + { + rfapiEthAddr2Str (&rn->p.u.prefix_eth, buf, BUFSIZ); + } + else + { + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ); + } + + fp (out, "%s/%d @%p #%d%s", buf, rn->p.prefixlen, rn, rn->lock - 1, /* account for loop iterator locking */ + HVTY_NEWLINE); + + for (bi = rn->info; bi; bi = bi->next) + { + rfapiPrintBi (stream, bi); + } + + if (isvpn) + { + struct rfapi_monitor_vpn *m; + for (m = RFAPI_MONITOR_VPN (rn); m; m = m->next) + { + rfapiDebugPrintMonitorVpn (stream, m); + } + } + else + { + struct rfapi_monitor_encap *m; + for (m = RFAPI_MONITOR_ENCAP (rn); m; m = m->next) + { + rfapiDebugPrintMonitorEncap (stream, m); + } + } + } +} + +int +rfapiShowVncQueries (void *stream, struct prefix *pfx_match) +{ + struct bgp *bgp; + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + int printedheader = 0; + + int nves_total = 0; + int nves_with_queries = 0; + int nves_displayed = 0; + + int queries_total = 0; + int queries_displayed = 0; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return CMD_WARNING; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + if (!bgp) + { + vty_out (vty, "No BGP instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + h = bgp->rfapi; + if (!h) + { + vty_out (vty, "No RFAPI instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + + struct route_node *rn; + int printedquerier = 0; + + + ++nves_total; + + if (rfd->mon || (rfd->mon_eth && skiplist_count (rfd->mon_eth))) + { + ++nves_with_queries; + } + else + { + continue; + } + + /* + * IP Queries + */ + if (rfd->mon) + { + for (rn = route_top (rfd->mon); rn; rn = route_next (rn)) + { + struct rfapi_monitor_vpn *m; + char buf_remain[BUFSIZ]; + char buf_pfx[BUFSIZ]; + + if (!rn->info) + continue; + + m = rn->info; + + ++queries_total; + + if (pfx_match && !prefix_match (pfx_match, &rn->p) && + !prefix_match (&rn->p, pfx_match)) + continue; + + ++queries_displayed; + + if (!printedheader) + { + ++printedheader; + fp (out, "%s", VTY_NEWLINE); + fp (out, "%-15s %-15s %-15s %-10s%s", + "VN Address", "UN Address", + "Target", "Remaining", VTY_NEWLINE); + } + + if (!printedquerier) + { + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + + rfapiRfapiIpAddr2Str (&rfd->un_addr, buf_un, BUFSIZ); + rfapiRfapiIpAddr2Str (&rfd->vn_addr, buf_vn, BUFSIZ); + + fp (out, "%-15s %-15s", buf_vn, buf_un); + printedquerier = 1; + + ++nves_displayed; + } + else + fp (out, "%-15s %-15s", "", ""); + buf_remain[0] = 0; + if (m->timer) + { + rfapiFormatSeconds (thread_timer_remain_second (m->timer), + buf_remain, BUFSIZ); + } + fp (out, " %-15s %-10s%s", + inet_ntop (m->p.family, &m->p.u.prefix, buf_pfx, BUFSIZ), + buf_remain, VTY_NEWLINE); + } + } + + /* + * Ethernet Queries + */ + if (rfd->mon_eth && skiplist_count (rfd->mon_eth)) + { + + int rc; + void *cursor; + struct rfapi_monitor_eth *mon_eth; + + for (cursor = NULL, + rc = + skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth, + &cursor); rc == 0; + rc = + skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth, + &cursor)) + { + + char buf_remain[BUFSIZ]; + char buf_pfx[BUFSIZ]; + struct prefix pfx_mac; + + ++queries_total; + + zlog_debug ("%s: checking rfd=%p mon_eth=%p", __func__, rfd, + mon_eth); + + memset ((void *) &pfx_mac, 0, sizeof (struct prefix)); + pfx_mac.family = AF_ETHERNET; + pfx_mac.prefixlen = 48; + pfx_mac.u.prefix_eth = mon_eth->macaddr; + + if (pfx_match && !prefix_match (pfx_match, &pfx_mac) && + !prefix_match (&pfx_mac, pfx_match)) + continue; + + ++queries_displayed; + + if (!printedheader) + { + ++printedheader; + fp (out, "%s", VTY_NEWLINE); + fp (out, "%-15s %-15s %-17s %10s %-10s%s", + "VN Address", "UN Address", + "Target", "LNI", "Remaining", VTY_NEWLINE); + } + + if (!printedquerier) + { + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + + rfapiRfapiIpAddr2Str (&rfd->un_addr, buf_un, BUFSIZ); + rfapiRfapiIpAddr2Str (&rfd->vn_addr, buf_vn, BUFSIZ); + + fp (out, "%-15s %-15s", buf_vn, buf_un); + printedquerier = 1; + + ++nves_displayed; + } + else + fp (out, "%-15s %-15s", "", ""); + buf_remain[0] = 0; + if (mon_eth->timer) + { + rfapiFormatSeconds (thread_timer_remain_second + (mon_eth->timer), buf_remain, BUFSIZ); + } + fp (out, " %-17s %10d %-10s%s", + rfapi_ntop (pfx_mac.family, &pfx_mac.u.prefix, buf_pfx, + BUFSIZ), mon_eth->logical_net_id, buf_remain, + VTY_NEWLINE); + } + } + } + + if (queries_total) + { + fp (out, "%s", VTY_NEWLINE); + fp (out, "Displayed %d out of %d total queries%s", + queries_displayed, queries_total, VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +static int +rfapiPrintRemoteRegBi ( + struct bgp *bgp, + void *stream, + struct route_node *rn, + struct bgp_info *bi) +{ + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + uint32_t factor; + + struct prefix pfx_un; + struct prefix pfx_vn; + uint8_t cost; + uint32_t lifetime; + bgp_encap_types tun_type; + + char buf_pfx[BUFSIZ]; + char buf_ntop[BUFSIZ]; + char buf_un[BUFSIZ]; + char buf_vn[BUFSIZ]; + char buf_lifetime[BUFSIZ]; + int nlines = 0; + + if (bgp && bgp->rfapi_cfg) + factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor; + else + factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR; + + + if (!stream) + return 0; /* for debug log, print into buf & call output once */ + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return 0; + + /* + * Prefix + */ + buf_pfx[0] = 0; + snprintf (buf_pfx, BUFSIZ, "%s/%d", + rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf_ntop, BUFSIZ), + rn->p.prefixlen); + buf_pfx[BUFSIZ - 1] = 0; + nlines++; + + /* + * UN addr + */ + buf_un[0] = 0; + if (!rfapiGetUnAddrOfVpnBi (bi, &pfx_un)) + { + snprintf (buf_un, BUFSIZ, "%s", + inet_ntop (pfx_un.family, &pfx_un.u.prefix, buf_ntop, + BUFSIZ)); + } + buf_un[BUFSIZ - 1] = 0; + + rfapiGetTunnelType(bi->attr,&tun_type); + /* + * VN addr + */ + buf_vn[0] = 0; + if (tun_type == BGP_ENCAP_TYPE_MPLS) + { + /* MPLS carries un in nrli next hop (same as vn for IP tunnels) */ + if (bi->extra) + { + u_int32_t l = decode_label (bi->extra->tag); + snprintf (buf_vn, BUFSIZ, "Label: %d", l); + } + else /* should never happen */ + { + snprintf (buf_vn, BUFSIZ, "Label: N/A"); + } + } + else + { + rfapiNexthop2Prefix (bi->attr, &pfx_vn); + snprintf (buf_vn, BUFSIZ, "%s", + inet_ntop (pfx_vn.family, &pfx_vn.u.prefix, buf_ntop, BUFSIZ)); + } + buf_vn[BUFSIZ - 1] = 0; + + + /* + * Cost is encoded in local_pref as (255-cost) + * See rfapi_import.c'rfapiRouteInfo2NextHopEntry() for conversion + * back to cost. + */ + if (bi->attr) + { + uint32_t local_pref; + if (bi->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + local_pref = bi->attr->local_pref; + else + local_pref = 0; + cost = (local_pref > 255) ? 0 : 255 - local_pref; + } + else + { + cost = 0; + } + + fp (out, "%-20s ", buf_pfx); + fp (out, "%-15s ", buf_vn); + fp (out, "%-15s ", buf_un); + fp (out, "%-4d ", cost); + + /* Lifetime */ + /* NB rfapiGetVncLifetime sets infinite value when returning !0 */ + if (rfapiGetVncLifetime (bi->attr, &lifetime) || + (lifetime == RFAPI_INFINITE_LIFETIME)) + { + + fp (out, "%-10s ", "infinite"); + } + else + { + time_t t_lifetime = lifetime; + rfapiFormatSeconds (t_lifetime, buf_lifetime, BUFSIZ); + fp (out, "%-10s ", buf_lifetime); + } + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && + bi->extra && bi->extra->vnc.import.timer) + { + + uint32_t remaining; + time_t age; + char buf_age[BUFSIZ]; + + struct thread *t = (struct thread *) bi->extra->vnc.import.timer; + remaining = thread_timer_remain_second (t); + +#if RFAPI_REGISTRATIONS_REPORT_AGE + /* + * Calculate when the timer started. Doing so here saves + * us a timestamp field in "struct bgp_info". + * + * See rfapi_import.c'rfapiBiStartWithdrawTimer() for the + * original calculation. + */ + age = rfapiGetHolddownFromLifetime (lifetime, factor) - remaining; +#else /* report remaining time */ + age = remaining; +#endif + rfapiFormatSeconds (age, buf_age, BUFSIZ); + + fp (out, "%-10s ", buf_age); + + } + else if (RFAPI_LOCAL_BI (bi)) + { + + char buf_age[BUFSIZ]; + + if (bi && bi->extra && bi->extra->vnc.import.create_time) + { + rfapiFormatAge (bi->extra->vnc.import.create_time, buf_age, BUFSIZ); + } + else + { + buf_age[0] = '?'; + buf_age[1] = 0; + } + fp (out, "%-10s ", buf_age); + } + fp (out, "%s", HVTY_NEWLINE); + + if (rn->p.family == AF_ETHERNET) + { + /* + * If there is a corresponding IP address && != VN address, + * print that on the next line + */ + + if (bi && bi->extra && bi->extra->vnc.import.aux_prefix.family) + { + const char *sp; + + sp = rfapi_ntop (bi->extra->vnc.import.aux_prefix.family, + &bi->extra->vnc.import.aux_prefix.u.prefix, + buf_ntop, BUFSIZ); + buf_ntop[BUFSIZ - 1] = 0; + + if (sp && strcmp (buf_vn, sp) != 0) + { + fp (out, " IP: %s", sp); + if (nlines == 1) + nlines++; + } + } + } + if (tun_type != BGP_ENCAP_TYPE_MPLS && bi->extra) + { + u_int32_t l = decode_label (bi->extra->tag); + if (!MPLS_LABEL_IS_NULL (l)) + { + fp (out, " Label: %d", l); + if (nlines == 1) + nlines++; + } + } + if (nlines > 1) + fp (out, "%s", HVTY_NEWLINE); + + return 1; +} + +static int +rfapiShowRemoteRegistrationsIt ( + struct bgp *bgp, + void *stream, + struct rfapi_import_table *it, + struct prefix *prefix_only, + int show_expiring, /* either/or */ + int show_local, + int show_remote, + int show_imported, /* either/or */ + uint32_t *pLni) /* AFI_ETHER only */ +{ + afi_t afi; + int printed_rtlist_hdr = 0; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + int total = 0; + int printed = 0; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return printed; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + struct route_node *rn; + + if (!it->imported_vpn[afi]) + continue; + + for (rn = route_top (it->imported_vpn[afi]); rn; rn = route_next (rn)) + { + + struct bgp_info *bi; + int count_only; + + /* allow for wider or more narrow mask from user */ + if (prefix_only && + !prefix_match (prefix_only, &rn->p) && + !prefix_match (&rn->p, prefix_only)) + count_only = 1; + else + count_only = 0; + + for (bi = rn->info; bi; bi = bi->next) + { + + if (!show_local && RFAPI_LOCAL_BI (bi)) + { + + /* local route from RFP */ + continue; + } + + if (!show_remote && !RFAPI_LOCAL_BI (bi)) + { + + /* remote route */ + continue; + } + + if (show_expiring && !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + if (!show_expiring && CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + if (bi->type == ZEBRA_ROUTE_BGP_DIRECT || + bi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + { + if (!show_imported) + continue; + } + else + { + if (show_imported) + continue; + } + + total++; + if (count_only == 1) + continue; + if (!printed_rtlist_hdr) + { + const char *agetype = ""; + char *s; + const char *type = ""; + if (show_imported) + { + type = "Imported"; + } + else + { + if (show_expiring) + { + type = "Holddown"; + } + else + { + if (RFAPI_LOCAL_BI (bi)) + { + type = "Local"; + } + else + { + type = "Remote"; + } + } + } + + s = ecommunity_ecom2str (it->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + + if (pLni) + { + fp (out, "%s[%s] L2VPN Network 0x%x (%u) RT={%s}%s", + HVTY_NEWLINE, type, *pLni, (*pLni & 0xfff), s, + HVTY_NEWLINE); + } + else + { + fp (out, "%s[%s] Prefix RT={%s}%s", + HVTY_NEWLINE, type, s, HVTY_NEWLINE); + } + XFREE (MTYPE_ECOMMUNITY_STR, s); + + if (show_expiring) + { +#if RFAPI_REGISTRATIONS_REPORT_AGE + agetype = "Age"; +#else + agetype = "Remaining"; +#endif + } + else if (show_local) + { + agetype = "Age"; + } + + printed_rtlist_hdr = 1; + + fp (out, "%-20s %-15s %-15s %4s %-10s %-10s%s", + (pLni ? "L2 Address/IP" : "Prefix"), + "VN Address", "UN Address", "Cost", + "Lifetime", agetype, HVTY_NEWLINE); + } + printed += rfapiPrintRemoteRegBi (bgp, stream, rn, bi); + } + } + } + + if (printed > 0) + { + + const char *type = "prefixes"; + + if (show_imported) + { + type = "imported prefixes"; + } + else + { + if (show_expiring) + { + type = "prefixes in holddown"; + } + else + { + if (show_local && !show_remote) + { + type = "locally registered prefixes"; + } + else if (!show_local && show_remote) + { + type = "remotely registered prefixes"; + } + } + } + + fp (out, "Displayed %d out of %d %s%s", + printed, total, type, HVTY_NEWLINE); + } + return printed; +} + + + +/* + * rfapiShowRemoteRegistrations + * + * Similar to rfapiShowImportTable() above. This function + * is mean to produce the "remote" portion of the output + * of "show vnc registrations". + */ +int +rfapiShowRemoteRegistrations ( + void *stream, + struct prefix *prefix_only, + int show_expiring, + int show_local, + int show_remote, + int show_imported) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + int printed = 0; + + bgp = bgp_get_default (); + if (!bgp) + { + return printed; + } + + h = bgp->rfapi; + if (!h) + { + return printed; + } + + for (it = h->imports; it; it = it->next) + { + printed += + rfapiShowRemoteRegistrationsIt (bgp, stream, it, prefix_only, + show_expiring, show_local, + show_remote, show_imported, NULL); + } + + if (h->import_mac) + { + void *cursor = NULL; + int rc; + uintptr_t lni_as_ptr; + uint32_t lni; + uint32_t *pLni; + + for (rc = + skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it, + &cursor); !rc; + rc = + skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it, + &cursor)) + { + pLni = NULL; + if ((lni_as_ptr & 0xffffffff) == lni_as_ptr) + { + lni = (uint32_t) (lni_as_ptr & 0xffffffff); + pLni = &lni; + } + + printed += + rfapiShowRemoteRegistrationsIt (bgp, stream, it, prefix_only, + show_expiring, show_local, + show_remote, show_imported, pLni); + } + } + + return printed; +} + +/*------------------------------------------ + * rfapiRfapiIpAddr2Str + * + * UI helper: generate string from rfapi_ip_addr + * + * input: + * a IP v4/v6 address + * + * output + * buf put string here + * bufsize max space to write + * + * return value: + * NULL conversion failed + * non-NULL pointer to buf + --------------------------------------------*/ +const char * +rfapiRfapiIpAddr2Str (struct rfapi_ip_addr *a, char *buf, int bufsize) +{ + const char *rc = NULL; + + switch (a->addr_family) + { + case AF_INET: + rc = inet_ntop (a->addr_family, &a->addr.v4, buf, bufsize); + break; + case AF_INET6: + rc = inet_ntop (a->addr_family, &a->addr.v6, buf, bufsize); + break; + } + return rc; +} + +void +rfapiPrintRfapiIpAddr (void *stream, struct rfapi_ip_addr *a) +{ + char buf[BUFSIZ]; + const char *rc = NULL; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out = NULL; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + rc = rfapiRfapiIpAddr2Str (a, buf, BUFSIZ); + + if (rc) + fp (out, "%s", buf); +} + +const char * +rfapiRfapiIpPrefix2Str (struct rfapi_ip_prefix *p, char *buf, int bufsize) +{ + struct rfapi_ip_addr *a = &p->prefix; + const char *rc = NULL; + + switch (a->addr_family) + { + case AF_INET: + rc = inet_ntop (a->addr_family, &a->addr.v4, buf, bufsize); + break; + case AF_INET6: + rc = inet_ntop (a->addr_family, &a->addr.v6, buf, bufsize); + break; + } + + if (rc) + { + int alen = strlen (buf); + int remaining = bufsize - alen - 1; + int slen; + + if (remaining > 0) + { + slen = snprintf (buf + alen, remaining, "/%u", p->length); + if (slen < remaining) /* see man page for snprintf(3) */ + return rc; + } + } + + return NULL; +} + +void +rfapiPrintRfapiIpPrefix (void *stream, struct rfapi_ip_prefix *p) +{ + char buf[BUFSIZ]; + const char *rc; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out = NULL; + const char *vty_newline; + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + rc = rfapiRfapiIpPrefix2Str (p, buf, BUFSIZ); + + if (rc) + fp (out, "%s:%u", buf, p->cost); + else + fp (out, "?/?:?"); +} + +void +rfapiPrintRd (struct vty *vty, struct prefix_rd *prd) +{ + char buf[BUFSIZ]; + + buf[0] = 0; + prefix_rd2str (prd, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + vty_out (vty, "%s", buf); +} + +void +rfapiPrintAdvertisedInfo ( + struct vty *vty, + struct rfapi_descriptor *rfd, + safi_t safi, + struct prefix *p) +{ + afi_t afi; /* of the VN address */ + struct bgp_node *bn; + struct bgp_info *bi; + uint8_t type = ZEBRA_ROUTE_BGP; + struct bgp *bgp; + int printed = 0; + struct prefix_rd prd0; + struct prefix_rd *prd; + + /* + * Find the bgp_info in the RIB corresponding to this + * prefix and rfd + */ + + afi = family2afi (p->family); + assert (afi == AFI_IP || afi == AFI_IP6); + + bgp = bgp_get_default (); /* assume 1 instance for now */ + assert (bgp); + + if (safi == SAFI_ENCAP) + { + memset (&prd0, 0, sizeof (prd0)); + prd0.family = AF_UNSPEC; + prd0.prefixlen = 64; + prd = &prd0; + } + else + { + prd = &rfd->rd; + } + bn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + + vty_out (vty, " bn=%p%s", bn, HVTY_NEWLINE); + + for (bi = bn->info; bi; bi = bi->next) + { + if (bi->peer == rfd->peer && + bi->type == type && + bi->sub_type == BGP_ROUTE_RFP && + bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd) + { + + rfapiPrintBi (vty, bi); + printed = 1; + } + } + + if (!printed) + { + vty_out (vty, " --?--%s", HVTY_NEWLINE); + return; + } + +} + +void +rfapiPrintDescriptor (struct vty *vty, struct rfapi_descriptor *rfd) +{ + /* pHD un-addr vn-addr pCB cookie rd lifetime */ + /* RT export list */ + /* RT import list */ + /* list of advertised prefixes */ + /* dump import table */ + + char *s; + void *cursor; + int rc; + afi_t afi; + struct rfapi_adb *adb; + char buf[BUFSIZ]; + + vty_out (vty, "%-10p ", rfd); + rfapiPrintRfapiIpAddr (vty, &rfd->un_addr); + vty_out (vty, " "); + rfapiPrintRfapiIpAddr (vty, &rfd->vn_addr); + vty_out (vty, " %p %p ", rfd->response_cb, rfd->cookie); + rfapiPrintRd (vty, &rfd->rd); + vty_out (vty, " %d", rfd->response_lifetime); + vty_out (vty, " %s", (rfd->rfg ? rfd->rfg->name : "")); + vty_out (vty, "%s", HVTY_NEWLINE); + + vty_out (vty, " Peer %p #%d%s", rfd->peer, rfd->peer->lock, HVTY_NEWLINE); + + /* export RT list */ + if (rfd->rt_export_list) + { + s = + ecommunity_ecom2str (rfd->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " Export %s%s", s, HVTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, s); + } + else + { + vty_out (vty, " Export (nil)%s", HVTY_NEWLINE); + } + + /* import RT list */ + if (rfd->import_table) + { + s = ecommunity_ecom2str (rfd->import_table->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " Import %s%s", s, HVTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, s); + } + else + { + vty_out (vty, " Import (nil)%s", HVTY_NEWLINE); + } + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + u_char family; + + family = afi2family (afi); + if (!family) + continue; + + cursor = NULL; + for (rc = + skiplist_next (rfd->advertised.ipN_by_prefix, NULL, (void **) &adb, + &cursor); rc == 0; + rc = + skiplist_next (rfd->advertised.ipN_by_prefix, NULL, (void **) &adb, + &cursor)) + { + + /* group like family prefixes together in output */ + if (family != adb->prefix_ip.family) + continue; + + prefix2str (&adb->prefix_ip, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + vty_out (vty, " Adv Pfx: %s%s", buf, HVTY_NEWLINE); + rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->prefix_ip); + } + } + for (rc = + skiplist_next (rfd->advertised.ip0_by_ether, NULL, (void **) &adb, + &cursor); rc == 0; + rc = + skiplist_next (rfd->advertised.ip0_by_ether, NULL, (void **) &adb, + &cursor)) + { + + prefix2str (&adb->prefix_eth, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + vty_out (vty, " Adv Pfx: %s%s", buf, HVTY_NEWLINE); + + /* TBD update the following function to print ethernet info */ + /* Also need to pass/use rd */ + rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->prefix_ip); + } + vty_out (vty, "%s", HVTY_NEWLINE); +} + +/* + * test scripts rely on first line for each nve starting in 1st column, + * leading whitespace for additional detail of that nve + */ +void +rfapiPrintMatchingDescriptors (struct vty *vty, + struct prefix *vn_prefix, + struct prefix *un_prefix) +{ + struct bgp *bgp; + struct rfapi *h; + struct listnode *ln; + struct rfapi_descriptor *rfd; + int printed = 0; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + if (!bgp) + return; + + h = bgp->rfapi; + assert (h); + + for (ln = listhead (&h->descriptors); ln; ln = listnextnode (ln)) + { + rfd = listgetdata (ln); + + struct prefix pfx; + + if (vn_prefix) + { + assert (!rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx)); + if (!prefix_match (vn_prefix, &pfx)) + continue; + } + + if (un_prefix) + { + assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx)); + if (!prefix_match (un_prefix, &pfx)) + continue; + } + + if (!printed) + { + /* print column header */ + vty_out (vty, + "%s %s %s %s %s %s %s %s%s", + "descriptor", "un-addr", "vn-addr", "callback", "cookie", + "RD", "lifetime", "group", HVTY_NEWLINE); + } + rfapiPrintDescriptor (vty, rfd); + printed = 1; + } +} + + +/* + * Parse an address and put into a struct prefix + */ +int +rfapiCliGetPrefixAddr (struct vty *vty, const char *str, struct prefix *p) +{ + if (!str2prefix (str, p)) + { + vty_out (vty, "Malformed address \"%s\"%s", str, HVTY_NEWLINE); + return CMD_WARNING; + } + switch (p->family) + { + case AF_INET: + if (p->prefixlen != 32) + { + vty_out (vty, "Not a host address: \"%s\"%s", str, HVTY_NEWLINE); + return CMD_WARNING; + } + break; + case AF_INET6: + if (p->prefixlen != 128) + { + vty_out (vty, "Not a host address: \"%s\"%s", str, HVTY_NEWLINE); + return CMD_WARNING; + } + break; + default: + vty_out (vty, "Invalid address \"%s\"%s", str, HVTY_NEWLINE); + return CMD_WARNING; + } + return 0; +} + +int +rfapiCliGetRfapiIpAddr ( + struct vty *vty, + const char *str, + struct rfapi_ip_addr *hai) +{ + struct prefix pfx; + int rc; + + rc = rfapiCliGetPrefixAddr (vty, str, &pfx); + if (rc) + return rc; + + hai->addr_family = pfx.family; + if (pfx.family == AF_INET) + hai->addr.v4 = pfx.u.prefix4; + else + hai->addr.v6 = pfx.u.prefix6; + + return 0; +} + +/* + * Note: this function does not flush vty output, so if it is called + * with a stream pointing to a vty, the user will have to type something + * before the callback output shows up + */ +void +rfapiPrintNhl (void *stream, struct rfapi_next_hop_entry *next_hops) +{ + struct rfapi_next_hop_entry *nh; + int count; + + int (*fp) (void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + +#define REMAIN (BUFSIZ - (p-line)) +#define INCP {p += (r > REMAIN)? REMAIN: r;} + + + if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + for (nh = next_hops, count = 1; nh; nh = nh->next, ++count) + { + + char line[BUFSIZ]; + char *p = line; + int r; + + r = snprintf (p, REMAIN, "%3d pfx=", count); + INCP; + + if (rfapiRfapiIpPrefix2Str (&nh->prefix, p, REMAIN)) + { + /* it fit, so count length */ + r = strlen (p); + } + else + { + /* didn't fit */ + goto truncate; + } + INCP; + + r = snprintf (p, REMAIN, ", un="); + INCP; + + if (rfapiRfapiIpAddr2Str (&nh->un_address, p, REMAIN)) + { + /* it fit, so count length */ + r = strlen (p); + } + else + { + /* didn't fit */ + goto truncate; + } + INCP; + + r = snprintf (p, REMAIN, ", vn="); + INCP; + + if (rfapiRfapiIpAddr2Str (&nh->vn_address, p, REMAIN)) + { + /* it fit, so count length */ + r = strlen (p); + } + else + { + /* didn't fit */ + goto truncate; + } + INCP; + + truncate: + line[BUFSIZ - 1] = 0; + fp (out, "%s%s", line, HVTY_NEWLINE); + + /* + * options + */ + if (nh->vn_options) + { + struct rfapi_vn_option *vo; + char offset[] = " "; + + for (vo = nh->vn_options; vo; vo = vo->next) + { + char pbuf[100]; + + switch (vo->type) + { + case RFAPI_VN_OPTION_TYPE_L2ADDR: + rfapiEthAddr2Str (&vo->v.l2addr.macaddr, pbuf, + sizeof (pbuf)); + fp (out, "%sL2 %s LBL=0x%06x NETID=0x%06x NVEID=%d%s", + offset, pbuf, (vo->v.l2addr.label & 0x00ffffff), + (vo->v.l2addr.logical_net_id & 0x00ffffff), + vo->v.l2addr.local_nve_id, HVTY_NEWLINE); + break; + + case RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP: + prefix2str (&vo->v.local_nexthop.addr, pbuf, sizeof (pbuf)); + fp (out, "%sLNH %s cost=%d%s", + offset, pbuf, vo->v.local_nexthop.cost, HVTY_NEWLINE); + break; + + default: + fp (out, "%svn option type %d (unknown)%s", + offset, vo->type, HVTY_NEWLINE); + break; + } + } + } + if (nh->un_options) + { + struct rfapi_un_option *uo; + char offset[] = " "; + + for (uo = nh->un_options; uo; uo = uo->next) + { + switch (uo->type) + { + case RFAPI_UN_OPTION_TYPE_TUNNELTYPE: + rfapi_print_tunneltype_option (stream, 8, &uo->v.tunnel); + break; + default: + fp (out, "%sUN Option type %d%s", + offset, uo->type, vty_newline); + break; + } + + } + } + } +} + +/*********************************************************************** + * STATIC ROUTES + ***********************************************************************/ + +/* + * Add another nexthop to the NHL + */ +static void +rfapiAddDeleteLocalRfpPrefix ( + struct rfapi_ip_addr *un_addr, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_prefix *rprefix, + int is_add, + uint32_t lifetime, /* add only */ + struct rfapi_vn_option *vn_options, + struct rfapi_next_hop_entry **head, + struct rfapi_next_hop_entry **tail) +{ + struct rfapi_next_hop_entry *new; + + /* + * construct NHL + */ + + new = XCALLOC (MTYPE_RFAPI_NEXTHOP, sizeof (struct rfapi_next_hop_entry)); + new->prefix = *rprefix; + new->un_address = *un_addr; + new->vn_address = *vn_addr; + + new->vn_options = vn_options; + if (is_add) + { + new->lifetime = lifetime; + } + else + { + new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME; + } + + if (*tail) + (*tail)->next = new; + *tail = new; + if (!*head) + { + *head = new; + } +} + + +static int +register_add ( + struct vty *vty, + const char *arg_prefix, + const char *arg_vn, + const char *arg_un, + const char *arg_cost, /* optional */ + const char *arg_lifetime, /* optional */ + const char *arg_macaddr, /* optional */ + const char *arg_vni, /* mac present=>mandatory Virtual Network ID */ + int argc, + const char **argv) +{ + struct rfapi_ip_addr vn_address; + struct rfapi_ip_addr un_address; + struct prefix pfx; + struct rfapi_ip_prefix rpfx; + uint32_t cost; + uint32_t lnh_cost; + uint32_t lifetime; + rfapi_handle rfd; + struct rfapi_vn_option optary[10]; /* XXX must be big enough */ + struct rfapi_vn_option *opt = NULL; + int opt_next = 0; + + int rc = CMD_WARNING; + char *endptr; + struct bgp *bgp; + struct rfapi *h; + struct rfapi_cfg *rfapi_cfg; + + const char *arg_lnh = NULL; + const char *arg_lnh_cost = NULL; + + bgp = bgp_get_default (); /* assume 1 instance for now */ + if (!bgp) + { + if (vty) + vty_out (vty, "BGP not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + h = bgp->rfapi; + rfapi_cfg = bgp->rfapi_cfg; + if (!h || !rfapi_cfg) + { + if (vty) + vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (; argc; --argc, ++argv) + { + if (!strcmp (*argv, "local-next-hop")) + { + if (arg_lnh) + { + vty_out (vty, "local-next-hop specified more than once%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (argc <= 1) + { + vty_out (vty, "Missing parameter for local-next-hop%s", + VTY_NEWLINE); + return CMD_WARNING; + } + ++argv, --argc; + arg_lnh = *argv; + } + if (!strcmp (*argv, "local-cost")) + { + if (arg_lnh_cost) + { + vty_out (vty, "local-cost specified more than once%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (argc <= 1) + { + vty_out (vty, "Missing parameter for local-cost%s", + VTY_NEWLINE); + return CMD_WARNING; + } + ++argv, --argc; + arg_lnh_cost = *argv; + } + } + + if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_vn, &vn_address))) + goto fail; + if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_un, &un_address))) + goto fail; + + /* arg_prefix is optional if mac address is given */ + if (arg_macaddr && !arg_prefix) + { + /* + * fake up a 0/32 or 0/128 prefix + */ + switch (vn_address.addr_family) + { + case AF_INET: + arg_prefix = "0.0.0.0/32"; + break; + case AF_INET6: + arg_prefix = "0::0/128"; + break; + default: + vty_out (vty, "Internal error, unknown VN address family%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + } + + if (!str2prefix (arg_prefix, &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", arg_prefix, + VTY_NEWLINE); + goto fail; + } + if (pfx.family != AF_INET + && pfx.family != AF_INET6) + { + vty_out (vty, "prefix \"%s\" has invalid address family%s", + arg_prefix, VTY_NEWLINE); + goto fail; + } + + + memset (optary, 0, sizeof (optary)); + + if (arg_cost) + { + endptr = NULL; + cost = strtoul (arg_cost, &endptr, 10); + if (*endptr != '\0' || cost > 255) + { + vty_out (vty, "%% Invalid %s value%s", "cost", VTY_NEWLINE); + goto fail; + } + } + else + { + cost = 255; + } + + if (arg_lifetime) + { + if (!strcmp (arg_lifetime, "infinite")) + { + lifetime = RFAPI_INFINITE_LIFETIME; + } + else + { + endptr = NULL; + lifetime = strtoul (arg_lifetime, &endptr, 10); + if (*endptr != '\0') + { + vty_out (vty, "%% Invalid %s value%s", "lifetime", + VTY_NEWLINE); + goto fail; + } + } + } + else + { + lifetime = RFAPI_INFINITE_LIFETIME; /* default infinite */ + } + + if (arg_lnh_cost) + { + if (!arg_lnh) + { + vty_out (vty, + "%% %s may only be specified with local-next-hop%s", + "local-cost", VTY_NEWLINE); + goto fail; + } + endptr = NULL; + lnh_cost = strtoul (arg_lnh_cost, &endptr, 10); + if (*endptr != '\0' || lnh_cost > 255) + { + vty_out (vty, "%% Invalid %s value%s", "local-cost", + VTY_NEWLINE); + goto fail; + } + } + else + { + lnh_cost = 255; + } + + if (arg_lnh) + { + if (!arg_prefix) + { + vty_out (vty, "%% %s may only be specified with prefix%s", + "local-next-hop", VTY_NEWLINE); + goto fail; + } + if ((rc = rfapiCliGetPrefixAddr (vty, arg_lnh, + &optary[opt_next].v. + local_nexthop.addr))) + { + + goto fail; + } + + optary[opt_next].v.local_nexthop.cost = lnh_cost; + optary[opt_next].type = RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP; + + if (opt_next) + { + optary[opt_next - 1].next = optary + opt_next; + } + else + { + opt = optary; + } + ++opt_next; + } + + if (arg_vni && !arg_macaddr) + { + vty_out (vty, "%% %s may only be specified with mac address%s", + "virtual-network-identifier", VTY_NEWLINE); + goto fail; + } + + if (arg_macaddr) + { + if (!arg_vni) + { + vty_out (vty, + "Missing \"vni\" parameter (mandatory with mac)%s", + VTY_NEWLINE); + return CMD_WARNING; + } + VTY_GET_INTEGER ("Logical Network ID", + optary[opt_next].v.l2addr.logical_net_id, + arg_vni); + + if ((rc = rfapiStr2EthAddr (arg_macaddr, + &optary[opt_next].v.l2addr.macaddr))) + { + vty_out (vty, "Invalid %s value%s", "mac address", + VTY_NEWLINE); + goto fail; + } + /* TBD label, NVE ID */ + + optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR; + + if (opt_next) + { + optary[opt_next - 1].next = optary + opt_next; + } + else + { + opt = optary; + } + ++opt_next; + } + + zlog_debug + ("%s: vn=%s, un=%s, prefix=%s, cost=%s, lifetime=%s, lnh=%s", + __func__, arg_vn, arg_un, arg_prefix, + (arg_cost ? arg_cost : "NULL"), + (arg_lifetime ? arg_lifetime : "NULL"), + (arg_lnh ? arg_lnh : "NULL")); + + rfapiQprefix2Rprefix (&pfx, &rpfx); + + rpfx.cost = cost & 255; + + /* look up rf descriptor, call open if it doesn't exist */ + rc = + rfapi_find_rfd (bgp, &vn_address, &un_address, + (struct rfapi_descriptor **) &rfd); + if (rc) + { + if (ENOENT == rc) + { + struct rfapi_un_option uo; + + /* + * flag descriptor as provisionally opened for static route + * registration so that we can fix up the other parameters + * when the real open comes along + */ + memset (&uo, 0, sizeof (uo)); + uo.type = RFAPI_UN_OPTION_TYPE_PROVISIONAL; + + rc = rfapi_open (rfapi_get_rfp_start_val_by_bgp (bgp), &vn_address, &un_address, &uo, /* flags */ + NULL, NULL, /* no userdata */ + &rfd); + if (rc) + { + vty_out (vty, "Can't open session for this NVE: %s%s", + rfapi_error_str (rc), VTY_NEWLINE); + rc = CMD_WARNING; + goto fail; + } + } + else + { + vty_out (vty, "Can't find session for this NVE: %s%s", + rfapi_error_str (rc), VTY_NEWLINE); + goto fail; + } + } + + rc = + rfapi_register (rfd, &rpfx, lifetime, NULL, opt, RFAPI_REGISTER_ADD); + if (!rc) + { + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct rfapi_vn_option *vn_opt_new; + + zlog_debug ("%s: rfapi_register succeeded, returning 0", __func__); + + if (h->rfp_methods.local_cb) + { + struct rfapi_descriptor *r = (struct rfapi_descriptor *) rfd; + vn_opt_new = rfapi_vn_options_dup (opt); + + rfapiAddDeleteLocalRfpPrefix (&r->un_addr, &r->vn_addr, &rpfx, + 1, lifetime, vn_opt_new, &head, + &tail); + if (head) + { + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.local_cb) (head, r->cookie); + h->flags &= ~RFAPI_INCALLBACK; + } + head = tail = NULL; + } + return 0; + } + + zlog_debug ("%s: rfapi_register failed", __func__); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "Registration failed.%s", VTY_NEWLINE); + vty_out (vty, + "Confirm that either the VN or UN address matches a configured NVE group.%s", + VTY_NEWLINE); + return CMD_WARNING; + + fail: + zlog_debug ("%s: fail, rc=%d", __func__, rc); + return rc; +} + +/************************************************************************ + * Add prefix With .LNH_OPTIONS + ************************************************************************/ +DEFUN (add_vnc_prefix_cost_life_lnh, + add_vnc_prefix_cost_life_lnh_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> lifetime <1-4294967295> .LNH_OPTIONS", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], argv[3], argv[4], + /* mac vni */ + NULL, NULL, argc, argv); +} + +DEFUN (add_vnc_prefix_life_cost_lnh, + add_vnc_prefix_life_cost_lnh_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295> cost <0-255> .LNH_OPTIONS", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], argv[4], argv[3], + /* mac vni */ + NULL, NULL, argc, argv); +} + +DEFUN (add_vnc_prefix_cost_lnh, + add_vnc_prefix_cost_lnh_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> .LNH_OPTIONS", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], argv[3], NULL, + /* mac vni */ + NULL, NULL, argc, argv); +} + +DEFUN (add_vnc_prefix_life_lnh, + add_vnc_prefix_life_lnh_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295> .LNH_OPTIONS", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], NULL, argv[3], + /* mac vni */ + NULL, NULL, argc, argv); +} + +DEFUN (add_vnc_prefix_lnh, + add_vnc_prefix_lnh_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) .LNH_OPTIONS", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], NULL, NULL, + /* mac vni */ + NULL, NULL, argc, argv); +} + +/************************************************************************ + * Add prefix Without .LNH_OPTIONS + ************************************************************************/ +DEFUN (add_vnc_prefix_cost_life, + add_vnc_prefix_cost_life_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> lifetime <1-4294967295>", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], argv[3], argv[4], + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix_life_cost, + add_vnc_prefix_life_cost_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295> cost <0-255>", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], argv[4], argv[3], + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix_cost, + add_vnc_prefix_cost_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255>", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], argv[3], NULL, + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix_life, + add_vnc_prefix_life_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295>", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], NULL, argv[3], + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix, + add_vnc_prefix_cmd, + "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[0], argv[1], argv[2], NULL, NULL, + /* mac vni */ + NULL, NULL, 0, NULL); +} + +/************************************************************************ + * Mac address registrations + ************************************************************************/ +DEFUN (add_vnc_mac_vni_prefix_cost_life, + add_vnc_mac_vni_prefix_cost_life_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) cost <0-255> lifetime <1-4294967295>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[4], argv[2], argv[3], argv[5], argv[6], + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni_prefix_life, + add_vnc_mac_vni_prefix_life_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) lifetime <1-4294967295>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[4], argv[2], argv[3], NULL, argv[5], + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + +DEFUN (add_vnc_mac_vni_prefix_cost, + add_vnc_mac_vni_prefix_cost_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) cost <0-255>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Administrative cost [default: 255]\n" "Administrative cost\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[4], argv[2], argv[3], argv[5], NULL, + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + +DEFUN (add_vnc_mac_vni_prefix, + add_vnc_mac_vni_prefix_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related infomation\n" + "IPv4 prefix\n" "IPv6 prefix\n") +{ + /* pfx vn un cost life */ + return register_add (vty, argv[4], argv[2], argv[3], NULL, NULL, + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + +DEFUN (add_vnc_mac_vni_cost_life, + add_vnc_mac_vni_cost_life_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> lifetime <1-4294967295>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add (vty, NULL, argv[2], argv[3], argv[4], argv[5], + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni_cost, + add_vnc_mac_vni_cost_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" "Administrative cost\n") +{ + /* pfx vn un cost life */ + return register_add (vty, NULL, argv[2], argv[3], argv[4], NULL, + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni_life, + add_vnc_mac_vni_life_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add (vty, NULL, argv[2], argv[3], NULL, argv[4], + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni, + add_vnc_mac_vni_cmd, + "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address infomation\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" "UN IPv6 interface address\n") +{ + /* pfx vn un cost life */ + return register_add (vty, NULL, argv[2], argv[3], NULL, NULL, + /* mac vni */ + argv[0], argv[1], 0, NULL); +} + +/************************************************************************ + * Delete prefix + ************************************************************************/ + +struct rfapi_local_reg_delete_arg +{ + /* + * match parameters + */ + struct rfapi_ip_addr un_address; /* AF==0: wildcard */ + struct rfapi_ip_addr vn_address; /* AF==0: wildcard */ + struct prefix prefix; /* AF==0: wildcard */ + + struct rfapi_l2address_option_match l2o; + + /* + * result parameters + */ + struct vty *vty; + uint32_t reg_count; + uint32_t pfx_count; + uint32_t query_count; + + uint32_t failed_pfx_count; + + uint32_t nve_count; + struct skiplist *nves; + + uint32_t remote_active_nve_count; + uint32_t remote_active_pfx_count; + uint32_t remote_holddown_nve_count; + uint32_t remote_holddown_pfx_count; +}; + +struct nve_addr +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_descriptor *rfd; + struct rfapi_local_reg_delete_arg *cda; +}; + +static void +nve_addr_free (void *hap) +{ + ((struct nve_addr *) hap)->cda->nve_count += 1; + XFREE (MTYPE_RFAPI_NVE_ADDR, hap); +} + +static int +nve_addr_cmp (void *k1, void *k2) +{ + struct nve_addr *a = (struct nve_addr *) k1; + struct nve_addr *b = (struct nve_addr *) k2; + int ret = 0; + + if (!a || !b) + { + return (a - b); + } + if (a->un.addr_family != b->un.addr_family) + { + return (a->un.addr_family - b->un.addr_family); + } + if (a->vn.addr_family != b->vn.addr_family) + { + return (a->vn.addr_family - b->vn.addr_family); + } + if (a->un.addr_family == AF_INET) + { + ret = IPV4_ADDR_CMP (&a->un.addr.v4, &b->un.addr.v4); + if (ret != 0) + { + return ret; + } + } + else if (a->un.addr_family == AF_INET6) + { + ret = IPV6_ADDR_CMP (&a->un.addr.v6, &b->un.addr.v6); + if (ret != 0) + { + return ret; + } + } + else + { + assert (0); + } + if (a->vn.addr_family == AF_INET) + { + ret = IPV4_ADDR_CMP (&a->vn.addr.v4, &b->vn.addr.v4); + if (ret != 0) + return ret; + } + else if (a->vn.addr_family == AF_INET6) + { + ret = IPV6_ADDR_CMP (&a->vn.addr.v6, &b->vn.addr.v6); + if (ret == 0) + { + return ret; + } + } + else + { + assert (0); + } + return 0; +} + +static int +parse_deleter_args ( + struct vty *vty, + const char *arg_prefix, + const char *arg_vn, + const char *arg_un, + const char *arg_l2addr, + const char *arg_vni, + struct rfapi_local_reg_delete_arg *rcdarg) +{ + int rc = CMD_WARNING; + + memset (rcdarg, 0, sizeof (struct rfapi_local_reg_delete_arg)); + + if (arg_vn && strcmp (arg_vn, "*")) + { + if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_vn, &rcdarg->vn_address))) + return rc; + } + if (arg_un && strcmp (arg_un, "*")) + { + if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_un, &rcdarg->un_address))) + return rc; + } + if (arg_prefix && strcmp (arg_prefix, "*")) + { + + if (!str2prefix (arg_prefix, &rcdarg->prefix)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", arg_prefix, VTY_NEWLINE); + return rc; + } + } + + if (arg_l2addr) + { + if (!arg_vni) + { + vty_out (vty, "Missing VNI%s", VTY_NEWLINE); + return rc; + } + if (strcmp (arg_l2addr, "*")) + { + if ((rc = rfapiStr2EthAddr (arg_l2addr, &rcdarg->l2o.o.macaddr))) + { + vty_out (vty, "Malformed L2 Address \"%s\"%s", + arg_l2addr, VTY_NEWLINE); + return rc; + } + rcdarg->l2o.flags |= RFAPI_L2O_MACADDR; + } + if (strcmp (arg_vni, "*")) + { + VTY_GET_INTEGER ("Logical Network ID", + rcdarg->l2o.o.logical_net_id, arg_vni); + rcdarg->l2o.flags |= RFAPI_L2O_LNI; + } + } + return 0; +} + +static void +record_nve_in_cda_list ( + struct rfapi_local_reg_delete_arg *cda, + struct rfapi_ip_addr *un_address, + struct rfapi_ip_addr *vn_address, + struct rfapi_descriptor *rfd) +{ + struct nve_addr ha; + struct nve_addr *hap; + + memset (&ha, 0, sizeof (ha)); + ha.un = *un_address; + ha.vn = *vn_address; + ha.rfd = rfd; + + if (!cda->nves) + cda->nves = skiplist_new (0, nve_addr_cmp, nve_addr_free); + + if (skiplist_search (cda->nves, &ha, (void *) &hap)) + { + hap = XCALLOC (MTYPE_RFAPI_NVE_ADDR, sizeof (struct nve_addr)); + assert (hap); + ha.cda = cda; + * hap = ha; + skiplist_insert (cda->nves, hap, hap); + } +} + +static void +clear_vnc_responses (struct rfapi_local_reg_delete_arg *cda) +{ + struct rfapi *h; + struct rfapi_descriptor *rfd; + int query_count = 0; + struct listnode *node; + struct bgp *bgp_default = bgp_get_default (); + + if (cda->vn_address.addr_family && cda->un_address.addr_family) + { + /* + * Single nve case + */ + if (rfapi_find_rfd + (bgp_default, &cda->vn_address, &cda->un_address, &rfd)) + return; + + rfapiRibClear (rfd); + rfapi_query_done_all (rfd, &query_count); + cda->query_count += query_count; + + /* + * Track unique nves seen + */ + record_nve_in_cda_list (cda, &rfd->un_addr, &rfd->vn_addr, rfd); + return; + } + + /* + * wildcard case + */ + + if (!bgp_default) + return; /* ENXIO */ + + h = bgp_default->rfapi; + + if (!h) + return; /* ENXIO */ + + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + /* + * match un, vn addresses of NVEs + */ + if (cda->un_address.addr_family && + rfapi_ip_addr_cmp (&cda->un_address, &rfd->un_addr)) + { + continue; + } + if (cda->vn_address.addr_family && + rfapi_ip_addr_cmp (&cda->vn_address, &rfd->vn_addr)) + { + continue; + } + + rfapiRibClear (rfd); + + rfapi_query_done_all (rfd, &query_count); + cda->query_count += query_count; + + /* + * Track unique nves seen + */ + record_nve_in_cda_list (cda, &rfd->un_addr, &rfd->vn_addr, rfd); + } +} + +/* + * TBD need to count deleted prefixes and nves? + * + * ENXIO BGP or VNC not configured + */ +static int +rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) +{ + struct rfapi_ip_addr *pUn; /* NULL = wildcard */ + struct rfapi_ip_addr *pVn; /* NULL = wildcard */ + struct prefix *pPrefix; /* NULL = wildcard */ + + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + struct rfapi_ip_prefix rprefix; + struct bgp *bgp_default = bgp_get_default (); + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct rfapi_cfg *rfapi_cfg; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: entry", __func__); +#endif + + if (!bgp_default) + return ENXIO; + + pUn = (cda->un_address.addr_family ? &cda->un_address : NULL); + pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL); + pPrefix = (cda->prefix.family ? &cda->prefix : NULL); + + h = bgp_default->rfapi; + rfapi_cfg = bgp_default->rfapi_cfg; + + if (!h || !rfapi_cfg) + return ENXIO; + + if (pPrefix) + { + rfapiQprefix2Rprefix (pPrefix, &rprefix); + } + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: starting descriptor loop", __func__); +#endif + + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + struct rfapi_adb *adb; + int rc; + int deleted_from_this_nve; + struct nve_addr ha; + struct nve_addr *hap; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: rfd=%p", __func__, rfd); +#endif + + /* + * match un, vn addresses of NVEs + */ + if (pUn && (rfapi_ip_addr_cmp (pUn, &rfd->un_addr))) + continue; + if (pVn && (rfapi_ip_addr_cmp (pVn, &rfd->vn_addr))) + continue; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: un, vn match", __func__); +#endif + + /* + * match prefix + */ + + deleted_from_this_nve = 0; + + { + struct skiplist *sl; + struct rfapi_ip_prefix rp; + void *cursor; + struct list *adb_delete_list; + + /* + * The advertisements are stored in a skiplist. Withdrawing + * the registration deletes the advertisement from the + * skiplist, which we can't do while iterating over that + * same skiplist using the current skiplist API. + * + * Strategy: iterate over the skiplist and build another + * list containing only the matching ADBs. Then delete + * _everything_ in that second list (which can be done + * using either skiplists or quagga linklists). + */ + adb_delete_list = list_new (); + + /* + * Advertised IP prefixes (not 0/32 or 0/128) + */ + sl = rfd->advertised.ipN_by_prefix; + + for (cursor = NULL, + rc = skiplist_next (sl, NULL, (void **) &adb, &cursor); + !rc; rc = skiplist_next (sl, NULL, (void **) &adb, &cursor)) + { + + if (pPrefix) + { + if (!prefix_same (pPrefix, &adb->prefix_ip)) + { +#if DEBUG_L2_EXTRA + zlog_debug ("%s: adb=%p, prefix doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_MACADDR)) + { + if (memcmp + (cda->l2o.o.macaddr.octet, + adb->prefix_eth.u.prefix_eth.octet, ETHER_ADDR_LEN)) + { +#if DEBUG_L2_EXTRA + zlog_debug ("%s: adb=%p, macaddr doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + + if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_LNI)) + { + if (cda->l2o.o.logical_net_id != adb->l2o.logical_net_id) + { +#if DEBUG_L2_EXTRA + zlog_debug ("%s: adb=%p, LNI doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: ipN adding adb %p to delete list", __func__, + adb); +#endif + + listnode_add (adb_delete_list, adb); + } + + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (adb_delete_list, node, adb)) + { + + struct rfapi_vn_option vn1; + struct rfapi_vn_option vn2; + struct rfapi_vn_option *pVn; + int this_advertisement_prefix_count; + + this_advertisement_prefix_count = 1; + + rfapiQprefix2Rprefix (&adb->prefix_ip, &rp); + + /* if mac addr present in advert, make l2o vn option */ + if (adb->prefix_eth.family == AF_ETHERNET) + { + + memset (&vn1, 0, sizeof (vn1)); + memset (&vn2, 0, sizeof (vn2)); + + vn1.type = RFAPI_VN_OPTION_TYPE_L2ADDR; + vn1.v.l2addr.macaddr = adb->prefix_eth.u.prefix_eth; + + /* + * use saved RD value instead of trying to invert + * complex L2-style RD computation in rfapi_register() + */ + vn2.type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + vn2.v.internal_rd = adb->prd; + + vn1.next = &vn2; + + pVn = &vn1; + ++this_advertisement_prefix_count; + } + else + { + pVn = NULL; + } + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: ipN killing reg from adb %p ", __func__, adb); +#endif + + rc = rfapi_register (rfd, &rp, 0, NULL, pVn, RFAPI_REGISTER_KILL); + if (!rc) + { + cda->pfx_count += this_advertisement_prefix_count; + cda->reg_count += 1; + deleted_from_this_nve = 1; + } + if (h->rfp_methods.local_cb) + { + rfapiAddDeleteLocalRfpPrefix (&rfd->un_addr, &rfd->vn_addr, + &rp, 0, 0, NULL, &head, &tail); + } + } + list_delete_all_node (adb_delete_list); + + if (!(pPrefix && !RFAPI_0_PREFIX (pPrefix))) + { + void *cursor; + + /* + * Caller didn't specify a prefix, or specified (0/32 or 0/128) + */ + + /* + * Advertised 0/32 and 0/128 (indexed by ethernet address) + */ + sl = rfd->advertised.ip0_by_ether; + + for (cursor = NULL, + rc = skiplist_next (sl, NULL, (void **) &adb, &cursor); + !rc; rc = skiplist_next (sl, NULL, (void **) &adb, &cursor)) + { + + if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_MACADDR)) + { + if (memcmp (cda->l2o.o.macaddr.octet, + adb->prefix_eth.u.prefix_eth.octet, + ETHER_ADDR_LEN)) + { + + continue; + } + } + if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_LNI)) + { + if (cda->l2o.o.logical_net_id != adb->l2o.logical_net_id) + { + continue; + } + } +#if DEBUG_L2_EXTRA + zlog_debug ("%s: ip0 adding adb %p to delete list", + __func__, adb); +#endif + listnode_add (adb_delete_list, adb); + } + + + for (ALL_LIST_ELEMENTS_RO (adb_delete_list, node, adb)) + { + + struct rfapi_vn_option vn; + + rfapiQprefix2Rprefix (&adb->prefix_ip, &rp); + + memset (&vn, 0, sizeof (vn)); + vn.type = RFAPI_VN_OPTION_TYPE_L2ADDR; + vn.v.l2addr = adb->l2o; + +#if DEBUG_L2_EXTRA + zlog_debug ("%s: ip0 killing reg from adb %p ", + __func__, adb); +#endif + + rc = rfapi_register (rfd, &rp, 0, NULL, &vn, + RFAPI_REGISTER_KILL); + if (!rc) + { + cda->pfx_count += 1; + cda->reg_count += 1; + deleted_from_this_nve = 1; + } + if (h->rfp_methods.local_cb) + { + struct rfapi_vn_option *vn_opt_new; + + vn_opt_new = rfapi_vn_options_dup (&vn); + rfapiAddDeleteLocalRfpPrefix (&rfd->un_addr, + &rfd->vn_addr, &rp, 0, 0, + vn_opt_new, &head, &tail); + } + } + list_delete_all_node (adb_delete_list); + } + list_delete (adb_delete_list); + } + + + if (head) + { /* should not be set if (NULL == rfapi_cfg->local_cb) */ + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.local_cb) (head, rfd->cookie); + h->flags &= ~RFAPI_INCALLBACK; + head = tail = NULL; + } + + if (deleted_from_this_nve) + { + /* + * track unique NVEs seen + */ + memset (&ha, 0, sizeof (ha)); + ha.un = rfd->un_addr; + ha.vn = rfd->vn_addr; + + if (!cda->nves) + cda->nves = skiplist_new (0, nve_addr_cmp, nve_addr_free); + if (skiplist_search (cda->nves, &ha, (void **) &hap)) + { + hap = XCALLOC (MTYPE_RFAPI_NVE_ADDR, sizeof (struct nve_addr)); + assert (hap); + ha.cda = cda; + *hap = ha; + skiplist_insert (cda->nves, hap, hap); + } + } + } + + return 0; +} + +/* + * clear_vnc_prefix + * + * Deletes local and remote prefixes that match + */ +static void +clear_vnc_prefix (struct rfapi_local_reg_delete_arg *cda) +{ + struct prefix pfx_un; + struct prefix pfx_vn; + + struct prefix *pUN = NULL; + struct prefix *pVN = NULL; + struct prefix *pPrefix = NULL; + + /* + * Delete matching remote prefixes in holddown + */ + if (cda->vn_address.addr_family) + { + if (!rfapiRaddr2Qprefix (&cda->vn_address, &pfx_vn)) + pVN = &pfx_vn; + } + if (cda->un_address.addr_family) + { + if (!rfapiRaddr2Qprefix (&cda->un_address, &pfx_un)) + pUN = &pfx_un; + } + if (cda->prefix.family) + { + pPrefix = &cda->prefix; + } + rfapiDeleteRemotePrefixes (pUN, pVN, pPrefix, + 0, 1, &cda->remote_active_pfx_count, + &cda->remote_active_nve_count, + &cda->remote_holddown_pfx_count, + &cda->remote_holddown_nve_count); + + /* + * Now do local prefixes + */ + rfapiDeleteLocalPrefixes (cda); +} + +static void +print_cleared_stats (struct rfapi_local_reg_delete_arg *cda) +{ + struct vty *vty = cda->vty; /* for benefit of VTY_NEWLINE */ + + /* Our special element-deleting function counts nves */ + if (cda->nves) + { + skiplist_free (cda->nves); + cda->nves = NULL; + } + if (cda->failed_pfx_count) + vty_out (vty, "Failed to delete %d prefixes%s", + cda->failed_pfx_count, VTY_NEWLINE); + + /* left as "prefixes" even in single case for ease of machine parsing */ + vty_out (vty, + "[Local] Cleared %u registrations, %u prefixes, %u responses from %d NVEs%s", + cda->reg_count, cda->pfx_count, cda->query_count, cda->nve_count, + VTY_NEWLINE); + +/* + * We don't currently allow deletion of active remote prefixes from + * the command line + */ + + vty_out (vty, "[Holddown] Cleared %u prefixes from %u NVEs%s", + cda->remote_holddown_pfx_count, cda->remote_holddown_nve_count, + VTY_NEWLINE); +} + +/* + * Caller has already deleted registrations and queries for this/these + * NVEs. Now we just have to close their descriptors. + */ +static void +clear_vnc_nve_closer (struct rfapi_local_reg_delete_arg *cda) +{ + struct skiplist *sl = cda->nves; /* contains affected NVEs */ + struct nve_addr *pKey; + struct nve_addr *pValue; + void *cursor = NULL; + int rc; + + if (!sl) + return; + + for (rc = skiplist_next (sl, (void **) &pKey, (void **) &pValue, &cursor); + !rc; + rc = skiplist_next (sl, (void **) &pKey, (void **) &pValue, &cursor)) + { + + if (pValue->rfd) + { + ((struct rfapi_descriptor *) pValue->rfd)->flags |= + RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY; + rfapi_close (pValue->rfd); + } + } +} + +DEFUN (clear_vnc_nve_all, + clear_vnc_nve_all_cmd, + "clear vnc nve *", + "clear\n" + "VNC Information\n" "Clear per NVE information\n" "For all NVEs\n") +{ + + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_args (vty, NULL, NULL, NULL, NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses (&cda); + clear_vnc_prefix (&cda); + clear_vnc_nve_closer (&cda); + + print_cleared_stats (&cda); + + return 0; +} + +DEFUN (clear_vnc_nve_vn_un, + clear_vnc_nve_vn_un_cmd, + "clear vnc nve vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = + parse_deleter_args (vty, NULL, argv[0], argv[1], NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses (&cda); + clear_vnc_prefix (&cda); + clear_vnc_nve_closer (&cda); + + print_cleared_stats (&cda); + + return 0; +} + +DEFUN (clear_vnc_nve_un_vn, + clear_vnc_nve_un_vn_cmd, + "clear vnc nve un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = + parse_deleter_args (vty, NULL, argv[1], argv[0], NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses (&cda); + clear_vnc_prefix (&cda); + clear_vnc_nve_closer (&cda); + + print_cleared_stats (&cda); + + return 0; +} + +DEFUN (clear_vnc_nve_vn, + clear_vnc_nve_vn_cmd, + "clear vnc nve vn (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_args (vty, NULL, argv[0], NULL, NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses (&cda); + clear_vnc_prefix (&cda); + clear_vnc_nve_closer (&cda); + + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_nve_un, + clear_vnc_nve_un_cmd, + "clear vnc nve un (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_args (vty, NULL, NULL, argv[0], NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses (&cda); + clear_vnc_prefix (&cda); + clear_vnc_nve_closer (&cda); + + print_cleared_stats (&cda); + return 0; +} + +/*------------------------------------------------- + * Clear VNC Prefix + *-------------------------------------------------*/ + +/* + * This function is defined in this file (rather than in rfp_registration.c) + * because here we have access to all the task handles. + */ +DEFUN (clear_vnc_prefix_vn_un, + clear_vnc_prefix_vn_un_cmd, + "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = + parse_deleter_args (vty, argv[0], argv[1], argv[2], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_un_vn, + clear_vnc_prefix_un_vn_cmd, + "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = + parse_deleter_args (vty, argv[0], argv[2], argv[1], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_un, + clear_vnc_prefix_un_cmd, + "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) un (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = + parse_deleter_args (vty, argv[0], NULL, argv[1], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_vn, + clear_vnc_prefix_vn_cmd, + "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) vn (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = + parse_deleter_args (vty, argv[0], argv[1], NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_all, + clear_vnc_prefix_all_cmd, + "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) *", + "clear\n" + "VNC Information\n" + "Clear prefix registration infomation\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "From any NVE\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_args (vty, argv[0], NULL, NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +/*------------------------------------------------- + * Clear VNC MAC + *-------------------------------------------------*/ + +/* + * This function is defined in this file (rather than in rfp_registration.c) + * because here we have access to all the task handles. + */ +DEFUN (clear_vnc_mac_vn_un, + clear_vnc_mac_vn_un_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "Virtual network identifier\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, NULL, argv[2], argv[3], argv[0], argv[1], + &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un_vn, + clear_vnc_mac_un_vn_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, NULL, argv[3], argv[2], argv[0], argv[1], + &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un, + clear_vnc_mac_un_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, NULL, NULL, argv[2], argv[0], argv[1], &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_vn, + clear_vnc_mac_vn_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, NULL, argv[2], NULL, argv[0], argv[1], &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_all, + clear_vnc_mac_all_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) *", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "From any NVE\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, NULL, NULL, NULL, argv[0], argv[1], &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +/*------------------------------------------------- + * Clear VNC MAC PREFIX + *-------------------------------------------------*/ + +DEFUN (clear_vnc_mac_vn_un_prefix, + clear_vnc_mac_vn_un_prefix_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "Virtual network identifier\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Clear prefix registration infomation\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, argv[4], argv[2], argv[3], argv[0], argv[1], + &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un_vn_prefix, + clear_vnc_mac_un_vn_prefix_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M) prefix (*|A.B.C.D/M|X:X::X:X/M)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, argv[4], argv[3], argv[2], argv[0], argv[1], + &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un_prefix, + clear_vnc_mac_un_prefix_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, argv[3], NULL, argv[2], argv[0], argv[1], + &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_vn_prefix, + clear_vnc_mac_vn_prefix_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, argv[3], argv[2], NULL, argv[0], argv[1], + &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +DEFUN (clear_vnc_mac_all_prefix, + clear_vnc_mac_all_prefix_cmd, + "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) prefix (*|A.B.C.D/M|X:X::X:X/M)", + "clear\n" + "VNC Information\n" + "Clear mac registration infomation\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = + parse_deleter_args (vty, argv[2], NULL, NULL, argv[0], argv[1], &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix (&cda); + print_cleared_stats (&cda); + return 0; +} + +/************************************************************************ + * Show commands + ************************************************************************/ + + +/* copied from rfp_vty.c */ +static int +check_and_display_is_vnc_running (struct vty *vty) +{ + if (!bgp_rfapi_is_vnc_configured (NULL)) + return 1; /* is running */ + + if (vty) + { + vty_out (vty, + "VNC is not configured. (There are no configured BGP VPN SAFI peers.)%s", + VTY_NEWLINE); + } + return 0; /* not running */ +} + +static int +rfapi_vty_show_nve_summary (struct vty *vty, show_nve_summary_t show_type) +{ + struct bgp *bgp_default = bgp_get_default (); + struct rfapi *h; + int is_vnc_running = !bgp_rfapi_is_vnc_configured (bgp_default); + + int active_local_routes; + int active_remote_routes; + int holddown_remote_routes; + int imported_remote_routes; + + if (!bgp_default) + goto notcfg; + + h = bgp_default->rfapi; + + if (!h) + goto notcfg; + + /* don't show local info if not running RFP */ + if (is_vnc_running || show_type == SHOW_NVE_SUMMARY_REGISTERED) + { + + switch (show_type) + { + + case SHOW_NVE_SUMMARY_ACTIVE_NVES: + vty_out (vty, "%-24s ", "NVEs:"); + vty_out (vty, "%-8s %-8u ", "Active:", h->descriptors.count); + vty_out (vty, "%-8s %-8u ", "Maximum:", h->stat.max_descriptors); + vty_out (vty, "%-8s %-8u", "Unknown:", h->stat.count_unknown_nves); + break; + + case SHOW_NVE_SUMMARY_REGISTERED: + /* + * NB: With the introduction of L2 route support, we no + * longer have a one-to-one correspondence between + * locally-originated route advertisements and routes in + * the import tables that have local origin. This + * discrepancy arises because a single advertisement + * may contain both an IP prefix and a MAC address. + * Such an advertisement results in two import table + * entries: one indexed by IP prefix, the other indexed + * by MAC address. + * + * TBD: update computation and display of registration + * statistics to reflect the underlying semantics. + */ + if (is_vnc_running) + { + vty_out (vty, "%-24s ", "Registrations:"); + vty_out (vty, "%-8s %-8u ", "Active:", + rfapiApCountAll (bgp_default)); + vty_out (vty, "%-8s %-8u ", "Failed:", + h->stat.count_registrations_failed); + vty_out (vty, "%-8s %-8u", "Total:", + h->stat.count_registrations); + vty_out (vty, "%s", VTY_NEWLINE); + } + vty_out (vty, "%-24s ", "Prefixes registered:"); + vty_out (vty, "%s", VTY_NEWLINE); + + rfapiCountAllItRoutes (&active_local_routes, + &active_remote_routes, + &holddown_remote_routes, + &imported_remote_routes); + + /* local */ + if (is_vnc_running) + { + vty_out (vty, " %-20s ", "Locally:"); + vty_out (vty, "%-8s %-8u ", "Active:", active_local_routes); + vty_out (vty, "%s", VTY_NEWLINE); + } + + + vty_out (vty, " %-20s ", "Remotely:"); + vty_out (vty, "%-8s %-8u", "Active:", active_remote_routes); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " %-20s ", "In Holddown:"); + vty_out (vty, "%-8s %-8u", "Active:", holddown_remote_routes); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " %-20s ", "Imported:"); + vty_out (vty, "%-8s %-8u", "Active:", imported_remote_routes); + break; + + case SHOW_NVE_SUMMARY_QUERIES: + vty_out (vty, "%-24s ", "Queries:"); + vty_out (vty, "%-8s %-8u ", "Active:", rfapi_monitor_count (NULL)); + vty_out (vty, "%-8s %-8u ", "Failed:", + h->stat.count_queries_failed); + vty_out (vty, "%-8s %-8u", "Total:", h->stat.count_queries); + break; + + case SHOW_NVE_SUMMARY_RESPONSES: + rfapiRibShowResponsesSummary (vty); + + default: + break; + } + vty_out (vty, "%s", VTY_NEWLINE); + } + return 0; + +notcfg: + vty_out (vty, "VNC is not configured.%s", VTY_NEWLINE); + return CMD_WARNING; +} + +static int +rfapi_show_nves ( + struct vty *vty, + struct prefix *vn_prefix, + struct prefix *un_prefix) +{ + //struct hash *rfds; + //struct rfp_rfapi_descriptor_param param; + + struct bgp *bgp_default = bgp_get_default (); + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + + int total = 0; + int printed = 0; + int rc; + + if (!bgp_default) + goto notcfg; + + h = bgp_default->rfapi; + + if (!h) + goto notcfg; + + rc = rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_ACTIVE_NVES); + if (rc) + return rc; + + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + struct prefix pfx; + char vn_addr_buf[INET6_ADDRSTRLEN] = + { + 0,}; + char un_addr_buf[INET6_ADDRSTRLEN] = + { + 0,}; + char age[10]; + + ++total; + + if (vn_prefix) + { + assert (!rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx)); + if (!prefix_match (vn_prefix, &pfx)) + continue; + } + + if (un_prefix) + { + assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx)); + if (!prefix_match (un_prefix, &pfx)) + continue; + } + + rfapiRfapiIpAddr2Str (&rfd->vn_addr, vn_addr_buf, INET6_ADDRSTRLEN); + rfapiRfapiIpAddr2Str (&rfd->un_addr, un_addr_buf, INET6_ADDRSTRLEN); + + if (!printed) + { + /* print out a header */ + vty_out (vty, " " + "Active Next Hops%s", VTY_NEWLINE); + vty_out (vty, "%-15s %-15s %-5s %-5s %-6s %-6s %s%s", + "VN Address", + "UN Address", + "Regis", "Resps", "Reach", "Remove", "Age", VTY_NEWLINE); + } + + ++printed; + + vty_out (vty, "%-15s %-15s %-5u %-5u %-6u %-6u %s%s", + vn_addr_buf, + un_addr_buf, + rfapiApCount (rfd), + rfapi_monitor_count (rfd), + rfd->stat_count_nh_reachable, + rfd->stat_count_nh_removal, + rfapiFormatAge (rfd->open_time, age, 10), VTY_NEWLINE); + } + + if (printed > 0 || vn_prefix || un_prefix) + vty_out (vty, "Displayed %d out of %d active NVEs%s", + printed, total, VTY_NEWLINE); + + return 0; + +notcfg: + vty_out (vty, "VNC is not configured.%s", VTY_NEWLINE); + return CMD_WARNING; +} + + +DEFUN (vnc_show_summary, + vnc_show_summary_cmd, + "show vnc summary", + SHOW_STR + VNC_SHOW_STR + "Display VNC status summary\n") +{ + if (!check_and_display_is_vnc_running (vty)) + return CMD_SUCCESS; + bgp_rfapi_show_summary (bgp_get_default (), vty); + vty_out (vty, "%s", VTY_NEWLINE); + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_ACTIVE_NVES); + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES); + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_RESPONSES); + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_REGISTERED); + return CMD_SUCCESS; +} + +DEFUN (vnc_show_nves, + vnc_show_nves_cmd, + "show vnc nves", + SHOW_STR + VNC_SHOW_STR + "List known NVEs\n") +{ + rfapi_show_nves (vty, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN (vnc_show_nves_ptct, + vnc_show_nves_ptct_cmd, + "show vnc nves (vn|un) (A.B.C.D|X:X::X:X)", + SHOW_STR + VNC_SHOW_STR + "List known NVEs\n" + "VN address of NVE\n" + "UN address of NVE\n" + "IPv4 interface address\n" + "IPv6 interface address\n") +{ + struct prefix pfx; + + if (!check_and_display_is_vnc_running (vty)) + return CMD_SUCCESS; + + if (!str2prefix (argv[1], &pfx)) + { + vty_out (vty, "Malformed address \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Invalid address \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (*(argv[0]) == 'u') + { + rfapi_show_nves (vty, NULL, &pfx); + } + else + { + rfapi_show_nves (vty, &pfx, NULL); + } + + return CMD_SUCCESS; +} + +/* adapted from rfp_registration_cache_log() */ +static void +rfapi_show_registrations ( + struct vty *vty, + struct prefix *restrict_to, + int show_local, + int show_remote, + int show_holddown, + int show_imported) +{ + int printed = 0; + + if (!vty) + return; + + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_REGISTERED); + + if (show_local) + { + /* non-expiring, local */ + printed += rfapiShowRemoteRegistrations (vty, restrict_to, 0, 1, 0, 0); + } + if (show_remote) + { + /* non-expiring, non-local */ + printed += rfapiShowRemoteRegistrations (vty, restrict_to, 0, 0, 1, 0); + } + if (show_holddown) + { + /* expiring, including local */ + printed += rfapiShowRemoteRegistrations (vty, restrict_to, 1, 1, 1, 0); + } + if (show_imported) + { + /* non-expiring, non-local */ + printed += rfapiShowRemoteRegistrations (vty, restrict_to, 0, 0, 1, 1); + } + if (!printed) + { + vty_out (vty, "%s", VTY_NEWLINE); + } +} + +DEFUN (vnc_show_registrations_pfx, + vnc_show_registrations_pfx_cmd, + "show vnc registrations ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])", + SHOW_STR + VNC_SHOW_STR + "List active prefix registrations\n" + "Limit output to a particular prefix or address\n" + "Limit output to a particular prefix or address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + if (argc == 1) + { + if (!str2prefix (argv[0], &p)) + { + vty_out (vty, "Invalid prefix: %s%s", argv[0], VTY_NEWLINE); + return CMD_SUCCESS; + } + else + { + p_addr = &p; + } + } + + rfapi_show_registrations (vty, p_addr, 1, 1, 1, 1); + return CMD_SUCCESS; +} + +ALIAS (vnc_show_registrations_pfx, + vnc_show_registrations_cmd, + "show vnc registrations", + SHOW_STR + VNC_SHOW_STR + "List active prefix registrations\n") + DEFUN (vnc_show_registrations_some_pfx, + vnc_show_registrations_some_pfx_cmd, + "show vnc registrations (all|holddown|imported|local|remote) ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])", + SHOW_STR + VNC_SHOW_STR + "List active prefix registrations\n" + "show all registrations\n" + "show only registrations in holddown\n" + "show only imported prefixes\n" + "show only local registrations\n" + "show only remote registrations\n" + "Limit output to a particular prefix or address\n" + "Limit output to a particular prefix or address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + int show_local = 0; + int show_remote = 0; + int show_holddown = 0; + int show_imported = 0; + + if (argc == 2) + { + if (!str2prefix (argv[1], &p)) + { + vty_out (vty, "Invalid prefix: %s%s", argv[1], VTY_NEWLINE); + return CMD_SUCCESS; + } + else + { + p_addr = &p; + } + } + switch (*argv[0]) + { + case 'a': + show_local = 1; + show_remote = 1; + show_holddown = 1; + show_imported = 1; + break; + + case 'h': + show_holddown = 1; + break; + + case 'i': + show_imported = 1; + break; + + case 'l': + show_local = 1; + break; + + case 'r': + show_remote = 1; + break; + } + + rfapi_show_registrations (vty, p_addr, + show_local, show_remote, show_holddown, + show_imported); + return CMD_SUCCESS; +} + +ALIAS (vnc_show_registrations_some_pfx, + vnc_show_registrations_some_cmd, + "show vnc registrations (all|holddown|imported|local|remote)", + SHOW_STR + VNC_SHOW_STR + "List active prefix registrations\n" + "show all registrations\n" + "show only registrations in holddown\n" + "show only imported prefixes\n" + "show only local registrations\n" + "show only remote registrations\n") + +DEFUN (vnc_show_responses_pfx, + vnc_show_responses_pfx_cmd, + "show vnc responses ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])", + SHOW_STR + VNC_SHOW_STR + "List recent query responses\n" + "Limit output to a particular prefix or address\n" + "Limit output to a particular prefix or address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + if (argc == 1) + { + if (!str2prefix (argv[0], &p)) + { + vty_out (vty, "Invalid prefix: %s%s", argv[0], VTY_NEWLINE); + return CMD_SUCCESS; + } + else + { + p_addr = &p; + } + } + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES); + + rfapiRibShowResponsesSummary (vty); + + rfapiRibShowResponses (vty, p_addr, 0); + rfapiRibShowResponses (vty, p_addr, 1); + + return CMD_SUCCESS; +} + +ALIAS (vnc_show_responses_pfx, + vnc_show_responses_cmd, + "show vnc responses", + SHOW_STR + VNC_SHOW_STR + "List recent query responses\n") + +DEFUN (vnc_show_responses_some_pfx, + vnc_show_responses_some_pfx_cmd, + "show vnc responses (active|removed) ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])", + SHOW_STR + VNC_SHOW_STR + "List recent query responses\n" + "show only active query responses\n" + "show only removed query responses\n" + "Limit output to a particular prefix or address\n" + "Limit output to a particular prefix or address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + int show_active = 0; + int show_removed = 0; + + if (!check_and_display_is_vnc_running (vty)) + return CMD_SUCCESS; + + if (argc == 2) + { + if (!str2prefix (argv[1], &p)) + { + vty_out (vty, "Invalid prefix: %s%s", argv[1], VTY_NEWLINE); + return CMD_SUCCESS; + } + else + { + p_addr = &p; + } + } + + switch (*argv[0]) + { + case 'a': + show_active = 1; + break; + + case 'r': + show_removed = 1; + break; + } + + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES); + + rfapiRibShowResponsesSummary (vty); + + if (show_active) + rfapiRibShowResponses (vty, p_addr, 0); + if (show_removed) + rfapiRibShowResponses (vty, p_addr, 1); + + return CMD_SUCCESS; +} + +ALIAS (vnc_show_responses_some_pfx, + vnc_show_responses_some_cmd, + "show vnc responses (active|removed)", + SHOW_STR + VNC_SHOW_STR + "List recent query responses\n" + "show only active query responses\n" + "show only removed query responses\n") + +DEFUN (show_vnc_queries_pfx, + show_vnc_queries_pfx_cmd, + "show vnc queries ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])", + SHOW_STR + VNC_SHOW_STR + "List active queries\n" + "Limit output to a particular IPv4 prefix or address\n" + "Limit output to a particular IPv6 prefix or address\n") +{ + struct prefix pfx; + struct prefix *p = NULL; + + if (argc == 1) + { + if (!str2prefix (argv[0], &pfx)) + { + vty_out (vty, "Invalid prefix: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + p = &pfx; + } + + rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES); + + return rfapiShowVncQueries (vty, p); +} + +ALIAS (show_vnc_queries_pfx, + show_vnc_queries_cmd, + "show vnc queries", + SHOW_STR + VNC_SHOW_STR + "List active queries\n") + +DEFUN (vnc_clear_counters, + vnc_clear_counters_cmd, + "clear vnc counters", + CLEAR_STR + VNC_SHOW_STR + "Reset VNC counters\n") +{ + struct bgp *bgp_default = bgp_get_default (); + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + + if (!bgp_default) + goto notcfg; + + h = bgp_default->rfapi; + + if (!h) + goto notcfg; + + /* per-rfd */ + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + rfd->stat_count_nh_reachable = 0; + rfd->stat_count_nh_removal = 0; + } + + /* global */ + memset (&h->stat, 0, sizeof (h->stat)); + + /* + * 151122 per bug 103, set count_registrations = number active. + * Do same for queries + */ + h->stat.count_registrations = rfapiApCountAll (bgp_default); + h->stat.count_queries = rfapi_monitor_count (NULL); + + rfapiRibShowResponsesSummaryClear (); + + return CMD_SUCCESS; + +notcfg: + vty_out (vty, "VNC is not configured.%s", VTY_NEWLINE); + return CMD_WARNING; +} + +void rfapi_vty_init () +{ + install_element (ENABLE_NODE, &add_vnc_prefix_cost_life_lnh_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_life_cost_lnh_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_cost_lnh_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_life_lnh_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_lnh_cmd); + + install_element (ENABLE_NODE, &add_vnc_prefix_cost_life_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_life_cost_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_cost_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_life_cmd); + install_element (ENABLE_NODE, &add_vnc_prefix_cmd); + + install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_life_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_life_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_cost_life_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_cost_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_life_cmd); + install_element (ENABLE_NODE, &add_vnc_mac_vni_cmd); + + install_element (ENABLE_NODE, &clear_vnc_nve_all_cmd); + install_element (ENABLE_NODE, &clear_vnc_nve_vn_un_cmd); + install_element (ENABLE_NODE, &clear_vnc_nve_un_vn_cmd); + install_element (ENABLE_NODE, &clear_vnc_nve_vn_cmd); + install_element (ENABLE_NODE, &clear_vnc_nve_un_cmd); + + install_element (ENABLE_NODE, &clear_vnc_prefix_vn_un_cmd); + install_element (ENABLE_NODE, &clear_vnc_prefix_un_vn_cmd); + install_element (ENABLE_NODE, &clear_vnc_prefix_un_cmd); + install_element (ENABLE_NODE, &clear_vnc_prefix_vn_cmd); + install_element (ENABLE_NODE, &clear_vnc_prefix_all_cmd); + + install_element (ENABLE_NODE, &clear_vnc_mac_vn_un_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_un_vn_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_un_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_vn_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_all_cmd); + + install_element (ENABLE_NODE, &clear_vnc_mac_vn_un_prefix_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_un_vn_prefix_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_un_prefix_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_vn_prefix_cmd); + install_element (ENABLE_NODE, &clear_vnc_mac_all_prefix_cmd); + + install_element (ENABLE_NODE, &vnc_clear_counters_cmd); + + install_element (VIEW_NODE, &vnc_show_summary_cmd); + install_element (ENABLE_NODE, &vnc_show_summary_cmd); + install_element (VIEW_NODE, &vnc_show_nves_cmd); + install_element (ENABLE_NODE, &vnc_show_nves_cmd); + install_element (VIEW_NODE, &vnc_show_nves_ptct_cmd); + install_element (ENABLE_NODE, &vnc_show_nves_ptct_cmd); + + install_element (VIEW_NODE, &vnc_show_registrations_cmd); + install_element (ENABLE_NODE, &vnc_show_registrations_cmd); + install_element (VIEW_NODE, &vnc_show_registrations_pfx_cmd); + install_element (ENABLE_NODE, &vnc_show_registrations_pfx_cmd); + + install_element (VIEW_NODE, &vnc_show_registrations_some_cmd); + install_element (ENABLE_NODE, &vnc_show_registrations_some_cmd); + install_element (VIEW_NODE, &vnc_show_registrations_some_pfx_cmd); + install_element (ENABLE_NODE, &vnc_show_registrations_some_pfx_cmd); + + install_element (VIEW_NODE, &vnc_show_responses_cmd); + install_element (ENABLE_NODE, &vnc_show_responses_cmd); + install_element (VIEW_NODE, &vnc_show_responses_pfx_cmd); + install_element (ENABLE_NODE, &vnc_show_responses_pfx_cmd); + + install_element (VIEW_NODE, &vnc_show_responses_some_cmd); + install_element (ENABLE_NODE, &vnc_show_responses_some_cmd); + install_element (VIEW_NODE, &vnc_show_responses_some_pfx_cmd); + install_element (ENABLE_NODE, &vnc_show_responses_some_pfx_cmd); + + install_element (ENABLE_NODE, &show_vnc_queries_cmd); + install_element (VIEW_NODE, &show_vnc_queries_cmd); + install_element (ENABLE_NODE, &show_vnc_queries_pfx_cmd); + install_element (VIEW_NODE, &show_vnc_queries_pfx_cmd); +} diff --git a/bgpd/rfapi/rfapi_vty.h b/bgpd/rfapi/rfapi_vty.h new file mode 100644 index 0000000000..08c8e1cf41 --- /dev/null +++ b/bgpd/rfapi/rfapi_vty.h @@ -0,0 +1,223 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef RFAPI_VTY_H +#define RFAPI_VTY_H + +#include "vty.h" + +typedef enum +{ + SHOW_NVE_SUMMARY_ACTIVE_NVES, + SHOW_NVE_SUMMARY_UNKNOWN_NVES, /* legacy */ + SHOW_NVE_SUMMARY_REGISTERED, + SHOW_NVE_SUMMARY_QUERIES, + SHOW_NVE_SUMMARY_RESPONSES, + SHOW_NVE_SUMMARY_MAX +} show_nve_summary_t; + +#define VNC_SHOW_STR "VNC information\n" + +extern char * +rfapiFormatSeconds (uint32_t seconds, char *buf, size_t len); + +extern char * +rfapiFormatAge (time_t age, char *buf, size_t len); + +extern void +rfapiRprefixApplyMask (struct rfapi_ip_prefix *rprefix); + +extern int +rfapiQprefix2Raddr (struct prefix *qprefix, struct rfapi_ip_addr *raddr); + +extern void +rfapiQprefix2Rprefix (struct prefix *qprefix, + struct rfapi_ip_prefix *rprefix); + +extern int +rfapiRprefix2Qprefix (struct rfapi_ip_prefix *rprefix, + struct prefix *qprefix); + +extern int +rfapiRaddr2Qprefix (struct rfapi_ip_addr *hia, struct prefix *pfx); + +extern int +rfapiRprefixSame (struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2); + +extern void +rfapiL2o2Qprefix (struct rfapi_l2address_option *l2o, struct prefix *pfx); + +extern int +rfapiStr2EthAddr (const char *str, struct ethaddr *ea); + +extern const char * +rfapi_ntop ( + int af, + const void *src, + char *buf, + socklen_t size); + +extern int +rfapiDebugPrintf (void *dummy, const char *format, ...); + +extern int +rfapiStream2Vty ( + void *stream, /* input */ + int (**fp) (void *, const char *, ...), /* output */ + struct vty **vty, /* output */ + void **outstream, /* output */ + const char **vty_newline); /* output */ + +/*------------------------------------------ + * rfapiRfapiIpAddr2Str + * + * UI helper: generate string from rfapi_ip_addr + * + * input: + * a IP v4/v6 address + * + * output + * buf put string here + * bufsize max space to write + * + * return value: + * NULL conversion failed + * non-NULL pointer to buf + --------------------------------------------*/ +extern const char * +rfapiRfapiIpAddr2Str (struct rfapi_ip_addr *a, char *buf, int bufsize); + +extern void +rfapiPrintRfapiIpAddr (void *stream, struct rfapi_ip_addr *a); + +extern void +rfapiPrintRfapiIpPrefix (void *stream, struct rfapi_ip_prefix *p); + +void +rfapiPrintRd (struct vty *vty, struct prefix_rd *prd); + +extern void +rfapiPrintAdvertisedInfo ( + struct vty *vty, + struct rfapi_descriptor *rfd, + safi_t safi, + struct prefix *p); + +extern void +rfapiPrintDescriptor (struct vty *vty, struct rfapi_descriptor *rfd); + +extern void +rfapiPrintMatchingDescriptors (struct vty *vty, + struct prefix *vn_prefix, + struct prefix *un_prefix); + +extern void +rfapiPrintAttrPtrs (void *stream, struct attr *attr); + +/* + * Parse an address and put into a struct prefix + */ +extern int +rfapiCliGetPrefixAddr (struct vty *vty, const char *str, struct prefix *p); + +extern int +rfapiCliGetRfapiIpAddr ( + struct vty *vty, + const char *str, + struct rfapi_ip_addr *hai); + +extern void +rfapiPrintNhl (void *stream, struct rfapi_next_hop_entry *next_hops); + +extern char * +rfapiMonitorVpn2Str ( + struct rfapi_monitor_vpn *m, + char *buf, + int size); + +extern const char * +rfapiRfapiIpPrefix2Str ( + struct rfapi_ip_prefix *p, + char *buf, + int bufsize); + +extern void +rfapiShowItNode (void *stream, struct route_node *rn); + +extern char * +rfapiEthAddr2Str ( + const struct ethaddr *ea, + char *buf, + int bufsize); + +/* install vty commands */ +extern void +rfapi_vty_init (void); + +/*------------------------------------------ + * rfapiShowRemoteRegistrations + * + * UI helper: produces the "remote" portion of the output + * of "show vnc registrations". + * + * input: + * stream pointer to output stream + * prefix_only pointer to prefix. If non-NULL, print only registrations + * matching the specified prefix + * show_expiring if non-zero, show expiring registrations + * show_local if non-zero, show local registrations + * show_imported if non-zero, show imported registrations + * + * return value: + * 0 nothing printed + * >0 something printed + --------------------------------------------*/ +extern int +rfapiShowRemoteRegistrations ( + void *stream, + struct prefix *prefix_only, + int show_expiring, + int show_local, + int show_remote, + int show_imported); + +/*------------------------------------------ + * rfapi_monitor_count + * + * UI helper: count number of active monitors + * + * input: + * handle rfapi handle (NULL to count across + * all open handles) + * + * output + * + * return value: + * count of monitors + --------------------------------------------*/ +extern uint32_t +rfapi_monitor_count (rfapi_handle); + +extern int +rfapiShowVncQueries (void *stream, struct prefix *pfx_match); + + +#endif diff --git a/bgpd/rfapi/vnc_debug.c b/bgpd/rfapi/vnc_debug.c new file mode 100644 index 0000000000..8f45781592 --- /dev/null +++ b/bgpd/rfapi/vnc_debug.c @@ -0,0 +1,230 @@ +/* + * + * Copyright 2016, LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include "prefix.h" +#include "linklist.h" +#include "stream.h" +#include "command.h" +#include "str.h" +#include "log.h" +#include "vnc_debug.h" + +/* + * debug state storage + */ +unsigned long conf_vnc_debug; +unsigned long term_vnc_debug; + +struct vnc_debug { + unsigned long bit; + const char *name; +}; + +struct vnc_debug vncdebug[] = +{ + {VNC_DEBUG_RFAPI_QUERY, "rfapi-query"}, + {VNC_DEBUG_IMPORT_BI_ATTACH, "import-bi-attach"}, + {VNC_DEBUG_IMPORT_DEL_REMOTE, "import-del-remote"}, + {VNC_DEBUG_EXPORT_BGP_GETCE, "export-bgp-getce"}, + {VNC_DEBUG_EXPORT_BGP_DIRECT_ADD, "export-bgp-direct-add"}, + {VNC_DEBUG_IMPORT_BGP_ADD_ROUTE, "import-bgp-add-route"}, +}; + +#define VNC_STR "VNC information\n" + +/*********************************************************************** + * debug bgp vnc + ***********************************************************************/ +DEFUN (debug_bgp_vnc, + debug_bgp_vnc_cmd, + "debug bgp vnc (rfapi-query|import-bi-attach|import-del-remote)", + DEBUG_STR + BGP_STR + VNC_STR + "rfapi query handling\n" + "import BI atachment\n" + "import delete remote routes\n") +{ + size_t i; + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) + { + if (!strcmp(argv[0], vncdebug[i].name)) + { + if (vty->node == CONFIG_NODE) + { + conf_vnc_debug |= vncdebug[i].bit; + term_vnc_debug |= vncdebug[i].bit; + } + else + { + term_vnc_debug |= vncdebug[i].bit; + vty_out (vty, "BGP vnc %s debugging is on%s", + vncdebug[i].name, VTY_NEWLINE); + } + return CMD_SUCCESS; + } + } + vty_out (vty, "Unknown debug flag: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; +} + +DEFUN (no_debug_bgp_vnc, + no_debug_bgp_vnc_cmd, + "no debug bgp vnc (rfapi-query|import-bi-attach|import-del-remote)", + NO_STR + DEBUG_STR + BGP_STR + VNC_STR + "rfapi query handling\n" + "import BI atachment\n" + "import delete remote routes\n") +{ + size_t i; + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) + { + if (!strcmp(argv[0], vncdebug[i].name)) + { + if (vty->node == CONFIG_NODE) + { + conf_vnc_debug &= ~vncdebug[i].bit; + term_vnc_debug &= ~vncdebug[i].bit; + } + else + { + term_vnc_debug &= ~vncdebug[i].bit; + vty_out (vty, "BGP vnc %s debugging is off%s", + vncdebug[i].name, VTY_NEWLINE); + } + return CMD_SUCCESS; + } + } + vty_out (vty, "Unknown debug flag: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; +} + +ALIAS (no_debug_bgp_vnc, + undebug_bgp_vnc_cmd, + "undebug bgp vnc (rfapi-query|import-bi-attach|import-del-remote)", + UNDEBUG_STR + BGP_STR + VNC_STR + "rfapi query handling\n" + "import BI atachment\n" + "import delete remote routes\n") + + +/*********************************************************************** + * no debug bgp vnc all + ***********************************************************************/ + +DEFUN (no_debug_bgp_vnc_all, + no_debug_bgp_vnc_all_cmd, + "no debug all bgp vnc", + NO_STR + DEBUG_STR + "Disable all VNC debugging\n" + BGP_STR + VNC_STR) +{ + term_vnc_debug = 0; + vty_out (vty, "All possible VNC debugging has been turned off%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_vnc_all, + undebug_bgp_vnc_all_cmd, + "undebug all bgp vnc", + UNDEBUG_STR + "Disable all VNC debugging\n" + BGP_STR + VNC_STR) + +/*********************************************************************** + * show/save + ***********************************************************************/ + +DEFUN (show_debugging_bgp_vnc, + show_debugging_bgp_vnc_cmd, + "show debugging bgp vnc", + SHOW_STR + DEBUG_STR + BGP_STR + VNC_STR) +{ + size_t i; + + vty_out (vty, "BGP VNC debugging status:%s", VTY_NEWLINE); + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) + { + if (term_vnc_debug & vncdebug[i].bit) + { + vty_out (vty, " BGP VNC %s debugging is on%s", + vncdebug[i].name, VTY_NEWLINE); + } + } + vty_out (vty, "%s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +static int +bgp_vnc_config_write_debug (struct vty *vty) +{ + int write = 0; + size_t i; + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) + { + if (conf_vnc_debug & vncdebug[i].bit) + { + vty_out (vty, "debug bgp vnc %s%s", vncdebug[i].name, VTY_NEWLINE); + write++; + } + } + return write; +} + +static struct cmd_node debug_node = +{ + DEBUG_VNC_NODE, + "", + 1 +}; + +void +vnc_debug_init (void) +{ + install_node (&debug_node, bgp_vnc_config_write_debug); + install_element (ENABLE_NODE, &show_debugging_bgp_vnc_cmd); + + install_element (ENABLE_NODE, &debug_bgp_vnc_cmd); + install_element (CONFIG_NODE, &debug_bgp_vnc_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_vnc_cmd); + install_element (ENABLE_NODE, &undebug_bgp_vnc_cmd); + + install_element (ENABLE_NODE, &no_debug_bgp_vnc_all_cmd); + install_element (ENABLE_NODE, &undebug_bgp_vnc_all_cmd); +} diff --git a/bgpd/rfapi/vnc_debug.h b/bgpd/rfapi/vnc_debug.h new file mode 100644 index 0000000000..9d4270651e --- /dev/null +++ b/bgpd/rfapi/vnc_debug.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2016, LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_VNC_DEBUG_H +#define _QUAGGA_BGP_VNC_DEBUG_H + +#if ENABLE_BGP_VNC + +/* + * debug state storage + */ +extern unsigned long conf_vnc_debug; +extern unsigned long term_vnc_debug; + +/* + * debug flag bits + */ +#define VNC_DEBUG_RFAPI_QUERY 0x00000001 +#define VNC_DEBUG_IMPORT_BI_ATTACH 0x00000002 +#define VNC_DEBUG_IMPORT_DEL_REMOTE 0x00000004 +#define VNC_DEBUG_EXPORT_BGP_GETCE 0x00000008 +#define VNC_DEBUG_EXPORT_BGP_DIRECT_ADD 0x00000010 +#define VNC_DEBUG_IMPORT_BGP_ADD_ROUTE 0x00000020 + +#define VNC_DEBUG(bit) (term_vnc_debug & (VNC_DEBUG_ ## bit)) + +extern void +vnc_debug_init (void); + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_VNC_DEBUG_H */ diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c new file mode 100644 index 0000000000..f0a6289224 --- /dev/null +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -0,0 +1,2177 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: vnc_export_bgp.c + * Purpose: Export routes to BGP directly (not via zebra) + */ + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "log.h" +#include "stream.h" +#include "memory.h" +#include "linklist.h" +#include "plist.h" +#include "routemap.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_aspath.h" + +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_export_table.h" +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_backend.h" +#include "rfapi_vty.h" +#include "vnc_debug.h" + +/*********************************************************************** + * Export methods that set nexthop to CE (from 5226 roo EC) BEGIN + ***********************************************************************/ + +/* + * Memory allocation approach: make a ghost attr that + * has non-interned parts for the modifications. ghost attr + * memory is allocated by caller. + * + * - extract ce (=5226) EC and use as new nexthop + * - strip Tunnel Encap attr + * - copy all ECs + */ +static void +encap_attr_export_ce ( + struct attr *new, + struct attr *orig, + struct prefix *use_nexthop) +{ + /* + * Make "new" a ghost attr copy of "orig" + */ + memset (new, 0, sizeof (struct attr)); + bgp_attr_dup (new, orig); + bgp_attr_extra_get (new); + bgp_attr_flush_encap (new); + + /* + * Set nexthop + */ + switch (use_nexthop->family) + { + case AF_INET: + new->nexthop = use_nexthop->u.prefix4; + new->extra->mp_nexthop_len = 4; /* bytes */ + new->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); + break; + + case AF_INET6: + if (!new->extra) + { + new->extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra)); + } + new->extra->mp_nexthop_global = use_nexthop->u.prefix6; + new->extra->mp_nexthop_len = 16; /* bytes */ + break; + + default: + assert (0); + break; + } + + /* + * Set MED + * + * Note that it will be deleted when BGP sends to any eBGP + * peer unless PEER_FLAG_MED_UNCHANGED is set: + * + * neighbor NEIGHBOR attribute-unchanged med + */ + if (!CHECK_FLAG (new->flag, BGP_ATTR_MULTI_EXIT_DISC)) + { + if (CHECK_FLAG (new->flag, BGP_ATTR_LOCAL_PREF)) + { + if (new->local_pref > 255) + new->med = 0; + else + new->med = 255 - new->local_pref; + } + else + { + new->med = 255; /* shouldn't happen */ + } + new->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + } + + /* + * "new" is now a ghost attr: + * - it owns an "extra" struct + * - it owns any non-interned parts + * - any references to interned parts are not counted + * + * Caller should, after using the attr, call: + * - bgp_attr_flush() to free non-interned parts + * - call bgp_attr_extra_free() to free extra + */ +} + +static int +getce (struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce) +{ + uint8_t *ecp; + int i; + uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin; + + for (ecp = attr->extra->ecommunity->val, i = 0; + i < attr->extra->ecommunity->size; ++i, ecp += ECOMMUNITY_SIZE) + { + + if (VNC_DEBUG(EXPORT_BGP_GETCE)) + { + zlog_debug ("%s: %02x %02x %02x %02x %02x %02x %02x %02x", + __func__, + ecp[0], ecp[1], ecp[2], ecp[3], ecp[4], ecp[5], ecp[6], + ecp[7]); + } + + /* + * is it ROO? + */ + if (ecp[0] != 1 || ecp[1] != 3) + { + continue; + } + + /* + * Match local admin value? + */ + if (ecp[6] != ((localadmin & 0xff00) >> 8) || + ecp[7] != (localadmin & 0xff)) + continue; + + memset ((uint8_t *) pfx_ce, 0, sizeof (*pfx_ce)); + memcpy (&pfx_ce->u.prefix4, ecp + 2, 4); + pfx_ce->family = AF_INET; + pfx_ce->prefixlen = 32; + + return 0; + } + return -1; +} + + +void +vnc_direct_bgp_add_route_ce ( + struct bgp *bgp, + struct route_node *rn, + struct bgp_info *bi) +{ + struct attr *attr = bi->attr; + struct peer *peer = bi->peer; + struct prefix *prefix = &rn->p; + afi_t afi = family2afi (prefix->family); + struct bgp_node *urn; + struct bgp_info *ubi; + struct attr hattr; + struct attr *iattr; + struct prefix ce_nexthop; + struct prefix post_routemap_nexthop; + + + if (!afi) + { + zlog_err ("%s: can't get afi of route node", __func__); + return; + } + + if ((bi->type != ZEBRA_ROUTE_BGP) || + (bi->sub_type != BGP_ROUTE_NORMAL && + bi->sub_type != BGP_ROUTE_RFP && bi->sub_type != BGP_ROUTE_STATIC)) + { + + zlog_debug ("%s: wrong route type/sub_type for export, skipping", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + if (!VNC_EXPORT_BGP_CE_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp ce mode not enabled, skipping", + __func__); + return; + } + + /* + * prefix list check + */ + if (bgp->rfapi_cfg->plist_export_bgp[afi]) + { + if (prefix_list_apply (bgp->rfapi_cfg->plist_export_bgp[afi], prefix) == + PREFIX_DENY) + { + zlog_debug ("%s: prefix list denied, skipping", __func__); + return; + } + } + + + /* + * Extract CE + * This works only for IPv4 because IPv6 addresses are too big + * to fit in an extended community + */ + if (getce (bgp, attr, &ce_nexthop)) + { + zlog_debug ("%s: EC has no encoded CE, skipping", __func__); + return; + } + + /* + * Is this route already represented in the unicast RIB? + * (look up prefix; compare route type, sub_type, peer, nexthop) + */ + urn = + bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, prefix, + NULL); + for (ubi = urn->info; ubi; ubi = ubi->next) + { + struct prefix unicast_nexthop; + + if (CHECK_FLAG (ubi->flags, BGP_INFO_REMOVED)) + continue; + + rfapiUnicastNexthop2Prefix (afi, ubi->attr, &unicast_nexthop); + + if (ubi->type == ZEBRA_ROUTE_VNC_DIRECT && + ubi->sub_type == BGP_ROUTE_REDISTRIBUTE && + ubi->peer == peer && prefix_same (&unicast_nexthop, &ce_nexthop)) + { + + zlog_debug + ("%s: already have matching exported unicast route, skipping", + __func__); + return; + } + } + + /* + * Construct new attribute set with CE addr as + * nexthop and without Tunnel Encap attr + */ + encap_attr_export_ce (&hattr, attr, &ce_nexthop); + if (bgp->rfapi_cfg->routemap_export_bgp) + { + struct bgp_info info; + route_map_result_t ret; + + memset (&info, 0, sizeof (info)); + info.peer = peer; + info.attr = &hattr; + ret = + route_map_apply (bgp->rfapi_cfg->routemap_export_bgp, prefix, + RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + return; + } + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + /* + * Rule: disallow route-map alteration of next-hop, because it + * would make it too difficult to keep track of the correspondence + * between VPN routes and unicast routes. + */ + rfapiUnicastNexthop2Prefix (afi, iattr, &post_routemap_nexthop); + + if (!prefix_same (&ce_nexthop, &post_routemap_nexthop)) + { + zlog_debug + ("%s: route-map modification of nexthop not allowed, skipping", + __func__); + bgp_attr_unintern (&iattr); + return; + } + + bgp_update (peer, prefix, + 0, /* addpath_id */ + iattr, /* bgp_update copies this attr */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0); + bgp_attr_unintern (&iattr); +} + + +/* + * "Withdrawing a Route" export process + */ +void +vnc_direct_bgp_del_route_ce ( + struct bgp *bgp, + struct route_node *rn, + struct bgp_info *bi) +{ + afi_t afi = family2afi (rn->p.family); + struct bgp_info *vbi; + struct prefix ce_nexthop; + + if (!afi) + { + zlog_err ("%s: bad afi", __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + if (!VNC_EXPORT_BGP_CE_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp ce mode not enabled, skipping", + __func__); + return; + } + + /* + * Extract CE + * This works only for IPv4 because IPv6 addresses are too big + * to fit in an extended community + */ + if (getce (bgp, bi->attr, &ce_nexthop)) + { + zlog_debug ("%s: EC has no encoded CE, skipping", __func__); + return; + } + + /* + * Look for other VPN routes with same prefix, same 5226 CE, + * same peer. If at least one is present, don't remove the + * route from the unicast RIB + */ + + for (vbi = rn->info; vbi; vbi = vbi->next) + { + struct prefix ce; + if (bi == vbi) + continue; + if (bi->peer != vbi->peer) + continue; + if (getce (bgp, vbi->attr, &ce)) + continue; + if (prefix_same (&ce, &ce_nexthop)) + { + zlog_debug ("%s: still have a route via CE, not deleting unicast", + __func__); + return; + } + } + + /* + * withdraw the route + */ + bgp_withdraw (bi->peer, &rn->p, + 0, /* addpath_id */ + NULL, /* attr, ignored */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + +} + +static void +vnc_direct_bgp_vpn_enable_ce (struct bgp *bgp, afi_t afi) +{ + struct rfapi_cfg *hc; + struct route_node *rn; + struct bgp_info *ri; + + zlog_debug ("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (!(hc = bgp->rfapi_cfg)) + return; + + if (!VNC_EXPORT_BGP_CE_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export of CE routes not enabled, skipping", __func__); + return; + } + + if (afi != AFI_IP + && afi != AFI_IP6) + { + zlog_debug ("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through entire ce import table and export to BGP unicast. + */ + for (rn = route_top (bgp->rfapi->it_ce->imported_vpn[afi]); rn; + rn = route_next (rn)) + { + + if (!rn->info) + continue; + + { + char prefixstr[BUFSIZ]; + + prefixstr[0] = 0; + inet_ntop (rn->p.family, &rn->p.u.prefix, prefixstr, BUFSIZ); + zlog_debug ("%s: checking prefix %s/%d", __func__, prefixstr, + rn->p.prefixlen); + } + + for (ri = rn->info; ri; ri = ri->next) + { + + zlog_debug ("%s: ri->sub_type: %d", __func__, ri->sub_type); + + if (ri->sub_type == BGP_ROUTE_NORMAL || + ri->sub_type == BGP_ROUTE_RFP || + ri->sub_type == BGP_ROUTE_STATIC) + { + + vnc_direct_bgp_add_route_ce (bgp, rn, ri); + } + + } + } +} + +static void +vnc_direct_bgp_vpn_disable_ce (struct bgp *bgp, afi_t afi) +{ + struct bgp_node *rn; + + zlog_debug ("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (afi != AFI_IP + && afi != AFI_IP6) + { + zlog_debug ("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through the entire BGP unicast table and remove routes that + * originated from us + */ + for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]); rn; + rn = bgp_route_next (rn)) + { + + struct bgp_info *ri; + struct bgp_info *next; + + for (ri = rn->info, next = NULL; ri; ri = next) + { + + next = ri->next; + + if (ri->type == ZEBRA_ROUTE_VNC_DIRECT && + ri->sub_type == BGP_ROUTE_REDISTRIBUTE) + { + + bgp_withdraw (ri->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + NULL, /* ignored */ + AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + } + } + } +} + +/*********************************************************************** + * Export methods that set nexthop to CE (from 5226 roo EC) END + ***********************************************************************/ + +/*********************************************************************** + * Export methods that proxy nexthop BEGIN + ***********************************************************************/ + +static struct ecommunity * +vnc_route_origin_ecom (struct route_node *rn) +{ + struct ecommunity *new; + struct bgp_info *bi; + + if (!rn->info) + return NULL; + + new = ecommunity_new (); + + for (bi = rn->info; bi; bi = bi->next) + { + + struct ecommunity_val roec; + + switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len)) + { + case AF_INET: + memset (&roec, 0, sizeof (roec)); + roec.val[0] = 0x01; + roec.val[1] = 0x03; + memcpy (roec.val + 2, + &bi->attr->extra->mp_nexthop_global_in.s_addr, 4); + roec.val[6] = 0; + roec.val[7] = 0; + ecommunity_add_val (new, &roec); + break; + case AF_INET6: + /* No support for IPv6 addresses in extended communities */ + break; + } + } + + if (!new->size) + { + ecommunity_free (&new); + new = NULL; + } + + return new; +} + +static struct ecommunity * +vnc_route_origin_ecom_single (struct in_addr *origin) +{ + struct ecommunity *new; + struct ecommunity_val roec; + + memset (&roec, 0, sizeof (roec)); + roec.val[0] = 0x01; + roec.val[1] = 0x03; + memcpy (roec.val + 2, &origin->s_addr, 4); + roec.val[6] = 0; + roec.val[7] = 0; + + new = ecommunity_new (); + assert (new); + ecommunity_add_val (new, &roec); + + if (!new->size) + { + ecommunity_free (&new); + new = NULL; + } + + return new; +} + + +/* + * New memory allocation approach: make a ghost attr that + * has non-interned parts for the modifications. ghost attr + * memory is allocated by caller. + */ +static int +encap_attr_export ( + struct attr *new, + struct attr *orig, + struct prefix *new_nexthop, + struct route_node *rn) /* for VN addrs for ecom list */ + /* if rn is 0, use route's nexthop */ +{ + struct prefix orig_nexthop; + struct prefix *use_nexthop; + static struct ecommunity *ecom_ro; + + if (new_nexthop) + { + use_nexthop = new_nexthop; + } + else + { + use_nexthop = &orig_nexthop; + orig_nexthop.family = + BGP_MP_NEXTHOP_FAMILY (orig->extra->mp_nexthop_len); + if (orig_nexthop.family == AF_INET) + { + orig_nexthop.prefixlen = 32; + orig_nexthop.u.prefix4 = orig->extra->mp_nexthop_global_in; + } + else if (orig_nexthop.family == AF_INET6) + { + orig_nexthop.prefixlen = 128; + orig_nexthop.u.prefix6 = orig->extra->mp_nexthop_global; + } + else + { + return -1; /* FAIL - can't compute nexthop */ + } + } + + + /* + * Make "new" a ghost attr copy of "orig" + */ + memset (new, 0, sizeof (struct attr)); + bgp_attr_dup (new, orig); + + /* + * Set nexthop + */ + switch (use_nexthop->family) + { + case AF_INET: + new->nexthop = use_nexthop->u.prefix4; + new->extra->mp_nexthop_len = 4; /* bytes */ + new->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); + break; + + case AF_INET6: + if (!new->extra) + { + new->extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra)); + } + new->extra->mp_nexthop_global = use_nexthop->u.prefix6; + new->extra->mp_nexthop_len = 16; /* bytes */ + break; + + default: + assert (0); + break; + } + + bgp_attr_extra_get (new); + if (rn) + { + ecom_ro = vnc_route_origin_ecom (rn); + } + else + { + /* TBD test/assert for IPv6 */ + ecom_ro = vnc_route_origin_ecom_single (&use_nexthop->u.prefix4); + } + if (new->extra->ecommunity) + { + if (ecom_ro) + { + new->extra->ecommunity = + ecommunity_merge (ecom_ro, new->extra->ecommunity); + } + } + else + { + new->extra->ecommunity = ecom_ro; + } + if (ecom_ro) + { + new->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); + } + + /* + * Set MED + * + * Note that it will be deleted when BGP sends to any eBGP + * peer unless PEER_FLAG_MED_UNCHANGED is set: + * + * neighbor NEIGHBOR attribute-unchanged med + */ + if (!CHECK_FLAG (new->flag, BGP_ATTR_MULTI_EXIT_DISC)) + { + if (CHECK_FLAG (new->flag, BGP_ATTR_LOCAL_PREF)) + { + if (new->local_pref > 255) + new->med = 0; + else + new->med = 255 - new->local_pref; + } + else + { + new->med = 255; /* shouldn't happen */ + } + new->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + } + + /* + * "new" is now a ghost attr: + * - it owns an "extra" struct + * - it owns any non-interned parts + * - any references to interned parts are not counted + * + * Caller should, after using the attr, call: + * - bgp_attr_flush() to free non-interned parts + * - call bgp_attr_extra_free() to free extra + */ + + return 0; +} + +/* + * "Adding a Route" export process + */ +void +vnc_direct_bgp_add_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn) +{ + struct attr attr = { 0 }; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + afi_t afi = family2afi (rn->p.family); + + if (!afi) + { + zlog_err ("%s: can't get afi of route node", __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!listcount (bgp->rfapi_cfg->rfg_export_direct_bgp_l)) + { + zlog_debug ("%s: no bgp-direct export nve group, skipping", __func__); + return; + } + + bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE); + /* TBD set some configured med, see add_vnc_route() */ + + zlog_debug ("%s: looping over nve-groups in direct-bgp export list", + __func__); + + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + + struct listnode *ln; + + /* + * If nve group is not defined yet, skip it + */ + if (!rfgn->rfg) + continue; + + /* + * If the nve group uses a different import table, skip it + */ + if (import_table != rfgn->rfg->rfapi_import_table) + continue; + + /* + * if no NVEs currently associated with this group, skip it + */ + if (!rfgn->rfg->nves) + continue; + + /* + * per-nve-group prefix list check + */ + if (rfgn->rfg->plist_export_bgp[afi]) + { + if (prefix_list_apply (rfgn->rfg->plist_export_bgp[afi], &rn->p) == + PREFIX_DENY) + + continue; + } + + /* + * For each NVE that is assigned to the export nve group, generate + * a route with that NVE as its next hop + */ + for (ln = listhead (rfgn->rfg->nves); ln; ln = listnextnode (ln)) + { + + struct prefix nhp; + struct rfapi_descriptor *irfd; + struct bgp_info info; + struct attr hattr; + struct attr *iattr; + + irfd = listgetdata (ln); + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + /* + * Construct new attribute set with NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export (&hattr, &attr, &nhp, rn)) + continue; + + if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) + { + zlog_debug ("%s: attr follows", __func__); + rfapiPrintAttrPtrs (NULL, &attr); + zlog_debug ("%s: hattr follows", __func__); + rfapiPrintAttrPtrs (NULL, &hattr); + } + + if (rfgn->rfg->routemap_export_bgp) + { + route_map_result_t ret; + info.peer = irfd->peer; + info.attr = &hattr; + ret = route_map_apply (rfgn->rfg->routemap_export_bgp, &rn->p, + RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + zlog_debug + ("%s: route map says DENY, so not calling bgp_update", + __func__); + continue; + } + } + + if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) + { + zlog_debug ("%s: hattr after route_map_apply:", __func__); + rfapiPrintAttrPtrs (NULL, &hattr); + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + bgp_update (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies it */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0); + + bgp_attr_unintern (&iattr); + } + } + + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); +} + +/* + * "Withdrawing a Route" export process + */ +void +vnc_direct_bgp_del_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn) +{ + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + afi_t afi = family2afi (rn->p.family); + + if (!afi) + { + zlog_err ("%s: can't get afi route node", __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!listcount (bgp->rfapi_cfg->rfg_export_direct_bgp_l)) + { + zlog_debug ("%s: no bgp-direct export nve group, skipping", __func__); + return; + } + + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + + struct listnode *ln; + + /* + * If nve group is not defined yet, skip it + */ + if (!rfgn->rfg) + continue; + + /* + * if no NVEs currently associated with this group, skip it + */ + if (!rfgn->rfg->nves) + continue; + + /* + * If the nve group uses a different import table, + * skip it + */ + if (import_table != rfgn->rfg->rfapi_import_table) + continue; + + /* + * For each NVE that is assigned to the export nve group, generate + * a route with that NVE as its next hop + */ + for (ln = listhead (rfgn->rfg->nves); ln; ln = listnextnode (ln)) + { + + struct prefix nhp; + struct rfapi_descriptor *irfd; + + irfd = listgetdata (ln); + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + bgp_withdraw (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + } + } +} + +void +vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + struct rfapi_nve_group_cfg *rfg = rfd->rfg; + afi_t afi = family2afi (rfd->vn_addr.addr_family); + + if (!afi) + { + zlog_err ("%s: can't get afi of nve vn addr", __func__); + return; + } + + if (!bgp) + return; + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + /* + * Loop over the list of NVE-Groups configured for + * exporting to direct-bgp and see if this new NVE's + * group is among them. + */ + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + + /* + * Yes, this NVE's group is configured for export to direct-bgp + */ + if (rfgn->rfg == rfg) + { + + struct route_table *rt = NULL; + struct route_node *rn; + struct attr attr = { 0 }; + struct rfapi_import_table *import_table; + + + import_table = rfg->rfapi_import_table; + + bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE); + /* TBD set some configured med, see add_vnc_route() */ + + if (afi == AFI_IP + || afi == AFI_IP6) + { + rt = import_table->imported_vpn[afi]; + } + else + { + zlog_err ("%s: bad afi %d", __func__, afi); + return; + } + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + if (rn->info) + { + + struct prefix nhp; + struct rfapi_descriptor *irfd = rfd; + struct attr hattr; + struct attr *iattr; + struct bgp_info info; + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + /* + * per-nve-group prefix list check + */ + if (rfgn->rfg->plist_export_bgp[afi]) + { + if (prefix_list_apply (rfgn->rfg->plist_export_bgp[afi], + &rn->p) == PREFIX_DENY) + + continue; + } + + + /* + * Construct new attribute set with NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export (&hattr, &attr, &nhp, rn)) + continue; + + if (rfgn->rfg->routemap_export_bgp) + { + route_map_result_t ret; + info.peer = irfd->peer; + info.attr = &hattr; + ret = route_map_apply (rfgn->rfg->routemap_export_bgp, + &rn->p, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + continue; + } + + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + bgp_update (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies it */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0); + + bgp_attr_unintern (&iattr); + + } + } + + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + } + } +} + + +void +vnc_direct_bgp_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + struct rfapi_nve_group_cfg *rfg = rfd->rfg; + afi_t afi = family2afi (rfd->vn_addr.addr_family); + + if (!afi) + { + zlog_err ("%s: can't get afi of nve vn addr", __func__); + return; + } + + if (!bgp) + return; + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + /* + * Loop over the list of NVE-Groups configured for + * exporting to direct-bgp and see if this new NVE's + * group is among them. + */ + for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) + { + + /* + * Yes, this NVE's group is configured for export to direct-bgp + */ + if (rfg && rfgn->rfg == rfg) + { + + struct route_table *rt = NULL; + struct route_node *rn; + struct rfapi_import_table *import_table; + + import_table = rfg->rfapi_import_table; + + if (afi == AFI_IP + || afi == AFI_IP6) + { + rt = import_table->imported_vpn[afi]; + } + else + { + zlog_err ("%s: bad afi %d", __func__, afi); + return; + } + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + if (rn->info) + { + + struct prefix nhp; + struct rfapi_descriptor *irfd = rfd; + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + bgp_withdraw (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + + } + } + } + } +} + + + +/* + * Caller is responsible for ensuring that the specified nve-group + * is actually part of the list of exported nve groups. + */ +static void +vnc_direct_bgp_add_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct route_table *rt = NULL; + struct route_node *rn; + struct attr attr = { 0 }; + struct rfapi_import_table *import_table; + + zlog_debug ("%s: entry", __func__); + + import_table = rfg->rfapi_import_table; + if (!import_table) + { + zlog_debug ("%s: import table not defined, returning", __func__); + return; + } + + if (afi == AFI_IP + || afi == AFI_IP6) + { + rt = import_table->imported_vpn[afi]; + } + else + { + zlog_err ("%s: bad afi %d", __func__, afi); + return; + } + + if (!rfg->nves) + { + /* avoid segfault below if list doesn't exist */ + zlog_debug ("%s: no NVEs in this group", __func__); + return; + } + + bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE); + /* TBD set some configured med, see add_vnc_route() */ + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + if (rn->info) + { + + struct listnode *ln; + + /* + * per-nve-group prefix list check + */ + if (rfg->plist_export_bgp[afi]) + { + if (prefix_list_apply (rfg->plist_export_bgp[afi], &rn->p) == + PREFIX_DENY) + + continue; + } + + /* + * For each NVE that is assigned to the export nve group, generate + * a route with that NVE as its next hop + */ + for (ln = listhead (rfg->nves); ln; ln = listnextnode (ln)) + { + + struct prefix nhp; + struct rfapi_descriptor *irfd; + struct bgp_info info; + struct attr hattr; + struct attr *iattr; + + irfd = listgetdata (ln); + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + /* + * Construct new attribute set with NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export (&hattr, &attr, &nhp, rn)) + continue; + + if (rfg->routemap_export_bgp) + { + route_map_result_t ret; + info.peer = irfd->peer; + info.attr = &hattr; + ret = route_map_apply (rfg->routemap_export_bgp, + &rn->p, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + continue; + } + + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + bgp_update (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies it */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0); + + bgp_attr_unintern (&iattr); + } + } + } + + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); +} + + +/* + * Caller is responsible for ensuring that the specified nve-group + * is actually part of the list of exported nve groups. + */ +void +vnc_direct_bgp_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + vnc_direct_bgp_add_group_afi (bgp, rfg, AFI_IP); + vnc_direct_bgp_add_group_afi (bgp, rfg, AFI_IP6); +} + + + +/* + * Caller is responsible for ensuring that the specified nve-group + * was actually part of the list of exported nve groups. + */ +static void +vnc_direct_bgp_del_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct route_table *rt = NULL; + struct route_node *rn; + struct rfapi_import_table *import_table; + + zlog_debug ("%s: entry", __func__); + + import_table = rfg->rfapi_import_table; + if (!import_table) + { + zlog_debug ("%s: import table not defined, returning", __func__); + return; + } + + assert (afi == AFI_IP + || afi == AFI_IP6); + rt = import_table->imported_vpn[afi]; + + if (!rfg->nves) + { + /* avoid segfault below if list does not exist */ + zlog_debug ("%s: no NVEs in this group", __func__); + return; + } + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + if (rn->info) + { + + struct listnode *ln; + + /* + * For each NVE that is assigned to the export nve group, generate + * a route with that NVE as its next hop + */ + for (ln = listhead (rfg->nves); ln; ln = listnextnode (ln)) + { + + struct rfapi_descriptor *irfd; + + irfd = listgetdata (ln); + + bgp_withdraw (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + + } + } + } +} + + +/* + * Caller is responsible for ensuring that the specified nve-group + * was actually part of the list of exported nve groups. + */ +void +vnc_direct_bgp_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + vnc_direct_bgp_del_group_afi (bgp, rfg, AFI_IP); + vnc_direct_bgp_del_group_afi (bgp, rfg, AFI_IP6); +} + +void +vnc_direct_bgp_reexport_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + if (VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + { + /* + * look in the list of currently-exported groups + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + /* + * If it matches, reexport it + */ + vnc_direct_bgp_del_group_afi (bgp, rfg, afi); + vnc_direct_bgp_add_group_afi (bgp, rfg, afi); + break; + } + } + } +} + + +static void +vnc_direct_bgp_unexport_table ( + afi_t afi, + struct route_table *rt, + struct list *nve_list) +{ + if (nve_list) + { + + struct route_node *rn; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + if (rn->info) + { + + struct listnode *hln; + struct rfapi_descriptor *irfd; + + for (ALL_LIST_ELEMENTS_RO (nve_list, hln, irfd)) + { + + bgp_withdraw (irfd->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + + } + } + } + } +} + +static void +import_table_to_nve_list_direct_bgp ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct list **nves, + uint8_t family) +{ + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* + * Loop over the list of NVE-Groups configured for + * exporting to direct-bgp. + * + * Build a list of NVEs that use this import table + */ + *nves = NULL; + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + /* + * If this NVE-Group's import table matches the current one + */ + if (rfgn->rfg && rfgn->rfg->nves && rfgn->rfg->rfapi_import_table == it) + { + + nve_group_to_nve_list (rfgn->rfg, nves, family); + } + } +} + +void +vnc_direct_bgp_vpn_enable (struct bgp *bgp, afi_t afi) +{ + struct listnode *rfgn; + struct rfapi_nve_group_cfg *rfg; + + if (!bgp) + return; + + if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (afi != AFI_IP + && afi != AFI_IP6) + { + zlog_debug ("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Policy is applied per-nve-group, so we need to iterate + * over the groups to add everything. + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->nve_groups_sequential, + rfgn, rfg)) + { + + /* + * contains policy management + */ + vnc_direct_bgp_add_group_afi (bgp, rfg, afi); + } +} + + +void +vnc_direct_bgp_vpn_disable (struct bgp *bgp, afi_t afi) +{ + struct rfapi_import_table *it; + uint8_t family = afi2family (afi); + + zlog_debug ("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (!bgp->rfapi) + { + zlog_debug ("%s: rfapi not initialized", __func__); + return; + } + + if (!family || (afi != AFI_IP + && afi != AFI_IP6)) + { + zlog_debug ("%s: bad afi: %d", __func__, afi); + return; + } + + for (it = bgp->rfapi->imports; it; it = it->next) + { + + struct list *nve_list = NULL; + + import_table_to_nve_list_direct_bgp (bgp, it, &nve_list, family); + + if (nve_list) + { + vnc_direct_bgp_unexport_table (afi, it->imported_vpn[afi], + nve_list); + list_free (nve_list); + } + } +} + + +/*********************************************************************** + * Export methods that proxy nexthop END + ***********************************************************************/ + + +/*********************************************************************** + * Export methods that preserve original nexthop BEGIN + * rh = "registering nve" + ***********************************************************************/ + + +/* + * "Adding a Route" export process + * TBD do we need to check bi->type and bi->sub_type here, or does + * caller do it? + */ +void +vnc_direct_bgp_rh_add_route ( + struct bgp *bgp, + afi_t afi, + struct prefix *prefix, + struct peer *peer, + struct attr *attr) +{ + struct vnc_export_info *eti; + struct attr hattr; + struct rfapi_cfg *hc; + struct attr *iattr; + + if (!afi) + { + zlog_err ("%s: can't get afi of route node", __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + if (!VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp RH mode not enabled, skipping", + __func__); + return; + } + + /* + * prefix list check + */ + if (hc->plist_export_bgp[afi]) + { + if (prefix_list_apply (hc->plist_export_bgp[afi], prefix) == + PREFIX_DENY) + return; + } + + /* + * Construct new attribute set with NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export (&hattr, attr, NULL, NULL)) + return; + if (hc->routemap_export_bgp) + { + struct bgp_info info; + route_map_result_t ret; + + memset (&info, 0, sizeof (info)); + info.peer = peer; + info.attr = &hattr; + ret = + route_map_apply (hc->routemap_export_bgp, prefix, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + return; + } + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + /* + * record route information that we will need to expire + * this route + */ + eti = vnc_eti_get (bgp, EXPORT_TYPE_BGP, prefix, peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); + rfapiGetVncLifetime (attr, &eti->lifetime); + eti->lifetime = rfapiGetHolddownFromLifetime (eti->lifetime); + + if (eti->timer) + { + /* + * export expiration timer is already running on + * this route: cancel it + */ + thread_cancel (eti->timer); + eti->timer = NULL; + } + + bgp_update (peer, prefix, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies this attr */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0); + bgp_attr_unintern (&iattr); + +} + +static int +vncExportWithdrawTimer (struct thread *t) +{ + struct vnc_export_info *eti = t->arg; + + /* + * withdraw the route + */ + bgp_withdraw ( + eti->peer, + &eti->node->p, + 0, /* addpath_id */ + NULL, /* attr, ignored */ + family2afi (eti->node->p.family), + SAFI_UNICAST, + eti->type, + eti->subtype, + NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + + /* + * Free the eti + */ + vnc_eti_delete (eti); + + return 0; +} + +/* + * "Withdrawing a Route" export process + * TBD do we need to check bi->type and bi->sub_type here, or does + * caller do it? + */ +void +vnc_direct_bgp_rh_del_route ( + struct bgp *bgp, + afi_t afi, + struct prefix *prefix, + struct peer *peer) +{ + struct vnc_export_info *eti; + + if (!afi) + { + zlog_err ("%s: can't get afi route node", __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + if (!VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + eti = vnc_eti_get (bgp, EXPORT_TYPE_BGP, prefix, peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); + + if (!eti->timer && eti->lifetime <= INT32_MAX) + { + eti->timer = thread_add_timer (bm->master, + vncExportWithdrawTimer, + eti, eti->lifetime); + zlog_debug ("%s: set expiration timer for %u seconds", + __func__, eti->lifetime); + } +} + + +void +vnc_direct_bgp_rh_vpn_enable (struct bgp *bgp, afi_t afi) +{ + struct prefix_rd prd; + struct bgp_node *prn; + struct rfapi_cfg *hc; + + zlog_debug ("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (!(hc = bgp->rfapi_cfg)) + return; + + if (!VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg)) + { + zlog_debug ("%s: export of RH routes not enabled, skipping", __func__); + return; + } + + if (afi != AFI_IP + && afi != AFI_IP6) + { + zlog_debug ("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through the entire BGP VPN table and export to BGP unicast. + */ + + zlog_debug ("%s: starting RD loop", __func__); + + /* Loop over all the RDs */ + for (prn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); prn; + prn = bgp_route_next (prn)) + { + + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_info *ri; + + memset (&prd, 0, sizeof (prd)); + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy (prd.val, prn->p.u.val, 8); + + /* This is the per-RD table of prefixes */ + table = prn->info; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + + /* + * skip prefix list check if no routes here + */ + if (!rn->info) + continue; + + { + char prefixstr[BUFSIZ]; + + prefixstr[0] = 0; + inet_ntop (rn->p.family, &rn->p.u.prefix, prefixstr, BUFSIZ); + zlog_debug ("%s: checking prefix %s/%d", __func__, prefixstr, + rn->p.prefixlen); + } + + /* + * prefix list check + */ + if (hc->plist_export_bgp[afi]) + { + if (prefix_list_apply (hc->plist_export_bgp[afi], &rn->p) == + PREFIX_DENY) + { + + zlog_debug ("%s: prefix list says DENY", __func__); + continue; + } + } + + for (ri = rn->info; ri; ri = ri->next) + { + + zlog_debug ("%s: ri->sub_type: %d", __func__, ri->sub_type); + + if (ri->sub_type == BGP_ROUTE_NORMAL || + ri->sub_type == BGP_ROUTE_RFP) + { + + struct vnc_export_info *eti; + struct attr hattr; + struct attr *iattr; + + /* + * Construct new attribute set with NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export (&hattr, ri->attr, NULL, NULL)) + { + zlog_debug ("%s: encap_attr_export failed", __func__); + continue; + } + + if (hc->routemap_export_bgp) + { + struct bgp_info info; + route_map_result_t ret; + + memset (&info, 0, sizeof (info)); + info.peer = ri->peer; + info.attr = &hattr; + ret = route_map_apply (hc->routemap_export_bgp, + &rn->p, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + zlog_debug ("%s: route map says DENY", __func__); + continue; + } + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + /* + * record route information that we will need to expire + * this route + */ + eti = vnc_eti_get (bgp, EXPORT_TYPE_BGP, &rn->p, ri->peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE); + rfapiGetVncLifetime (ri->attr, &eti->lifetime); + + if (eti->timer) + { + /* + * export expiration timer is already running on + * this route: cancel it + */ + thread_cancel (eti->timer); + eti->timer = NULL; + } + + zlog_debug ("%s: calling bgp_update", __func__); + + bgp_update (ri->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies it */ + AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0); + bgp_attr_unintern (&iattr); + } + } + } + } +} + +void +vnc_direct_bgp_rh_vpn_disable (struct bgp *bgp, afi_t afi) +{ + struct bgp_node *rn; + + zlog_debug ("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (afi != AFI_IP + && afi != AFI_IP6) + { + zlog_debug ("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through the entire BGP unicast table and remove routes that + * originated from us + */ + for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]); rn; + rn = bgp_route_next (rn)) + { + + struct bgp_info *ri; + struct bgp_info *next; + + for (ri = rn->info, next = NULL; ri; ri = next) + { + + next = ri->next; + + if (ri->type == ZEBRA_ROUTE_VNC_DIRECT_RH && + ri->sub_type == BGP_ROUTE_REDISTRIBUTE) + { + + struct vnc_export_info *eti; + + /* + * Delete routes immediately (no timer) + */ + eti = + vnc_eti_checktimer (bgp, EXPORT_TYPE_BGP, &rn->p, ri->peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE); + if (eti) + { + if (eti->timer) + thread_cancel (eti->timer); + vnc_eti_delete (eti); + } + + bgp_withdraw (ri->peer, &rn->p, /* prefix */ + 0, /* addpath_id */ + NULL, /* ignored */ + AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL); /* tag not used for unicast */ + } + } + } +} + +void +vnc_direct_bgp_rh_reexport (struct bgp *bgp, afi_t afi) +{ + if (VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg)) + { + vnc_direct_bgp_rh_vpn_disable (bgp, afi); + vnc_direct_bgp_rh_vpn_enable (bgp, afi); + } +} + +/*********************************************************************** + * Generic Export methods + ***********************************************************************/ + +/* + * Assumes the correct mode bits are already turned on. Thus it + * is OK to call this function from, e.g., bgp_redistribute_set() + * without caring if export is enabled or not + */ +void +vnc_export_bgp_enable (struct bgp *bgp, afi_t afi) +{ + switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) + { + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP: + vnc_direct_bgp_vpn_enable (bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH: + vnc_direct_bgp_rh_vpn_enable (bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE: + vnc_direct_bgp_vpn_enable_ce (bgp, afi); + break; + } +} + +void +vnc_export_bgp_disable (struct bgp *bgp, afi_t afi) +{ + switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) + { + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP: + vnc_direct_bgp_vpn_disable (bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH: + vnc_direct_bgp_rh_vpn_disable (bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE: + vnc_direct_bgp_vpn_disable_ce (bgp, afi); + break; + } +} + +void +vnc_export_bgp_prechange (struct bgp *bgp) +{ + vnc_export_bgp_disable (bgp, AFI_IP); + vnc_export_bgp_disable (bgp, AFI_IP6); +} + +void +vnc_export_bgp_postchange (struct bgp *bgp) +{ + vnc_export_bgp_enable (bgp, AFI_IP); + vnc_export_bgp_enable (bgp, AFI_IP6); +} + +void +vnc_direct_bgp_reexport (struct bgp *bgp, afi_t afi) +{ + vnc_export_bgp_disable (bgp, afi); + vnc_export_bgp_enable (bgp, afi); +} diff --git a/bgpd/rfapi/vnc_export_bgp.h b/bgpd/rfapi/vnc_export_bgp.h new file mode 100644 index 0000000000..ab2197d129 --- /dev/null +++ b/bgpd/rfapi/vnc_export_bgp.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ +#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ + +#include "zebra.h" +#include "prefix.h" + +#include "bgpd.h" +#include "bgp_route.h" + + +extern void vnc_direct_bgp_rh_reexport (struct bgp *bgp, afi_t afi); + +extern void vnc_export_bgp_prechange (struct bgp *bgp); + +extern void vnc_export_bgp_postchange (struct bgp *bgp); + +extern void vnc_export_bgp_enable (struct bgp *bgp, afi_t afi); + +extern void vnc_export_bgp_disable (struct bgp *bgp, afi_t afi); + +#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ */ diff --git a/bgpd/rfapi/vnc_export_bgp_p.h b/bgpd/rfapi/vnc_export_bgp_p.h new file mode 100644 index 0000000000..628778af3e --- /dev/null +++ b/bgpd/rfapi/vnc_export_bgp_p.h @@ -0,0 +1,95 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ +#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ + +#include "zebra.h" +#include "prefix.h" + +#include "bgpd.h" +#include "bgp_route.h" + +#include "rfapi_private.h" + +extern void +vnc_direct_bgp_add_route_ce ( + struct bgp *bgp, + struct route_node *rn, + struct bgp_info *bi); + +extern void +vnc_direct_bgp_del_route_ce ( + struct bgp *bgp, + struct route_node *rn, + struct bgp_info *bi); + +extern void +vnc_direct_bgp_add_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn); + +extern void +vnc_direct_bgp_del_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn); + +extern void +vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void +vnc_direct_bgp_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void +vnc_direct_bgp_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg); + +extern void +vnc_direct_bgp_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg); + +extern void +vnc_direct_bgp_reexport_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi); + + +extern void +vnc_direct_bgp_rh_add_route ( + struct bgp *bgp, + afi_t afi, + struct prefix *prefix, + struct peer *peer, + struct attr *attr); + + +extern void +vnc_direct_bgp_rh_del_route ( + struct bgp *bgp, + afi_t afi, + struct prefix *prefix, + struct peer *peer); + +extern void +vnc_direct_bgp_reexport (struct bgp *bgp, afi_t afi); + +#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ */ diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c new file mode 100644 index 0000000000..5a96d8a52b --- /dev/null +++ b/bgpd/rfapi/vnc_export_table.c @@ -0,0 +1,214 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "vty.h" + +#include "bgpd.h" +#include "bgp_route.h" + +#include "vnc_export_table.h" +#include "rfapi_private.h" +#include "rfapi_import.h" + +struct route_node * +vnc_etn_get (struct bgp *bgp, vnc_export_type_t type, struct prefix *p) +{ + struct route_table *t = NULL; + struct route_node *rn = NULL; + afi_t afi; + + if (!bgp || !bgp->rfapi) + return NULL; + + afi = family2afi (p->family); + assert (afi == AFI_IP || afi == AFI_IP6); + + switch (type) + { + case EXPORT_TYPE_BGP: + if (!bgp->rfapi->rt_export_bgp[afi]) + bgp->rfapi->rt_export_bgp[afi] = route_table_init (); + t = bgp->rfapi->rt_export_bgp[afi]; + break; + + case EXPORT_TYPE_ZEBRA: + if (!bgp->rfapi->rt_export_zebra[afi]) + bgp->rfapi->rt_export_zebra[afi] = route_table_init (); + t = bgp->rfapi->rt_export_zebra[afi]; + break; + } + + if (t) + rn = route_node_get (t, p); + return rn; +} + +struct route_node * +vnc_etn_lookup (struct bgp *bgp, vnc_export_type_t type, struct prefix *p) +{ + struct route_table *t = NULL; + struct route_node *rn = NULL; + afi_t afi; + + if (!bgp || !bgp->rfapi) + return NULL; + + afi = family2afi (p->family); + assert (afi == AFI_IP || afi == AFI_IP6); + + switch (type) + { + case EXPORT_TYPE_BGP: + if (!bgp->rfapi->rt_export_bgp[afi]) + bgp->rfapi->rt_export_bgp[afi] = route_table_init (); + t = bgp->rfapi->rt_export_bgp[afi]; + break; + + case EXPORT_TYPE_ZEBRA: + if (!bgp->rfapi->rt_export_zebra[afi]) + bgp->rfapi->rt_export_zebra[afi] = route_table_init (); + t = bgp->rfapi->rt_export_zebra[afi]; + break; + } + + if (t) + rn = route_node_lookup (t, p); + return rn; +} + +struct vnc_export_info * +vnc_eti_get ( + struct bgp *bgp, + vnc_export_type_t etype, + struct prefix *p, + struct peer *peer, + uint8_t type, + uint8_t subtype) +{ + struct route_node *etn; + struct vnc_export_info *eti; + + etn = vnc_etn_get (bgp, etype, p); + assert (etn); + + for (eti = etn->info; eti; eti = eti->next) + { + if (peer == eti->peer && type == eti->type && subtype == eti->subtype) + { + + break; + } + } + + if (eti) + { + route_unlock_node (etn); + } + else + { + eti = XCALLOC (MTYPE_RFAPI_ETI, sizeof (struct vnc_export_info)); + assert (eti); + eti->node = etn; + eti->peer = peer; + peer_lock (peer); + eti->type = type; + eti->subtype = subtype; + eti->next = etn->info; + etn->info = eti; + } + + return eti; +} + +void +vnc_eti_delete (struct vnc_export_info *goner) +{ + struct route_node *etn; + struct vnc_export_info *eti; + struct vnc_export_info *eti_prev = NULL; + + etn = goner->node; + + for (eti = etn->info; eti; eti_prev = eti, eti = eti->next) + { + if (eti == goner) + break; + } + + if (!eti) + { + zlog_debug ("%s: COULDN'T FIND ETI", __func__); + return; + } + + if (eti_prev) + { + eti_prev->next = goner->next; + } + else + { + etn->info = goner->next; + } + + peer_unlock (eti->peer); + goner->node = NULL; + XFREE (MTYPE_RFAPI_ETI, goner); + + route_unlock_node (etn); +} + +struct vnc_export_info * +vnc_eti_checktimer ( + struct bgp *bgp, + vnc_export_type_t etype, + struct prefix *p, + struct peer *peer, + uint8_t type, + uint8_t subtype) +{ + struct route_node *etn; + struct vnc_export_info *eti; + + etn = vnc_etn_lookup (bgp, etype, p); + if (!etn) + return NULL; + + for (eti = etn->info; eti; eti = eti->next) + { + if (peer == eti->peer && type == eti->type && subtype == eti->subtype) + { + + break; + } + } + + route_unlock_node (etn); + + if (eti && eti->timer) + return eti; + + return NULL; +} diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h new file mode 100644 index 0000000000..231861af49 --- /dev/null +++ b/bgpd/rfapi/vnc_export_table.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ +#define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ + +#include "table.h" +#include "thread.h" +#include "vty.h" + +#include "bgpd.h" + +#define VNC_EXPORT_TYPE_BGP 1 +#define VNC_EXPORT_TYPE_ZEBRA 2 + +typedef enum vnc_export_type +{ + EXPORT_TYPE_BGP, + EXPORT_TYPE_ZEBRA +} vnc_export_type_t; + +struct vnc_export_info +{ + struct vnc_export_info *next; + struct route_node *node; + struct peer *peer; + u_char type; + u_char subtype; + uint32_t lifetime; + struct thread *timer; +}; + +extern struct route_node * +vnc_etn_get ( + struct bgp *bgp, + vnc_export_type_t type, + struct prefix *p); + +extern struct route_node * +vnc_etn_lookup ( + struct bgp *bgp, + vnc_export_type_t type, + struct prefix *p); + +extern struct vnc_export_info * +vnc_eti_get ( + struct bgp *bgp, + vnc_export_type_t etype, + struct prefix *p, + struct peer *peer, + uint8_t type, + uint8_t subtype); + +extern void +vnc_eti_delete (struct vnc_export_info *goner); + +extern struct vnc_export_info * +vnc_eti_checktimer ( + struct bgp *bgp, + vnc_export_type_t etype, + struct prefix *p, + struct peer *peer, + uint8_t type, + uint8_t subtype); + + +#endif /* _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ */ diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c new file mode 100644 index 0000000000..020cf181b5 --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -0,0 +1,3165 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: vnc_import_bgp.c + * Purpose: Import routes from BGP unicast directly (not via zebra) + */ + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "log.h" +#include "memory.h" +#include "linklist.h" +#include "plist.h" +#include "routemap.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_mplsvpn.h" /* for RD_TYPE_IP */ + +#include "vnc_export_bgp.h" +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "vnc_import_bgp.h" +#include "vnc_import_bgp_p.h" +#include "vnc_debug.h" + +#define ENABLE_VNC_RHNCK + +#define DEBUG_RHN_LIST 0 + +static struct rfapi_descriptor vncHDBgpDirect; /* dummy nve descriptor */ +static struct rfapi_descriptor vncHDResolveNve; /* dummy nve descriptor */ + +/* + * For routes from another AS: + * + * If MED is set, + * LOCAL_PREF = 255 - MIN(255, MED) + * else + * LOCAL_PREF = default_local_pref + * + * For routes from the same AS: + * + * LOCAL_PREF unchanged + */ +uint32_t +calc_local_pref (struct attr *attr, struct peer *peer) +{ + uint32_t local_pref = 0; + + if (!attr) + { + if (peer) + { + return peer->bgp->default_local_pref; + } + return bgp_get_default ()->default_local_pref; + } + + if (peer && (peer->as != peer->bgp->as)) + { + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + { + if (attr->med > 255) + { + local_pref = 0; + } + else + { + local_pref = 255 - attr->med; + } + } + else + { + local_pref = peer->bgp->default_local_pref; + } + } + else + { + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + { + local_pref = attr->local_pref; + } + else + { + if (peer && peer->bgp) + { + local_pref = peer->bgp->default_local_pref; + } + } + } + + return local_pref; +} + +static int +is_host_prefix (struct prefix *p) +{ + switch (p->family) + { + case AF_INET: + return (p->prefixlen == 32); + case AF_INET6: + return (p->prefixlen == 128); + } + return 0; +} + +/*********************************************************************** + * RHN list + ***********************************************************************/ + +struct prefix_bag +{ + struct prefix hpfx; /* ce address = unicast nexthop */ + struct prefix upfx; /* unicast prefix */ + struct bgp_info *ubi; /* unicast route */ +}; + +static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff +}; + +int +vnc_prefix_cmp (void *pfx1, void *pfx2) +{ + int offset; + int shift; + u_char mask; + + struct prefix *p1 = pfx1; + struct prefix *p2 = pfx2; + + if (p1->family < p2->family) + return -1; + if (p1->family > p2->family) + return 1; + + if (p1->prefixlen < p2->prefixlen) + return -1; + if (p1->prefixlen > p2->prefixlen) + return 1; + + offset = p1->prefixlen / 8; + shift = p1->prefixlen % 8; + if (shift == 0 && offset) + { /* catch aligned case */ + offset--; + shift = 8; + } + + /* Set both prefix's head pointer. */ + const u_char *pp1 = (const u_char *) &p1->u.prefix; + const u_char *pp2 = (const u_char *) &p2->u.prefix; + + while (offset--) + { + if (*pp1 < *pp2) + return -1; + if (*pp1 > *pp2) + return 1; + ++pp1; + ++pp2; + } + + mask = maskbit[shift]; + if ((*pp1 & mask) < (*pp2 & mask)) + return -1; + if ((*pp1 & mask) > (*pp2 & mask)) + return 1; + + return 0; +} + +static void +prefix_bag_free (void *pb) +{ + XFREE (MTYPE_RFAPI_PREFIX_BAG, pb); +} + +#if DEBUG_RHN_LIST +static void +print_rhn_list (const char *tag1, const char *tag2) +{ + struct bgp *bgp = bgp_get_default (); + struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop; + struct skiplistnode *p; + struct prefix_bag *pb; + int count = 0; + + if (!sl) + { + zlog_debug ("%s: %s: RHN List is empty", (tag1 ? tag1 : ""), + (tag2 ? tag2 : "")); + return; + } + + zlog_debug ("%s: %s: RHN list:", (tag1 ? tag1 : ""), (tag2 ? tag2 : "")); + + /* XXX uses secret knowledge of skiplist structure */ + for (p = sl->header->forward[0]; p; p = p->forward[0]) + { + char kbuf[BUFSIZ]; + char hbuf[BUFSIZ]; + char ubuf[BUFSIZ]; + + pb = p->value; + + prefix2str (p->key, kbuf, BUFSIZ); + prefix2str (&pb->hpfx, hbuf, BUFSIZ); + prefix2str (&pb->upfx, ubuf, BUFSIZ); + + zlog_debug ("RHN Entry %d (q=%p): kpfx=%s, upfx=%s, hpfx=%s, ubi=%p", + ++count, p, kbuf, ubuf, hbuf, pb->ubi); + } +} +#endif + +#ifdef ENABLE_VNC_RHNCK +static void +vnc_rhnck (char *tag) +{ + struct bgp *bgp; + struct skiplist *sl; + struct skiplistnode *p; + + bgp = bgp_get_default (); + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) + return; + + /* XXX uses secret knowledge of skiplist structure */ + for (p = sl->header->forward[0]; p; p = p->forward[0]) + { + struct prefix_bag *pb; + struct prefix *pkey; + afi_t afi; + struct prefix pfx_orig_nexthop; + + memset (&pfx_orig_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */ + + pkey = p->key; + pb = p->value; + + afi = family2afi (pb->upfx.family); + + rfapiUnicastNexthop2Prefix (afi, pb->ubi->attr, &pfx_orig_nexthop); + + /* pb->hpfx, pb->ubi nexthop, pkey should all reflect the same pfx */ + assert (!vnc_prefix_cmp (&pb->hpfx, pkey)); + if (vnc_prefix_cmp (&pb->hpfx, &pfx_orig_nexthop)) + { + char str_onh[BUFSIZ]; + char str_nve_pfx[BUFSIZ]; + + prefix2str (&pfx_orig_nexthop, str_onh, BUFSIZ); + str_onh[BUFSIZ - 1] = 0; + + prefix2str (&pb->hpfx, str_nve_pfx, BUFSIZ); + str_nve_pfx[BUFSIZ - 1] = 0; + + zlog_debug + ("%s: %s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s", + __func__, tag, str_onh, str_nve_pfx); + assert (0); + } + } + zlog_debug ("%s: vnc_rhnck OK", tag); +} + +#define VNC_RHNCK(n) do {char buf[BUFSIZ];sprintf(buf,"%s: %s", __func__, #n);vnc_rhnck(buf);} while (0) + +#else + +#define VNC_RHNCK(n) + +#endif + +/*********************************************************************** + * Add/Delete Unicast Route + ***********************************************************************/ + +/* + * "Adding a Route" import process + */ + +/* + * extract and package information from the BGP unicast route. + * Return code 0 means OK, non-0 means drop. + * + * If return code is 0, caller MUST release ecom + */ +static int +process_unicast_route ( + struct bgp *bgp, /* in */ + afi_t afi, /* in */ + struct prefix *prefix, /* in */ + struct bgp_info *info, /* in */ + struct ecommunity **ecom, /* OUT */ + struct prefix *unicast_nexthop) /* OUT */ +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct route_map *rmap = NULL; + struct prefix pfx_orig_nexthop; + + memset (&pfx_orig_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */ + + /* + * prefix list check + */ + if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) + { + zlog_debug ("%s: HC prefix list is set, checking", __func__); + if (prefix_list_apply + (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi], + prefix) == PREFIX_DENY) + { + zlog_debug ("%s: prefix list returns DENY, blocking route", + __func__); + return -1; + } + zlog_debug ("%s: prefix list returns PASS, allowing route", __func__); + } + + /* apply routemap, if any, later */ + rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * Extract original nexthop, which we expect to be a NVE connected router + * Note that this is the nexthop before any possible application of policy + */ + /* + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix (afi, attr, &pfx_orig_nexthop); + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset (&hattr, 0, sizeof (struct attr)); + bgp_attr_dup (&hattr, attr); /* hattr becomes a ghost attr */ + + if (rmap) + { + struct bgp_info info; + route_map_result_t ret; + + memset (&info, 0, sizeof (info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply (rmap, prefix, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + zlog_debug ("%s: route map \"%s\" says DENY, returning", __func__, + rmap->name); + return -1; + } + } + + /* + * Get the (possibly altered by policy) unicast nexthop + * for later lookup in the Import Table by caller + */ + rfapiUnicastNexthop2Prefix (afi, &hattr, unicast_nexthop); + + if (hattr.extra && hattr.extra->ecommunity) + *ecom = ecommunity_dup (hattr.extra->ecommunity); + else + *ecom = ecommunity_new (); + + /* + * Done with hattr, clean up + */ + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + /* + * Add EC that carries original NH of iBGP route (2 bytes = magic + * value indicating it came from an VNC gateway; default 5226, but + * must be user configurable). Note that this is the nexthop before + * any application of policy. + */ + { + struct ecommunity_val vnc_gateway_magic; + uint16_t localadmin; + + /* Using route origin extended community type */ + memset (&vnc_gateway_magic, 0, sizeof (vnc_gateway_magic)); + vnc_gateway_magic.val[0] = 0x01; + vnc_gateway_magic.val[1] = 0x03; + + /* Only works for IPv4 nexthops */ + if (prefix->family == AF_INET) + { + memcpy (vnc_gateway_magic.val + 2, &unicast_nexthop->u.prefix4, 4); + } + localadmin = htons (hc->resolve_nve_roo_local_admin); + memcpy (vnc_gateway_magic.val + 6, (char *) &localadmin, 2); + + ecommunity_add_val (*ecom, &vnc_gateway_magic); + } + + return 0; +} + + +static void +vnc_import_bgp_add_route_mode_resolve_nve_one_bi ( + struct bgp *bgp, + afi_t afi, + struct bgp_info *bi, /* VPN bi */ + struct prefix_rd *prd, /* RD */ + struct prefix *prefix, /* unicast route prefix */ + uint32_t *local_pref,/* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct ecommunity *ecom) /* generated ecoms */ +{ + struct prefix un; + struct prefix nexthop; + struct rfapi_ip_addr nexthop_h; + uint32_t lifetime; + uint32_t *plifetime; + struct bgp_attr_encap_subtlv *encaptlvs; + + zlog_debug ("%s: entry", __func__); + + if (bi->type != ZEBRA_ROUTE_BGP && bi->type != ZEBRA_ROUTE_BGP_DIRECT) + { + + return; + } + if (bi->sub_type != BGP_ROUTE_NORMAL && + bi->sub_type != BGP_ROUTE_STATIC && bi->sub_type != BGP_ROUTE_RFP) + { + + return; + } + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + return; + + vncHDResolveNve.peer = bi->peer; + if (!rfapiGetVncTunnelUnAddr (bi->attr, &un)) + { + if (rfapiQprefix2Raddr (&un, &vncHDResolveNve.un_addr)) + return; + } + else + { + memset (&vncHDResolveNve.un_addr, 0, sizeof (vncHDResolveNve.un_addr)); + } + + /* Use nexthop of VPN route as nexthop of constructed route */ + rfapiNexthop2Prefix (bi->attr, &nexthop); + rfapiQprefix2Raddr (&nexthop, &nexthop_h); + + if (rfapiGetVncLifetime (bi->attr, &lifetime)) + { + plifetime = NULL; + } + else + { + plifetime = &lifetime; + } + + if (bi->attr && bi->attr->extra) + { + encaptlvs = bi->attr->extra->vnc_subtlvs; + } + else + { + encaptlvs = NULL; + } + + struct ecommunity *new_ecom = ecommunity_dup (ecom); + + if (bi->attr && bi->attr->extra && bi->attr->extra->ecommunity) + ecommunity_merge (new_ecom, bi->attr->extra->ecommunity); + + add_vnc_route ( + &vncHDResolveNve, + bgp, + SAFI_MPLS_VPN, + prefix, /* unicast route prefix */ + prd, + &nexthop_h, /* new nexthop */ + local_pref, + plifetime, + (struct bgp_tea_options *) encaptlvs, /* RFP options */ + NULL, + NULL, + new_ecom, + med, /* NULL => don't set med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + RFAPI_AHR_RFPOPT_IS_VNCTLV); /* flags */ + + ecommunity_free (&new_ecom); + +} + +static void +vnc_import_bgp_add_route_mode_resolve_nve_one_rd ( + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + afi_t afi, + struct bgp *bgp, + struct prefix *prefix, /* unicast prefix */ + struct ecommunity *ecom, /* generated ecoms */ + uint32_t *local_pref, /* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct prefix *ubi_nexthop) /* unicast nexthop */ +{ + struct bgp_node *bn; + struct bgp_info *bi; + + if (!table_rd) + return; + + { + char str_nh[BUFSIZ]; + + prefix2str (ubi_nexthop, str_nh, BUFSIZ); + str_nh[BUFSIZ - 1] = 0; + + zlog_debug ("%s: ubi_nexthop=%s", __func__, str_nh); + } + + /* exact match */ + bn = bgp_node_lookup (table_rd, ubi_nexthop); + if (!bn) + { + zlog_debug ("%s: no match in RD's table for ubi_nexthop", __func__); + return; + } + + /* Iterate over bgp_info items at this node */ + for (bi = bn->info; bi; bi = bi->next) + { + + vnc_import_bgp_add_route_mode_resolve_nve_one_bi (bgp, afi, bi, /* VPN bi */ + prd, + prefix, + local_pref, + med, ecom); + } + + bgp_unlock_node (bn); +} + +static void +vnc_import_bgp_add_route_mode_resolve_nve ( + struct bgp *bgp, + struct prefix *prefix,/* unicast prefix */ + struct bgp_info *info) /* unicast info */ +{ + afi_t afi = family2afi (prefix->family); + struct rfapi_cfg *hc = NULL; + + struct prefix pfx_unicast_nexthop = { 0 }; /* happy valgrind */ + + struct ecommunity *ecom = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + struct prefix_bag *pb; + struct bgp_node *bnp; /* prd table node */ + + /*debugging */ + { + char str_pfx[BUFSIZ]; + char str_nh[BUFSIZ]; + struct prefix nh; + + prefix2str (prefix, str_pfx, BUFSIZ); + str_pfx[BUFSIZ - 1] = 0; + + nh.prefixlen = 0; + rfapiUnicastNexthop2Prefix (afi, info->attr, &nh); + if (nh.prefixlen) + { + prefix2str (&nh, str_nh, BUFSIZ); + str_nh[BUFSIZ - 1] = 0; + } + else + { + str_nh[0] = '?'; + str_nh[1] = 0; + } + + zlog_debug ("%s(bgp=%p, unicast prefix=%s, unicast nh=%s)", + __func__, bgp, str_pfx, str_nh); + } + + if (info->type != ZEBRA_ROUTE_BGP) + { + zlog_debug ("%s: unicast type %d=\"%s\" is not %d=%s, skipping", + __func__, info->type, zebra_route_string (info->type), + ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP"); + return; + } + + /* + * Preliminary checks + */ + + if (!afi) + { + zlog_err ("%s: can't get afi of prefix", __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + + if (process_unicast_route (bgp, afi, prefix, info, + &ecom, &pfx_unicast_nexthop)) + { + + zlog_debug ("%s: process_unicast_route error, skipping", __func__); + return; + } + + local_pref = calc_local_pref (info->attr, info->peer); + if (info->attr && + (info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) + { + + med = &info->attr->med; + } + + + /* + * At this point, we have allocated: + * + * ecom ecommunity ptr, union of unicast and ROO parts (no NVE part) + * + * And we have set: + * + * pfx_unicast_nexthop nexthop of uncast route + */ + + if (!bgp->rfapi->resolve_nve_nexthop) + { + bgp->rfapi->resolve_nve_nexthop = + skiplist_new (SKIPLIST_FLAG_ALLOW_DUPLICATES, vnc_prefix_cmp, + prefix_bag_free); + } + + pb = XCALLOC (MTYPE_RFAPI_PREFIX_BAG, sizeof (struct prefix_bag)); + pb->hpfx = pfx_unicast_nexthop; + pb->ubi = info; + pb->upfx = *prefix; + + bgp_info_lock (info); /* skiplist refers to it */ + skiplist_insert (bgp->rfapi->resolve_nve_nexthop, &pb->hpfx, pb); + + /* + * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop + * (exact match, /32). If an exact match is found, call add_vnc_route. + */ + + for (bnp = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); bnp; + bnp = bgp_route_next (bnp)) + { + + struct bgp_table *table; + + table = (struct bgp_table *) (bnp->info); + + if (!table) + continue; + + vnc_import_bgp_add_route_mode_resolve_nve_one_rd ((struct prefix_rd *) + &bnp->p, table, afi, + bgp, prefix, ecom, + &local_pref, med, + &pfx_unicast_nexthop); + + } + + + if (ecom) + ecommunity_free (&ecom); + + zlog_debug ("%s: done", __func__); +} + + +static void +vnc_import_bgp_add_route_mode_plain (struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info) +{ + afi_t afi = family2afi (prefix->family); + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct rfapi_cfg *hc = NULL; + struct attr *iattr = NULL; + + struct rfapi_ip_addr vnaddr; + struct prefix vn_pfx_space; + struct prefix *vn_pfx = NULL; + int ahr_flags = 0; + struct ecommunity *ecom = NULL; + struct prefix_rd prd; + struct route_map *rmap = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + { + char buf[BUFSIZ]; + + buf[0] = 0; + prefix2str (prefix, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s(prefix=%s) entry", __func__, buf); + } + + if (!afi) + { + zlog_err ("%s: can't get afi of prefix", __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + /* + * mode "plain" specific code + */ + { + zlog_debug ("%s: NOT using redist RFG", __func__); + + /* + * prefix list check + */ + if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) + { + zlog_debug ("%s: HC prefix list is set, checking", __func__); + if (prefix_list_apply + (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi], + prefix) == PREFIX_DENY) + { + zlog_debug ("%s: prefix list returns DENY, blocking route", + __func__); + return; + } + zlog_debug ("%s: prefix list returns PASS, allowing route", __func__); + } + + /* apply routemap, if any, later */ + rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix (afi, attr, &vn_pfx_space); + vn_pfx = &vn_pfx_space; + + /* UN address */ + ahr_flags |= RFAPI_AHR_NO_TUNNEL_SUBTLV; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + { + char buf[BUFSIZ]; + + buf[0] = 0; + prefix2str (vn_pfx, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s vn_pfx=%s", __func__, buf); + } + + /* + * Compute VN address + */ + if (rfapiQprefix2Raddr (vn_pfx, &vnaddr)) + { + zlog_debug ("%s: redist VN invalid, skipping", __func__); + return; + } + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset (&hattr, 0, sizeof (struct attr)); + bgp_attr_dup (&hattr, attr); /* hattr becomes a ghost attr */ + + if (rmap) + { + struct bgp_info info; + route_map_result_t ret; + + memset (&info, 0, sizeof (info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply (rmap, prefix, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + zlog_debug ("%s: route map \"%s\" says DENY, returning", __func__, + rmap->name); + return; + } + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + /* Now iattr is an allocated interned attr */ + + /* + * Mode "plain" specific code + * + * Sets RD in dummy HD + * Allocates ecom + */ + { + if (vnaddr.addr_family != AF_INET) + { + zlog_debug + ("%s: can't auto-assign RD, VN AF (%d) is not IPv4, skipping", + __func__, vnaddr.addr_family); + if (iattr) + { + bgp_attr_unintern (&iattr); + } + return; + } + memset (&prd, 0, sizeof (prd)); + rfapi_set_autord_from_vn (&prd, &vnaddr); + + if (iattr && iattr->extra && iattr->extra->ecommunity) + ecom = ecommunity_dup (iattr->extra->ecommunity); + + } + + local_pref = calc_local_pref (iattr, peer); + + if (iattr && (iattr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) + { + med = &iattr->med; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + { + char buf[BUFSIZ]; + + buf[0] = 0; + rfapiRfapiIpAddr2Str (&vnaddr, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: setting vnaddr to %s", __func__, buf); + } + + vncHDBgpDirect.peer = peer; + add_vnc_route (&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd, &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), NULL, /* RFP options */ + NULL, NULL, ecom, med, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, ahr_flags); + vncHDBgpDirect.peer = NULL; + + if (ecom) + ecommunity_free (&ecom); +} + +static void +vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info, + struct rfapi_nve_group_cfg *rfg) +{ + afi_t afi = family2afi (prefix->family); + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct rfapi_cfg *hc = NULL; + struct attr *iattr = NULL; + + struct rfapi_ip_addr vnaddr; + struct prefix *vn_pfx = NULL; + int ahr_flags = 0; + struct ecommunity *ecom = NULL; + struct prefix_rd prd; + struct route_map *rmap = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + { + char buf[BUFSIZ]; + + buf[0] = 0; + prefix2str (prefix, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s(prefix=%s) entry", __func__, buf); + } + + assert (rfg); + + if (!afi) + { + zlog_err ("%s: can't get afi of prefix", __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + + /* + * RFG-specific code + */ + { + + struct rfapi_ip_prefix pfx_un; + + zlog_debug ("%s: using redist RFG", __func__); + + /* + * RFG prefix list check + */ + if (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) + { + zlog_debug ("%s: RFG prefix list is set, checking", __func__); + if (prefix_list_apply + (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi], + prefix) == PREFIX_DENY) + { + zlog_debug ("%s: prefix list returns DENY, blocking route", + __func__); + return; + } + zlog_debug ("%s: prefix list returns PASS, allowing route", __func__); + } + + /* apply routemap, if any, later */ + rmap = rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * export nve group's VN addr prefix must be a /32 which + * will yield the VN addr to use + */ + vn_pfx = &rfg->vn_prefix; + + /* + * UN Address + */ + if (!is_host_prefix (&rfg->un_prefix)) + { + /* NB prefixlen==0 means it has not been configured */ + zlog_debug ("%s: redist RFG UN pfx not host pfx (plen=%d), skipping", + __func__, rfg->un_prefix.prefixlen); + return; + } + + rfapiQprefix2Rprefix (&rfg->un_prefix, &pfx_un); + + vncHDBgpDirect.un_addr = pfx_un.prefix; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + { + char buf[BUFSIZ]; + + buf[0] = 0; + prefix2str (vn_pfx, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s vn_pfx=%s", __func__, buf); + } + + /* + * Compute VN address + */ + if (rfapiQprefix2Raddr (vn_pfx, &vnaddr)) + { + zlog_debug ("%s: redist VN invalid, skipping", __func__); + return; + } + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset (&hattr, 0, sizeof (struct attr)); + bgp_attr_dup (&hattr, attr); /* hattr becomes a ghost attr */ + + if (rmap) + { + struct bgp_info info; + route_map_result_t ret; + + memset (&info, 0, sizeof (info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply (rmap, prefix, RMAP_BGP, &info); + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + zlog_debug ("%s: route map \"%s\" says DENY, returning", __func__, + rmap->name); + return; + } + } + + iattr = bgp_attr_intern (&hattr); + bgp_attr_flush (&hattr); + bgp_attr_extra_free (&hattr); + + /* Now iattr is an allocated interned attr */ + + /* + * RFG-specific code + * + * Sets RD in dummy HD + * Allocates ecom + */ + { + + memset (&prd, 0, sizeof (prd)); + prd = rfg->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + if (rfg->rd.family == AF_UNIX) + { + rfapi_set_autord_from_vn (&prd, &vnaddr); + } + + if (rfg->rt_export_list) + ecom = ecommunity_dup (bgp->rfapi_cfg->rfg_redist->rt_export_list); + else + ecom = ecommunity_new (); + + if (iattr && iattr->extra && iattr->extra->ecommunity) + ecom = ecommunity_merge (ecom, iattr->extra->ecommunity); + } + + local_pref = calc_local_pref (iattr, peer); + + if (iattr && (iattr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) + { + + med = &iattr->med; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + { + char buf[BUFSIZ]; + + buf[0] = 0; + rfapiRfapiIpAddr2Str (&vnaddr, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: setting vnaddr to %s", __func__, buf); + } + + vncHDBgpDirect.peer = peer; + add_vnc_route ( + &vncHDBgpDirect, + bgp, + SAFI_MPLS_VPN, + prefix, + &prd, + &vnaddr, + &local_pref, + &(bgp->rfapi_cfg->redist_lifetime), + NULL, /* RFP options */ + NULL, + NULL, + ecom, + NULL, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + ahr_flags); + vncHDBgpDirect.peer = NULL; + + if (ecom) + ecommunity_free (&ecom); +} + +static void +vnc_import_bgp_del_route_mode_plain (struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info) +{ + struct prefix_rd prd; + afi_t afi = family2afi (prefix->family); + struct prefix *vn_pfx = NULL; + struct rfapi_ip_addr vnaddr; + struct prefix vn_pfx_space; + + + assert (afi); + + /* + * Compute VN address + */ + + if (info && info->attr) + { + rfapiUnicastNexthop2Prefix (afi, info->attr, &vn_pfx_space); + } + else + { + zlog_debug ("%s: no attr, can't delete route", __func__); + return; + } + vn_pfx = &vn_pfx_space; + + vnaddr.addr_family = vn_pfx->family; + switch (vn_pfx->family) + { + case AF_INET: + if (vn_pfx->prefixlen != 32) + { + zlog_debug ("%s: redist VN plen (%d) != 32, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v4 = vn_pfx->u.prefix4; + break; + + case AF_INET6: + if (vn_pfx->prefixlen != 128) + { + zlog_debug ("%s: redist VN plen (%d) != 128, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v6 = vn_pfx->u.prefix6; + break; + + default: + zlog_debug ("%s: no redist RFG VN host pfx configured, skipping", + __func__); + return; + } + + + memset (&prd, 0, sizeof (prd)); + if (rfapi_set_autord_from_vn (&prd, &vnaddr)) + { + zlog_debug ("%s: can't auto-assign RD, skipping", __func__); + return; + } + + vncHDBgpDirect.peer = info->peer; + zlog_debug ("%s: setting peer to %p", __func__, vncHDBgpDirect.peer); + del_vnc_route (&vncHDBgpDirect, + info->peer, + bgp, + SAFI_MPLS_VPN, + prefix, + &prd, + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 1); + + vncHDBgpDirect.peer = NULL; +} + +static void +vnc_import_bgp_del_route_mode_nvegroup (struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info) +{ + struct prefix_rd prd; + afi_t afi = family2afi (prefix->family); + struct rfapi_nve_group_cfg *rfg = NULL; + struct prefix *vn_pfx = NULL; + struct rfapi_ip_addr vnaddr; + + + assert (afi); + + assert ((rfg = bgp->rfapi_cfg->rfg_redist)); + + /* + * Compute VN address + */ + + /* + * export nve group's VN addr prefix must be a /32 which + * will yield the VN addr to use + */ + vn_pfx = &rfg->vn_prefix; + + + vnaddr.addr_family = vn_pfx->family; + switch (vn_pfx->family) + { + case AF_INET: + if (vn_pfx->prefixlen != 32) + { + zlog_debug ("%s: redist VN plen (%d) != 32, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v4 = vn_pfx->u.prefix4; + break; + + case AF_INET6: + if (vn_pfx->prefixlen != 128) + { + zlog_debug ("%s: redist VN plen (%d) != 128, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v6 = vn_pfx->u.prefix6; + break; + + default: + zlog_debug ("%s: no redist RFG VN host pfx configured, skipping", + __func__); + return; + } + + memset (&prd, 0, sizeof (prd)); + prd = rfg->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + if (rfg->rd.family == AF_UNIX) + { + /* means "auto" with VN addr */ + if (rfapi_set_autord_from_vn (&prd, &vnaddr)) + { + zlog_debug ("%s: can't auto-assign RD, skipping", __func__); + return; + } + } + + + vncHDBgpDirect.peer = info->peer; + zlog_debug ("%s: setting peer to %p", __func__, vncHDBgpDirect.peer); + del_vnc_route (&vncHDBgpDirect, + info->peer, + bgp, + SAFI_MPLS_VPN, + prefix, + &prd, + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 1); + + vncHDBgpDirect.peer = NULL; +} + +static void +vnc_import_bgp_del_route_mode_resolve_nve_one_bi ( + struct bgp *bgp, + afi_t afi, + struct bgp_info *bi, /* VPN bi */ + struct prefix_rd *prd, /* RD */ + struct prefix *prefix)/* unicast route prefix */ +{ + struct prefix un; + uint32_t lifetime; + uint32_t *plifetime; + struct bgp_attr_encap_subtlv *encaptlvs; + + if (bi->type != ZEBRA_ROUTE_BGP && bi->type != ZEBRA_ROUTE_BGP_DIRECT) + { + + return; + } + if (bi->sub_type != BGP_ROUTE_NORMAL && + bi->sub_type != BGP_ROUTE_STATIC && bi->sub_type != BGP_ROUTE_RFP) + { + + return; + } + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + return; + + vncHDResolveNve.peer = bi->peer; + if (!rfapiGetVncTunnelUnAddr (bi->attr, &un)) + { + if (rfapiQprefix2Raddr (&un, &vncHDResolveNve.un_addr)) + return; + } + else + { + memset (&vncHDResolveNve.un_addr, 0, sizeof (vncHDResolveNve.un_addr)); + } + + if (rfapiGetVncLifetime (bi->attr, &lifetime)) + { + plifetime = NULL; + } + else + { + plifetime = &lifetime; + } + + if (bi->attr && bi->attr->extra) + { + encaptlvs = bi->attr->extra->vnc_subtlvs; + } + else + { + encaptlvs = NULL; + } + + del_vnc_route (&vncHDResolveNve, vncHDResolveNve.peer, bgp, SAFI_MPLS_VPN, prefix, /* unicast route prefix */ + prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 0); /* flags */ + +} + +static void +vnc_import_bgp_del_route_mode_resolve_nve_one_rd ( + struct prefix_rd *prd, + struct bgp_table *table_rd, /* per-rd VPN route table */ + afi_t afi, + struct bgp *bgp, + struct prefix *prefix, /* unicast prefix */ + struct prefix *ubi_nexthop) /* unicast bi's nexthop */ +{ + struct bgp_node *bn; + struct bgp_info *bi; + + if (!table_rd) + return; + + { + char str_nh[BUFSIZ]; + + prefix2str (ubi_nexthop, str_nh, BUFSIZ); + str_nh[BUFSIZ - 1] = 0; + + zlog_debug ("%s: ubi_nexthop=%s", __func__, str_nh); + } + + + /* exact match */ + bn = bgp_node_lookup (table_rd, ubi_nexthop); + if (!bn) + { + zlog_debug ("%s: no match in RD's table for ubi_nexthop", __func__); + return; + } + + /* Iterate over bgp_info items at this node */ + for (bi = bn->info; bi; bi = bi->next) + { + + vnc_import_bgp_del_route_mode_resolve_nve_one_bi (bgp, afi, bi, /* VPN bi */ + prd, /* VPN RD */ + prefix); /* unicast route prefix */ + } + + bgp_unlock_node (bn); +} + +static void +vnc_import_bgp_del_route_mode_resolve_nve (struct bgp *bgp, + afi_t afi, + struct prefix *prefix, + struct bgp_info *info) +{ + struct ecommunity *ecom = NULL; + struct prefix pfx_unicast_nexthop = { 0 }; /* happy valgrind */ + + //struct listnode *hnode; + //struct rfapi_descriptor *rfd; + struct prefix_bag *pb; + void *cursor; + struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop; + int rc; + struct bgp_node *bnp; /* prd table node */ + + if (!sl) + { + zlog_debug ("%s: no RHN entries, skipping", __func__); + return; + } + + if (info->type != ZEBRA_ROUTE_BGP) + { + zlog_debug ("%s: unicast type %d=\"%s\" is not %d=%s, skipping", + __func__, info->type, zebra_route_string (info->type), + ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP"); + return; + } + + if (process_unicast_route (bgp, afi, prefix, info, + &ecom, &pfx_unicast_nexthop)) + { + + zlog_debug ("%s: process_unicast_route error, skipping", __func__); + return; + } + + rc = skiplist_first_value (sl, &pfx_unicast_nexthop, (void *) &pb, &cursor); + while (!rc) + { + if (pb->ubi == info) + { + skiplist_delete (sl, &pfx_unicast_nexthop, pb); + bgp_info_unlock (info); + break; + } + rc = + skiplist_next_value (sl, &pfx_unicast_nexthop, (void *) &pb, &cursor); + } + + /* + * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop + * (exact match, /32). If an exact match is found, call add_vnc_route. + */ + + for (bnp = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); bnp; + bnp = bgp_route_next (bnp)) + { + + struct bgp_table *table; + + table = (struct bgp_table *) (bnp->info); + + if (!table) + continue; + + vnc_import_bgp_del_route_mode_resolve_nve_one_rd ((struct prefix_rd *) &bnp->p, table, afi, bgp, prefix, &pfx_unicast_nexthop); /* TBD how is this set? */ + } + + if (ecom) + ecommunity_free (&ecom); +} + + + + +/*********************************************************************** + * Add/Delete CE->NVE routes + ***********************************************************************/ + +/* + * Should be called whan a bi is added to VPN RIB. This function + * will check if it is a host route and return immediately if not. + */ +void +vnc_import_bgp_add_vnc_host_route_mode_resolve_nve ( + struct bgp *bgp, + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + struct prefix *prefix, /* VPN prefix */ + struct bgp_info *bi) /* new VPN host route */ +{ + afi_t afi = family2afi (prefix->family); + struct skiplist *sl = NULL; + int rc; + struct prefix_bag *pb; + void *cursor; + struct rfapi_cfg *hc = NULL; + + zlog_debug ("%s: entry", __func__); + + if (afi != AFI_IP && afi != AFI_IP6) + { + zlog_debug ("%s: bad afi %d, skipping", __func__, afi); + return; + } + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) + { + zlog_debug ("%s: not in resolve-nve mode, skipping", __func__); + return; + } + + if (bgp && bgp->rfapi) + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) + { + zlog_debug ("%s: no resolve_nve_nexthop skiplist, skipping", __func__); + return; + } + + if (!is_host_prefix (prefix)) + { + zlog_debug ("%s: not host prefix, skipping", __func__); + return; + } + + rc = skiplist_first_value (sl, prefix, (void *) &pb, &cursor); + while (!rc) + { + struct ecommunity *ecom; + struct prefix pfx_unicast_nexthop; + uint32_t *med = NULL; + uint32_t local_pref; + + memset (&pfx_unicast_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */ + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + { + char hbuf[BUFSIZ]; + char ubuf[BUFSIZ]; + + prefix2str (&pb->hpfx, hbuf, BUFSIZ); + prefix2str (&pb->upfx, ubuf, BUFSIZ); + + zlog_debug + ("%s: examining RHN Entry (q=%p): upfx=%s, hpfx=%s, ubi=%p", + __func__, cursor, ubuf, hbuf, pb->ubi); + } + + if (process_unicast_route (bgp, afi, &pb->upfx, pb->ubi, + &ecom, &pfx_unicast_nexthop)) + { + + zlog_debug ("%s: process_unicast_route error, skipping", __func__); + continue; + } + local_pref = calc_local_pref (pb->ubi->attr, pb->ubi->peer); + + if (pb->ubi->attr && + (pb->ubi->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) + { + + med = &pb->ubi->attr->med; + } + + /* + * Sanity check + */ + if (vnc_prefix_cmp (&pfx_unicast_nexthop, prefix)) + { + char str_unh[BUFSIZ]; + char str_nve_pfx[BUFSIZ]; + + prefix2str (&pfx_unicast_nexthop, str_unh, BUFSIZ); + str_unh[BUFSIZ - 1] = 0; + + prefix2str (prefix, str_nve_pfx, BUFSIZ); + str_nve_pfx[BUFSIZ - 1] = 0; + + zlog_debug + ("%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s", + __func__, str_unh, str_nve_pfx); + assert (0); + } + + vnc_import_bgp_add_route_mode_resolve_nve_one_bi (bgp, afi, bi, /* VPN bi */ + prd, &pb->upfx, /* unicast prefix */ + &local_pref, + med, ecom); + + if (ecom) + ecommunity_free (&ecom); + +#if DEBUG_RHN_LIST + /* debug */ + { + char pbuf[BUFSIZ]; + + prefix2str (prefix, pbuf, BUFSIZ); + + zlog_debug ("%s: advancing past RHN Entry (q=%p): with prefix %s", + __func__, cursor, pbuf); + print_rhn_list (__func__, NULL); /* debug */ + } +#endif + rc = skiplist_next_value (sl, prefix, (void *) &pb, &cursor); + } + zlog_debug ("%s: done", __func__); +} + + +void +vnc_import_bgp_del_vnc_host_route_mode_resolve_nve ( + struct bgp *bgp, + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + struct prefix *prefix, /* VPN prefix */ + struct bgp_info *bi) /* old VPN host route */ +{ + afi_t afi = family2afi (prefix->family); + struct skiplist *sl = NULL; + struct prefix_bag *pb; + void *cursor; + struct rfapi_cfg *hc = NULL; + int rc; + + { + char str_pfx[BUFSIZ]; + + prefix2str (prefix, str_pfx, BUFSIZ); + str_pfx[BUFSIZ - 1] = 0; + + zlog_debug ("%s(bgp=%p, nve prefix=%s)", __func__, bgp, str_pfx); + } + + if (afi != AFI_IP && afi != AFI_IP6) + return; + + if (!(hc = bgp->rfapi_cfg)) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) + { + zlog_debug ("%s: not in resolve-nve mode, skipping", __func__); + return; + } + + if (bgp && bgp->rfapi) + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) + { + zlog_debug ("%s: no RHN entries, skipping", __func__); + return; + } + + if (!is_host_prefix (prefix)) + { + zlog_debug ("%s: not host route, skip", __func__); + return; + } + + /* + * Find all entries with key == CE in the RHN list + */ + rc = skiplist_first_value (sl, prefix, (void *) &pb, &cursor); + while (!rc) + { + + struct ecommunity *ecom; + struct prefix pfx_unicast_nexthop; + + memset (&pfx_unicast_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */ + + if (process_unicast_route (bgp, afi, &pb->upfx, pb->ubi, + &ecom, &pfx_unicast_nexthop)) + { + + zlog_debug ("%s: process_unicast_route error, skipping", __func__); + continue; + } + + /* + * Sanity check + */ + if (vnc_prefix_cmp (&pfx_unicast_nexthop, prefix)) + { + char str_unh[BUFSIZ]; + char str_nve_pfx[BUFSIZ]; + + prefix2str (&pfx_unicast_nexthop, str_unh, BUFSIZ); + str_unh[BUFSIZ - 1] = 0; + + prefix2str (prefix, str_nve_pfx, BUFSIZ); + str_nve_pfx[BUFSIZ - 1] = 0; + + zlog_debug + ("%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s", + __func__, str_unh, str_nve_pfx); + assert (0); + } + + vnc_import_bgp_del_route_mode_resolve_nve_one_bi (bgp, + afi, + bi, prd, &pb->upfx); + + if (ecom) + ecommunity_free (&ecom); + + rc = skiplist_next_value (sl, prefix, (void *) &pb, &cursor); + } +} + + +/*********************************************************************** + * Exterior Routes + ***********************************************************************/ + +#define DEBUG_IS_USABLE_INTERIOR 1 + +static int +is_usable_interior_route (struct bgp_info *bi_interior) +{ + if (!VALID_INTERIOR_TYPE (bi_interior->type)) + { +#if DEBUG_IS_USABLE_INTERIOR + zlog_debug ("%s: NO: type %d is not valid interior type", + __func__, bi_interior->type); +#endif + return 0; + } + if (!CHECK_FLAG (bi_interior->flags, BGP_INFO_VALID)) + { +#if DEBUG_IS_USABLE_INTERIOR + zlog_debug ("%s: NO: BGP_INFO_VALID not set", __func__); +#endif + return 0; + } + return 1; +} + +/* + * There should be only one of these per prefix at a time. + * This should be called as a result of selection operation + * + * NB should be called espacially for bgp instances that are named, + * because the exterior routes will always come from one of those. + * We filter here on the instance name to make sure we get only the + * right routes. + */ +static void +vnc_import_bgp_exterior_add_route_it ( + struct bgp *bgp, /* exterior instance, we hope */ + struct prefix *prefix,/* unicast prefix */ + struct bgp_info *info, /* unicast info */ + struct rfapi_import_table *it_only)/* NULL, or limit to this IT */ +{ + struct rfapi *h; + struct rfapi_cfg *hc; + struct prefix pfx_orig_nexthop; + struct rfapi_import_table *it; + struct bgp *bgp_default = bgp_get_default (); + afi_t afi = family2afi (prefix->family); + + h = bgp_default->rfapi; + hc = bgp_default->rfapi_cfg; + + zlog_debug ("%s: entry with it=%p", __func__, it_only); + + if (!h || !hc) + { + zlog_debug ("%s: rfapi or rfapi_cfg not instantiated, skipping", + __func__); + return; + } + if (!hc->redist_bgp_exterior_view) + { + zlog_debug ("%s: exterior view not set, skipping", __func__); + return; + } + if (bgp != hc->redist_bgp_exterior_view) + { + zlog_debug ("%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping", + __func__, bgp, hc->redist_bgp_exterior_view); + return; + } + + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: redist of exterior routes not enabled, skipping", + __func__); + return; + } + + if (!info->attr) + { + zlog_debug ("%s: no info, skipping", __func__); + return; + } + + /* + * Extract nexthop from exterior route + * + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_orig_nexthop); + + for (it = h->imports; it; it = it->next) + { + struct route_table *table; + struct route_node *rn; + struct route_node *par; + struct bgp_info *bi_interior; + int have_usable_route; + + zlog_debug ("%s: doing it %p", __func__, it); + + if (it_only && (it_only != it)) + { + zlog_debug ("%s: doesn't match it_only %p", __func__, it_only); + continue; + } + + table = it->imported_vpn[afi]; + + for (rn = route_node_match (table, &pfx_orig_nexthop), + have_usable_route = 0; (!have_usable_route) && rn;) + { + + zlog_debug ("%s: it %p trying rn %p", __func__, it, rn); + + for (bi_interior = rn->info; bi_interior; + bi_interior = bi_interior->next) + { + struct prefix_rd *prd; + struct attr new_attr; + u_int32_t label = 0; + + if (!is_usable_interior_route (bi_interior)) + continue; + + zlog_debug ("%s: usable: bi_interior %p", __func__, + bi_interior); + + /* + * have a legitimate route to exterior's nexthop + * via NVE. + * + * Import unicast route to the import table + */ + have_usable_route = 1; + + if (bi_interior->extra) + { + prd = &bi_interior->extra->vnc.import.rd; + label = decode_label (bi_interior->extra->tag); + } + else + prd = NULL; + + /* use local_pref from unicast route */ + memset (&new_attr, 0, sizeof (struct attr)); + bgp_attr_dup (&new_attr, bi_interior->attr); + if (info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + { + new_attr.local_pref = info->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL, /* rfd */ + prefix, + NULL, + afi, + prd, + &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + bgp_attr_extra_free (&new_attr); + } + + if (have_usable_route) + { + /* + * Make monitor + * + * TBD factor this out into its own function + */ + struct prefix *pfx_mon = prefix_new (); + if (!RFAPI_MONITOR_EXTERIOR (rn)->source) + { + RFAPI_MONITOR_EXTERIOR (rn)->source = + skiplist_new (0, NULL, (void (*)(void *)) prefix_free); + route_lock_node (rn); /* for skiplist */ + } + route_lock_node (rn); /* for skiplist entry */ + prefix_copy (pfx_mon, prefix); + if (!skiplist_insert (RFAPI_MONITOR_EXTERIOR (rn)->source, + info, pfx_mon)) + { + + bgp_info_lock (info); + } + } + par = rn->parent; + if (par) + route_lock_node (par); + route_unlock_node (rn); + rn = par; + } + if (rn) + route_unlock_node (rn); + + if (!have_usable_route) + { + struct prefix *pfx_mon = prefix_new (); + prefix_copy (pfx_mon, prefix); + if (!skiplist_insert (it->monitor_exterior_orphans, info, pfx_mon)) + { + + bgp_info_lock (info); + } + } + } +} + +void +vnc_import_bgp_exterior_add_route ( + struct bgp *bgp, /* exterior instance, we hope */ + struct prefix *prefix,/* unicast prefix */ + struct bgp_info *info) /* unicast info */ +{ + vnc_import_bgp_exterior_add_route_it (bgp, prefix, info, NULL); +} + +/* + * There should be only one of these per prefix at a time. + * This should probably be called as a result of selection operation. + * + * NB should be called espacially for bgp instances that are named, + * because the exterior routes will always come from one of those. + * We filter here on the instance name to make sure we get only the + * right routes. + */ +void +vnc_import_bgp_exterior_del_route ( + struct bgp *bgp, + struct prefix *prefix, /* unicast prefix */ + struct bgp_info *info) /* unicast info */ +{ + struct rfapi *h; + struct rfapi_cfg *hc; + struct rfapi_import_table *it; + struct prefix pfx_orig_nexthop; + afi_t afi = family2afi (prefix->family); + struct bgp *bgp_default = bgp_get_default (); + + memset (&pfx_orig_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */ + + h = bgp_default->rfapi; + hc = bgp_default->rfapi_cfg; + + if (!h || !hc) + { + zlog_debug ("%s: rfapi or rfapi_cfg not instantiated, skipping", + __func__); + return; + } + if (!hc->redist_bgp_exterior_view) + { + zlog_debug ("%s: exterior view not set, skipping", __func__); + return; + } + if (bgp != hc->redist_bgp_exterior_view) + { + zlog_debug ("%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping", + __func__, bgp, hc->redist_bgp_exterior_view); + return; + } + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (!info->attr) + { + zlog_debug ("%s: no info, skipping", __func__); + return; + } + + /* + * Extract nexthop from exterior route + * + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_orig_nexthop); + + for (it = h->imports; it; it = it->next) + { + struct route_table *table; + struct route_node *rn; + struct route_node *par; + struct bgp_info *bi_interior; + int have_usable_route; + + table = it->imported_vpn[afi]; + + for (rn = route_node_match (table, &pfx_orig_nexthop), + have_usable_route = 0; (!have_usable_route) && rn;) + { + + for (bi_interior = rn->info; bi_interior; + bi_interior = bi_interior->next) + { + struct prefix_rd *prd; + u_int32_t label = 0; + + if (!is_usable_interior_route (bi_interior)) + continue; + + /* + * have a legitimate route to exterior's nexthop + * via NVE. + * + * Import unicast route to the import table + */ + have_usable_route = 1; + + if (bi_interior->extra) + { + prd = &bi_interior->extra->vnc.import.rd; + label = decode_label (bi_interior->extra->tag); + } + else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_KILL, bi_interior->peer, NULL, /* rfd */ + prefix, + NULL, + afi, + prd, + bi_interior->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + /* + * Delete monitor + * + * TBD factor this out into its own function + */ + { + if (RFAPI_MONITOR_EXTERIOR (rn)->source) + { + if (!skiplist_delete (RFAPI_MONITOR_EXTERIOR (rn)->source, + info, NULL)) + { + + bgp_info_unlock (info); + route_unlock_node (rn); /* sl entry */ + } + if (skiplist_empty (RFAPI_MONITOR_EXTERIOR (rn)->source)) + { + skiplist_free (RFAPI_MONITOR_EXTERIOR (rn)->source); + RFAPI_MONITOR_EXTERIOR (rn)->source = NULL; + route_unlock_node (rn); /* skiplist itself */ + } + } + } + } + par = rn->parent; + if (par) + route_lock_node (par); + route_unlock_node (rn); + rn = par; + } + if (rn) + route_unlock_node (rn); + + if (!have_usable_route) + { + if (!skiplist_delete (it->monitor_exterior_orphans, info, NULL)) + { + + bgp_info_unlock (info); + } + } + } +} + +/* + * This function should be called after a new interior VPN route + * has been added to an import_table. + * + * NB should also be called whenever an existing vpn interior route + * becomes valid (e.g., valid_interior_count is inremented) + */ +void +vnc_import_bgp_exterior_add_route_interior ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct route_node *rn_interior, /* VPN IT node */ + struct bgp_info *bi_interior) /* VPN IT route */ +{ + afi_t afi = family2afi (rn_interior->p.family); + struct route_node *par; + struct bgp_info *bi_exterior; + struct prefix *pfx_exterior; /* exterior pfx */ + void *cursor; + int rc; + struct list *list_adopted; + + zlog_debug ("%s: entry", __func__); + + if (!is_usable_interior_route (bi_interior)) + { + zlog_debug ("%s: not usable interior route, skipping", __func__); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (it == bgp->rfapi->it_ce) + { + zlog_debug ("%s: import table is it_ce, skipping", __func__); + return; + } + + /*debugging */ + { + char str_pfx[BUFSIZ]; + + prefix2str (&rn_interior->p, str_pfx, BUFSIZ); + str_pfx[BUFSIZ - 1] = 0; + + zlog_debug ("%s: interior prefix=%s, bi type=%d", + __func__, str_pfx, bi_interior->type); + } + + if (RFAPI_HAS_MONITOR_EXTERIOR (rn_interior)) + { + + int count = 0; /* debugging */ + + zlog_debug ("%s: has exterior monitor; ext src: %p", __func__, + RFAPI_MONITOR_EXTERIOR (rn_interior)->source); + + /* + * There is a monitor here already. Therefore, we do not need + * to do any pulldown. Just construct exterior routes based + * on the new interior route. + */ + cursor = NULL; + for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, + (void **) &pfx_exterior, &cursor); !rc; + rc = + skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, (void **) &pfx_exterior, + &cursor)) + { + + struct prefix_rd *prd; + struct attr new_attr; + u_int32_t label = 0; + + + ++count; /* debugging */ + + assert (bi_exterior); + assert (pfx_exterior); + + if (bi_interior->extra) + { + prd = &bi_interior->extra->vnc.import.rd; + label = decode_label (bi_interior->extra->tag); + } + else + prd = NULL; + + /* use local_pref from unicast route */ + memset (&new_attr, 0, sizeof (struct attr)); + bgp_attr_dup (&new_attr, bi_interior->attr); + if (bi_exterior && + (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + { + new_attr.local_pref = bi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL, /* rfd */ + pfx_exterior, + NULL, + afi, + prd, + &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + bgp_attr_extra_free (&new_attr); + } + zlog_debug + ("%s: finished constructing exteriors based on existing monitors", + __func__); + return; + } + + zlog_debug ("%s: no exterior monitor", __func__); + + /* + * No monitor at this node. Is this the first valid interior + * route at this node? + */ + if (RFAPI_MONITOR_EXTERIOR (rn_interior)->valid_interior_count > 1) + { + zlog_debug + ("%s: new interior route not first valid one, skipping pulldown", + __func__); + return; + } + + /* + * Look up the tree for possible pulldown candidates. + * Find nearest parent with an exterior route monitor + */ + for (par = rn_interior->parent; par; par = par->parent) + { + if (RFAPI_HAS_MONITOR_EXTERIOR (par)) + break; + } + + if (par) + { + + zlog_debug ("%s: checking parent %p for possible pulldowns", + __func__, par); + + /* check monitors at par for possible pulldown */ + cursor = NULL; + for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (par)->source, + (void **) &bi_exterior, + (void **) &pfx_exterior, &cursor); !rc; + rc = + skiplist_next (RFAPI_MONITOR_EXTERIOR (par)->source, + (void **) &bi_exterior, (void **) &pfx_exterior, + &cursor)) + { + + struct prefix pfx_nexthop; + + memset (&pfx_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */ + + /* check original nexthop for prefix match */ + rfapiUnicastNexthop2Prefix (afi, bi_exterior->attr, &pfx_nexthop); + + if (prefix_match (&rn_interior->p, &pfx_nexthop)) + { + + struct bgp_info *bi; + struct prefix_rd *prd; + struct attr new_attr; + u_int32_t label = 0; + + /* do pull-down */ + + /* + * add monitor to longer prefix + */ + struct prefix *pfx_mon = prefix_new (); + prefix_copy (pfx_mon, pfx_exterior); + if (!RFAPI_MONITOR_EXTERIOR (rn_interior)->source) + { + RFAPI_MONITOR_EXTERIOR (rn_interior)->source = + skiplist_new (0, NULL, (void (*)(void *)) prefix_free); + route_lock_node (rn_interior); + } + skiplist_insert (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + bi_exterior, pfx_mon); + route_lock_node (rn_interior); + + /* + * Delete constructed exterior routes based on + * parent routes. + */ + for (bi = par->info; bi; bi = bi->next) + { + + if (bi->extra) + { + prd = &bi->extra->vnc.import.rd; + label = decode_label (bi->extra->tag); + } + else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_KILL, bi->peer, NULL, /* rfd */ + pfx_exterior, + NULL, + afi, + prd, + bi->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, + &label); + } + + + /* + * Add constructed exterior routes based on + * the new interior route at longer prefix. + */ + if (bi_interior->extra) + { + prd = &bi_interior->extra->vnc.import.rd; + label = decode_label (bi_interior->extra->tag); + } + else + prd = NULL; + + /* use local_pref from unicast route */ + memset (&new_attr, 0, sizeof (struct attr)); + bgp_attr_dup (&new_attr, bi_interior->attr); + if (bi_exterior && + (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + { + new_attr.local_pref = bi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL, /* rfd */ + pfx_exterior, + NULL, + afi, + prd, + &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + bgp_attr_extra_free (&new_attr); + } + } + + /* + * The only monitors at rn_interior are the ones we added just + * above, so we can use the rn_interior list to identify which + * monitors to delete from the parent. + */ + cursor = NULL; + for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, NULL, &cursor); + !rc; + rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, NULL, &cursor)) + { + + + skiplist_delete (RFAPI_MONITOR_EXTERIOR (par)->source, + bi_exterior, NULL); + route_unlock_node (par); /* sl entry */ + } + if (skiplist_empty (RFAPI_MONITOR_EXTERIOR (par)->source)) + { + skiplist_free (RFAPI_MONITOR_EXTERIOR (par)->source); + RFAPI_MONITOR_EXTERIOR (par)->source = NULL; + route_unlock_node (par); /* sl itself */ + } + } + + zlog_debug ("%s: checking orphans", __func__); + + /* + * See if any orphans can be pulled down to the current node + */ + cursor = NULL; + list_adopted = NULL; + for (rc = skiplist_next (it->monitor_exterior_orphans, + (void **) &bi_exterior, (void **) &pfx_exterior, + &cursor); !rc; + rc = + skiplist_next (it->monitor_exterior_orphans, (void **) &bi_exterior, + (void **) &pfx_exterior, &cursor)) + { + + struct prefix pfx_nexthop; + char buf[BUFSIZ]; + afi_t afi_exterior = family2afi (pfx_exterior->family); + + prefix2str (pfx_exterior, buf, sizeof (buf)); + buf[sizeof (buf) - 1] = 0; + zlog_debug ("%s: checking exterior orphan at prefix %s", __func__, buf); + + if (afi_exterior != afi) + { + zlog_debug ("%s: exterior orphan afi %d != interior afi %d, skip", + __func__, afi_exterior, afi); + continue; + } + + /* check original nexthop for prefix match */ + rfapiUnicastNexthop2Prefix (afi, bi_exterior->attr, &pfx_nexthop); + + if (prefix_match (&rn_interior->p, &pfx_nexthop)) + { + + struct prefix_rd *prd; + struct attr new_attr; + u_int32_t label = 0; + + /* do pull-down */ + + /* + * add monitor to longer prefix + */ + + struct prefix *pfx_mon = prefix_new (); + prefix_copy (pfx_mon, pfx_exterior); + if (!RFAPI_MONITOR_EXTERIOR (rn_interior)->source) + { + RFAPI_MONITOR_EXTERIOR (rn_interior)->source = + skiplist_new (0, NULL, (void (*)(void *)) prefix_free); + route_lock_node (rn_interior); /* sl */ + } + skiplist_insert (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + bi_exterior, pfx_mon); + route_lock_node (rn_interior); /* sl entry */ + if (!list_adopted) + { + list_adopted = list_new (); + } + listnode_add (list_adopted, bi_exterior); + + /* + * Add constructed exterior routes based on the + * new interior route at the longer prefix. + */ + if (bi_interior->extra) + { + prd = &bi_interior->extra->vnc.import.rd; + label = decode_label (bi_interior->extra->tag); + } + else + prd = NULL; + + /* use local_pref from unicast route */ + memset (&new_attr, 0, sizeof (struct attr)); + bgp_attr_dup (&new_attr, bi_interior->attr); + if (bi_exterior && + (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + { + new_attr.local_pref = bi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL, /* rfd */ + pfx_exterior, + NULL, + afi, + prd, + &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + bgp_attr_extra_free (&new_attr); + } + } + if (list_adopted) + { + struct listnode *node; + struct route_node *bi_exterior; + + for (ALL_LIST_ELEMENTS_RO (list_adopted, node, bi_exterior)) + { + skiplist_delete (it->monitor_exterior_orphans, bi_exterior, NULL); + } + list_delete (list_adopted); + } +} + +/* + * This function should be called after an interior VPN route + * has been deleted from an import_table. + * bi_interior must still be valid, but it must already be detached + * from its route node and the route node's valid_interior_count + * must already be decremented. + * + * NB should also be called whenever an existing vpn interior route + * becomes invalid (e.g., valid_interior_count is decremented) + */ +void +vnc_import_bgp_exterior_del_route_interior ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct route_node *rn_interior, /* VPN IT node */ + struct bgp_info *bi_interior) /* VPN IT route */ +{ + afi_t afi = family2afi (rn_interior->p.family); + struct route_node *par; + struct bgp_info *bi_exterior; + struct prefix *pfx_exterior; /* exterior pfx */ + void *cursor; + int rc; + + if (!VALID_INTERIOR_TYPE (bi_interior->type)) + { + zlog_debug ("%s: type %d not valid interior type, skipping", + __func__, bi_interior->type); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (it == bgp->rfapi->it_ce) + { + zlog_debug ("%s: it is it_ce, skipping", __func__); + return; + } + + /* If no exterior routes depend on this prefix, nothing to do */ + if (!RFAPI_HAS_MONITOR_EXTERIOR (rn_interior)) + { + zlog_debug ("%s: no exterior monitor, skipping", __func__); + return; + } + + /*debugging */ + { + char str_pfx[BUFSIZ]; + + prefix2str (&rn_interior->p, str_pfx, BUFSIZ); + str_pfx[BUFSIZ - 1] = 0; + + zlog_debug ("%s: interior prefix=%s, bi type=%d", + __func__, str_pfx, bi_interior->type); + } + + /* + * Remove constructed routes based on the deleted interior route + */ + cursor = NULL; + for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, (void **) &pfx_exterior, + &cursor); !rc; + rc = + skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, (void **) &pfx_exterior, + &cursor)) + { + + struct prefix_rd *prd; + u_int32_t label = 0; + + if (bi_interior->extra) + { + prd = &bi_interior->extra->vnc.import.rd; + label = decode_label (bi_interior->extra->tag); + } + else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_KILL, bi_interior->peer, NULL, /* rfd */ + pfx_exterior, + NULL, + afi, + prd, + bi_interior->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + /* + * If there are no remaining valid interior routes at this prefix, + * we need to look up the tree for a possible node to move monitors to + */ + if (RFAPI_MONITOR_EXTERIOR (rn_interior)->valid_interior_count) + { + zlog_debug ("%s: interior routes still present, skipping", __func__); + return; + } + + /* + * Find nearest parent with at least one valid interior route + * If none is found, par will end up NULL, and we will move + * the monitors to the orphan list for this import table + */ + for (par = rn_interior->parent; par; par = par->parent) + { + if (RFAPI_MONITOR_EXTERIOR (par)->valid_interior_count) + break; + } + + zlog_debug ("%s: par=%p, ext src: %p", __func__, + par, RFAPI_MONITOR_EXTERIOR (rn_interior)->source); + + /* move all monitors */ + /* + * We will use and delete every element of the source skiplist + */ + while (!skiplist_first (RFAPI_MONITOR_EXTERIOR (rn_interior)->source, + (void **) &bi_exterior, (void **) &pfx_exterior)) + { + + struct prefix *pfx_mon = prefix_new (); + + prefix_copy (pfx_mon, pfx_exterior); + + if (par) + { + + struct bgp_info *bi; + + /* + * Add monitor to parent node + */ + if (!RFAPI_MONITOR_EXTERIOR (par)->source) + { + RFAPI_MONITOR_EXTERIOR (par)->source = + skiplist_new (0, NULL, (void (*)(void *)) prefix_free); + route_lock_node (par); /* sl */ + } + skiplist_insert (RFAPI_MONITOR_EXTERIOR (par)->source, + bi_exterior, pfx_mon); + route_lock_node (par); /* sl entry */ + + /* Add constructed exterior routes based on parent */ + for (bi = par->info; bi; bi = bi->next) + { + + struct prefix_rd *prd; + struct attr new_attr; + u_int32_t label = 0; + + if (bi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + continue; + + if (bi->extra) + { + prd = &bi->extra->vnc.import.rd; + label = decode_label (bi->extra->tag); + } + else + prd = NULL; + + /* use local_pref from unicast route */ + memset (&new_attr, 0, sizeof (struct attr)); + bgp_attr_dup (&new_attr, bi->attr); + if (bi_exterior && + (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + { + new_attr.local_pref = bi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi->peer, NULL, /* rfd */ + pfx_exterior, + NULL, + afi, + prd, + &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + bgp_attr_extra_free (&new_attr); + } + + } + else + { + + /* + * No interior route for exterior's nexthop. Save monitor + * in orphan list to await future route. + */ + skiplist_insert (it->monitor_exterior_orphans, + bi_exterior, pfx_mon); + } + + skiplist_delete_first (RFAPI_MONITOR_EXTERIOR (rn_interior)->source); + route_unlock_node (rn_interior); /* sl entry */ + } + if (skiplist_empty (RFAPI_MONITOR_EXTERIOR (rn_interior)->source)) + { + skiplist_free (RFAPI_MONITOR_EXTERIOR (rn_interior)->source); + RFAPI_MONITOR_EXTERIOR (rn_interior)->source = NULL; + route_unlock_node (rn_interior); /* sl itself */ + } + +} + +/*********************************************************************** + * Generic add/delete unicast routes + ***********************************************************************/ + +void +vnc_import_bgp_add_route ( + struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info) +{ + afi_t afi = family2afi (prefix->family); + + { + struct prefix pfx_nexthop; + char buf[BUFSIZ]; + char buf_nh[BUFSIZ]; + + prefix2str (prefix, buf, BUFSIZ); + rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_nexthop); + prefix2str (&pfx_nexthop, buf_nh, BUFSIZ); + + zlog_debug ("%s: pfx %s, nh %s", __func__, buf, buf_nh); + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "ENTER "); +#endif + VNC_RHNCK (enter); + + if (!afi) + { + zlog_err ("%s: can't get afi of prefix", __func__); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); + return; + } + + switch (bgp->rfapi_cfg->redist_mode) + { + case VNC_REDIST_MODE_PLAIN: + vnc_import_bgp_add_route_mode_plain (bgp, prefix, info); + break; + + case VNC_REDIST_MODE_RFG: + if (bgp->rfapi_cfg->rfg_redist) + vnc_import_bgp_add_route_mode_nvegroup (bgp, prefix, info, + bgp->rfapi_cfg->rfg_redist); + else + zlog_debug ("%s: mode RFG but no redist RFG", __func__); + break; + + case VNC_REDIST_MODE_RESOLVE_NVE: + vnc_import_bgp_add_route_mode_resolve_nve (bgp, prefix, info); + break; + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "LEAVE "); +#endif + VNC_RHNCK (leave); +} + +/* + * "Withdrawing a Route" import process + */ +void +vnc_import_bgp_del_route ( + struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info) /* unicast info */ +{ + afi_t afi = family2afi (prefix->family); + + assert (afi); + + { + struct prefix pfx_nexthop; + char buf[BUFSIZ]; + char buf_nh[BUFSIZ]; + + prefix2str (prefix, buf, BUFSIZ); + rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_nexthop); + prefix2str (&pfx_nexthop, buf_nh, BUFSIZ); + + zlog_debug ("%s: pfx %s, nh %s", __func__, buf, buf_nh); + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "ENTER "); +#endif + VNC_RHNCK (enter); + + /* check bgp redist flag for vnc direct ("vpn") routes */ + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug ("%s: bgp redistribution of afi=%d VNC direct routes is off", + __func__, afi); + return; + } + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + switch (bgp->rfapi_cfg->redist_mode) + { + case VNC_REDIST_MODE_PLAIN: + vnc_import_bgp_del_route_mode_plain (bgp, prefix, info); + break; + + case VNC_REDIST_MODE_RFG: + if (bgp->rfapi_cfg->rfg_redist) + vnc_import_bgp_del_route_mode_nvegroup (bgp, prefix, info); + else + zlog_debug ("%s: mode RFG but no redist RFG", __func__); + break; + + case VNC_REDIST_MODE_RESOLVE_NVE: + vnc_import_bgp_del_route_mode_resolve_nve (bgp, afi, prefix, info); + break; + + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "LEAVE "); +#endif + VNC_RHNCK (leave); +} + + +/*********************************************************************** + * Enable/Disable + ***********************************************************************/ + +void +vnc_import_bgp_redist_enable (struct bgp *bgp, afi_t afi) +{ + /* iterate over bgp unicast v4 and v6 routes, call vnc_import_bgp_add_route */ + + struct bgp_node *rn; + + zlog_debug ("%s: entry, afi=%d", __func__, afi); + + if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug ("%s: already enabled for afi %d, skipping", __func__, afi); + return; + } + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 1; + + for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]); + rn; rn = bgp_route_next (rn)) + { + + struct bgp_info *bi; + + for (bi = rn->info; bi; bi = bi->next) + { + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + vnc_import_bgp_add_route (bgp, &rn->p, bi); + } + } + zlog_debug ("%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); +} + +void +vnc_import_bgp_exterior_redist_enable (struct bgp *bgp, afi_t afi) +{ + struct bgp *bgp_exterior; + struct bgp_node *rn; + + bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; + + if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: already enabled for afi %d, skipping", __func__, afi); + return; + } + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 1; + + if (!bgp_exterior) + { + zlog_debug ("%s: no exterior view set yet, no routes to import yet", + __func__); + return; + } + + for (rn = bgp_table_top (bgp_exterior->rib[afi][SAFI_UNICAST]); + rn; rn = bgp_route_next (rn)) + { + + struct bgp_info *bi; + + for (bi = rn->info; bi; bi = bi->next) + { + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + vnc_import_bgp_exterior_add_route (bgp_exterior, &rn->p, bi); + } + } + zlog_debug ("%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); + +} + +/* + * This function is for populating a newly-created Import Table + */ +void +vnc_import_bgp_exterior_redist_enable_it ( + struct bgp *bgp, + afi_t afi, + struct rfapi_import_table *it_only) +{ + struct bgp *bgp_exterior; + struct bgp_node *rn; + + zlog_debug ("%s: entry", __func__); + + bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: not enabled for afi %d, skipping", __func__, afi); + return; + } + + if (!bgp_exterior) + { + zlog_debug ("%s: no exterior view set yet, no routes to import yet", + __func__); + return; + } + + for (rn = bgp_table_top (bgp_exterior->rib[afi][SAFI_UNICAST]); + rn; rn = bgp_route_next (rn)) + { + + struct bgp_info *bi; + + for (bi = rn->info; bi; bi = bi->next) + { + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + vnc_import_bgp_exterior_add_route_it (bgp_exterior, &rn->p, bi, + it_only); + } + } + +} + + +void +vnc_import_bgp_redist_disable (struct bgp *bgp, afi_t afi) +{ + /* + * iterate over vpn routes, find routes of type ZEBRA_ROUTE_BGP_DIRECT, + * delete (call timer expire immediately) + */ + struct bgp_node *rn1; + struct bgp_node *rn2; + + zlog_debug ("%s: entry", __func__); + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) + { + zlog_debug ("%s: already disabled for afi %d, skipping", __func__, afi); + return; + } + + /* + * Two-level table for SAFI_MPLS_VPN + * Be careful when changing the things we iterate over + */ + for (rn1 = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); + rn1; rn1 = bgp_route_next (rn1)) + { + + if (rn1->info) + { + for (rn2 = bgp_table_top (rn1->info); + rn2; rn2 = bgp_route_next (rn2)) + { + + struct bgp_info *bi; + struct bgp_info *nextbi; + + for (bi = rn2->info; bi; bi = nextbi) + { + + nextbi = bi->next; + + if (bi->type == ZEBRA_ROUTE_BGP_DIRECT) + { + + struct rfapi_descriptor *rfd; + vncHDBgpDirect.peer = bi->peer; + + rfd = bi->extra->vnc.export.rfapi_handle; + + zlog_debug + ("%s: deleting bi=%p, bi->peer=%p, bi->type=%d, bi->sub_type=%d, bi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", + __func__, bi, bi->peer, bi->type, bi->sub_type, + (bi->extra ? bi->extra->vnc. + export.rfapi_handle : NULL), rfd); + + + del_vnc_route (rfd, bi->peer, bgp, SAFI_MPLS_VPN, &rn2->p, (struct prefix_rd *) &rn1->p, bi->type, bi->sub_type, NULL, 1); /* kill */ + + vncHDBgpDirect.peer = NULL; + } + } + } + } + } + /* Clear RHN list */ + if (bgp->rfapi->resolve_nve_nexthop) + { + struct prefix_bag *pb; + struct bgp_info *info; + while (!skiplist_first + (bgp->rfapi->resolve_nve_nexthop, NULL, (void *) &pb)) + { + info = pb->ubi; + skiplist_delete_first (bgp->rfapi->resolve_nve_nexthop); + bgp_info_unlock (info); + } + } + + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 0; + zlog_debug ("%s: return", __func__); +} + + +void +vnc_import_bgp_exterior_redist_disable (struct bgp *bgp, afi_t afi) +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct bgp *bgp_exterior = hc->redist_bgp_exterior_view; + + zlog_debug ("%s: entry", __func__); + + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + zlog_debug ("%s: already disabled for afi %d, skipping", __func__, afi); + return; + } + + if (!bgp_exterior) + { + zlog_debug ("%s: bgp exterior view not defined, skipping", __func__); + return; + } + + + { + struct bgp_node *rn; + for (rn = bgp_table_top (bgp_exterior->rib[afi][SAFI_UNICAST]); + rn; rn = bgp_route_next (rn)) + { + + struct bgp_info *bi; + + for (bi = rn->info; bi; bi = bi->next) + { + + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + continue; + + vnc_import_bgp_exterior_del_route (bgp_exterior, &rn->p, bi); + } + } +#if DEBUG_RHN_LIST + print_rhn_list (__func__, NULL); +#endif + } + + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 0; + zlog_debug ("%s: return", __func__); +} diff --git a/bgpd/rfapi/vnc_import_bgp.h b/bgpd/rfapi/vnc_import_bgp.h new file mode 100644 index 0000000000..acab0c62f5 --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp.h @@ -0,0 +1,93 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ +#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ + +#include "zebra.h" +#include "prefix.h" + +#include "bgpd.h" +#include "bgp_route.h" + +#define VALID_INTERIOR_TYPE(type) \ + (((type) == ZEBRA_ROUTE_BGP) || ((type) == ZEBRA_ROUTE_BGP_DIRECT)) + +extern uint32_t +calc_local_pref (struct attr *attr, struct peer *peer); + +extern int +vnc_prefix_cmp (void *pfx1, void *pfx2); + +extern void +vnc_import_bgp_add_route ( + struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info); + +extern void +vnc_import_bgp_del_route ( + struct bgp *bgp, + struct prefix *prefix, + struct bgp_info *info); + +extern void +vnc_import_bgp_redist_enable (struct bgp *bgp, afi_t afi); + +extern void +vnc_import_bgp_redist_disable (struct bgp *bgp, afi_t afi); + +extern void +vnc_import_bgp_exterior_redist_enable (struct bgp *bgp, afi_t afi); + +extern void +vnc_import_bgp_exterior_redist_disable (struct bgp *bgp, afi_t afi); + + +extern void +vnc_import_bgp_exterior_add_route ( + struct bgp *bgp, /* exterior instance, we hope */ + struct prefix *prefix,/* unicast prefix */ + struct bgp_info *info); /* unicast info */ + +extern void +vnc_import_bgp_exterior_del_route ( + struct bgp *bgp, + struct prefix *prefix,/* unicast prefix */ + struct bgp_info *info); /* unicast info */ + +extern void +vnc_import_bgp_add_vnc_host_route_mode_resolve_nve ( + struct bgp *bgp, + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + struct prefix *prefix, /* VPN prefix */ + struct bgp_info *bi); /* new VPN host route */ + +extern void +vnc_import_bgp_del_vnc_host_route_mode_resolve_nve ( + struct bgp *bgp, + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + struct prefix *prefix, /* VPN prefix */ + struct bgp_info *bi); /* old VPN host route */ + +#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ */ diff --git a/bgpd/rfapi/vnc_import_bgp_p.h b/bgpd/rfapi/vnc_import_bgp_p.h new file mode 100644 index 0000000000..4d37ce9cdf --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp_p.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ +#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ + +#include "zebra.h" +#include "prefix.h" + +#include "bgpd.h" +#include "bgp_route.h" + +extern void +vnc_import_bgp_exterior_add_route_interior ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct route_node *rn_interior, /* VPN IT node */ + struct bgp_info *bi_interior); /* VPN IT route */ + +extern void +vnc_import_bgp_exterior_del_route_interior ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct route_node *rn_interior, /* VPN IT node */ + struct bgp_info *bi_interior); /* VPN IT route */ + +extern void +vnc_import_bgp_exterior_redist_enable_it ( + struct bgp *bgp, + afi_t afi, + struct rfapi_import_table *it_only); + +#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ */ diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c new file mode 100644 index 0000000000..54e8a2a3e3 --- /dev/null +++ b/bgpd/rfapi/vnc_zebra.c @@ -0,0 +1,1121 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: vnc_zebra.c + * Purpose: Handle exchange of routes between VNC and Zebra + */ + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "log.h" +#include "command.h" +#include "zclient.h" +#include "stream.h" +#include "memory.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_route.h" +#include "bgp_debug.h" +#include "bgp_advertise.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "vnc_zebra.h" +#include "rfapi_vty.h" +#include "rfapi_backend.h" + +static struct rfapi_descriptor vncHD1VR; /* Single-VR export dummy nve descr */ +static struct zclient *zclient_vnc = NULL; + +/*********************************************************************** + * REDISTRIBUTE: Zebra sends updates/withdraws to BGPD + ***********************************************************************/ + +/* + * Routes coming from zebra get added to VNC here + */ +static void +vnc_redistribute_add ( + struct prefix *p, + struct in_addr *nexthop, + u_int32_t metric, + uint8_t type) +{ + struct bgp *bgp = bgp_get_default (); + struct prefix_rd prd; + struct rfapi_ip_addr vnaddr; + afi_t afi; + uint32_t local_pref = rfp_cost_to_localpref (metric > 255 ? 255 : metric); + + if (!bgp) + return; + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + afi = family2afi (p->family); + if (!afi) + { + zlog_debug ("%s: unknown prefix address family %d", __func__, + p->family); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][type]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping", + __func__, afi, type); + return; + } + if (!bgp->rfapi_cfg->rfg_redist) + { + zlog_debug ("%s: no redist nve group, skipping", __func__); + return; + } + + /* + * Assume nve group's configured VN address prefix is a host + * route which also happens to give the NVE VN address to use + * for redistributing into VNC. + */ + vnaddr.addr_family = bgp->rfapi_cfg->rfg_redist->vn_prefix.family; + switch (bgp->rfapi_cfg->rfg_redist->vn_prefix.family) + { + case AF_INET: + if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen != 32) + { + zlog_debug + ("%s: redist nve group VN prefix len (%d) != 32, skipping", + __func__, bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen); + return; + } + vnaddr.addr.v4 = bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix4; + break; + case AF_INET6: + if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen != 128) + { + zlog_debug + ("%s: redist nve group VN prefix len (%d) != 128, skipping", + __func__, bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen); + return; + } + vnaddr.addr.v6 = bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix6; + break; + default: + zlog_debug + ("%s: no redist nve group VN host prefix configured, skipping", + __func__); + return; + } + + /* + * Assume nve group's configured UN address prefix is a host + * route which also happens to give the NVE UN address to use + * for redistributing into VNC. + */ + + /* + * Set UN address in dummy nve descriptor so add_vnc_route + * can use it in VNC tunnel SubTLV + */ + { + struct rfapi_ip_prefix pfx_un; + + rfapiQprefix2Rprefix (&bgp->rfapi_cfg->rfg_redist->un_prefix, &pfx_un); + + switch (pfx_un.prefix.addr_family) + { + case AF_INET: + if (pfx_un.length != 32) + { + zlog_debug + ("%s: redist nve group UN prefix len (%d) != 32, skipping", + __func__, pfx_un.length); + return; + } + break; + case AF_INET6: + if (pfx_un.length != 128) + { + zlog_debug + ("%s: redist nve group UN prefix len (%d) != 128, skipping", + __func__, pfx_un.length); + return; + } + break; + default: + zlog_debug + ("%s: no redist nve group UN host prefix configured, skipping", + __func__); + return; + } + + vncHD1VR.un_addr = pfx_un.prefix; + + if (!vncHD1VR.peer) + { + /* + * Same setup as in rfapi_open() + */ + vncHD1VR.peer = peer_new (bgp); + vncHD1VR.peer->status = Established; /* keep bgp core happy */ + bgp_sync_delete (vncHD1VR.peer); /* don't need these */ + if (vncHD1VR.peer->ibuf) + { + stream_free (vncHD1VR.peer->ibuf); /* don't need it */ + vncHD1VR.peer->ibuf = NULL; + } + if (vncHD1VR.peer->obuf) + { + stream_fifo_free (vncHD1VR.peer->obuf); /* don't need it */ + vncHD1VR.peer->obuf = NULL; + } + if (vncHD1VR.peer->work) + { + stream_free (vncHD1VR.peer->work); /* don't need it */ + vncHD1VR.peer->work = NULL; + } + /* base code assumes have valid host pointer */ + vncHD1VR.peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, ".zebra."); + + /* Mark peer as belonging to HD */ + SET_FLAG (vncHD1VR.peer->flags, PEER_FLAG_IS_RFAPI_HD); + } + } + + memset (&prd, 0, sizeof (prd)); + prd = bgp->rfapi_cfg->rfg_redist->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + add_vnc_route (&vncHD1VR, /* cookie + UN addr */ + bgp, SAFI_MPLS_VPN, p, &prd, &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), NULL, /* RFP options */ + NULL, /* struct rfapi_un_option */ + NULL, /* struct rfapi_vn_option */ + bgp->rfapi_cfg->rfg_redist->rt_export_list, NULL, NULL, /* label: default */ + type, BGP_ROUTE_REDISTRIBUTE, 0); /* flags */ +} + +/* + * Route deletions from zebra propagate to VNC here + */ +static void +vnc_redistribute_delete (struct prefix *p, uint8_t type) +{ + struct bgp *bgp = bgp_get_default (); + struct prefix_rd prd; + afi_t afi; + + if (!bgp) + return; + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + afi = family2afi (p->family); + if (!afi) + { + zlog_debug ("%s: unknown prefix address family %d", __func__, + p->family); + return; + } + if (!bgp->rfapi_cfg->redist[afi][type]) + { + zlog_debug + ("%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping", + __func__, afi, type); + return; + } + if (!bgp->rfapi_cfg->rfg_redist) + { + zlog_debug ("%s: no redist nve group, skipping", __func__); + return; + } + + memset (&prd, 0, sizeof (prd)); + prd = bgp->rfapi_cfg->rfg_redist->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + del_vnc_route (&vncHD1VR, /* use dummy ptr as cookie */ + vncHD1VR.peer, + bgp, + SAFI_MPLS_VPN, + p, &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0); +} + +/* + * Flush all redistributed routes of type + */ +static void +vnc_redistribute_withdraw (struct bgp *bgp, afi_t afi, uint8_t type) +{ + struct prefix_rd prd; + struct bgp_table *table; + struct bgp_node *prn; + struct bgp_node *rn; + + zlog_debug ("%s: entry", __func__); + + if (!bgp) + return; + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + /* + * Loop over all the RDs + */ + for (prn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); prn; + prn = bgp_route_next (prn)) + { + memset (&prd, 0, sizeof (prd)); + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy (prd.val, prn->p.u.val, 8); + + /* This is the per-RD table of prefixes */ + table = prn->info; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + + struct bgp_info *ri; + + for (ri = rn->info; ri; ri = ri->next) + { + if (ri->type == type) + { /* has matching redist type */ + break; + } + } + if (ri) + { + del_vnc_route (&vncHD1VR, /* use dummy ptr as cookie */ + vncHD1VR.peer, + bgp, + SAFI_MPLS_VPN, + &(rn->p), + &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0); + } + } + } + zlog_debug ("%s: return", __func__); +} + +/* + * Zebra route add and delete treatment. + * + * Assumes 1 nexthop + */ +static int +vnc_zebra_read_ipv4 ( + int command, + struct zclient *zclient, + zebra_size_t length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct zapi_ipv4 api; + unsigned long ifindex; + struct in_addr nexthop; + struct prefix_ipv4 p; + + s = zclient->ibuf; + ifindex = 0; + nexthop.s_addr = 0; + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv4 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = stream_getc (s); + stream_get (&p.prefix, s, PSIZE (p.prefixlen)); + + /* Nexthop, ifindex, distance, metric. */ + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) + { + api.nexthop_num = stream_getc (s); + nexthop.s_addr = stream_get_ipv4 (s); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) + { + api.ifindex_num = stream_getc (s); + ifindex = stream_getl (s); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) + api.distance = stream_getc (s); + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) + api.metric = stream_getl (s); + else + api.metric = 0; + + if (command == ZEBRA_IPV4_ROUTE_ADD) + { + if (BGP_DEBUG (zebra, ZEBRA)) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug + ("%s: Zebra rcvd: IPv4 route add %s %s/%d nexthop %s metric %u", + __func__, zebra_route_string (api.type), inet_ntop (AF_INET, + &p.prefix, + buf[0], + sizeof (buf + [0])), + p.prefixlen, inet_ntop (AF_INET, &nexthop, buf[1], + sizeof (buf[1])), api.metric); + } + vnc_redistribute_add ((struct prefix *) &p, &nexthop, api.metric, + api.type); + } + else + { + if (BGP_DEBUG (zebra, ZEBRA)) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug ("%s: Zebra rcvd: IPv4 route delete %s %s/%d " + "nexthop %s metric %u", + __func__, + zebra_route_string (api.type), + inet_ntop (AF_INET, &p.prefix, buf[0], sizeof (buf[0])), + p.prefixlen, + inet_ntop (AF_INET, &nexthop, buf[1], sizeof (buf[1])), + api.metric); + } + vnc_redistribute_delete ((struct prefix *) &p, api.type); + } + + return 0; +} + +/* Zebra route add and delete treatment. */ +static int +vnc_zebra_read_ipv6 ( + int command, + struct zclient *zclient, + zebra_size_t length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct zapi_ipv6 api; + unsigned long ifindex; + struct in6_addr nexthop; + struct prefix_ipv6 p; + + s = zclient->ibuf; + ifindex = 0; + memset (&nexthop, 0, sizeof (struct in6_addr)); + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv6 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + p.prefixlen = stream_getc (s); + stream_get (&p.prefix, s, PSIZE (p.prefixlen)); + + /* Nexthop, ifindex, distance, metric. */ + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) + { + api.nexthop_num = stream_getc (s); + stream_get (&nexthop, s, 16); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) + { + api.ifindex_num = stream_getc (s); + ifindex = stream_getl (s); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) + api.distance = stream_getc (s); + else + api.distance = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) + api.metric = stream_getl (s); + else + api.metric = 0; + + /* Simply ignore link-local address. */ + if (IN6_IS_ADDR_LINKLOCAL (&p.prefix)) + return 0; + + if (command == ZEBRA_IPV6_ROUTE_ADD) + { + if (BGP_DEBUG (zebra, ZEBRA)) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug ("Zebra rcvd: IPv6 route add %s %s/%d metric %u", + zebra_route_string (api.type), + inet_ntop (AF_INET6, &p.prefix, buf, sizeof (buf)), + p.prefixlen, api.metric); + } + vnc_redistribute_add ((struct prefix *) &p, NULL, api.metric, api.type); + } + else + { + if (BGP_DEBUG (zebra, ZEBRA)) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug ("Zebra rcvd: IPv6 route delete %s %s/%d metric %u", + zebra_route_string (api.type), + inet_ntop (AF_INET6, &p.prefix, buf, sizeof (buf)), + p.prefixlen, api.metric); + } + vnc_redistribute_delete ((struct prefix *) &p, api.type); + } + + return 0; +} + +/*********************************************************************** + * vnc_bgp_zebra_*: VNC sends updates/withdraws to Zebra + ***********************************************************************/ + +/* + * low-level message builder + */ +static void +vnc_zebra_route_msg ( + struct prefix *p, + int nhp_count, + void *nhp_ary, + int add) /* 1 = add, 0 = del */ +{ + if (!nhp_count) + { + zlog_debug ("%s: empty nexthop list, skipping", __func__); + return; + } + + if (p->family == AF_INET) + { + + struct zapi_ipv4 api; + + api.flags = 0; + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_VNC; + api.message = 0; + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); /* TBD what's it mean? */ + api.nexthop_num = nhp_count; + api.nexthop = nhp_ary; + api.ifindex_num = 0; + + if (BGP_DEBUG (zebra, ZEBRA)) + { + + char buf[INET_ADDRSTRLEN]; + zlog_debug ("%s: Zebra send: IPv4 route %s %s/%d, nhp_count=%d", + __func__, + (add ? "add" : "del"), + inet_ntop (AF_INET, &p->u.prefix4, buf, sizeof (buf)), + p->prefixlen, nhp_count); + } + + zapi_ipv4_route ((add ? ZEBRA_IPV4_NEXTHOP_ADD : + ZEBRA_IPV4_NEXTHOP_DELETE), zclient_vnc, + (struct prefix_ipv4 *) p, &api); + + } + else if (p->family == AF_INET6) + { + + struct zapi_ipv6 api; + ifindex_t ifindex = 0; + + /* Make Zebra API structure. */ + api.flags = 0; + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_VNC; + api.message = 0; + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); /* TBD means? */ + api.nexthop_num = nhp_count; + api.nexthop = nhp_ary; + SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); + api.ifindex_num = 1; + api.ifindex = &ifindex; + + if (BGP_DEBUG (zebra, ZEBRA)) + { + + char buf[INET6_ADDRSTRLEN]; + zlog_debug ("%s: Zebra send: IPv6 route %s %s/%d nhp_count=%d", + __func__, + (add ? "add" : "del"), + inet_ntop (AF_INET6, &p->u.prefix6, buf, sizeof (buf)), + p->prefixlen, nhp_count); + } + + zapi_ipv6_route ((add ? ZEBRA_IPV6_NEXTHOP_ADD : + ZEBRA_IPV6_NEXTHOP_DELETE), zclient_vnc, + (struct prefix_ipv6 *) p, &api); + } + else + { + zlog_debug ("%s: unknown prefix address family, skipping", __func__); + return; + } +} + + +static void +nve_list_to_nh_array ( + u_char family, + struct list *nve_list, + int *nh_count_ret, + void **nh_ary_ret, /* returned address array */ + void **nhp_ary_ret) /* returned pointer array */ +{ + int nve_count = listcount (nve_list); + + *nh_count_ret = 0; + *nh_ary_ret = NULL; + *nhp_ary_ret = NULL; + + if (!nve_count) + { + zlog_debug ("%s: empty nve_list, skipping", __func__); + return; + } + + if (family == AF_INET) + { + struct listnode *ln; + struct in_addr *iap; + struct in_addr **v; + + /* + * Array of nexthop addresses + */ + *nh_ary_ret = XCALLOC (MTYPE_TMP, nve_count * sizeof (struct in_addr)); + + /* + * Array of pointers to nexthop addresses + */ + *nhp_ary_ret = + XCALLOC (MTYPE_TMP, nve_count * sizeof (struct in_addr *)); + iap = *nh_ary_ret; + v = *nhp_ary_ret; + + for (ln = listhead (nve_list); ln; ln = listnextnode (ln)) + { + + struct rfapi_descriptor *irfd; + struct prefix nhp; + + irfd = listgetdata (ln); + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + *iap = nhp.u.prefix4; + *v = iap; + zlog_debug ("%s: ipadr: (%p)<-0x%x, ptr: (%p)<-%p", + __func__, iap, nhp.u.prefix4.s_addr, v, iap); + + ++iap; + ++v; + ++*nh_count_ret; + } + + } + else if (family == AF_INET6) + { + + struct listnode *ln; + + *nh_ary_ret = XCALLOC (MTYPE_TMP, nve_count * sizeof (struct in6_addr)); + + *nhp_ary_ret = XCALLOC (MTYPE_TMP, + nve_count * sizeof (struct in6_addr *)); + + for (ln = listhead (nve_list); ln; ln = listnextnode (ln)) + { + + struct rfapi_descriptor *irfd; + struct in6_addr *iap = *nh_ary_ret; + struct in6_addr **v = *nhp_ary_ret; + struct prefix nhp; + + irfd = listgetdata (ln); + + if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp)) + continue; + + *iap = nhp.u.prefix6; + *v = iap; + + ++iap; + ++v; + ++*nh_count_ret; + } + } +} + +static void +import_table_to_nve_list_zebra ( + struct bgp *bgp, + struct rfapi_import_table *it, + struct list **nves, + uint8_t family) +{ + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* + * Loop over the list of NVE-Groups configured for + * exporting to direct-bgp. + * + * Build a list of NVEs that use this import table + */ + *nves = NULL; + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + /* + * If this NVE-Group's import table matches the current one + */ + if (rfgn->rfg && rfgn->rfg->nves && rfgn->rfg->rfapi_import_table == it) + { + + nve_group_to_nve_list (rfgn->rfg, nves, family); + } + } +} + +static void +vnc_zebra_add_del_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn, + int add) /* !0 = add, 0 = del */ +{ + struct list *nves; + + int nexthop_count = 0; + void *nh_ary = NULL; + void *nhp_ary = NULL; + + zlog_debug ("%s: entry, add=%d", __func__, add); + + if (zclient_vnc->sock < 0) + return; + + if (rn->p.family != AF_INET + && rn->p.family != AF_INET6) + { + zlog_err ("%s: invalid route node addr family", __func__); + return; + } + + if (!zclient_vnc->redist[family2afi(rn->p.family)][ZEBRA_ROUTE_VNC]) + return; + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + if (!listcount (bgp->rfapi_cfg->rfg_export_zebra_l)) + { + zlog_debug ("%s: no zebra export nve group, skipping", __func__); + return; + } + + import_table_to_nve_list_zebra (bgp, import_table, &nves, rn->p.family); + + if (nves) + { + nve_list_to_nh_array (rn->p.family, + nves, &nexthop_count, &nh_ary, &nhp_ary); + + list_delete (nves); + + if (nexthop_count) + vnc_zebra_route_msg (&rn->p, nexthop_count, nhp_ary, add); + } + + if (nhp_ary) + XFREE (MTYPE_TMP, nhp_ary); + if (nh_ary) + XFREE (MTYPE_TMP, nh_ary); +} + +void +vnc_zebra_add_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn) +{ + vnc_zebra_add_del_prefix (bgp, import_table, rn, 1); +} + +void +vnc_zebra_del_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn) +{ + vnc_zebra_add_del_prefix (bgp, import_table, rn, 0); +} + + + +static void +vnc_zebra_add_del_nve ( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + int add) /* 0 = del, !0 = add */ +{ + struct listnode *node; + struct rfapi_rfg_name *rfgn; + struct rfapi_nve_group_cfg *rfg = rfd->rfg; + afi_t afi = family2afi (rfd->vn_addr.addr_family); + struct prefix nhp; +// struct prefix *nhpp; + void *pAddr; + + zlog_debug ("%s: entry, add=%d", __func__, add); + + if (zclient_vnc->sock < 0) + return; + + if (!zclient_vnc->redist[afi][ZEBRA_ROUTE_VNC]) + return; + + if (afi != AFI_IP && afi != AFI_IP6) + { + zlog_err ("%s: invalid vn addr family", __func__); + return; + } + + if (!bgp) + return; + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + + if (rfapiRaddr2Qprefix (&rfd->vn_addr, &nhp)) + { + zlog_debug ("%s: can't convert vn address, skipping", __func__); + return; + } + + pAddr = &nhp.u.prefix4; + + /* + * Loop over the list of NVE-Groups configured for + * exporting to zebra and see if this new NVE's + * group is among them. + */ + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + /* + * Yes, this NVE's group is configured for export to zebra + */ + if (rfgn->rfg == rfg) + { + + struct route_table *rt = NULL; + struct route_node *rn; + struct rfapi_import_table *import_table; + import_table = rfg->rfapi_import_table; + + zlog_debug ("%s: this nve's group is in zebra export list", + __func__); + + rt = import_table->imported_vpn[afi]; + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + + if (rn->info) + { + + zlog_debug ("%s: sending %s", __func__, + (add ? "add" : "del")); + vnc_zebra_route_msg (&rn->p, 1, &pAddr, add); + } + } + } + } +} + +void +vnc_zebra_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + vnc_zebra_add_del_nve (bgp, rfd, 1); +} + +void +vnc_zebra_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + vnc_zebra_add_del_nve (bgp, rfd, 0); +} + +static void +vnc_zebra_add_del_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi, + int add) +{ + struct route_table *rt = NULL; + struct route_node *rn; + struct rfapi_import_table *import_table; + uint8_t family = afi2family (afi); + + struct list *nves = NULL; + int nexthop_count = 0; + void *nh_ary = NULL; + void *nhp_ary = NULL; + + zlog_debug ("%s: entry", __func__); + import_table = rfg->rfapi_import_table; + if (!import_table) + { + zlog_debug ("%s: import table not defined, returning", __func__); + return; + } + + if (afi == AFI_IP + || afi == AFI_IP6) + { + rt = import_table->imported_vpn[afi]; + } + else + { + zlog_err ("%s: bad afi %d", __func__, afi); + return; + } + + if (!family) + { + zlog_err ("%s: computed bad family: %d", __func__, family); + return; + } + + if (!rfg->nves) + { + /* avoid segfault below if list doesn't exist */ + zlog_debug ("%s: no NVEs in this group", __func__); + return; + } + + nve_group_to_nve_list (rfg, &nves, family); + if (nves) + { + zlog_debug ("%s: have nves", __func__); + nve_list_to_nh_array (family, nves, &nexthop_count, &nh_ary, &nhp_ary); + + zlog_debug ("%s: family: %d, nve count: %d", __func__, family, + nexthop_count); + + list_delete (nves); + + if (nexthop_count) + { + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + if (rn->info) + { + vnc_zebra_route_msg (&rn->p, nexthop_count, nhp_ary, add); + } + } + } + if (nhp_ary) + XFREE (MTYPE_TMP, nhp_ary); + if (nh_ary) + XFREE (MTYPE_TMP, nh_ary); + } +} + +void +vnc_zebra_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP, 1); + vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP6, 1); +} + +void +vnc_zebra_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + zlog_debug ("%s: entry", __func__); + vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP, 0); + vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP6, 0); +} + +void +vnc_zebra_reexport_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + vnc_zebra_add_del_group_afi (bgp, rfg, afi, 0); + vnc_zebra_add_del_group_afi (bgp, rfg, afi, 1); + break; + } + } +} + + +/*********************************************************************** + * CONTROL INTERFACE + ***********************************************************************/ + + +/* Other routes redistribution into BGP. */ +int +vnc_redistribute_set (struct bgp *bgp, afi_t afi, int type) +{ + if (!bgp->rfapi_cfg) + { + return CMD_WARNING; + } + + /* Set flag to BGP instance. */ + bgp->rfapi_cfg->redist[afi][type] = 1; + +// bgp->redist[afi][type] = 1; + + /* Return if already redistribute flag is set. */ + if (zclient_vnc->redist[afi][type]) + return CMD_WARNING; + + vrf_bitmap_set (zclient_vnc->redist[afi][type], VRF_DEFAULT); + + //zclient_vnc->redist[afi][type] = 1; + + /* Return if zebra connection is not established. */ + if (zclient_vnc->sock < 0) + return CMD_WARNING; + + if (BGP_DEBUG (zebra, ZEBRA)) + zlog_debug ("Zebra send: redistribute add %s", zebra_route_string (type)); + + /* Send distribute add message to zebra. */ + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient_vnc, afi, type, 0, VRF_DEFAULT); + + return CMD_SUCCESS; +} + +/* Unset redistribution. */ +int +vnc_redistribute_unset (struct bgp *bgp, afi_t afi, int type) +{ + zlog_debug ("%s: type=%d entry", __func__, type); + + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: return (no rfapi_cfg)", __func__); + return CMD_WARNING; + } + + /* Unset flag from BGP instance. */ + bgp->rfapi_cfg->redist[afi][type] = 0; + + /* Return if zebra connection is disabled. */ + if (!zclient_vnc->redist[afi][type]) + return CMD_WARNING; + zclient_vnc->redist[afi][type] = 0; + + if (bgp->rfapi_cfg->redist[AFI_IP][type] == 0 + && bgp->rfapi_cfg->redist[AFI_IP6][type] == 0 && zclient_vnc->sock >= 0) + { + /* Send distribute delete message to zebra. */ + if (BGP_DEBUG (zebra, ZEBRA)) + zlog_debug ("Zebra send: redistribute delete %s", + zebra_route_string (type)); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient_vnc, afi, type, + 0, VRF_DEFAULT); + } + + /* Withdraw redistributed routes from current BGP's routing table. */ + vnc_redistribute_withdraw (bgp, afi, type); + + zlog_debug ("%s: return", __func__); + + return CMD_SUCCESS; +} + + +/* + * Modeled after bgp_zebra.c'bgp_zebra_init() + * Charriere asks, "Is it possible to carry two?" + */ +void +vnc_zebra_init (struct thread_master *master) +{ + /* Set default values. */ + zclient_vnc = zclient_new (master); + zclient_init (zclient_vnc, ZEBRA_ROUTE_VNC, 0); + + zclient_vnc->redistribute_route_ipv4_add = vnc_zebra_read_ipv4; + zclient_vnc->redistribute_route_ipv4_del = vnc_zebra_read_ipv4; + zclient_vnc->redistribute_route_ipv6_add = vnc_zebra_read_ipv6; + zclient_vnc->redistribute_route_ipv6_del = vnc_zebra_read_ipv6; +} + +void +vnc_zebra_destroy (void) +{ + if (zclient_vnc == NULL) + return; + zclient_stop (zclient_vnc); + zclient_free (zclient_vnc); + zclient_vnc = NULL; +} diff --git a/bgpd/rfapi/vnc_zebra.h b/bgpd/rfapi/vnc_zebra.h new file mode 100644 index 0000000000..226136ea69 --- /dev/null +++ b/bgpd/rfapi/vnc_zebra.h @@ -0,0 +1,67 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * File: vnc_zebra.h + */ + +#ifndef _QUAGGA_BGP_VNC_ZEBRA_H +#define _QUAGGA_BGP_VNC_ZEBRA_H + +#include "zebra.h" + +extern void +vnc_zebra_add_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn); + +extern void +vnc_zebra_del_prefix ( + struct bgp *bgp, + struct rfapi_import_table *import_table, + struct route_node *rn); + +extern void +vnc_zebra_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void +vnc_zebra_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void +vnc_zebra_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg); + +extern void +vnc_zebra_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg); + +extern void +vnc_zebra_reexport_group_afi ( + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi); + +extern int +vnc_redistribute_set (struct bgp *bgp, afi_t afi, int type); + +extern int +vnc_redistribute_unset (struct bgp *bgp, afi_t afi, int type); + +#endif /* _QUAGGA_BGP_VNC_ZEBRA_H */ diff --git a/bgpd/rfp-example/librfp/Makefile.am b/bgpd/rfp-example/librfp/Makefile.am new file mode 100644 index 0000000000..fc66a40f00 --- /dev/null +++ b/bgpd/rfp-example/librfp/Makefile.am @@ -0,0 +1,40 @@ +# +# This file has been modified by LabN Consulting, L.L.C. +# +# +## Process this file with automake to produce Makefile.in. + +if ENABLE_BGP_VNC +BGP_VNC_RFAPI_INC=-I$(top_srcdir)/bgpd/rfapi +BGP_VNC_RFP_LIBDIR=. +BGP_VNC_RFP_INCDIR=$(BGP_VNC_RFP_LIBDIR) +BGP_VNC_RFP_LIB=librfp.a +BGP_VNC_RFP_INC=-I$(BGP_VNC_RFP_INCDIR) + +librfp_a_SOURCES = \ + rfp_example.c + +librfp_a_INCLUDES = \ + rfp.h \ + rfp_internal.h + +else +BGP_VNC_RFAPI_INC= +BGP_VNC_RFAPI_SRC= +BGP_VNC_RFP_LIB= +BGP_VNC_RFP_INC= +endif + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib \ + -I$(top_builddir) -I$(top_builddir)/lib \ + $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC) +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +INSTALL_SDATA=@INSTALL@ -m 600 + +AM_CFLAGS = $(PICFLAGS) +AM_LDFLAGS = $(PILDFLAGS) + +noinst_LIBRARIES = $(BGP_VNC_RFP_LIB) + +noinst_HEADERS = \ + $(librfp_a_INCLUDES) diff --git a/bgpd/rfp-example/librfp/rfp.h b/bgpd/rfp-example/librfp/rfp.h new file mode 100644 index 0000000000..4aa307821d --- /dev/null +++ b/bgpd/rfp-example/librfp/rfp.h @@ -0,0 +1,31 @@ +/* + * + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Sample header file */ +#ifndef _RFP_H +#define _RFP_H + +#include "rfapi.h" +extern int bgp_rfp_cfg_write (void *vty, void *bgp); +/* TO BE REMOVED */ +void rfp_clear_vnc_nve_all (void); + +#endif /* _RFP_H */ diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c new file mode 100644 index 0000000000..b533550140 --- /dev/null +++ b/bgpd/rfp-example/librfp/rfp_example.c @@ -0,0 +1,286 @@ +/* + * + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* stub rfp */ +#include "rfp_internal.h" +#include "rfapi.h" +#include "command.h" + +struct rfp_instance_t +{ + struct rfapi_rfp_cfg rfapi_config; + struct rfapi_rfp_cb_methods rfapi_callbacks; + struct thread_master *master; + uint32_t config_var; +}; + +struct rfp_instance_t global_rfi; /* dynamically allocate in full implementation */ + +/*********************************************************************** + * Sample VTY / internal function + **********************************************************************/ +#define RFP_SHOW_STR "RFP information\n" +DEFUN (rfp_example_config_value, + rfp_example_config_value_cmd, + "rfp example-config-value VALUE", + RFP_SHOW_STR "Example value to be configured\n") +{ + uint32_t value = 0; + struct rfp_instance_t *rfi = NULL; + rfi = rfapi_get_rfp_start_val (vty->index); /* index=bgp for BGP_NODE */ + assert (rfi != NULL); + + VTY_GET_INTEGER ("Example value", value, argv[0]); + if (rfi) + rfi->config_var = value; + return CMD_SUCCESS; +} + +static void +rfp_vty_install () +{ + static int installed = 0; + if (installed) /* do this only once */ + return; + installed = 1; + /* example of new cli command */ + install_element (BGP_NODE, &rfp_example_config_value_cmd); +} + +/*********************************************************************** + * RFAPI Callbacks + **********************************************************************/ + +/*------------------------------------------ + * rfp_response_cb + * + * Callbacks of this type are used to provide asynchronous + * route updates from RFAPI to the RFP client. + * + * response_cb + * called to notify the rfp client that a next hop list + * that has previously been provided in response to an + * rfapi_query call has been updated. Deleted routes are indicated + * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME. + * + * By default, the routes an NVE receives via this callback include + * its own routes (that it has registered). However, these may be + * filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP + * flag is set. + * + * input: + * next_hops a list of possible next hops. + * This is a linked list allocated within the + * rfapi. The response_cb callback function is responsible + * for freeing this memory via rfapi_free_next_hop_list() + * in order to avoid memory leaks. + * + * userdata value (cookie) originally specified in call to + * rfapi_open() + * + *------------------------------------------*/ +static void +rfp_response_cb (struct rfapi_next_hop_entry *next_hops, void *userdata) +{ + /* + * Identify NVE based on userdata, which is a value passed + * to RFAPI in the rfapi_open call + */ + + /* process list of next_hops */ + + /* free next hops */ + rfapi_free_next_hop_list (next_hops); + return; +} + +/*------------------------------------------ + * rfp_local_cb + * + * Callbacks of this type are used to provide asynchronous + * route updates from RFAPI to the RFP client. + * + * local_cb + * called to notify the rfp client that a local route + * has been added or deleted. Deleted routes are indicated + * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME. + * + * input: + * next_hops a list of possible next hops. + * This is a linked list allocated within the + * rfapi. The local_cb callback function is responsible + * for freeing this memory via rfapi_free_next_hop_list() + * in order to avoid memory leaks. + * + * userdata value (cookie) originally specified in call to + * rfapi_open() + * + *------------------------------------------*/ +static void +rfp_local_cb (struct rfapi_next_hop_entry *next_hops, void *userdata) +{ + /* + * Identify NVE based on userdata, which is a value passed + * to RFAPI in the rfapi_open call + */ + + /* process list of local next_hops */ + + /* free next hops */ + rfapi_free_next_hop_list (next_hops); + return; +} + +/*------------------------------------------ + * rfp_close_cb + * + * Callbacks used to provide asynchronous + * notification that an rfapi_handle was invalidated + * + * input: + * pHandle Firmerly valid rfapi_handle returned to + * client via rfapi_open(). + * + * reason EIDRM handle administratively closed (clear nve ...) + * ESTALE handle invalidated by configuration change + * + *------------------------------------------*/ +static void +rfp_close_cb (rfapi_handle pHandle, int reason) +{ + /* close / invalidate NVE with the pHandle returned by the rfapi_open call */ + return; +} + +/*------------------------------------------ + * rfp_cfg_write_cb + * + * This callback is used to generate output for any config parameters + * that may supported by RFP via RFP defined vty commands at the bgp + * level. See loglevel as an example. + * + * input: + * vty -- quagga vty context + * rfp_start_val -- value returned by rfp_start + * + * output: + * to vty, rfp related configuration + * + * return value: + * lines written +--------------------------------------------*/ +static int +rfp_cfg_write_cb (struct vty *vty, void *rfp_start_val) +{ + struct rfp_instance_t *rfi = rfp_start_val; + int write = 0; + assert (rfp_start_val != NULL); + if (rfi->config_var != 0) + { + vty_out (vty, " rfp example-config-value %u", rfi->config_var); + vty_out (vty, "%s", VTY_NEWLINE); + write++; + } + + return write; +} + +/*********************************************************************** + * RFAPI required functions + **********************************************************************/ + +/*------------------------------------------ + * rfp_start + * + * This function will start the RFP code + * + * input: + * master quagga thread_master to tie into bgpd threads + * + * output: + * cfgp Pointer to rfapi_rfp_cfg (null = use defaults), + * copied by caller, updated via rfp_set_configuration + * cbmp Pointer to rfapi_rfp_cb_methods, may be null + * copied by caller, updated via rfapi_rfp_set_cb_methods + * + * return value: + * rfp_start_val rfp returned value passed on rfp_stop and rfp_cfg_write + * +--------------------------------------------*/ +void * +rfp_start (struct thread_master *master, + struct rfapi_rfp_cfg **cfgp, struct rfapi_rfp_cb_methods **cbmp) +{ + memset (&global_rfi, 0, sizeof (struct rfp_instance_t)); + global_rfi.master = master; /* for BGPD threads */ + + /* initilize struct rfapi_rfp_cfg, see rfapi.h */ + global_rfi.rfapi_config.download_type = RFAPI_RFP_DOWNLOAD_FULL; /* default=partial */ + global_rfi.rfapi_config.ftd_advertisement_interval = + RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL; + global_rfi.rfapi_config.holddown_factor = 0; /* default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR */ + global_rfi.rfapi_config.use_updated_response = 1; /* 0=no */ + global_rfi.rfapi_config.use_removes = 1; /* 0=no */ + + + /* initilize structrfapi_rfp_cb_methods , see rfapi.h */ + global_rfi.rfapi_callbacks.cfg_cb = rfp_cfg_write_cb; + /* no group config */ + global_rfi.rfapi_callbacks.response_cb = rfp_response_cb; + global_rfi.rfapi_callbacks.local_cb = rfp_local_cb; + global_rfi.rfapi_callbacks.close_cb = rfp_close_cb; + + if (cfgp != NULL) + *cfgp = &global_rfi.rfapi_config; + if (cbmp != NULL) + *cbmp = &global_rfi.rfapi_callbacks; + + rfp_vty_install (); + + return &global_rfi; +} + +/*------------------------------------------ + * rfp_stop + * + * This function is called on shutdown to trigger RFP cleanup + * + * input: + * none + * + * output: + * none + * + * return value: + * rfp_start_val +--------------------------------------------*/ +void +rfp_stop (void *rfp_start_val) +{ + assert (rfp_start_val != NULL); +} + +/* TO BE REMOVED */ +void +rfp_clear_vnc_nve_all (void) +{ + return; +} diff --git a/bgpd/rfp-example/librfp/rfp_internal.h b/bgpd/rfp-example/librfp/rfp_internal.h new file mode 100644 index 0000000000..94192534f4 --- /dev/null +++ b/bgpd/rfp-example/librfp/rfp_internal.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Sample header file */ +#ifndef _RFP_INTERNAL_H +#define _RFP_INTERNAL_H +#include +#include "rfp.h" +#include "rfapi.h" + +#endif /* _RFP_INTERNAL_H */ diff --git a/bgpd/rfp-example/rfptest/Makefile.am b/bgpd/rfp-example/rfptest/Makefile.am new file mode 100644 index 0000000000..a1001e4ef1 --- /dev/null +++ b/bgpd/rfp-example/rfptest/Makefile.am @@ -0,0 +1,52 @@ +# +# This file has been modified by LabN Consulting, L.L.C. +# +# +## Process this file with automake to produce Makefile.in. + +if ENABLE_BGP_VNC +BGP_VNC_RFAPI_INC=-I$(top_srcdir)/bgpd/rfapi +BGP_VNC_RFP_LIBDIR=../librfp +BGP_VNC_RFP_INCDIR=$(BGP_VNC_RFP_LIBDIR) +BGP_VNC_RFP_LIB=$(BGP_VNC_RFP_LIBDIR)/librfp.a +BGP_VNC_RFP_INC=-I$(BGP_VNC_RFP_INCDIR) + +rfptest_SOURCES = \ + rfptest.c + +rfptest_INCLUDES = \ + rfptest.h + + +RFPTEST_BIN = rfptest + +else +BGP_VNC_RFAPI_INC= +BGP_VNC_RFAPI_SRC= +BGP_VNC_RFP_LIB= +BGP_VNC_RFP_INC= +RFPTEST_BIN= +endif + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib \ + -I$(top_builddir) -I$(top_builddir)/lib \ + $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC) + +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +INSTALL_SDATA=@INSTALL@ -m 600 + + +AM_CFLAGS = $(PICFLAGS) +AM_LDFLAGS = $(PILDFLAGS) + + +noinst_HEADERS = \ + $(rfptest_INCLUDES) + +noinst_LIBRARIES = +sbin_PROGRAMS = $(RFPTEST_BIN) + +examplesdir = $(exampledir) + +rfptest_LDADD = $(top_builddir)/lib/libzebra.la $(BGP_VNC_RFP_LIB) +dist_examples_DATA = diff --git a/bgpd/rfp-example/rfptest/rfptest.c b/bgpd/rfp-example/rfptest/rfptest.c new file mode 100644 index 0000000000..39b798e516 --- /dev/null +++ b/bgpd/rfp-example/rfptest/rfptest.c @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +/* dummy test program */ +#include +#include +#include "rfptest.h" +int +main () +{ + printf ("Your test code goes here.\n"); + exit (1); +} diff --git a/bgpd/rfp-example/rfptest/rfptest.h b/bgpd/rfp-example/rfptest/rfptest.h new file mode 100644 index 0000000000..00effb8673 --- /dev/null +++ b/bgpd/rfp-example/rfptest/rfptest.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Sample header file */ +#ifndef _RFPTEST_H +#define _RFPTEST_H + +#endif /* _RFPTEST_H */ diff --git a/configure.ac b/configure.ac index d2a7a9da28..e6e1e53486 100755 --- a/configure.ac +++ b/configure.ac @@ -258,6 +258,10 @@ AC_ARG_ENABLE(pimd, AS_HELP_STRING([--disable-pimd], [do not build pimd])) AC_ARG_ENABLE(bgp-announce, AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) +AC_ARG_ENABLE(bgp-vnc, + AS_HELP_STRING([--enable-bgp-vnc],[turn on BGP VNC support])) +AC_ARG_WITH(rfp-path, + AS_HELP_STRING([--with-rfp-path[=DIR]],[path to replaced stub RFP used with BGP VNC])) AC_ARG_ENABLE(snmp, AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)])) AC_ARG_WITH(libpam, @@ -1362,8 +1366,32 @@ else AC_DEFINE(DISABLE_BGP_ANNOUNCE,0,Disable BGP installation to zebra) fi +if test "${with_rfp_path}" = "yes" || test x"${with_rfp_path}" = x""; then + with_rfp_path="bgpd/rfp-example" +fi +if test "${with_rfp_path}" != "no"; then + VNC_RFP_PATH="${with_rfp_path}" + AC_SUBST(VNC_RFP_PATH) +fi + +if test "${enable_bgp_vnc}" = "yes";then + AC_DEFINE(ENABLE_BGP_VNC,1,Enable BGP VNC support) + RFPTEST="${with_rfp_path}/rfptest" + LIBRFP="${with_rfp_path}/librfp" + RFPINC="${with_rfp_path}/librfp" +else + RFPTEST= + LIBRFP= + RFPINC="bgpd/rfp-example/librfp" +fi +# set +AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} = xyes]) + AC_SUBST(DOC) AC_SUBST(ZEBRA) +AC_SUBST(RFPTEST) +AC_SUBST(LIBRFP) +AC_SUBST(RFPINC) AC_SUBST(BGPD) AC_SUBST(RIPD) AC_SUBST(RIPNGD) @@ -1744,9 +1772,19 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile isisd/topology/Makefile pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh]) + +if test "${enable_bgp_vnc}" = "yes"; then + if test "${with_rfp_path}" = "bgpd/rfp-example" ; then + AC_CONFIG_FILES([bgpd/rfp-example/rfptest/Makefile bgpd/rfp-example/librfp/Makefile]) + else + AC_CONFIG_FILES([${with_rfp_path}/rfptest/Makefile ${with_rfp_path}/librfp/Makefile]) + fi +fi + AC_CONFIG_FILES([solaris/Makefile]) AC_CONFIG_FILES([vtysh/extract.pl],[chmod +x vtysh/extract.pl]) + ## Hack, but working solution to avoid rebuilding of quagga.info. ## It's already in CVS until texinfo 4.7 is more common. AC_OUTPUT diff --git a/doc/Makefile.am b/doc/Makefile.am index cc4c7abc94..d5db6cf497 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -19,13 +19,24 @@ PNGTOEPS = convert -antialias -contrast -despeckle PNGTOPDF = $(PNGTOEPS) EPSTOPDF = epstopdf +VNCFIGURES_PNG = +VNCFIGURES_DIA = -vnc-mesh -vnc-quagga-route-reflector \ +-vnc-commercial-route-reflector -vnc-redundant-route-reflectors \ +-vnc-gw -vnc-gw-rr + +# TODO: A target that creates an empty text file for each member of +# VNCFIGURES_TXT +VNCFIGURES_TXT = $(VNCFIGURES:%.png=%.txt) + # The figure sources figures_names_parts = -normal-processing -rs-processing \ - _topologies_full _topologies_rs + _topologies_full _topologies_rs \ + $(VNCFIGURES_DIA) + figures_sources = $(figures_names_parts:%=fig%.dia) -figures_png = $(figures_names_parts:%=fig%.png) -figures_pdf = $(figures_names_parts:%=fig%.pdf) -figures_eps = $(figures_names_parts:%=fig%.eps) +figures_png = $(figures_names_parts:%=fig%.png) $(VNCFIGURES_PNG) +figures_pdf = $(figures_names_parts:%=fig%.pdf) $(VNCFIGURES_PNG:%.png=%.pdf) +figures_eps = $(figures_names_parts:%=fig%.eps) $(VNCFIGURES_PNG:%.png=%.eps) figures_txt = $(figures_names_parts:%=fig%.txt) # rather twisted logic because we have to build PDFs of the EPS figures for @@ -47,6 +58,7 @@ quagga.pdf: $(info_TEXINFOS) $(figures_pdf) $(quagga_TEXINFOS) $(TEXI2PDF) -o "$@" $< || true quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \ + vnc.texi \ install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \ overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \ snmp.texi vtysh.texi routeserver.texi defines.texi $(figures_png) \ @@ -120,3 +132,11 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \ draft-zebra-00.txt: draft-zebra-00.ms groff -T ascii -ms $< > $@ + +# Ensure that all of the figures are copied into the html directory +html-local: $(HTMLS) + if test -d $(HTMLS) ; then \ + cp -p $(figures_png) $(HTMLS) ; \ + else \ + echo "$(HTMLS) is not a directory. Make it so, the rerun make."; \ + fi diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 3ef7c8f72f..54bed102f3 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -581,6 +581,10 @@ Redistribute RIP route to BGP process. Redistribute OSPF route to BGP process. @end deffn +@deffn {BGP} {redistribute vpn} {} +Redistribute VNC routes to BGP process. +@end deffn + @deffn {BGP} {update-delay @var{max-delay}} {} @deffnx {BGP} {update-delay @var{max-delay} @var{establish-wait}} {} This feature is used to enable read-only mode on BGP process restart or when diff --git a/doc/fig-vnc-commercial-route-reflector.dia b/doc/fig-vnc-commercial-route-reflector.dia new file mode 100644 index 0000000000..0da5bd1c86 --- /dev/null +++ b/doc/fig-vnc-commercial-route-reflector.dia @@ -0,0 +1,794 @@ + + + + + + + + + + + + + #Letter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 4 +VN 172.16.4.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 5 +VN 172.16.130.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 6 +VN 172.16.132.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 7 +VN 172.16.6.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 8 +VN 172.16.8.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 9 +VN 172.16.134.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 3 +192.168.1.102# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 2 +192.168.1.101# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Commercial Router +Route Reflector +192.168.1.104# + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-vnc-commercial-route-reflector.png b/doc/fig-vnc-commercial-route-reflector.png new file mode 100644 index 0000000000000000000000000000000000000000..ca8a2485026602e56c0c19f6f9a702787dd0539a GIT binary patch literal 50984 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%0DDTOu``afakwLAW$@u5w{_< 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 literal 0 HcmV?d00001 diff --git a/doc/fig-vnc-commercial-route-reflector.txt b/doc/fig-vnc-commercial-route-reflector.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/doc/fig-vnc-gw-rr.dia b/doc/fig-vnc-gw-rr.dia new file mode 100644 index 0000000000..dab27f7007 --- /dev/null +++ b/doc/fig-vnc-gw-rr.dia @@ -0,0 +1,1155 @@ + + + + + + + + + + + + + #Letter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 1 +VN 172.16.1.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 2 +VN 172.16.2.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 3 +VN 172.16.3.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 4 +VN 172.16.4.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 1 +172.16.1.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 2 +172.16.2.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 3 +172.16.3.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 4 +172.16.4.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #VNC Gateway 1 +192.168.1.101# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 1 (NVA) +192.168.1.103# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 2 (NVA) +192.168.1.104# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #VNC Gateway 2 +192.168.1.102# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #RR +192.168.1.105# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-vnc-gw-rr.png b/doc/fig-vnc-gw-rr.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae0630f6225a262fcb95f60aa913b1494567d6e GIT binary patch literal 80004 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±sofdJah>^S(qf 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 Q!!O%xPDfSQdTGnZ3*032k^lez literal 0 HcmV?d00001 diff --git a/doc/fig-vnc-gw-rr.txt b/doc/fig-vnc-gw-rr.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/doc/fig-vnc-gw.dia b/doc/fig-vnc-gw.dia new file mode 100644 index 0000000000..8270e208b5 --- /dev/null +++ b/doc/fig-vnc-gw.dia @@ -0,0 +1,1058 @@ + + + + + + + + + + + + + #Letter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 1 +VN 172.16.1.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 2 +VN 172.16.2.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 3 +VN 172.16.3.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 4 +VN 172.16.4.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 1 +172.16.1.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 2 +172.16.2.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 3 +172.16.3.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CE 4 +172.16.4.2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #VNC Gateway 1 +192.168.1.101# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 1 (NVA) +192.168.1.103# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 2 (NVA) +192.168.1.104# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #VNC Gateway 2 +192.168.1.102# + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-vnc-gw.png b/doc/fig-vnc-gw.png new file mode 100644 index 0000000000000000000000000000000000000000..df8f23f4382b72077af1587c1d050fd135845ec3 GIT binary patch literal 67376 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%0D6Ycwn=B-+pnwvM|9-=r?W=iJ$ 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 + + + + + + + + + + + + #Letter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 1 +192.168.1.100# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 2 +192.168.1.101# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 3 +192.168.1.102# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 7 +VN 172.16.6.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 8 +VN 172.16.8.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 9 +VN 172.16.134.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 4 +VN 172.16.4.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 5 +VN 172.16.130.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 6 +VN 172.16.132.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 1 +VN 172.16.0.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 2 +VN 172.16.2.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 3 +VN 172.16.128.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-vnc-mesh.png b/doc/fig-vnc-mesh.png new file mode 100644 index 0000000000000000000000000000000000000000..fa0762d16b470bfe428c0fd836f97d161a67c12e GIT binary patch literal 61028 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 + + + + + + + + + + + + #Letter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 4 +VN 172.16.4.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 5 +VN 172.16.130.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 6 +VN 172.16.132.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 7 +VN 172.16.6.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 8 +VN 172.16.8.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 9 +VN 172.16.134.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #BGP Route Reflector 1 +192.168.1.100# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 2 +192.168.1.101# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 3 +192.168.1.102# + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-vnc-quagga-route-reflector.png b/doc/fig-vnc-quagga-route-reflector.png new file mode 100644 index 0000000000000000000000000000000000000000..477052184fb23741d01cddd17f2a0d5161cff802 GIT binary patch literal 49978 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 QuKaeW?FgHb*6!>77Zx7>k^lez literal 0 HcmV?d00001 diff --git a/doc/fig-vnc-quagga-route-reflector.txt b/doc/fig-vnc-quagga-route-reflector.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/doc/fig-vnc-redundant-route-reflectors.dia b/doc/fig-vnc-redundant-route-reflectors.dia new file mode 100644 index 0000000000..4065b8ba1f --- /dev/null +++ b/doc/fig-vnc-redundant-route-reflectors.dia @@ -0,0 +1,871 @@ + + + + + + + + + + + + + #Letter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 4 +VN 172.16.4.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 5 +VN 172.16.130.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 6 +VN 172.16.132.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 7 +VN 172.16.6.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 8 +VN 172.16.8.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVE 9 +VN 172.16.134.1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #BGP Route Reflector 1 +192.168.1.100# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Commercial Router +Route Reflector +192.168.1.104# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 2 +192.168.1.101# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NVA 3 +192.168.1.102# + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-vnc-redundant-route-reflectors.png b/doc/fig-vnc-redundant-route-reflectors.png new file mode 100644 index 0000000000000000000000000000000000000000..06a27b6575465316e01e41aa4282fc9acf9d0765 GIT binary patch literal 75353 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 G!T$$3GMz5~ literal 0 HcmV?d00001 diff --git a/doc/fig-vnc-redundant-route-reflectors.txt b/doc/fig-vnc-redundant-route-reflectors.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/doc/ospfd.texi b/doc/ospfd.texi index 96dffe0000..7b9df8e78e 100644 --- a/doc/ospfd.texi +++ b/doc/ospfd.texi @@ -580,6 +580,10 @@ redistributed into OSPF (@pxref{OSPF redistribute}). @deffnx {OSPF Command} {no distance ospf} {} @end deffn +@deffn {Command} {router zebra} {} +@deffnx {Command} {no router zebra} {} +@end deffn + @node Showing OSPF information @section Showing OSPF information diff --git a/doc/quagga.texi b/doc/quagga.texi index 6831b30cdf..13b885b69c 100644 --- a/doc/quagga.texi +++ b/doc/quagga.texi @@ -1,5 +1,8 @@ \input texinfo @c -*- texinfo -*- +@c Set variables - sourced from defines.texi +@include defines.texi + @c %**start of header @setfilename quagga.info @c Set variables - sourced from defines.texi @@ -28,6 +31,7 @@ Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by Kunihiro Ishiguro. + @end quotation @end copying @@ -66,7 +70,7 @@ Version @value{VERSION}. @ifnottex @node Top -@top Quagga +@top Quagga -- With Virtual Network Control @uref{http://www.quagga.net,,Quagga} is an advanced routing software package that provides a suite of TCP/IP based routing protocols. This is the Manual @@ -88,6 +92,7 @@ for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of * ISIS:: * BGP:: * Configuring Quagga as a Route Server:: +* VNC and VNC-GW:: * VTY shell:: * Filtering:: * Route Map:: @@ -113,6 +118,7 @@ for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of @include isisd.texi @include bgpd.texi @include routeserver.texi +@include vnc.texi @include vtysh.texi @include filter.texi @include routemap.texi diff --git a/doc/routemap.texi b/doc/routemap.texi index b3ef7ca76f..33062a7f61 100644 --- a/doc/routemap.texi +++ b/doc/routemap.texi @@ -167,7 +167,7 @@ Set the BGP nexthop address. @end deffn @deffn {Route-map Command} {set local-preference @var{local_pref}} {} -Set the BGP local preference. +Set the BGP local preference to @var{local_pref}. @end deffn @deffn {Route-map Command} {set weight @var{weight}} {} diff --git a/doc/vnc.texi b/doc/vnc.texi new file mode 100644 index 0000000000..341cbfcce8 --- /dev/null +++ b/doc/vnc.texi @@ -0,0 +1,1584 @@ +@c -*-texinfo-*- +@c This is part of the Quagga Manual. +@c @value{COPYRIGHT_STR} +@c See file quagga.texi for copying conditions. + +@node VNC and VNC-GW +@chapter VNC and VNC-GW +This chapter describes how to use +Virtual Network Control (@acronym{VNC}) services, +including Network Virtualization Authority (@acronym{NVA}) and +VNC Gateway (@acronym{VNC-GW}) functions. +Background information on NVAs, +Network Virtualization Edges (@acronym{NVE}s), underlay networks (@acronym{UN}s), +and virtual networks (@acronym{VN}s) is available from the +@url{https://datatracker.ietf.org/wg/nvo3,IETF Network Virtualization Overlays (@acronym{NVO3}) Working Group}. +VNC Gateways (@acronym{VNC-GW}s) support the import/export of routing +information between VNC and customer edge routers (@acronym{CE}s) +operating within a VN. Both IP/Layer 3 (L3) VNs, and IP with +Ethernet/Layer 2 (L2) VNs are supported. + +BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VN +information between NVAs. BGP based IP VPN support is defined in +@cite{RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs)}, and +@cite{RFC4659, BGP-MPLS IP Virtual Private Network (VPN) Extension for +IPv6 VPN }. Both the Encapsulation Subsequent Address Family Identifier +(SAFI) and the Tunnel Encapsulation Attribute, @cite{RFC5512, The BGP +Encapsulation Subsequent Address Family Identifier (SAFI) and the BGP +Tunnel Encapsulation Attribute}, are supported. + +The protocol that is used to communicate routing and Ethernet / Layer 2 +(L2) forwarding information between NVAs and NVEs is referred to as the +Remote Forwarder Protocol (RFP). @code{OpenFlow} is an example +RFP. Specific RFP implementations may choose to implement either a +@code{hard-state} or @code{soft-state} prefix and address registration +model. To support a @code{soft-state} refresh model, a @var{lifetime} +in seconds is associated with all registrations and responses. + +The chapter also provides sample configurations for basic example scenarios. + +@menu +* Configuring VNC Services:: +* Manual Address Control:: +* Other VNC-Related Commands:: +* Example VNC and VNC-GW Configurations:: +* Release Notes:: +@end menu + +@node Configuring VNC Services +@section Configuring VNC + +Virtual Network Control (@acronym{VNC}) service configuration commands +appear in the @code{router bgp} section of the BGPD configuration file +(@pxref{BGP Configuration Examples}). The commands are broken down into +the following areas: + +@menu +* General VNC Configuration:: +* RFP Related Configuration:: +* VNC Defaults Configuration:: +* VNC NVE Group Configuration:: +* VNC L2 Group Configuration:: +* Configuring Redistribution of Routes from Other Routing Protocols:: +* Configuring Export of Routes to Other Routing Protocols:: +@end menu + +@code{General VNC} configuration applies to general VNC operation and is +primarily used to control the method used to advertise tunnel +information. + +@code{Remote Forwarder Protocol (RFP)} configuration relates to the +protocol used between NVAs and NVEs. + +@code{VNC Defaults} provides default parameters for registered NVEs. + +@code{VNC NVE Group} provides for configuration of a specific set of +registered NVEs and overrides default parameters. + +@code{Redistribution} and @code{Export} control VNC-GW operation, i.e., +the import/export of routing +information between VNC and customer edge routers (@acronym{CE}s) +operating within a VN. + +@node General VNC Configuration +@subsection General VNC Configuration + +@deffn {VNC} {vnc advertise-un-method encap-safi|encap-attr} {} +Advertise NVE underlay-network IP addresses using the encapsulation SAFI +(@code{encap-safi}) or the UN address sub-TLV of the Tunnel Encapsulation attribute +(@code{encap-attr}). When @code{encap-safi} is used, neighbors under +@code{address-family encap} and/or @code{address-family encapv6} must be +configured. The default is @code{encap-attr}. +@end deffn + +@node RFP Related Configuration +@subsection RFP Related Configuration + +The protocol that is used to communicate routing and Ethernet / L2 +forwarding information between NVAs and NVEs is referred to as the +Remote Forwarder Protocol (RFP). Currently, only a simple example RFP +is included in Quagga. Developers may use this example as a starting +point to integrate Quagga with an RFP of their choosing, e.g., +@code{OpenFlow}. The example code includes the following sample +configuration: + +@deffn {RFP} {rfp example-config-value @var{VALUE}} +This is a simple example configuration parameter included as part of the +RFP example code. @code{VALUE} must be in the range of 0 to 4294967295. +@end deffn + +@node VNC Defaults Configuration +@subsection VNC Defaults Configuration + +The VNC Defaults section allows the user to specify default values for +configuration parameters for all registered NVEs. +Default values are overridden by @ref{VNC NVE Group Configuration}. + +@deffn {VNC} {vnc defaults} {} +Enter VNC configuration mode for specifying VNC default behaviors. Use +@code{exit-vnc} to leave VNC configuration mode. @code{vnc +defaults} is optional. + +@example +vnc defaults + ... various VNC defaults +exit-vnc +@end example +@end deffn + +These are the statements that can appear between @code{vnc defaults} +and @code{exit-vnc}. + +@deffn {VNC} {rt import @var{rt-list}} {} +@deffnx {VNC} {rt export @var{rt-list}} {} +@deffnx {VNC} {rt both @var{rt-list}} {} + +Specify default route target import and export lists. @var{rt-list} is a +space-separated list of route targets, each element of which is +in one of the following forms: +@itemize +@item @var{IPv4-address}:@var{two-byte-integer} +@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer} +@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer} +@end itemize + +If no default import RT list is specified, then the default import RT +list is empty. +If no default export RT list is specified, then the default export RT +list is empty. + +A complete definition of these parameters is +given below (@pxref{VNC NVE Group Configuration}). + +@end deffn + +@deffn {VNC} {rd @var{route-distinguisher}} + +Specify the default route distinguisher (RD) for routes advertised via BGP +VPNs. The route distinguisher must be in one of four forms: +@itemize +@item @var{IPv4-address}:@var{two-byte-integer} +@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer} +@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer} +@item auto:vn:@var{two-byte-integer} +@end itemize + +If RD is specified in the defaults section, the default RD +value is @var{two-byte-autonomous-system-number=0}:@var{four-byte-integer=0}. + +A complete definition of this parameter is +given below (@pxref{VNC NVE Group Configuration}). +@end deffn + +@deffn {VNC} {l2rd @var{nve-id-value}} +Set the value used to distinguish NVEs connected to the same logical +Ethernet segment (i.e., L2VPN). + +A complete definition of this parameter is +given below (@pxref{VNC NVE Group Configuration}). +@end deffn + +@deffn {VNC} {response-lifetime @var{lifetime}|infinite} {} +Specify the default lifetime to be included in RFP +response messages sent to NVEs. + +A complete definition of this parameter is +given below (@pxref{VNC NVE Group Configuration}). + +@end deffn + +@deffn {VNC} {export bgp|zebra route-map MAP-NAME} +Specify that the named route-map should be applied to routes +being exported to bgp or zebra. +@end deffn + +@deffn {VNC} {export bgp|zebra no route-map} +Specify that no route-map should be applied to routes +being exported to bgp or zebra. +@end deffn + +@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME} +Specify that the named prefix-list filter should be applied to +routes being exported to bgp or zebra. +Prefix-lists for ipv4 and ipv6 are independent of each other. +@end deffn + +@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list} +Specify that no prefix-list filter should be applied to +routes being exported to bgp or zebra. +@end deffn + +@deffn {VNC} {exit-vnc} {} +Exit VNC configuration mode. +@end deffn + +@c The following example @code{vnc defaults} defines a route target import-export +@c list for the route targets 1000:1 and 1000:2; a default route +@c distinguisher, 4444:10; and a default response lifetime of 500 +@c seconds. +@c +@c @example +@c vnc defaults +@c rt both 1000:1 1000:2 +@c rd 4444:10 +@c response-lifetime 500 +@c exit-vnc +@c @end example + +@node VNC NVE Group Configuration +@subsection VNC NVE Group Configuration + +A NVE Group corresponds to a specific set of NVEs. A Client NVE is +assigned to an NVE Group based on whether there is a match for either +its virtual or underlay network address against the VN and/or UN address +prefixes specified in the NVE Group definition. When an NVE Group +definition specifies both VN and UN address prefixes, then an NVE must +match both prefixes in order to be assigned to the NVE Group. In the +event that multiple NVE Groups match based on VN and/or UN addresses, +the NVE is assigned to the first NVE Group listed in the configuration. +If an NVE is not assigned to an NVE Group, its messages will be ignored. + +Configuration values specified for an NVE group apply to all +member NVEs and override configuration values specified in the VNC +Defaults section. + +@strong{At least one @code{nve-group} is mandatory for useful VNC +operation.} + +@deffn {VNC} {vnc nve-group @var{name}} {} +Enter VNC configuration mode for defining the NVE group @var{name}. +Use @code{exit} or @code{exit-vnc} to exit group configuration mode. + +@example +vnc nve-group group1 + ... configuration commands +exit-vnc +@end example +@end deffn + +@deffn {VNC} {no vnc nve-group @var{name}} {} +Delete the NVE group named @var{name}. +@end deffn + +The following statements are valid in an NVE group definition: + +@deffn {VNC} {l2rd @var{nve-id-value}} +Set the value used to distinguish NVEs connected to the same physical +Ethernet segment (i.e., at the same location)@footnote{The nve-id is +carried in the route +distinguisher. It is the second octet of the eight-octet route +distinguisher generated for Ethernet / L2 advertisements. +The first octet is a constant 0xFF, and the third through eighth +octets are set to the L2 ethernet address being advertised.} + +The nve-id subfield may be specified as either a literal value +in the range 1-255, or it may be specified as @code{auto:vn}, which +means to use the least-significant octet of the originating +NVE's VN address. +@end deffn + +@deffn {VNC} {prefix vn|un A.B.C.D/M|X:X::X:X/M} {} +@anchor{prefix} +Specify the matching prefix for this NVE group by either virtual-network address +(@code{vn}) or underlay-network address (@code{un}). Either or both virtual-network +and underlay-network prefixes may be specified. Subsequent virtual-network or +underlay-network values within a @code{vnc nve-group} @code{exit-vnc} +block override their respective previous values. + +These prefixes are used only for determining assignments of NVEs +to NVE Groups. +@end deffn + +@deffn {VNC} {rd @var{route-distinguisher}} +Specify the route distinguisher for routes advertised via BGP +VPNs. The route distinguisher must be in one of these forms: +@itemize +@item @var{IPv4-address}:@var{two-byte-integer} +@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer} +@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer} +@item auto:vn:@var{two-byte-integer} +@end itemize + +Routes originated by NVEs in the NVE group will use +the group's specified @var{route-distinguisher} when they are +advertised via BGP. +If the @code{auto} form is specified, it means that a matching NVE has +its RD set to +@var{rd_type=IP=1}:@var{IPv4-address=VN-address}:@var{two-byte-integer}, +for IPv4 VN addresses and +@var{rd_type=IP=1}:@var{IPv4-address=Last-four-bytes-of-VN-address}:@var{two-byte-integer}, +for IPv6 VN addresses. + +If the NVE group definition does not specify a @var{route-distinguisher}, +then the default @var{route-distinguisher} is used. +If neither a group nor a default @var{route-distinguisher} is +configured, then the advertised RD is set to +@var{two-byte-autonomous-system-number=0}:@var{four-byte-integer=0}. +@end deffn + +@deffn {VNC} {response-lifetime @var{lifetime}|infinite} {} +Specify the response lifetime, in seconds, to be included in RFP +response messages sent to NVEs. If the value +``infinite'' is given, an infinite lifetime will be used. + +Note that this parameter is not the same as the lifetime supplied by +NVEs in RFP registration messages. This parameter does not affect +the lifetime value attached to routes sent by this server via BGP. + +If the NVE group definition does not specify a @var{response-lifetime}, +the default @var{response-lifetime} will be used. +If neither a group nor a default @var{response-lifetime} is configured, +the value 3600 will be used. The maximum response lifetime is 2147483647. +@end deffn + +@deffn {VNC} {rt export @var{rt-list}} {} +@deffnx {VNC} {rt import @var{rt-list}} {} +@deffnx {VNC} {rt both @var{rt-list}} {} +Specify route target import and export lists. @var{rt-list} is a +space-separated list of route targets, each element of which is +in one of the following forms: +@itemize +@item @var{IPv4-address}:@var{two-byte-integer} +@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer} +@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer} +@end itemize + +The first form, @code{rt export}, specifies an @var{export rt-list}. +The @var{export rt-list} will be attached to routes originated by +NVEs in the NVE group when they are advertised via BGP. +If the NVE group definition does not specify an @var{export rt-list}, +then the default @var{export rt-list} is used. +If neither a group nor a default @var{export rt-list} is configured, +then no RT list will be sent; in turn, these routes will probably +not be processed +by receiving NVAs. + +The second form, @code{rt import} specifies an @var{import rt-list}, +which is a filter for incoming routes. +In order to be made available to NVEs in the group, +incoming BGP VPN and @w{ENCAP} @w{SAFI} (when @code{vnc +advertise-un-method encap-safi} is set) routes must have +RT lists that have at least one route target in common with the +group's @var{import rt-list}. + +If the NVE group definition does not specify an import filter, +then the default @var{import rt-list} is used. +If neither a group nor a default @var{import rt-list} is configured, +there can be no RT intersections when receiving BGP routes and +therefore no incoming BGP routes will be processed for the group. + +The third, @code{rt both}, is a shorthand way of specifying both +lists simultaneously, and is equivalent to @code{rt export @var{rt-list}} +followed by @code{rt import @var{rt-list}}. +@end deffn + +@deffn {VNC} {export bgp|zebra route-map MAP-NAME} +Specify that the named route-map should be applied to routes +being exported to bgp or zebra. +This paramter is used in conjunction with +@ref{Configuring Export of Routes to Other Routing Protocols}. +This item is optional. +@end deffn + +@deffn {VNC} {export bgp|zebra no route-map} +Specify that no route-map should be applied to routes +being exported to bgp or zebra. +This paramter is used in conjunction with +@ref{Configuring Export of Routes to Other Routing Protocols}. +This item is optional. +@end deffn + +@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME} +Specify that the named prefix-list filter should be applied to +routes being exported to bgp or zebra. +Prefix-lists for ipv4 and ipv6 are independent of each other. +This paramter is used in conjunction with +@ref{Configuring Export of Routes to Other Routing Protocols}. +This item is optional. +@end deffn + +@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list} +Specify that no prefix-list filter should be applied to +routes being exported to bgp or zebra. +This paramter is used in conjunction with +@ref{Configuring Export of Routes to Other Routing Protocols}. +This item is optional. +@end deffn + +@c The following example shows two @code{vnc nve-group} definitions. The first one, +@c ``group1'', applies to the IPV4 virtual-network route prefix 172.16/16. It +@c sets the response lifetime to 200 seconds. It defines a route target +@c import-export filter for the route targets 1000:1 and 1000:2 +@c +@c The second @code{vnc nve-group} definition, ``group2'', applies to the IPV6 +@c underlay-network route prefix 10.0.2/24. It defines the same response +@c lifetime and import-export filter as ``group1''. +@c +@c @example +@c vnc nve-group group1 +@c prefix vn 172.16/16 +@c response-lifetime 200 +@c rt both 1000:1 1000:2 +@c exit-vnc +@c +@c vnc nve-group group2 +@c prefix un 10.0.2/24 +@c response-lifetime 200 +@c rt both 1000:1 1000:2 +@c exit-vnc +@c @end example + +@node VNC L2 Group Configuration +@subsection VNC L2 Group Configuration + +The route targets advertised with prefixes and addresses registered by +an NVE are determined based on the NVE's associated VNC NVE Group +Configuration, @pxref{VNC NVE Group Configuration}. Layer 2 (L2) Groups +are used to override the route targets for an NVE's Ethernet +registrations based on the Logical Network Identifier and label value. +A Logical Network Identifier is used to uniquely identify a logical +Ethernet segment and is conceptually similar to the Ethernet Segment +Identifier defined in @cite{RFC7432, BGP MPLS-Based Ethernet VPN}. Both +the Logical Network Identifier and Label are passed to VNC via RFP +prefix and address registration. + +Note that a corresponding NVE group configuration must be present, and +that other NVE associated configuration information, notably RD, is +not impacted by L2 Group Configuration. + +@deffn {VNC} {vnc l2-group @var{name}} {} +Enter VNC configuration mode for defining the L2 group @var{name}. +Use @code{exit} or @code{exit-vnc} to exit group configuration mode. + +@example +vnc l2-group group1 + ... configuration commands +exit-vnc +@end example +@end deffn + +@deffn {VNC} {no vnc l2-group @var{name}} {} +Delete the L2 group named @var{name}. +@end deffn + +The following statements are valid in a L2 group definition: + +@deffn {VNC} {logical-network-id @var{VALUE}} +Define the Logical Network Identifier with a value in the range of +0-4294967295 that identifies the logical Ethernet segment. +@end deffn + +@deffn {VNC} {labels @var{label-list}} +@deffnx {VNC} {no labels @var{label-list}} +Add or remove labels associated with the group. @var{label-list} is a +space separated list of label values in the range of 0-1048575. +@end deffn + +@deffn {VNC} {rt import @var{rt-target}} {} +@deffnx {VNC} {rt export @var{rt-target}} {} +@deffnx {VNC} {rt both @var{rt-target}} {} +Specify the route target import and export value associated with the +group. A complete definition of these parameters is given above, +@pxref{VNC NVE Group Configuration}. +@end deffn + + +@node Configuring Redistribution of Routes from Other Routing Protocols +@subsection Configuring Redistribution of Routes from Other Routing Protocols + +Routes from other protocols (including BGP) can be provided to VNC (both +for RFP and for redistribution via BGP) +from three sources: the zebra kernel routing process; +directly from the main (default) unicast BGP RIB; or directly +from a designated BGP unicast exterior routing RIB instance. + +The protocol named in the @code{vnc redistribute} command indicates +the route source: +@code{bgp-direct} routes come directly from the main (default) +unicast BGP RIB and are available for RFP and are redistributed via BGP; +@code{bgp-direct-to-nve-groups} routes come directly from a designated +BGP unicast routing RIB and are made available only to RFP; +and routes from other protocols come from the zebra kernel +routing process. +Note that the zebra process does not need to be active if +only @code{bgp-direct} or @code{bgp-direct-to-nve-groups} routes are used. + +@subsubsection @code{zebra} routes + +Routes originating from protocols other than BGP must be obtained +via the zebra routing process. +Redistribution of these routes into VNC does not support policy mechanisms +such as prefix-lists or route-maps. + +@subsubsection @code{bgp-direct} routes + +@code{bgp-direct} redistribution supports policy via +prefix lists and route-maps. This policy is applied to incoming +original unicast routes before the redistribution translations +(described below) are performed. + +Redistribution of @code{bgp-direct} routes is performed in one of three +possible modes: @code{plain}, @code{nve-group}, or @code{resolve-nve}. +The default mode is @code{plain}. +These modes indicate the kind of translations applied to routes before +they are added to the VNC RIB. + +In @code{plain} mode, the route's next hop is unchanged and the RD is set +based on the next hop. +For @code{bgp-direct} redistribution, the following translations are performed: +@itemize @bullet +@item +The VN address is set to the original unicast route's next hop address. +@item +The UN address is NOT set. (VN->UN mapping will occur via +ENCAP route or attribute, based on @code{vnc advertise-un-method} +setting, generated by the RFP registration of the actual NVE) +@item +The RD is set to as if auto:vn:0 were specified (i.e., +@var{rd_type=IP=1}:@var{IPv4-address=VN-address}:@var{two-byte-integer=0}) +@item +The RT list is included in the extended community list copied from the +original unicast route (i.e., it must be set in the original unicast route). +@end itemize + + + +In @code{nve-group} mode, routes are registered with VNC as +if they came from an NVE in the nve-group designated in the +@code{vnc redistribute nve-group} command. The following +translations are performed: + +@itemize @bullet +@item +The next hop/VN address is set to the VN prefix configured for the +redistribute nve-group. +@item +The UN address is set to the UN prefix configured for the +redistribute nve-group. +@item +The RD is set to the RD configured for the redistribute nve-group. +@item +The RT list is set to the RT list configured for the redistribute nve-group. +If @code{bgp-direct} routes are being redistributed, +any extended communities present in the original unicast route +will also be included. +@end itemize + + +In @code{resolve-nve} mode, the next hop of the original BGP route is +typically the address of an NVE connected router (CE) connected by one or +more NVEs. +Each of the connected NVEs will register, via RFP, a VNC host route +to the CE. +This mode may be though of as a mechanism to proxy RFP registrations +of BGP unicast routes on behalf of registering NVEs. + +Multiple copies of the BGP route, one per matching NVE host route, will be +added to VNC. +In other words, for a given BGP unicast route, each instance of a +RFP-registered host route to the unicast route's next hop will result +in an instance of an imported VNC route. +Each such imported VNC route will have a prefix equal to the original +BGP unicast route's prefix, and a next hop equal to the next hop of the +matching RFP-registered host route. +If there is no RFP-registered host route to the next hop of the BGP unicast +route, no corresponding VNC route will be imported. + +The following translations are applied: + +@itemize @bullet +@item +The Next Hop is set to the next hop of the NVE route (i.e., the +VN address of the NVE). + +@item +The extended community list in the new route is set to the +union of: +@itemize @minus +@item +Any extended communities in the original BGP route +@item +Any extended communities in the NVE route +@item +An added route-origin extended community with the next hop of the +original BGP route +is added to the new route. +The value of the local administrator field defaults 5226 but may +be configured by the user via the @code{roo-ec-local-admin} parameter. +@end itemize + +@item +The Tunnel Encapsulation attribute is set to the value of the Tunnel +Encapsulation attribute of the NVE route, if any. + +@end itemize + +@subsubsection @code{bgp-direct-to-nve-groups} routes + +Unicast routes from the main or a designated instance of BGP +may be redistributed to VNC as bgp-direct-to-nve-groups routes. These +routes are NOT announced via BGP, +but they are made available for local RFP lookup in response to +queries from NVEs. + +A non-main/default BGP instance is configured using the +@code{bgp multiple-instance} and @code{router bgp AS view NAME} +commands as described elsewhere in this document. + +In order for a route in the unicast BGP RIB to be made +available to a querying NVE, there must already be, available to +that NVE, an (interior) VNC route matching the next hop address +of the unicast route. +When the unicast route is provided to the NVE, its next hop +is replaced by the next hop of the corresponding +NVE. If there are multiple longest-prefix-match VNC routes, +the unicast route will be replicated for each. + +There is currently no policy (prefix-list or route-map) support +for @code{bgp-direct-to-nve-groups} routes. + +@subsubsection Redistribution Command Syntax + +@deffn {VNC} {vnc redistribute ipv4|ipv6 bgp|bgp-direct|ipv6 bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static} {} +@deffnx {VNC} {vnc redistribute ipv4|ipv6 bgp-direct-to-nve-groups view @var{VIEWNAME}} {} +@deffnx {VNC} {no vnc redistribute ipv4|ipv6 bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static} {} +Import (or do not import) prefixes from another routing +protocols. Specify both the address family to import (@code{ipv4} or +@code{ipv6}) and the protocol (@code{bgp}, @code{bgp-direct}, +@code{bgp-direct-to-nve-groups}, @code{connected}, +@code{kernel}, @code{ospf}, @code{rip}, or @code{static}). Repeat +this statement as needed for each combination of address family and +routing protocol. +Prefixes from protocol @code{bgp-direct} are imported from unicast BGP +in the same bgpd process. +Prefixes from all other protocols (including @code{bgp}) are imported +via the @code{zebra} kernel routing process. +@end deffn + +@deffn {VNC} {vnc redistribute mode plain|nve-group|resolve-nve} +Redistribute routes from other protocols into VNC using the +specified mode. +Not all combinations of modes and protocols are supported. +@end deffn + +@deffn {VNC} {vnc redistribute nve-group @var{group-name}} {} +@deffnx {VNC} {no vnc redistribute nve-group @var{group-name}} {} +When using @code{nve-group} mode, +assign (or do not assign) the NVE group @var{group-name} to routes +redistributed from another routing protocol. @var{group-name} +must be configured using @code{vnc nve-group}. + +The VN and UN prefixes of the nve-group must both be configured, +and each prefix must be specified as a full-length (/32 for IPv4, +/128 for IPv6) prefix. +@end deffn + +@deffn {VNC} {vnc redistribute lifetime @var{lifetime}|infinite} {} +Assign a registration lifetime, either @var{lifetime} seconds or +@code{infinite}, to prefixes redistributed from other routing +protocols as if they had been received via RFP registration messages +from an NVE. @var{lifetime} can be any integer between 1 and +4294967295, inclusive. +@end deffn + +@deffn {VNC} {vnc redistribute resolve-nve roo-ec-local-admin @var{0-65536}} +Assign a value to the local-administrator subfield used in the +Route Origin extended community that is assigned to routes exported +under the @code{resolve-nve} mode. The default value is @var{5226}. +@end deffn + +The following four @code{prefix-list} and @code{route-map} commands +may be specified in the context of an nve-group or not. +If they are specified in the context of an nve-group, they +apply only if the redistribution mode is @code{nve-group}, +and then only for routes being redistributed from +@code{bgp-direct}. +If they are specified outside the context of an nve-group, then +they apply only for redistribution modes @code{plain} and @code{resolve-nve}, +and then only for routes being redistributed from @code{bgp-direct}. + +@deffn {VNC} {vnc redistribute bgp-direct (ipv4|ipv6) prefix-list @var{LIST-NAME}} +When redistributing @code{bgp-direct} routes, +specifies that the named prefix-list should be applied. +@end deffn + +@deffn {VNC} {vnc redistribute bgp-direct no (ipv4|ipv6) prefix-list} +When redistributing @code{bgp-direct} routes, +specifies that no prefix-list should be applied. +@end deffn + +@deffn {VNC} {vnc redistribute bgp-direct route-map @var{MAP-NAME}} +When redistributing @code{bgp-direct} routes, +specifies that the named route-map should be applied. +@end deffn + +@deffn {VNC} {vnc redistribute bgp-direct no route-map} +When redistributing @code{bgp-direct} routes, +specifies that no route-map should be applied. +@end deffn + +@node Configuring Export of Routes to Other Routing Protocols +@subsection Configuring Export of Routes to Other Routing Protocols + +Routes from VNC (both for RFP and for redistribution via BGP) can be +provided to other protocols, either via zebra or directly to BGP. + +It is important to note that when exporting routes to other protocols, +the downstream protocol must also be configured to import the routes. +For example, when VNC routes are exported to unicast BGP, the BGP +configuration must include a corresponding @code{redistribute vpn} +statement. + +@deffn {VNC} {export bgp|zebra mode none|group-nve|registering-nve|ce} +Specify how routes should be exported to bgp or zebra. +If the mode is @code{none}, routes are not exported. +If the mode is @code{group-nve}, routes are exported according +to nve-group configuration (@pxref{VNC NVE Group Configuration}): if a group is configured to +allow export, then each prefix visible to the group is exported +with next hops set to the currently-registered NVEs. +If the mode is @code{registering-nve}, then all VNC routes are +exported with their original next hops. +If the mode is @code{ce}, only VNC routes that have an NVE connected CE Router +encoded in a Route Origin Extended Community are exported. +This extended community must have an administrative value that +matches the configured @code{roo-ec-local-admin} value. +The next hop of the exported route is set to the encoded +NVE connected CE Router. + +The default for both bgp and zebra is mode @code{none}. +@end deffn + +@deffn {VNC} {vnc export bgp|zebra group-nve group @var{group-name}} +@deffnx {VNC} {vnc export bgp|zebra group-nve no group @var{group-name}} +When export mode is @code{group-nve}, +export (or do not export) prefixes from the specified nve-group +to unicast BGP or to zebra. +Repeat this statement as needed for each nve-group to be exported. +Each VNC prefix that is exported will result in N exported routes to the +prefix, each with a next hop corresponding to one of the N NVEs currently +associated with the nve-group. +@end deffn + +@deffn {VNC} export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME +When export mode is @code{ce} or @code{registering-nve}, +specifies that the named prefix-list should be applied to routes +being exported to bgp or zebra. +Prefix-lists for ipv4 and ipv6 are independent of each other. +@end deffn + +@deffn {VNC} export bgp|zebra no ipv4|ipv6 prefix-list +When export mode is @code{ce} or @code{registering-nve}, +specifies that no prefix-list should be applied to routes +being exported to bgp or zebra. +@end deffn + +@deffn {VNC} export bgp|zebra route-map MAP-NAME +When export mode is @code{ce} or @code{registering-nve}, +specifies that the named route-map should be applied to routes +being exported to bgp or zebra. +@end deffn + +@deffn {VNC} export bgp|zebra no route-map +When export mode is @code{ce} or @code{registering-nve}, +specifies that no route-map should be applied to routes +being exported to bgp or zebra. +@end deffn + +When the export mode is @code{group-nve}, policy for exported +routes is specified per-NVE-group inside a @code{nve-group} @var{RFG-NAME} block +via the following commands(@pxref{VNC NVE Group Configuration}): + +@deffn {VNC} {export bgp|zebra route-map MAP-NAME} +This command is valid inside a @code{nve-group} @var{RFG-NAME} block. +It specifies that the named route-map should be applied to routes +being exported to bgp or zebra. +@end deffn + +@deffn {VNC} {export bgp|zebra no route-map} +This command is valid inside a @code{nve-group} @var{RFG-NAME} block. +It specifies that no route-map should be applied to routes +being exported to bgp or zebra. +@end deffn + +@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME} +This command is valid inside a @code{nve-group} @var{RFG-NAME} block. +It specifies that the named prefix-list filter should be applied to +routes being exported to bgp or zebra. +Prefix-lists for ipv4 and ipv6 are independent of each other. +@end deffn + +@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list} +This command is valid inside a @code{nve-group} @var{RFG-NAME} block. +It specifies that no prefix-list filter should be applied to +routes being exported to bgp or zebra. +@end deffn + +@node Manual Address Control +@section Manual Address Control + +The commands in this section can be used to augment normal dynamic VNC. +The @code{add vnc} commands can be used to manually add IP prefix or +Ethernet MAC address forwarding information. The @code{clear vnc} +commands can be used to remove manually and dynamically added +information. + +@deffn {Command} {add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) [cost <0-255>] [lifetime (infinite|<1-4294967295>)] [local-next-hop (A.B.C.D|X:X::X:X) [local-cost <0-255>]]} {} +Register an IP prefix on behalf of the NVE identified by the VN and UN +addresses. The @code{cost} parameter provides the administrative +preference of the forwarding information for remote advertisement. If +omitted, it defaults to 255 (lowest preference). The @code{lifetime} +parameter identifies the period, in seconds, that the information +remains valid. If omitted, it defaults to @var{infinite}. The optional +@code{local-next-hop} parameter is used to configure a nexthop to be +used by an NVE to reach the prefix via a locally connected CE router. +This information remains local to the NVA, i.e., not passed to other +NVAs, and is only passed to registered NVEs. When specified, it is also +possible to provide a @code{local-cost} parameter to provide a +forwarding preference. If omitted, it defaults to 255 (lowest +preference). +@end deffn + + +@deffn {Command} {add vnc mac xx:xx:xx:xx:xx:xx virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) [prefix (A.B.C.D/M|X:X::X:X/M)] [cost <0-255>] [lifetime (infinite|<1-4294967295>)]} {} +Register a MAC address for a logical Ethernet (L2VPN) on behalf of the +NVE identified by the VN and UN addresses. +The optional @code{prefix} parameter is to support enable IP address +mediation for the given prefix. The @code{cost} parameter provides the administrative +preference of the forwarding information. If omitted, it defaults to +255. The @code{lifetime} parameter identifies the period, in seconds, +that the information remains valid. If omitted, it defaults to +@var{infinite}. +@end deffn + +@deffn {Command} {clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) (*|[(vn|un) (A.B.C.D|X:X::X:X|*) [(un|vn) (A.B.C.D|X:X::X:X|*)] [mac xx:xx:xx:xx:xx:xx] [local-next-hop (A.B.C.D|X:X::X:X)])} {} +Delete the information identified by prefix, VN address, and UN address. +Any or all of these parameters may be wilcarded to (potentially) match +more than one registration. +The optional @code{mac} parameter specifies a layer-2 MAC address +that must match the registration(s) to be deleted. +The optional @code{local-next-hop} parameter is used to +delete specific local nexthop information. +@end deffn + +@deffn {Command} {clear vnc mac (*|xx:xx:xx:xx:xx:xx) virtual-network-identifier (*|<1-4294967295>) (*|[(vn|un) (A.B.C.D|X:X::X:X|*) [(un|vn) (A.B.C.D|X:X::X:X|*)] [prefix (*|A.B.C.D/M|X:X::X:X/M)])} {} +Delete mac forwarding information. +Any or all of these parameters may be wilcarded to (potentially) match +more than one registration. +The default value for the @code{prefix} parameter is the wildcard value @var{*}. +@end deffn + +@deffn {Command} {clear vnc nve (*|((vn|un) (A.B.C.D|X:X::X:X) [(un|vn) (A.B.C.D|X:X::X:X)])) } {} +Delete prefixes associated with the NVE specified by the given VN and UN +addresses. +It is permissible to specify only one of VN or UN, in which case +any matching registration will be deleted. +It is also permissible to specify @code{*} in lieu of any VN or UN +address, in which case all registrations will match. +@end deffn + +@node Other VNC-Related Commands +@section Other VNC-Related Commands + +Note: VNC-Related configuration can be obtained via the @code{show +running-configuration} command when in @code{enable} mode. + +The following commands are used to clear and display +Virtual Network Control related information: + +@deffn {COMMAND} {clear vnc counters} {} +Reset the counter values stored by the NVA. Counter +values can be seen using the @code{show vnc} commands listed above. This +command is only available in @code{enable} mode. +@end deffn + +@deffn {Command} {show vnc summary} {} +Print counter values and other general information +about the NVA. Counter values can be reset +using the @code{clear vnc counters} command listed below. +@end deffn + +@deffn {Command} {show vnc nves} {} +@deffnx {Command} {show vnc nves vn|un @var{address}} {} +Display the NVA's current clients. Specifying @var{address} +limits the output to the NVEs whose addresses match @var{address}. +The time since the NVA last communicated with the NVE, per-NVE +summary counters and each NVE's addresses will be displayed. +@end deffn + +@deffn {Command} {show vnc queries} {} +@deffnx {Command} {show vnc queries @var{prefix}} {} +Display active Query information. Queries remain valid for the default +Response Lifetime (@pxref{VNC Defaults Configuration}) or NVE-group +Response Lifetime (@pxref{VNC NVE Group Configuration}). Specifying +@var{prefix} limits the output to Query Targets that fall within +@var{prefix}. + +Query information is provided for each querying NVE, and includes the +Query Target and the time remaining before the information is removed. +@end deffn + +@deffn {Command} {show vnc registrations [all|local|remote|holddown|imported]} {} +@deffnx {Command} {show vnc registrations [all|local|remote|holddown|imported] @var{prefix}} {} +Display local, remote, holddown, and/or imported registration information. +Local registrations are routes received via RFP, which are present in the +NVA Registrations Cache. +Remote registrations are routes received via BGP (VPN SAFIs), which +are present in the NVE-group import tables. +Holddown registrations are local and remote routes that have been +withdrawn but whose holddown timeouts have not yet elapsed. +Imported information represents routes that are imported into NVA and +are made available to querying NVEs. Depending on configuration, +imported routes may also be advertised via BGP. +Specifying @var{prefix} limits the output to the registered prefixes that +fall within @var{prefix}. + +Registration information includes the registered prefix, the registering +NVE addresses, the registered administrative cost, the registration +lifetime and the time since the information was registered or, in the +case of Holddown registrations, the amount of time remaining before the +information is removed. +@end deffn + +@deffn {Command} {show vnc responses [active|removed]} {} +@deffnx {Command} {show vnc responses [active|removed] @var{prefix}} {} +Display all, active and/or removed response information which are +present in the NVA Responses Cache. Responses remain valid for the +default Response Lifetime (@pxref{VNC Defaults Configuration}) or +NVE-group Response Lifetime (@pxref{VNC NVE Group Configuration}.) +When Removal Responses are enabled (@pxref{General VNC Configuration}), +such responses are listed for the Response Lifetime. Specifying +@var{prefix} limits the output to the addresses that fall within +@var{prefix}. + +Response information is provided for each querying NVE, and includes +the response prefix, the prefix-associated registering NVE addresses, +the administrative cost, the provided response lifetime and the time +remaining before the information is to be removed or will become inactive. +@end deffn + +@deffn {Command} {show memory vnc} {} +Print the number of memory items allocated by the NVA. +@end deffn + +@node Example VNC and VNC-GW Configurations +@section Example VNC and VNC-GW Configurations + +@menu +* Mesh NVA Configuration:: +* Mesh NVA and VNC-GW Configuration:: +* VNC with Quagga Route Reflector Configuration:: +* VNC with Commercial Route Reflector Configuration:: +* VNC with Redundant Route Reflectors Configuration:: +@c * Interfacing VNC to an IGP:: +@end menu + +@node Mesh NVA Configuration +@subsection Mesh NVA Configuration + +This example includes three NVAs, nine NVEs, and two NVE groups. Note +that while not shown, a single physical device may support multiple +logical NVEs. @ref{fig:fig-vnc-mesh} shows @code{NVA 1} +(192.168.1.100), @code{NVA 2} (192.168.1.101), and @code{NVA 3} +(192.168.1.102), which are connected in a full mesh. Each is a +member of the autonomous system 64512. Each NVA provides VNC +services to three NVE clients in the 172.16.0.0/16 virtual-network +address range. The 172.16.0.0/16 address range is partitioned into +two NVE groups, @code{group1} (172.16.0.0/17) and @code{group2} +(172.16.128.0/17). + +Each NVE belongs to either NVE group @code{group1} or NVE group +@code{group2}. The NVEs @code{NVE 1}, @code{NVE 2}, @code{NVE +4}, @code{NVE 7}, and @code{NVE 8} are members of the NVE group +@code{group1}. The NVEs @code{NVE 3}, @code{NVE 5}, @code{NVE +6}, and @code{NVE 9} are members of the NVE group @code{group2}. + +Each NVA advertises NVE underlay-network IP addresses using the +Tunnel Encapsulation Attribute. + +@float Figure,fig:fig-vnc-mesh +@center @image{fig-vnc-mesh,400pt,,Three-way Mesh} +@caption{A three-way full mesh with three NVEs per NVA} +@end float + +@file{bgpd.conf} for @code{NVA 1} (192.168.1.100) +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.100 + + neighbor 192.168.1.101 remote-as 64512 + neighbor 192.168.1.102 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.101 activate + neighbor 192.168.1.102 activate + exit-address-family + + vnc defaults + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.0.0/17 + rt both 1000:1 + exit-vnc + + vnc nve-group group2 + prefix vn 172.16.128.0/17 + rt both 1000:2 + exit-vnc + +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 2} (192.168.1.101): +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.101 + + neighbor 192.168.1.100 remote-as 64512 + neighbor 192.168.1.102 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + neighbor 192.168.1.102 activate + exit-address-family + + vnc nve-group group1 + prefix vn 172.16.0.0/17 + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 3} (192.168.1.102): +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.102 + + neighbor 192.168.1.101 remote-as 64512 + neighbor 192.168.1.102 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + neighbor 192.168.1.101 activate + exit-address-family + + vnc defaults + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.128.0/17 + exit-vnc +exit +@end verbatim + +@node Mesh NVA and VNC-GW Configuration +@subsection Mesh NVA and VNC-GW Configuration + +This example includes two NVAs, each with two associated NVEs, and two +VNC-GWs, each supporting two CE routers physically attached to the four +NVEs. Note that this example is showing a more complex configuration +where VNC-GW is separated from normal NVA functions; it is equally +possible to simplify the configuration and combine NVA and VNC-GW +functions in a single quagga instance. + +@float Figure,fig:fig-vnc-gw +@center @image{fig-vnc-gw,400pt,,Quagga VNC Gateway} +@caption{Meshed NVEs and VNC-GWs} +@end float + +As shown in @ref{fig:fig-vnc-gw}, NVAs and VNC-GWs are connected in a +full iBGP mesh. The VNC-GWs each have two CEs configured as +route-reflector clients. Each client provides BGP updates with unicast +routes that the VNC-GW reflects to the other client. The VNC-GW also +imports these unicast routes into VPN routes to be shared with the other +VNC-GW and the two NVAs. This route importation is controlled with the +@code{vnc redistribute} statements shown in the configuration. +Similarly, registrations sent by NVEs via RFP to the NVAs are exported +by the VNC-GWs to the route-reflector clients as unicast routes. RFP +registrations exported this way have a next-hop address of the CE behind +the connected (registering) NVE. Exporting VNC routes as IPv4 unicast +is enabled with the @code{vnc export} command below. + +The configuration for @code{VNC-GW 1} is shown below. +@verbatim +router bgp 64512 + bgp router-id 192.168.1.101 + bgp cluster-id 1.2.3.4 + redistribute vpn + neighbor 192.168.1.102 remote-as 64512 + no neighbor 192.168.1.102 activate + neighbor 192.168.1.103 remote-as 64512 + no neighbor 192.168.1.103 activate + neighbor 192.168.1.104 remote-as 64512 + no neighbor 192.168.1.104 activate + neighbor 172.16.1.2 remote-as 64512 + neighbor 172.16.1.2 route-reflector-client + neighbor 172.16.2.2 remote-as 64512 + neighbor 172.16.2.2 route-reflector-client +! + address-family vpnv4 unicast + neighbor 192.168.1.102 activate + neighbor 192.168.1.103 activate + neighbor 192.168.1.104 activate + exit-address-family + vnc export bgp mode ce + vnc redistribute mode resolve-nve + vnc redistribute ipv4 bgp-direct + exit +@end verbatim + +Note that in the VNC-GW configuration, the neighboring VNC-GW and +NVAs each have a statement disabling the IPv4 unicast address family. +IPv4 unicast is on by default and this prevents the other VNC-GW and +NVAs from learning unicast routes advertised by the route-reflector clients. + +Configuration for @code{NVA 2}: +@verbatim +router bgp 64512 + bgp router-id 192.168.1.104 + neighbor 192.168.1.101 remote-as 64512 + no neighbor 192.168.1.101 activate + neighbor 192.168.1.102 remote-as 64512 + no neighbor 192.168.1.102 activate + neighbor 192.168.1.103 remote-as 64512 + no neighbor 192.168.1.103 activate + address-family vpnv4 unicast + neighbor 192.168.1.101 activate + neighbor 192.168.1.102 activate + neighbor 192.168.1.103 activate + exit-address-family + vnc defaults + response-lifetime 3600 + exit-vnc + vnc nve-group nve1 + prefix vn 172.16.1.1/32 + response-lifetime 3600 + rt both 1000:1 1000:2 + exit-vnc + vnc nve-group nve2 + prefix vn 172.16.2.1/32 + response-lifetime 3600 + rt both 1000:1 1000:2 + exit-vnc + exit +@end verbatim + +@c TBD make this its own example: +@c +@c @float Figure,fig:fig-vnc-gw-rr +@c @center @image{fig-vnc-gw-rr,400pt,,Quagga VNC Gateway with RR} +@c @end float +@c An NVA can also import unicast routes from BGP without advertising the +@c imported routes as VPN routes. Such imported routes, while not +@c distributed to other NVAs or VNC-GWs, are are available to NVEs via +@c RFP query messages sent to the NVA. @ref{fig:fig-vnc-gw-rr} +@c shows an example topology where unicast routes are imported into NVAs +@c from a Route Reflector. (@pxref{Route Reflector} for route reflector +@c configuration details.) The following three lines can be added to the +@c @code{NVA 1} and @code{NVA 2} configurations to import routes into VNC +@c for local VNC use: +@c +@c @verbatim +@c neighbor 192.168.1.105 remote-as 64512 +@c vnc redistribute mode plain +@c vnc redistribute ipv4 bgp-direct-to-nve-groups +@c @end verbatim + +@node VNC with Quagga Route Reflector Configuration +@subsection VNC with Quagga Route Reflector Configuration +A route reflector eliminates the need for a fully meshed NVA +network by acting as the hub between NVAs. +@ref{fig:fig-vnc-quagga-route-reflector} shows BGP route reflector +@code{BGP Route Reflector 1} (192.168.1.100) as a route reflector for +NVAs @code{NVA 2}(192.168.1.101) and @code{NVA 3} +(192.168.1.102). + +@float Figure,fig:fig-vnc-quagga-route-reflector +@center @image{fig-vnc-quagga-route-reflector,400pt,,Quagga Route Reflector} +@caption{Two NVAs and a BGP Route Reflector} +@end float + +@code{NVA 2} and @code{NVA 3} +advertise NVE underlay-network IP addresses using the Tunnel Encapsulation Attribute. +@code{BGP Route Reflector 1} ``reflects'' advertisements from +@code{NVA 2} to @code{NVA 3} and vice versa. + +As in the example of @ref{Mesh NVA Configuration}, there are two NVE groups. +The 172.16.0.0/16 address range is partitioned into two NVE groups, +@code{group1} (172.16.0.0/17) and @code{group2} (172.16.128.0/17). +The NVE @code{NVE 4}, @code{NVE 7}, and @code{NVE 8} are +members of the NVE group @code{group1}. The NVEs @code{NVE 5}, +@code{NVE 6}, and @code{NVE 9} are members of the NVE group +@code{group2}. + +@file{bgpd.conf} for @code{BGP Route Reflector 1} on 192.168.1.100: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.100 + + neighbor 192.168.1.101 remote-as 64512 + neighbor 192.168.1.101 port 7179 + neighbor 192.168.1.101 description iBGP-client-192-168-1-101 + neighbor 192.168.1.101 route-reflector-client + + neighbor 192.168.1.102 remote-as 64512 + neighbor 192.168.1.102 port 7179 + neighbor 192.168.1.102 description iBGP-client-192-168-1-102 + neighbor 192.168.1.102 route-reflector-client + + address-family vpnv4 + neighbor 192.168.1.101 activate + neighbor 192.168.1.102 activate + + neighbor 192.168.1.101 route-reflector-client + neighbor 192.168.1.102 route-reflector-client + exit-address-family + +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.101 + + neighbor 192.168.1.100 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + exit-address-family + + vnc nve-group group1 + prefix vn 172.16.0.0/17 + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 2} on 192.168.1.102: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.102 + + neighbor 192.168.1.100 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + exit-address-family + + vnc defaults + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.128.0/17 + exit-vnc +exit +@end verbatim + +While not shown, an NVA can also be configured as a route reflector. + +@node VNC with Commercial Route Reflector Configuration +@subsection VNC with Commercial Route Reflector Configuration +This example is identical to @ref{VNC with Quagga Route Reflector +Configuration} with the exception that the route reflector is a +commercial router. Only the +VNC-relevant configuration is provided. + +@float Figure,fig:fig-vnc-commercial-route-reflector +@center @image{fig-vnc-commercial-route-reflector,400pt,,Commercial Route Reflector} +@caption{Two NVAs with a commercial route reflector} +@end float + +@file{bgpd.conf} for BGP route reflector @code{Commercial Router} on 192.168.1.104: +@verbatim +version 8.5R1.13; +routing-options { + rib inet.0 { + static { + route 172.16.0.0/16 next-hop 192.168.1.104; + } + } + autonomous-system 64512; + resolution { + rib inet.3 { + resolution-ribs inet.0; + } + rib bgp.l3vpn.0 { + resolution-ribs inet.0; + } + } +} +protocols { + bgp { + advertise-inactive; + family inet { + labeled-unicast; + } + group 1 { + type internal; + advertise-inactive; + advertise-peer-as; + import h; + family inet { + unicast; + } + family inet-vpn { + unicast; + } + cluster 192.168.1.104; + neighbor 192.168.1.101; + neighbor 192.168.1.102; + } + } +} +policy-options { + policy-statement h { + from protocol bgp; + then { + as-path-prepend 64512; + accept; + } + } +} +@end verbatim + +@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.101 + + neighbor 192.168.1.100 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + exit-address-family + + vnc nve-group group1 + prefix vn 172.16.0.0/17 + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 3} on 192.168.1.102: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.102 + + neighbor 192.168.1.100 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + exit-address-family + + vnc defaults + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.128.0/17 + exit-vnc +exit +@end verbatim + +@node VNC with Redundant Route Reflectors Configuration +@subsection VNC with Redundant Route Reflectors Configuration +This example combines the previous two (@ref{VNC with Quagga Route +Reflector Configuration} and @ref{VNC with Commercial Route Reflector +Configuration}) into a redundant route reflector configuration. BGP +route reflectors @code{BGP Route Reflector 1} and @code{Commercial Router} +are the route reflectors for NVAs @code{NVA 2} and +@code{NVA 3}. The two NVAs have connections to both +route reflectors. + +@float Figure,fig:fig-vnc-redundant-route-reflectors +@center @image{fig-vnc-redundant-route-reflectors,400pt,,Redundant Route Reflectors} +@caption{Quagga-based NVA with redundant route reflectors} +@end float + +@file{bgpd.conf} for @code{Bgpd Route Reflector 1} on 192.168.1.100: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.100 + bgp cluster-id 192.168.1.100 + + neighbor 192.168.1.104 remote-as 64512 + + neighbor 192.168.1.101 remote-as 64512 + neighbor 192.168.1.101 description iBGP-client-192-168-1-101 + neighbor 192.168.1.101 route-reflector-client + + neighbor 192.168.1.102 remote-as 64512 + neighbor 192.168.1.102 description iBGP-client-192-168-1-102 + neighbor 192.168.1.102 route-reflector-client + + address-family vpnv4 + neighbor 192.168.1.101 activate + neighbor 192.168.1.102 activate + neighbor 192.168.1.104 activate + + neighbor 192.168.1.101 route-reflector-client + neighbor 192.168.1.102 route-reflector-client + exit-address-family +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.101 + + neighbor 192.168.1.100 remote-as 64512 + neighbor 192.168.1.104 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + neighbor 192.168.1.104 activate + exit-address-family + + vnc nve-group group1 + prefix vn 172.16.0.0/17 + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc +exit +@end verbatim + +@file{bgpd.conf} for @code{NVA 3} on 192.168.1.102: +@verbatim +router bgp 64512 + + bgp router-id 192.168.1.102 + + neighbor 192.168.1.100 remote-as 64512 + neighbor 192.168.1.104 remote-as 64512 + + address-family vpnv4 + neighbor 192.168.1.100 activate + neighbor 192.168.1.104 activate + exit-address-family + + vnc defaults + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.128.0/17 + exit-vnc +exit +@end verbatim + +@file{bgpd.conf} for the Commercial Router route reflector on +192.168.1.104: +@verbatim +routing-options { + rib inet.0 { + static { + route 172.16.0.0/16 next-hop 192.168.1.104; + } + } + autonomous-system 64512; + resolution { + rib inet.3 { + resolution-ribs inet.0; + } + rib bgp.l3vpn.0 { + resolution-ribs inet.0; + } + } +} +protocols { + bgp { + advertise-inactive; + family inet { + labeled-unicast; + } + group 1 { + type internal; + advertise-inactive; + advertise-peer-as; + import h; + family inet { + unicast; + } + family inet-vpn { + unicast; + } + cluster 192.168.1.104; + neighbor 192.168.1.101; + neighbor 192.168.1.102; + } + + group 2 { + type internal; + advertise-inactive; + advertise-peer-as; + import h; + family inet { + unicast; + } + family inet-vpn { + unicast; + } + neighbor 192.168.1.100; + } + + } +} +policy-options { + policy-statement h { + from protocol bgp; + then { + as-path-prepend 64512; + accept; + } + } +} +@end verbatim + +@node Release Notes +@section Release Notes + +@c A paragraph that introduces our release notes. + +@c outer list, one item per VNC release, items preceded by bullet +@itemize @bullet +@item + +@c @item +@end itemize + +@evenheading @thispage@|@|@thistitle +@oddheading @thischapter@|@|@thispage +@everyfooting + diff --git a/lib/command.c b/lib/command.c index bf8b1b1d33..fa3a782a91 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2538,6 +2538,9 @@ node_parent ( enum node_type node ) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VNC_DEFAULTS_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -2949,6 +2952,9 @@ DEFUN (config_exit, case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VNC_DEFAULTS_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: vty->node = BGP_NODE; @@ -3007,6 +3013,9 @@ DEFUN (config_end, case BGP_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VNC_DEFAULTS_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: case BGP_IPV4_NODE: @@ -3630,6 +3639,7 @@ DEFUN (config_logmsg, zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); if (message) XFREE(MTYPE_TMP, message); + return CMD_SUCCESS; } diff --git a/lib/command.h b/lib/command.h index 7a4c53a616..5932743115 100644 --- a/lib/command.h +++ b/lib/command.h @@ -77,6 +77,7 @@ enum node_type SERVICE_NODE, /* Service node. */ DEBUG_NODE, /* Debug node. */ VRF_DEBUG_NODE, /* Vrf Debug node. */ + DEBUG_VNC_NODE, /* Debug VNC node. */ AAA_NODE, /* AAA node. */ KEYCHAIN_NODE, /* Key-chain node. */ KEYCHAIN_KEY_NODE, /* Key-chain key node. */ @@ -96,6 +97,10 @@ enum node_type BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ + BGP_VNC_DEFAULTS_NODE, /* BGP VNC nve defaults */ + BGP_VNC_NVE_GROUP_NODE, /* BGP VNC nve group */ + BGP_VNC_L2_GROUP_NODE, /* BGP VNC L2 group */ + RFP_DEFAULTS_NODE, /* RFP defaults node */ OSPF_NODE, /* OSPF protocol mode */ OSPF6_NODE, /* OSPF protocol for IPv6 mode */ LDP_NODE, /* LDP protocol mode */ diff --git a/lib/log.c b/lib/log.c index fb7b33dcf9..49c69efc8a 100644 --- a/lib/log.c +++ b/lib/log.c @@ -55,6 +55,7 @@ const char *zlog_proto_names[] = "ISIS", "PIM", "MASC", + "RFP", NULL, }; @@ -258,6 +259,44 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args) errno = original_errno; } +int +vzlog_test (struct zlog *zl, int priority) +{ + /* If zlog is not specified, use default one. */ + if (zl == NULL) + zl = zlog_default; + + /* When zlog_default is also NULL, use stderr for logging. */ + if (zl == NULL) + { + return 1; + } + + /* Syslog output */ + if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) + { + return 1; + } + + /* File output. */ + if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) + { + return 1; + } + + /* stdout output. */ + if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) + { + return 1; + } + + /* Terminal monitor. */ + if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) + return 1; + + return 0; +} + static char * str_append(char *dst, int len, const char *src) { @@ -680,6 +719,7 @@ _zlog_assert_failed (const char *assertion, const char *file, assertion,file,line,(function ? function : "?")); zlog_backtrace(LOG_CRIT); zlog_thread_info(LOG_CRIT); + log_memstats_stderr ("log"); abort(); } @@ -941,6 +981,10 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB), DESC_ENTRY (ZEBRA_MPLS_LABELS_ADD), DESC_ENTRY (ZEBRA_MPLS_LABELS_DELETE), + DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_ADD), + DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_DELETE), + DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_ADD), + DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_DELETE), }; #undef DESC_ENTRY @@ -1029,6 +1073,10 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_BGP; else if (strncmp (s, "ta", 2) == 0) return ZEBRA_ROUTE_TABLE; + else if (strncmp (s, "v", 1) == 0) + return ZEBRA_ROUTE_VNC; + else if (strncmp (s, "vd", 1) == 0) + return ZEBRA_ROUTE_VNC_DIRECT; } if (afi == AFI_IP6) { @@ -1048,6 +1096,10 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_BGP; else if (strncmp (s, "ta", 2) == 0) return ZEBRA_ROUTE_TABLE; + else if (strncmp (s, "v", 1) == 0) + return ZEBRA_ROUTE_VNC; + else if (strncmp (s, "vd", 1) == 0) + return ZEBRA_ROUTE_VNC_DIRECT; } return -1; } diff --git a/lib/log.h b/lib/log.h index cb8fac78c3..91cab3f96a 100644 --- a/lib/log.h +++ b/lib/log.h @@ -53,6 +53,7 @@ typedef enum ZLOG_OSPF6, ZLOG_LDP, ZLOG_ISIS, + ZLOG_RFP, ZLOG_PIM, ZLOG_MASC } zlog_proto_t; @@ -184,6 +185,10 @@ extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */, extern void zlog_hexdump(const void *mem, unsigned int len); + +extern int +vzlog_test (struct zlog *zl, int priority); + /* structure useful for avoiding repeated rendering of the same timestamp */ struct timestamp_control { size_t len; /* length of rendered timestamp */ diff --git a/lib/prefix.c b/lib/prefix.c index 34bb1a493a..112dae5822 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -240,6 +240,8 @@ afi2str(afi_t afi) return "IPv6"; case AFI_ETHER: return "ethernet"; + case AFI_MAX: + return "bad-value"; default: break; } diff --git a/lib/route_types.txt b/lib/route_types.txt index 0ac442d85d..698e8b9e5c 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -61,6 +61,16 @@ ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, "Table" ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, "LDP" +#vnc when sent to zebra +ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, "VNC" +# vnc when sent to bgp +ZEBRA_ROUTE_VNC_DIRECT, vpn, NULL, 'V', 1, 1, "VPN" +# vnc when sent to bgp (remote next hop?) +ZEBRA_ROUTE_VNC_DIRECT_RH, vpn-rh, NULL, 'V', 0, 0, "VPN" +# bgp unicast -> vnc +ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct" +# bgp unicast -> vnc +ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" ## help strings ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only" @@ -75,6 +85,8 @@ ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" +ZEBRA_ROUTE_VNC, "Virtual Network Control (VNC)" ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" ZEBRA_ROUTE_TABLE, "Non-main Kernel Routing Table" ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)" +ZEBRA_ROUTE_VNC_DIRECT, "VPN routes(VPN)" diff --git a/lib/vty.c b/lib/vty.c index 3f13500449..14ef7e6e4a 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -747,6 +747,9 @@ vty_end_config (struct vty *vty) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VNC_DEFAULTS_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: diff --git a/lib/zebra.h b/lib/zebra.h index 599daa3509..e625b4051b 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -427,6 +427,10 @@ typedef enum { ZEBRA_INTERFACE_LINK_PARAMS, ZEBRA_MPLS_LABELS_ADD, ZEBRA_MPLS_LABELS_DELETE, + ZEBRA_IPV4_NEXTHOP_ADD, + ZEBRA_IPV4_NEXTHOP_DELETE, + ZEBRA_IPV6_NEXTHOP_ADD, + ZEBRA_IPV6_NEXTHOP_DELETE, } zebra_message_types_t; /* Marker value used in new Zserv, in the byte location corresponding diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index 40c30e40ae..4c35a8bf68 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -24,6 +24,7 @@ %{!?vty_group: %global vty_group quaggavty } %{!?with_fpm: %global with_fpm 0 } %{!?with_watchquagga: %global with_watchquagga 1 } +%{!?with_bgp_vnc: %global with_bgp_vnc 0 } # path defines %define _sysconfdir /etc/quagga @@ -235,6 +236,11 @@ developing OSPF-API and quagga applications. --enable-watchquagga \ %else --disable-watchquagga \ +%endif +%if %{with_bgp_vnc} + --enable-bgp-vnc \ +%else + --disable-bgp-vnc \ %endif --enable-gcc-rdynamic \ --enable-isisd=yes \ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index ad39f75d68..23f2adf82c 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -248,6 +248,7 @@ static struct { {ZEBRA_ROUTE_STATIC, 1, "static"}, {ZEBRA_ROUTE_OSPF, 1, "ospf"}, {ZEBRA_ROUTE_BGP, 2, "bgp"}, + {ZEBRA_ROUTE_VNC, 1, "vnc"}, {0, 0, NULL} }; diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 637e8f5a27..c4ed0c52c6 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -255,6 +255,7 @@ static struct { {ZEBRA_ROUTE_STATIC, 1, "static"}, {ZEBRA_ROUTE_OSPF6, 1, "ospf6"}, {ZEBRA_ROUTE_BGP, 2, "bgp"}, + {ZEBRA_ROUTE_VNC, 1, "vnc"}, {0, 0, NULL} }; diff --git a/tests/Makefile.am b/tests/Makefile.am index 16c9e4c3db..0014fa34d1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,6 +25,12 @@ else TESTS_BGPD = endif +if ENABLE_BGP_VNC +BGP_VNC_RFP_LIB=@top_srcdir@/$(LIBRFP)/librfp.a +else +BGP_VNC_RFP_LIB = +endif + check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \ testprivs teststream testchecksum tabletest testnexthopiter \ testcommands test-timer-correctness test-timer-performance \ @@ -77,12 +83,12 @@ teststream_LDADD = ../lib/libzebra.la @LIBCAP@ heavy_LDADD = ../lib/libzebra.la @LIBCAP@ -lm heavywq_LDADD = ../lib/libzebra.la @LIBCAP@ -lm heavythread_LDADD = ../lib/libzebra.la @LIBCAP@ -lm -aspathtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm -testbgpcap_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm -ecommtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm -testbgpmpattr_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm +aspathtest_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm +testbgpcap_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm +ecommtest_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm +testbgpmpattr_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@ -testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm +testbgpmpath_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@ testcommands_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 71fb0ae53e..58ffdfca26 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -1,6 +1,23 @@ ## Process this file with Automake to create Makefile.in -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib + +if ENABLE_BGP_VNC +BGP_VNC_RFP_SRCDIR = @top_srcdir@/@LIBRFP@ +BGP_VNC_RFP_INCDIR = -I$(BGP_VNC_RFP_SRCDIR) +BGP_VNC_RFP_SRC = $(BGP_VNC_RFP_SRCDIR)/*.c +BGP_VNC_RFAPI_SRCDIR = @top_srcdir@/bgpd/rfapi +BGP_VNC_RFAPI_INCDIR = -I$(BGP_VNC_RFAPI_SRCDIR) -I$(top_srcdir)/bgpd +BGP_VNC_RFAPI_SRC = $(BGP_VNC_RFAPI_SRCDIR)/*.c +else +BGP_VNC_RFP_INCDIR = +BGP_VNC_RFP_SRCDIR = +BGP_VNC_RFP_SRC = +BGP_VNC_RFAPI_INCDIR = +BGP_VNC_RFAPI_SRCDIR = +BGP_VNC_RFAPI_SRC = +endif +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + $(BGP_VNC_RFAPI_INCDIR) $(BGP_VNC_RFP_INCDIR) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" LIBS = @LIBS@ @CURSES@ @LIBPAM@ @@ -68,7 +85,8 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/zebra/zebra_routemap.c \ $(top_srcdir)/zebra/zebra_fpm.c \ $(top_srcdir)/zebra/zebra_ptm.c \ - $(top_srcdir)/zebra/zebra_mpls_vty.c + $(top_srcdir)/zebra/zebra_mpls_vty.c \ + $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC) vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index b4884b9687..7c5dec9a54 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -65,6 +65,9 @@ $ignore{'"address-family encapv6"'} = "ignore"; $ignore{'"address-family vpnv6"'} = "ignore"; $ignore{'"address-family vpnv6 unicast"'} = "ignore"; $ignore{'"exit-address-family"'} = "ignore"; +$ignore{'"vnc defaults"'} = "ignore"; +$ignore{'"vnc nve-group NAME"'} = "ignore"; +$ignore{'"exit-vnc"'} = "ignore"; $ignore{'"key chain WORD"'} = "ignore"; $ignore{'"key <0-2147483647>"'} = "ignore"; $ignore{'"route-map WORD (deny|permit) <1-65535>"'} = "ignore"; @@ -80,7 +83,7 @@ my $cli_stomp = 0; foreach (@ARGV) { $file = $_; - open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/isisd/topology @CPPFLAGS@ $file |"); + open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/isisd/topology -I@top_srcdir@/bgpd -I@top_srcdir@/@LIBRFP@ -I@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $file |"); local $/; undef $/; $line = ; close (FH); @@ -159,7 +162,10 @@ foreach (@ARGV) { elsif ($file =~ /lib\/vty\.c$/) { $protocol = "VTYSH_ALL"; } - else { + elsif ($file =~ /librfp\/.*\.c$/ || $file =~ /rfapi\/.*\.c$/) { + $protocol = "VTYSH_BGPD"; + } + else { ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/); $protocol = "VTYSH_" . uc $protocol; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 9dc2eca0b7..4f64283a0e 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -375,6 +375,12 @@ vtysh_execute_func (const char *line, int pager) { vtysh_execute("exit-address-family"); } + else if ((saved_node == BGP_VNC_DEFAULTS_NODE + || saved_node == BGP_VNC_NVE_GROUP_NODE + || saved_node == BGP_VNC_L2_GROUP_NODE) && (tried == 1)) + { + vtysh_execute("exit-vnc"); + } else if ((saved_node == KEYCHAIN_KEY_NODE) && (tried == 1)) { vtysh_execute("exit"); @@ -1015,6 +1021,24 @@ static struct cmd_node bgp_ipv6m_node = "%s(config-router-af)# " }; +static struct cmd_node bgp_vnc_defaults_node = +{ + BGP_VNC_DEFAULTS_NODE, + "%s(config-router-vnc-defaults)# " +}; + +static struct cmd_node bgp_vnc_nve_group_node = +{ + BGP_VNC_NVE_GROUP_NODE, + "%s(config-router-vnc-nve-group)# " +}; + +static struct cmd_node bgp_vnc_l2_group_node = +{ + BGP_VNC_L2_GROUP_NODE, + "%s(config-router-vnc-l2-group)# " +}; + static struct cmd_node ospf_node = { OSPF_NODE, @@ -1289,6 +1313,41 @@ DEFUNSH (VTYSH_BGPD, return CMD_SUCCESS; } +DEFUNSH (VTYSH_BGPD, + vnc_defaults, + vnc_defaults_cmd, + "vnc defaults", + "VNC/RFP related configuration\n" + "Configure default NVE group\n") +{ + vty->node = BGP_VNC_DEFAULTS_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + vnc_nve_group, + vnc_nve_group_cmd, + "vnc nve-group NAME", + "VNC/RFP related configuration\n" + "Configure a NVE group\n" + "Group name\n") +{ + vty->node = BGP_VNC_NVE_GROUP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + vnc_l2_group, + vnc_l2_group_cmd, + "vnc l2-group NAME", + "VNC/RFP related configuration\n" + "Configure a L2 group\n" + "Group name\n") +{ + vty->node = BGP_VNC_L2_GROUP_NODE; + return CMD_SUCCESS; +} + DEFUNSH (VTYSH_RIPD, key_chain, key_chain_cmd, @@ -1553,6 +1612,9 @@ vtysh_exit (struct vty *vty) case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_VNC_DEFAULTS_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: vty->node = BGP_NODE; break; case LDP_IPV4_NODE: @@ -1612,6 +1674,19 @@ DEFUNSH (VTYSH_BGPD, return CMD_SUCCESS; } +DEFUNSH (VTYSH_BGPD, + exit_vnc_config, + exit_vnc_config_cmd, + "exit-vnc", + "Exit from VNC configuration mode\n") +{ + if (vty->node == BGP_VNC_DEFAULTS_NODE + || vty->node == BGP_VNC_NVE_GROUP_NODE + || vty->node == BGP_VNC_L2_GROUP_NODE) + vty->node = BGP_NODE; + return CMD_SUCCESS; +} + DEFUNSH (VTYSH_ZEBRA, vtysh_exit_zebra, vtysh_exit_zebra_cmd, @@ -3071,6 +3146,11 @@ vtysh_init_vty (void) /* #ifdef HAVE_IPV6 */ install_node (&bgp_ipv6_node, NULL); install_node (&bgp_ipv6m_node, NULL); +/* #endif */ +/*#if ENABLE_BGP_VNC */ + install_node (&bgp_vnc_defaults_node, NULL); + install_node (&bgp_vnc_nve_group_node, NULL); + install_node (&bgp_vnc_l2_group_node, NULL); /* #endif */ install_node (&ospf_node, NULL); /* #ifdef HAVE_IPV6 */ @@ -3108,6 +3188,11 @@ vtysh_init_vty (void) vtysh_install_default (BGP_IPV4M_NODE); vtysh_install_default (BGP_IPV6_NODE); vtysh_install_default (BGP_IPV6M_NODE); + /* #if ENABLE_BGP_VNC */ + vtysh_install_default (BGP_VNC_DEFAULTS_NODE); + vtysh_install_default (BGP_VNC_NVE_GROUP_NODE); + vtysh_install_default (BGP_VNC_L2_GROUP_NODE); + /* #endif */ vtysh_install_default (OSPF_NODE); vtysh_install_default (RIPNG_NODE); vtysh_install_default (OSPF6_NODE); @@ -3174,6 +3259,12 @@ vtysh_init_vty (void) install_element (BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_quit_bgpd_cmd); install_element (ISIS_NODE, &vtysh_exit_isisd_cmd); install_element (ISIS_NODE, &vtysh_quit_isisd_cmd); install_element (KEYCHAIN_NODE, &vtysh_exit_ripd_cmd); @@ -3208,6 +3299,9 @@ vtysh_init_vty (void) install_element (BGP_ENCAPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6M_NODE, &vtysh_end_all_cmd); + install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd); install_element (ISIS_NODE, &vtysh_end_all_cmd); install_element (KEYCHAIN_NODE, &vtysh_end_all_cmd); install_element (KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd); @@ -3256,6 +3350,8 @@ vtysh_init_vty (void) install_element (BGP_NODE, &address_family_vpnv6_unicast_cmd); install_element (BGP_NODE, &address_family_encap_cmd); install_element (BGP_NODE, &address_family_encapv6_cmd); + install_element (BGP_NODE, &vnc_defaults_cmd); + install_element (BGP_NODE, &vnc_nve_group_cmd); install_element (BGP_NODE, &address_family_ipv4_unicast_cmd); install_element (BGP_NODE, &address_family_ipv4_multicast_cmd); #ifdef HAVE_IPV6 @@ -3271,6 +3367,11 @@ vtysh_init_vty (void) install_element (BGP_IPV4M_NODE, &exit_address_family_cmd); install_element (BGP_IPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV6M_NODE, &exit_address_family_cmd); + + install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd); + install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_config_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &exit_vnc_config_cmd); + install_element (CONFIG_NODE, &key_chain_cmd); install_element (CONFIG_NODE, &route_map_cmd); install_element (CONFIG_NODE, &vtysh_line_vty_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index bf6215c401..eb58497310 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -173,10 +173,36 @@ vtysh_config_parse_line (const char *line) /* Store line to current configuration. */ if (config) { - if (config->index == RMAP_NODE || + if (strncmp (line, " address-family vpnv4", + strlen (" address-family vpnv4")) == 0) + config = config_get (BGP_VPNV4_NODE, line); + else if (strncmp (line, " address-family vpn6", + strlen (" address-family vpn6")) == 0) + config = config_get (BGP_VPNV6_NODE, line); + else if (strncmp (line, " address-family encapv6", + strlen (" address-family encapv6")) == 0) + config = config_get (BGP_ENCAPV6_NODE, line); + else if (strncmp (line, " address-family encap", + strlen (" address-family encap")) == 0) + config = config_get (BGP_ENCAP_NODE, line); + else if (strncmp (line, " address-family ipv4 multicast", + strlen (" address-family ipv4 multicast")) == 0) + config = config_get (BGP_IPV4M_NODE, line); + else if (strncmp (line, " address-family ipv6", + strlen (" address-family ipv6")) == 0) + config = config_get (BGP_IPV6_NODE, line); + else if (strncmp (line, " vnc defaults", + strlen (" vnc defaults")) == 0) + config = config_get (BGP_VNC_DEFAULTS_NODE, line); + else if (strncmp (line, " vnc nve-group", + strlen (" vnc nve-group")) == 0) + config = config_get (BGP_VNC_NVE_GROUP_NODE, line); + else if (strncmp (line, " vnc l2-group", + strlen (" vnc l2-group")) == 0) + config = config_get (BGP_VNC_L2_GROUP_NODE, line); + else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE || config->index == NS_NODE || - config->index == VRF_NODE || config->index == VTY_NODE) config_add_line_uniq (config->line, line); else diff --git a/zebra/zserv.c b/zebra/zserv.c index 25f028af69..135cd88a10 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1942,6 +1942,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD: zread_ipv4_route_ipv6_nexthop_add (client, length, zvrf); break; + case ZEBRA_IPV4_NEXTHOP_ADD: + zread_ipv4_add(client, length, zvrf); /* LB: r1.0 merge - id was 1 */ + break; + case ZEBRA_IPV4_NEXTHOP_DELETE: + zread_ipv4_delete(client, length, zvrf); /* LB: r1.0 merge - id was 1 */ + break; case ZEBRA_IPV6_ROUTE_ADD: zread_ipv6_add (client, length, zvrf); break; From f8b6f499121f83eafc13b0f7214825a0f46655aa Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Wed, 28 Sep 2016 18:03:43 -0400 Subject: [PATCH 090/136] vnc: use directories in includes (request from Martin W.) --- bgpd/bgp_attr.c | 2 +- bgpd/bgp_encap.c | 2 +- bgpd/bgp_main.c | 2 +- bgpd/bgp_mplsvpn.c | 2 +- bgpd/bgp_route.c | 6 +-- bgpd/bgp_routemap.c | 2 +- bgpd/bgp_zebra.c | 4 +- bgpd/bgpd.c | 4 +- bgpd/rfapi/bgp_rfapi_cfg.c | 38 +++++++------- bgpd/rfapi/bgp_rfapi_cfg.h | 4 +- bgpd/rfapi/rfapi.c | 70 ++++++++++++------------- bgpd/rfapi/rfapi.h | 10 ++-- bgpd/rfapi/rfapi_ap.c | 60 ++++++++++----------- bgpd/rfapi/rfapi_ap.h | 28 +++++----- bgpd/rfapi/rfapi_backend.h | 4 +- bgpd/rfapi/rfapi_descriptor_rfp_utils.c | 20 +++---- bgpd/rfapi/rfapi_encap_tlv.c | 30 +++++------ bgpd/rfapi/rfapi_import.c | 60 ++++++++++----------- bgpd/rfapi/rfapi_import.h | 2 +- bgpd/rfapi/rfapi_monitor.c | 38 +++++++------- bgpd/rfapi/rfapi_monitor.h | 6 +-- bgpd/rfapi/rfapi_nve_addr.c | 28 +++++----- bgpd/rfapi/rfapi_private.h | 8 +-- bgpd/rfapi/rfapi_rib.c | 44 ++++++++-------- bgpd/rfapi/rfapi_vty.c | 56 ++++++++++---------- bgpd/rfapi/rfapi_vty.h | 2 +- bgpd/rfapi/vnc_debug.c | 16 +++--- bgpd/rfapi/vnc_export_bgp.c | 48 ++++++++--------- bgpd/rfapi/vnc_export_bgp.h | 8 +-- bgpd/rfapi/vnc_export_bgp_p.h | 8 +-- bgpd/rfapi/vnc_export_table.c | 20 +++---- bgpd/rfapi/vnc_export_table.h | 8 +-- bgpd/rfapi/vnc_import_bgp.c | 46 ++++++++-------- bgpd/rfapi/vnc_import_bgp.h | 8 +-- bgpd/rfapi/vnc_import_bgp_p.h | 8 +-- bgpd/rfapi/vnc_zebra.c | 40 +++++++------- bgpd/rfapi/vnc_zebra.h | 2 +- bgpd/rfp-example/librfp/rfp.h | 2 +- bgpd/rfp-example/librfp/rfp_example.c | 4 +- bgpd/rfp-example/librfp/rfp_internal.h | 4 +- 40 files changed, 377 insertions(+), 377 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index cd92aec230..f5daf607e6 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -44,7 +44,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_encap_types.h" #if ENABLE_BGP_VNC -# include "bgp_rfapi_cfg.h" +# include "bgpd/rfapi/bgp_rfapi_cfg.h" # include "bgp_encap_types.h" # include "bgp_vnc_types.h" #endif diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c index 2cfb510295..136bab13b6 100644 --- a/bgpd/bgp_encap.c +++ b/bgpd/bgp_encap.c @@ -46,7 +46,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_encap.h" #if ENABLE_BGP_VNC -#include "rfapi_backend.h" +#include "bgpd/rfapi/rfapi_backend.h" #endif static void diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 9ee8838fdf..3aa16b2632 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -55,7 +55,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_zebra.h" #ifdef ENABLE_BGP_VNC -#include "rfapi_backend.h" +#include "bgpd/rfapi/rfapi_backend.h" #endif /* bgpd options, we use GNU getopt library. */ diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 991a92b850..e3e28c7a0f 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -36,7 +36,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_mplsvpn.h" #if ENABLE_BGP_VNC -#include "rfapi_backend.h" +#include "bgpd/rfapi/rfapi_backend.h" #endif u_int16_t diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 6c97dc333b..bace5ab31e 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -63,9 +63,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" #if ENABLE_BGP_VNC -#include "rfapi_backend.h" -#include "vnc_import_bgp.h" -#include "vnc_export_bgp.h" +#include "bgpd/rfapi/rfapi_backend.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp.h" #endif /* Extern from bgp_dump.c */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index efed71ebe9..beab1969b6 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -59,7 +59,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_debug.h" #if ENABLE_BGP_VNC -# include "bgp_rfapi_cfg.h" +# include "bgpd/rfapi/bgp_rfapi_cfg.h" #endif /* Memo of route-map commands. diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index cb24154061..d524c41a28 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -47,8 +47,8 @@ Boston, MA 02111-1307, USA. */ #include "bgpd/bgp_nht.h" #include "bgpd/bgp_bfd.h" #if ENABLE_BGP_VNC -# include "rfapi_backend.h" -# include "vnc_export_bgp.h" +# include "bgpd/rfapi/rfapi_backend.h" +# include "bgpd/rfapi/vnc_export_bgp.h" #endif /* All information about zebra. */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 291483a866..c2af6852f9 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -64,8 +64,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_encap.h" #if ENABLE_BGP_VNC -#include "bgp_rfapi_cfg.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_backend.h" #endif #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_network.h" diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 57fb04d232..2a8ba3dbdd 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -20,15 +20,15 @@ */ -#include +#include "lib/zebra.h" -#include "command.h" -#include "prefix.h" -#include "memory.h" -#include "linklist.h" -#include "table.h" -#include "plist.h" -#include "routemap.h" +#include "lib/command.h" +#include "lib/prefix.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/table.h" +#include "lib/plist.h" +#include "lib/routemap.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -36,17 +36,17 @@ #include "bgpd/bgp_route.h" #include "bgpd/bgp_ecommunity.h" -#include "rfapi.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi_backend.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "vnc_zebra.h" -#include "vnc_export_bgp.h" -#include "vnc_export_bgp_p.h" -#include "rfapi_vty.h" -#include "vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_backend.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" #if ENABLE_BGP_VNC diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h index cd3a28f600..12cedd56f2 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.h +++ b/bgpd/rfapi/bgp_rfapi_cfg.h @@ -22,8 +22,8 @@ #ifndef _QUAGGA_BGP_RFAPI_CFG_H #define _QUAGGA_BGP_RFAPI_CFG_H -#include "table.h" -#include "routemap.h" +#include "lib/table.h" +#include "lib/routemap.h" #if ENABLE_BGP_VNC #include "rfapi.h" diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 3360ad89ce..e33c33aa76 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -22,44 +22,44 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "routemap.h" -#include "log.h" -#include "linklist.h" -#include "command.h" -#include "stream.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" +#include "lib/stream.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_attr.h" -#include "bgp_mplsvpn.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" -#include "bgp_route.h" -#include "bgp_aspath.h" -#include "bgp_advertise.h" -#include "bgp_vnc_types.h" -#include "bgp_zebra.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_vnc_types.h" +#include "bgpd/bgp_zebra.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_vty.h" -#include "vnc_export_bgp.h" -#include "vnc_export_bgp_p.h" -#include "vnc_zebra.h" -#include "vnc_import_bgp.h" -#include "rfapi_rib.h" -#include "rfapi_ap.h" -#include "rfapi_encap_tlv.h" -#include "vnc_debug.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_ap.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" #ifdef HAVE_GLIBC_BACKTRACE /* for backtrace and friends */ @@ -3884,7 +3884,7 @@ DEFUN (debug_rfapi_response_omit_self, #ifdef RFAPI_DEBUG_SKIPLIST_CLI -#include "skiplist.h" +#include "lib/skiplist.h" DEFUN (skiplist_test_cli, skiplist_test_cli_cmd, "skiplist test", diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h index 6e1baa60c1..7d108432ae 100644 --- a/bgpd/rfapi/rfapi.h +++ b/bgpd/rfapi/rfapi.h @@ -26,11 +26,11 @@ #include #include -#include -#include "vty.h" -#include "prefix.h" -#include "../bgpd.h" -#include "../bgp_encap_types.h" +#include "lib/zebra.h" +#include "lib/vty.h" +#include "lib/prefix.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_encap_types.h" /* probably ought to have a field-specific define in config.h */ # ifndef s6_addr32 /* for solaris/bsd */ diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c index 94b6dea8dc..b0d5ab35c9 100644 --- a/bgpd/rfapi/rfapi_ap.c +++ b/bgpd/rfapi/rfapi_ap.c @@ -21,41 +21,41 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "routemap.h" -#include "log.h" -#include "linklist.h" -#include "command.h" -#include "stream.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" +#include "lib/stream.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_attr.h" -#include "bgp_mplsvpn.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" -#include "bgp_route.h" -#include "bgp_aspath.h" -#include "bgp_advertise.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_advertise.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_vty.h" -#include "vnc_export_bgp.h" -#include "vnc_export_bgp_p.h" -#include "vnc_zebra.h" -#include "vnc_import_bgp.h" -#include "rfapi_rib.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_rib.h" -#include "rfapi_ap.h" +#include "bgpd/rfapi/rfapi_ap.h" /* * Per-NVE Advertised prefixes diff --git a/bgpd/rfapi/rfapi_ap.h b/bgpd/rfapi/rfapi_ap.h index 3bb08a4b17..f2805f49cb 100644 --- a/bgpd/rfapi/rfapi_ap.h +++ b/bgpd/rfapi/rfapi_ap.h @@ -25,26 +25,26 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "routemap.h" -#include "log.h" -#include "linklist.h" -#include "command.h" -#include "stream.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" +#include "lib/stream.h" -#include "bgpd.h" +#include "bgpd/bgpd.h" #include "bgp_rfapi_cfg.h" #include "rfapi.h" #include "rfapi_backend.h" -#include "bgp_route.h" -#include "bgp_aspath.h" -#include "bgp_advertise.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_advertise.h" #include "rfapi_import.h" #include "rfapi_private.h" diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h index 451f5c20c7..788ec73751 100644 --- a/bgpd/rfapi/rfapi_backend.h +++ b/bgpd/rfapi/rfapi_backend.h @@ -24,8 +24,8 @@ #if ENABLE_BGP_VNC -#include "bgp_route.h" -#include "bgp_nexthop.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_nexthop.h" extern void rfapi_init (void); extern void vnc_zebra_init (struct thread_master *master); diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c index c2d11820e1..b2a8689881 100644 --- a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c +++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c @@ -22,18 +22,18 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "log.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" -#include "bgpd.h" +#include "bgpd/bgpd.h" -#include "rfapi.h" -#include "rfapi_private.h" -#include "rfapi_descriptor_rfp_utils.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_descriptor_rfp_utils.h" void * diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c index 17fee2cc0e..0a5962ccb6 100644 --- a/bgpd/rfapi/rfapi_encap_tlv.c +++ b/bgpd/rfapi/rfapi_encap_tlv.c @@ -17,25 +17,25 @@ * */ -#include +#include "lib/zebra.h" -#include -#include -#include -#include +#include "lib/memory.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" -#include "bgpd.h" -#include "bgp_attr.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" -#include "bgp_encap_types.h" -#include "bgp_encap_tlv.h" +#include "bgpd/bgp_encap_types.h" +#include "bgpd/bgp_encap_tlv.h" -#include "rfapi.h" -#include "rfapi_encap_tlv.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_vty.h" -#include "bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" static void rfapi_add_endpoint_address_to_subtlv ( diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 63c1079c44..110182a1fc 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -26,38 +26,38 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "log.h" -#include "skiplist.h" -#include "thread.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" +#include "lib/skiplist.h" +#include "lib/thread.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_attr.h" -#include "bgp_route.h" -#include "bgp_mplsvpn.h" /* prefix_rd2str() */ -#include "bgp_vnc_types.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" /* prefix_rd2str() */ +#include "bgpd/bgp_vnc_types.h" -#include "rfapi.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi_backend.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_nve_addr.h" -#include "rfapi_vty.h" -#include "vnc_export_bgp.h" -#include "vnc_export_bgp_p.h" -#include "vnc_zebra.h" -#include "vnc_import_bgp.h" -#include "vnc_import_bgp_p.h" -#include "rfapi_rib.h" -#include "rfapi_encap_tlv.h" -#include "vnc_debug.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_backend.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_nve_addr.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_import_bgp_p.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" #ifdef HAVE_GLIBC_BACKTRACE /* for backtrace and friends */ diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h index 9e88b52f52..3a1ae3573e 100644 --- a/bgpd/rfapi/rfapi_import.h +++ b/bgpd/rfapi/rfapi_import.h @@ -27,7 +27,7 @@ #ifndef QUAGGA_HGP_RFAPI_IMPORT_H #define QUAGGA_HGP_RFAPI_IMPORT_H -#include "thread.h" +#include "lib/thread.h" /* * These are per-rt-import-list diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index 4677d1fa68..216b45eec8 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -27,28 +27,28 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "log.h" -#include "table.h" -#include "skiplist.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" +#include "lib/table.h" +#include "lib/skiplist.h" -#include "bgpd.h" +#include "bgpd/bgpd.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" -#include "rfapi.h" -#include "rfapi_import.h" -#include "vnc_import_bgp.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_vty.h" -#include "rfapi_rib.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/rfapi_rib.h" #define DEBUG_L2_EXTRA 0 #define DEBUG_DUP_CHECK 0 diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h index b08a6e60c6..be04b0f09d 100644 --- a/bgpd/rfapi/rfapi_monitor.h +++ b/bgpd/rfapi/rfapi_monitor.h @@ -22,9 +22,9 @@ #ifndef QUAGGA_HGP_RFAPI_MONITOR_H #define QUAGGA_HGP_RFAPI_MONITOR_H -#include "zebra.h" -#include "prefix.h" -#include "table.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" /* * These get attached to the nodes in an import table (using "aggregate" ptr) diff --git a/bgpd/rfapi/rfapi_nve_addr.c b/bgpd/rfapi/rfapi_nve_addr.c index 835c2d2fae..ad34ff26c8 100644 --- a/bgpd/rfapi/rfapi_nve_addr.c +++ b/bgpd/rfapi/rfapi_nve_addr.c @@ -20,24 +20,24 @@ */ -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "skiplist.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/skiplist.h" -#include "bgpd.h" +#include "bgpd/bgpd.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_nve_addr.h" -#include "rfapi_vty.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_nve_addr.h" +#include "bgpd/rfapi/rfapi_vty.h" #define DEBUG_NVE_ADDR 0 diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h index aca034b572..33390c4f55 100644 --- a/bgpd/rfapi/rfapi_private.h +++ b/bgpd/rfapi/rfapi_private.h @@ -26,11 +26,11 @@ #ifndef _QUAGGA_BGP_RFAPI_PRIVATE_H #define _QUAGGA_BGP_RFAPI_PRIVATE_H -#include "linklist.h" -#include "skiplist.h" -#include "workqueue.h" +#include "lib/linklist.h" +#include "lib/skiplist.h" +#include "lib/workqueue.h" -#include "bgp_attr.h" +#include "bgpd/bgp_attr.h" #include "rfapi.h" diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 70acc14d3e..48c2d422d9 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -26,30 +26,30 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "log.h" -#include "skiplist.h" -#include "workqueue.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" +#include "lib/skiplist.h" +#include "lib/workqueue.h" -#include "bgpd.h" -#include "bgp_route.h" -#include "bgp_ecommunity.h" -#include "bgp_mplsvpn.h" -#include "bgp_vnc_types.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_vnc_types.h" -#include "rfapi.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_vty.h" -#include "vnc_import_bgp.h" -#include "rfapi_rib.h" -#include "rfapi_monitor.h" -#include "rfapi_encap_tlv.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" #define DEBUG_PROCESS_PENDING_NODE 0 #define DEBUG_PENDING_DELETE_ROUTE 0 diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 315bac4fe6..0cb6f31ac1 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -22,38 +22,38 @@ #include -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "memory.h" -#include "routemap.h" -#include "log.h" -#include "linklist.h" -#include "command.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_attr.h" -#include "bgp_mplsvpn.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" -#include "bgp_route.h" -#include "bgp_aspath.h" -#include "bgp_community.h" -#include "bgp_vnc_types.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_vnc_types.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_rib.h" -#include "rfapi_vty.h" -#include "rfapi_ap.h" -#include "rfapi_encap_tlv.h" -#include "vnc_debug.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/rfapi_ap.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" #define DEBUG_L2_EXTRA 0 diff --git a/bgpd/rfapi/rfapi_vty.h b/bgpd/rfapi/rfapi_vty.h index 08c8e1cf41..c1aeda953c 100644 --- a/bgpd/rfapi/rfapi_vty.h +++ b/bgpd/rfapi/rfapi_vty.h @@ -22,7 +22,7 @@ #ifndef RFAPI_VTY_H #define RFAPI_VTY_H -#include "vty.h" +#include "lib/vty.h" typedef enum { diff --git a/bgpd/rfapi/vnc_debug.c b/bgpd/rfapi/vnc_debug.c index 8f45781592..eaa8c56ee9 100644 --- a/bgpd/rfapi/vnc_debug.c +++ b/bgpd/rfapi/vnc_debug.c @@ -18,16 +18,16 @@ * */ -#include +#include "lib/zebra.h" #include -#include "prefix.h" -#include "linklist.h" -#include "stream.h" -#include "command.h" -#include "str.h" -#include "log.h" -#include "vnc_debug.h" +#include "lib/prefix.h" +#include "lib/linklist.h" +#include "lib/stream.h" +#include "lib/command.h" +#include "lib/str.h" +#include "lib/log.h" +#include "bgpd/rfapi/vnc_debug.h" /* * debug state storage diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index f0a6289224..6434c3744d 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -24,32 +24,32 @@ * Purpose: Export routes to BGP directly (not via zebra) */ -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "log.h" -#include "stream.h" -#include "memory.h" -#include "linklist.h" -#include "plist.h" -#include "routemap.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/log.h" +#include "lib/stream.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/plist.h" +#include "lib/routemap.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_attr.h" -#include "bgp_aspath.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_aspath.h" -#include "vnc_export_bgp.h" -#include "vnc_export_bgp_p.h" -#include "vnc_export_table.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_backend.h" -#include "rfapi_vty.h" -#include "vnc_debug.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_export_table.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_backend.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_debug.h" /*********************************************************************** * Export methods that set nexthop to CE (from 5226 roo EC) BEGIN diff --git a/bgpd/rfapi/vnc_export_bgp.h b/bgpd/rfapi/vnc_export_bgp.h index ab2197d129..ae113fdcb2 100644 --- a/bgpd/rfapi/vnc_export_bgp.h +++ b/bgpd/rfapi/vnc_export_bgp.h @@ -22,11 +22,11 @@ #ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ #define _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ -#include "zebra.h" -#include "prefix.h" +#include "lib/zebra.h" +#include "lib/prefix.h" -#include "bgpd.h" -#include "bgp_route.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" extern void vnc_direct_bgp_rh_reexport (struct bgp *bgp, afi_t afi); diff --git a/bgpd/rfapi/vnc_export_bgp_p.h b/bgpd/rfapi/vnc_export_bgp_p.h index 628778af3e..fceab02e05 100644 --- a/bgpd/rfapi/vnc_export_bgp_p.h +++ b/bgpd/rfapi/vnc_export_bgp_p.h @@ -22,11 +22,11 @@ #ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ #define _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ -#include "zebra.h" -#include "prefix.h" +#include "lib/zebra.h" +#include "lib/prefix.h" -#include "bgpd.h" -#include "bgp_route.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" #include "rfapi_private.h" diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c index 5a96d8a52b..16ffc801ec 100644 --- a/bgpd/rfapi/vnc_export_table.c +++ b/bgpd/rfapi/vnc_export_table.c @@ -20,18 +20,18 @@ */ -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "memory.h" -#include "vty.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/memory.h" +#include "lib/vty.h" -#include "bgpd.h" -#include "bgp_route.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" -#include "vnc_export_table.h" -#include "rfapi_private.h" -#include "rfapi_import.h" +#include "bgpd/rfapi/vnc_export_table.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_import.h" struct route_node * vnc_etn_get (struct bgp *bgp, vnc_export_type_t type, struct prefix *p) diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h index 231861af49..77829ca382 100644 --- a/bgpd/rfapi/vnc_export_table.h +++ b/bgpd/rfapi/vnc_export_table.h @@ -22,11 +22,11 @@ #ifndef _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ #define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ -#include "table.h" -#include "thread.h" -#include "vty.h" +#include "lib/table.h" +#include "lib/thread.h" +#include "lib/vty.h" -#include "bgpd.h" +#include "bgpd/bgpd.h" #define VNC_EXPORT_TYPE_BGP 1 #define VNC_EXPORT_TYPE_ZEBRA 2 diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 020cf181b5..79ef92e1f5 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -24,31 +24,31 @@ * Purpose: Import routes from BGP unicast directly (not via zebra) */ -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "vty.h" -#include "log.h" -#include "memory.h" -#include "linklist.h" -#include "plist.h" -#include "routemap.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/plist.h" +#include "lib/routemap.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_attr.h" -#include "bgp_mplsvpn.h" /* for RD_TYPE_IP */ +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" /* for RD_TYPE_IP */ -#include "vnc_export_bgp.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "rfapi_monitor.h" -#include "rfapi_vty.h" -#include "vnc_import_bgp.h" -#include "vnc_import_bgp_p.h" -#include "vnc_debug.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_import_bgp_p.h" +#include "bgpd/rfapi/vnc_debug.h" #define ENABLE_VNC_RHNCK diff --git a/bgpd/rfapi/vnc_import_bgp.h b/bgpd/rfapi/vnc_import_bgp.h index acab0c62f5..db739e3320 100644 --- a/bgpd/rfapi/vnc_import_bgp.h +++ b/bgpd/rfapi/vnc_import_bgp.h @@ -22,11 +22,11 @@ #ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ #define _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ -#include "zebra.h" -#include "prefix.h" +#include "lib/zebra.h" +#include "lib/prefix.h" -#include "bgpd.h" -#include "bgp_route.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" #define VALID_INTERIOR_TYPE(type) \ (((type) == ZEBRA_ROUTE_BGP) || ((type) == ZEBRA_ROUTE_BGP_DIRECT)) diff --git a/bgpd/rfapi/vnc_import_bgp_p.h b/bgpd/rfapi/vnc_import_bgp_p.h index 4d37ce9cdf..85800c1cab 100644 --- a/bgpd/rfapi/vnc_import_bgp_p.h +++ b/bgpd/rfapi/vnc_import_bgp_p.h @@ -22,11 +22,11 @@ #ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ #define _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ -#include "zebra.h" -#include "prefix.h" +#include "lib/zebra.h" +#include "lib/prefix.h" -#include "bgpd.h" -#include "bgp_route.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" extern void vnc_import_bgp_exterior_add_route_interior ( diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index 54e8a2a3e3..0ea0fa9e50 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -24,28 +24,28 @@ * Purpose: Handle exchange of routes between VNC and Zebra */ -#include "zebra.h" -#include "prefix.h" -#include "table.h" -#include "log.h" -#include "command.h" -#include "zclient.h" -#include "stream.h" -#include "memory.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/log.h" +#include "lib/command.h" +#include "lib/zclient.h" +#include "lib/stream.h" +#include "lib/memory.h" -#include "bgpd.h" -#include "bgp_ecommunity.h" -#include "bgp_route.h" -#include "bgp_debug.h" -#include "bgp_advertise.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_advertise.h" -#include "bgp_rfapi_cfg.h" -#include "rfapi.h" -#include "rfapi_import.h" -#include "rfapi_private.h" -#include "vnc_zebra.h" -#include "rfapi_vty.h" -#include "rfapi_backend.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/rfapi_backend.h" static struct rfapi_descriptor vncHD1VR; /* Single-VR export dummy nve descr */ static struct zclient *zclient_vnc = NULL; diff --git a/bgpd/rfapi/vnc_zebra.h b/bgpd/rfapi/vnc_zebra.h index 226136ea69..ad24844423 100644 --- a/bgpd/rfapi/vnc_zebra.h +++ b/bgpd/rfapi/vnc_zebra.h @@ -26,7 +26,7 @@ #ifndef _QUAGGA_BGP_VNC_ZEBRA_H #define _QUAGGA_BGP_VNC_ZEBRA_H -#include "zebra.h" +#include "lib/zebra.h" extern void vnc_zebra_add_prefix ( diff --git a/bgpd/rfp-example/librfp/rfp.h b/bgpd/rfp-example/librfp/rfp.h index 4aa307821d..91dbf5e71f 100644 --- a/bgpd/rfp-example/librfp/rfp.h +++ b/bgpd/rfp-example/librfp/rfp.h @@ -23,7 +23,7 @@ #ifndef _RFP_H #define _RFP_H -#include "rfapi.h" +#include "bgpd/rfapi/rfapi.h" extern int bgp_rfp_cfg_write (void *vty, void *bgp); /* TO BE REMOVED */ void rfp_clear_vnc_nve_all (void); diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c index b533550140..e8b546ddf4 100644 --- a/bgpd/rfp-example/librfp/rfp_example.c +++ b/bgpd/rfp-example/librfp/rfp_example.c @@ -21,8 +21,8 @@ /* stub rfp */ #include "rfp_internal.h" -#include "rfapi.h" -#include "command.h" +#include "bgpd/rfapi/rfapi.h" +#include "lib/command.h" struct rfp_instance_t { diff --git a/bgpd/rfp-example/librfp/rfp_internal.h b/bgpd/rfp-example/librfp/rfp_internal.h index 94192534f4..64452d2397 100644 --- a/bgpd/rfp-example/librfp/rfp_internal.h +++ b/bgpd/rfp-example/librfp/rfp_internal.h @@ -22,8 +22,8 @@ /* Sample header file */ #ifndef _RFP_INTERNAL_H #define _RFP_INTERNAL_H -#include +#include "lib/zebra.h" #include "rfp.h" -#include "rfapi.h" +#include "bgpd/rfapi/rfapi.h" #endif /* _RFP_INTERNAL_H */ From f95f2ad915eecda51cc3d2a27de87fb335173e1c Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Wed, 28 Sep 2016 18:16:25 -0400 Subject: [PATCH 091/136] vnc: default to enabled --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index e6e1e53486..703948553b 100755 --- a/configure.ac +++ b/configure.ac @@ -259,7 +259,7 @@ AC_ARG_ENABLE(pimd, AC_ARG_ENABLE(bgp-announce, AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(bgp-vnc, - AS_HELP_STRING([--enable-bgp-vnc],[turn on BGP VNC support])) + AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support])) AC_ARG_WITH(rfp-path, AS_HELP_STRING([--with-rfp-path[=DIR]],[path to replaced stub RFP used with BGP VNC])) AC_ARG_ENABLE(snmp, @@ -1374,7 +1374,7 @@ if test "${with_rfp_path}" != "no"; then AC_SUBST(VNC_RFP_PATH) fi -if test "${enable_bgp_vnc}" = "yes";then +if test "${enable_bgp_vnc}" != "no";then AC_DEFINE(ENABLE_BGP_VNC,1,Enable BGP VNC support) RFPTEST="${with_rfp_path}/rfptest" LIBRFP="${with_rfp_path}/librfp" @@ -1385,7 +1385,7 @@ else RFPINC="bgpd/rfp-example/librfp" fi # set -AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} = xyes]) +AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} != xno]) AC_SUBST(DOC) AC_SUBST(ZEBRA) @@ -1773,7 +1773,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh]) -if test "${enable_bgp_vnc}" = "yes"; then +if test "${enable_bgp_vnc}" != "no"; then if test "${with_rfp_path}" = "bgpd/rfp-example" ; then AC_CONFIG_FILES([bgpd/rfp-example/rfptest/Makefile bgpd/rfp-example/librfp/Makefile]) else From 5d370a29a05782df9e61bc18b189aec3f97c2d05 Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Thu, 29 Sep 2016 09:03:31 -0400 Subject: [PATCH 092/136] vnc: look for librfp.a in builddir --- bgpd/Makefile.am | 2 +- tests/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index ebd7932bd1..1730f5cdb8 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -3,7 +3,7 @@ AUTOMAKE_OPTIONS = subdir-objects if ENABLE_BGP_VNC #o file to keep linker happy -BGP_VNC_RFP_LIB=rfapi/rfapi_descriptor_rfp_utils.o @top_srcdir@/$(LIBRFP)/librfp.a +BGP_VNC_RFP_LIB=rfapi/rfapi_descriptor_rfp_utils.o @top_builddir@/$(LIBRFP)/librfp.a BGP_VNC_RFP_INC=-I@top_srcdir@/$(RFPINC) BGP_VNC_RFP_HD=\ @top_srcdir@/$(RFPINC)/rfp.h diff --git a/tests/Makefile.am b/tests/Makefile.am index 0014fa34d1..76280f7189 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,7 +26,7 @@ TESTS_BGPD = endif if ENABLE_BGP_VNC -BGP_VNC_RFP_LIB=@top_srcdir@/$(LIBRFP)/librfp.a +BGP_VNC_RFP_LIB=@top_builddir@/$(LIBRFP)/librfp.a else BGP_VNC_RFP_LIB = endif From 9ad5ef406dd4ff104230b81a52de9fff41f3c526 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 3 Oct 2016 08:19:06 -0400 Subject: [PATCH 093/136] debian: Disable build of vnc currently Signed-off-by: Donald Sharp --- debian/rules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 25461b89d8..b100813182 100755 --- a/debian/rules +++ b/debian/rules @@ -54,7 +54,8 @@ override_dh_auto_configure: --enable-poll=yes \ --enable-cumulus=yes \ --enable-pimd=no \ - --enable-dependency-tracking; \ + --enable-dependency-tracking \ + --enable-bgp-vnc=no; \ fi override_dh_auto_build: From 515b2a23e7a36a60783dae45d9bdbc9b2ecb447c Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 5 Oct 2016 10:58:43 -0400 Subject: [PATCH 094/136] zebra: Fix wrong afi used in zebra_static.c When calling a route uninstall in zebra_static.c use the afi passed in and don't hardcode AFI_IP Signed-off-by: Donald Sharp Reviewed-by: Daniel Walton --- zebra/zebra_static.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 1dc54e171c..dfe196c4b2 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -518,7 +518,7 @@ static_delete_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, } /* Install into rib. */ - static_uninstall_route (AFI_IP, safi, p, si); + static_uninstall_route (afi, safi, p, si); /* Unlink static route from linked list. */ if (si->prev) From 2de1475ffedc2303fb0f3ff00803bab3d327f1cd Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Wed, 5 Oct 2016 20:54:55 +0000 Subject: [PATCH 095/136] some rfapi compile fixes Signed-off-by: Daniel Walton --- bgpd/bgp_attr.c | 2 +- bgpd/bgp_mplsvpn.c | 2 +- bgpd/rfapi/.gitignore | 1 + bgpd/rfapi/rfapi.c | 19 ------------------- bgpd/rfapi/rfapi_import.c | 4 ---- bgpd/rfapi/rfapi_rib.c | 4 ---- bgpd/rfapi/rfapi_vty.c | 9 --------- bgpd/rfapi/vnc_import_bgp.c | 28 ---------------------------- bgpd/rfapi/vnc_zebra.c | 8 ++------ 9 files changed, 5 insertions(+), 72 deletions(-) create mode 100644 bgpd/rfapi/.gitignore diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f5daf607e6..b1388d0c4e 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1952,7 +1952,7 @@ bgp_attr_encap( bgp_size_t total; struct attr_extra *attre = NULL; struct bgp_attr_encap_subtlv *stlv_last = NULL; - uint16_t tunneltype; + uint16_t tunneltype = 0; total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index e3e28c7a0f..2fc5d28087 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -125,7 +125,7 @@ decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) #if ENABLE_BGP_VNC /* type == RD_TYPE_VNC_ETH */ -void +static void decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth) { rd_vnc_eth->type = RD_TYPE_VNC_ETH; diff --git a/bgpd/rfapi/.gitignore b/bgpd/rfapi/.gitignore new file mode 100644 index 0000000000..0638d7514b --- /dev/null +++ b/bgpd/rfapi/.gitignore @@ -0,0 +1 @@ +.dirstamp diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index e33c33aa76..3e292716de 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -1873,7 +1873,6 @@ rfapi_open_rfd (struct rfapi_descriptor *rfd, struct bgp *bgp) struct rfapi_nve_group_cfg *rfg; struct rfapi *h; struct rfapi_cfg *hc; - struct prefix_rd prd; int rc; h = bgp->rfapi; @@ -1914,14 +1913,6 @@ rfapi_open_rfd (struct rfapi_descriptor *rfd, struct bgp *bgp) return rc; } - - /* - * Construct route distinguisher for VPN routes - */ - prd = rfd->rd; - prd.family = AF_UNSPEC; - prd.prefixlen = 64; - /* * re-advertise registered routes, this time as part of new NVE-group */ @@ -2316,7 +2307,6 @@ rfapi_close (void *handle) struct route_node *node; struct bgp *bgp; struct rfapi *h; - struct rfapi_cfg *hc; zlog_debug ("%s: rfd=%p", __func__, rfd); @@ -2366,8 +2356,6 @@ rfapi_close (void *handle) return 0; } - hc = bgp->rfapi_cfg; - if (CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) { @@ -2478,7 +2466,6 @@ int rfapi_reopen (struct rfapi_descriptor *rfd, struct bgp *bgp) { struct rfapi *h; - struct rfapi_cfg *hc; int rc; if ((rc = rfapi_close_inner (rfd, bgp))) @@ -2489,7 +2476,6 @@ rfapi_reopen (struct rfapi_descriptor *rfd, struct bgp *bgp) { h = bgp->rfapi; - hc = bgp->rfapi_cfg; assert (!CHECK_FLAG (h->flags, RFAPI_INCALLBACK)); @@ -2538,7 +2524,6 @@ rfapi_register ( uint32_t *label = NULL; struct rfapi_vn_option *vo; struct rfapi_l2address_option *l2o = NULL; - struct rfapi_nexthop *lnh = NULL; struct prefix_rd *prd_override = NULL; switch (action) @@ -2566,10 +2551,6 @@ rfapi_register ( { l2o = &vo->v.l2addr; } - if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type) - { - lnh = &vo->v.local_nexthop; - } if (RFAPI_VN_OPTION_TYPE_INTERNAL_RD == vo->type) { prd_override = &vo->v.internal_rd; diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 110182a1fc..8783024f16 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -3731,8 +3731,6 @@ rfapiBgpInfoFilteredImportVPN ( } else { - int washolddown = 0; - zlog_debug ("%s: %s at prefix %s/%d", __func__, ((action == @@ -3757,8 +3755,6 @@ rfapiBgpInfoFilteredImportVPN ( import_table->holddown_count[afi] -= 1; RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, 1); - - washolddown = 1; } /* * decrement remote count (if route is remote) because diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 48c2d422d9..62ffd7b820 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -1069,12 +1069,8 @@ process_pending_node ( * then remove from pending list because the route * hasn't changed. */ - int same = 0; if (!rfapi_info_cmp (ori, ri)) { - /* same: delete from pending list */ - same = 1; - skiplist_delete (slPendPt, &ri->rk, NULL); assert (lPendCost); if (lPendCost) diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 0cb6f31ac1..3c76bb4223 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1142,9 +1142,6 @@ rfapiPrintRemoteRegBi ( struct vty *vty; void *out; const char *vty_newline; - - uint32_t factor; - struct prefix pfx_un; struct prefix pfx_vn; uint8_t cost; @@ -1158,12 +1155,6 @@ rfapiPrintRemoteRegBi ( char buf_lifetime[BUFSIZ]; int nlines = 0; - if (bgp && bgp->rfapi_cfg) - factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor; - else - factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR; - - if (!stream) return 0; /* for debug log, print into buf & call output once */ diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 79ef92e1f5..99ec9efb26 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -952,7 +952,6 @@ vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp, struct prefix_rd prd; struct route_map *rmap = NULL; uint32_t local_pref; - uint32_t *med = NULL; { char buf[BUFSIZ]; @@ -1120,12 +1119,6 @@ vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp, local_pref = calc_local_pref (iattr, peer); - if (iattr && (iattr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) - { - - med = &iattr->med; - } - if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { char buf[BUFSIZ]; @@ -1334,9 +1327,6 @@ vnc_import_bgp_del_route_mode_resolve_nve_one_bi ( struct prefix *prefix)/* unicast route prefix */ { struct prefix un; - uint32_t lifetime; - uint32_t *plifetime; - struct bgp_attr_encap_subtlv *encaptlvs; if (bi->type != ZEBRA_ROUTE_BGP && bi->type != ZEBRA_ROUTE_BGP_DIRECT) { @@ -1363,24 +1353,6 @@ vnc_import_bgp_del_route_mode_resolve_nve_one_bi ( memset (&vncHDResolveNve.un_addr, 0, sizeof (vncHDResolveNve.un_addr)); } - if (rfapiGetVncLifetime (bi->attr, &lifetime)) - { - plifetime = NULL; - } - else - { - plifetime = &lifetime; - } - - if (bi->attr && bi->attr->extra) - { - encaptlvs = bi->attr->extra->vnc_subtlvs; - } - else - { - encaptlvs = NULL; - } - del_vnc_route (&vncHDResolveNve, vncHDResolveNve.peer, bgp, SAFI_MPLS_VPN, prefix, /* unicast route prefix */ prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 0); /* flags */ diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index 0ea0fa9e50..e357ef6eff 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -348,12 +348,10 @@ vnc_zebra_read_ipv4 ( { struct stream *s; struct zapi_ipv4 api; - unsigned long ifindex; struct in_addr nexthop; struct prefix_ipv4 p; s = zclient->ibuf; - ifindex = 0; nexthop.s_addr = 0; /* Type, flags, message. */ @@ -376,7 +374,7 @@ vnc_zebra_read_ipv4 ( if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) { api.ifindex_num = stream_getc (s); - ifindex = stream_getl (s); + stream_getl (s); } if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) api.distance = stream_getc (s); @@ -433,12 +431,10 @@ vnc_zebra_read_ipv6 ( { struct stream *s; struct zapi_ipv6 api; - unsigned long ifindex; struct in6_addr nexthop; struct prefix_ipv6 p; s = zclient->ibuf; - ifindex = 0; memset (&nexthop, 0, sizeof (struct in6_addr)); /* Type, flags, message. */ @@ -461,7 +457,7 @@ vnc_zebra_read_ipv6 ( if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) { api.ifindex_num = stream_getc (s); - ifindex = stream_getl (s); + stream_getl (s); } if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) api.distance = stream_getc (s); From b705df1286a63bd444a8f7332c66110b2045331a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sun, 2 Oct 2016 18:33:32 -0400 Subject: [PATCH 096/136] bgpd: Fix crash in vnc_import_bgp_add_route The ordering to check for NULL pointer was reversed in this function. I switched the order after examing a couple other functions to see that they have the 'correct' order. Signed-off-by: Donald Sharp --- bgpd/rfapi/vnc_import_bgp.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 99ec9efb26..4215ce2bf7 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -2837,8 +2837,13 @@ vnc_import_bgp_del_route ( #endif VNC_RHNCK (enter); - /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->rfapi_cfg) + { + zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); + return; + } + /* check bgp redist flag for vnc direct ("vpn") routes */ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { zlog_debug ("%s: bgp redistribution of afi=%d VNC direct routes is off", @@ -2846,12 +2851,6 @@ vnc_import_bgp_del_route ( return; } - if (!bgp->rfapi_cfg) - { - zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__); - return; - } - switch (bgp->rfapi_cfg->redist_mode) { case VNC_REDIST_MODE_PLAIN: From 1ba2a97af940168a4ca7673287272000f27f93ca Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Thu, 6 Oct 2016 13:20:02 +0000 Subject: [PATCH 097/136] bgpd: 'Last write' does not update when we TX a keepalive Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp Ticket: CM-5518 --- bgpd/bgp_fsm.c | 2 +- bgpd/bgp_packet.c | 8 +++++++- bgpd/bgpd.h | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index f775bd048f..ab4ea71d61 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -688,7 +688,7 @@ bgp_adjust_routeadv (struct peer *peer) * * m > MRAI */ - diff = difftime(nowtime, peer->last_write); + diff = difftime(nowtime, peer->last_update); if (diff > (double) peer->v_routeadv) { BGP_TIMER_OFF(peer->t_routeadv); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ae54cd43d3..6811513448 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -341,6 +341,7 @@ bgp_write (struct thread *thread) u_char type; struct stream *s; int num; + int update_last_write = 0; unsigned int count = 0; unsigned int oc = 0; @@ -432,6 +433,7 @@ bgp_write (struct thread *thread) /* OK we send packet so delete it. */ bgp_packet_delete (peer); + update_last_write = 1; } while (++count < peer->bgp->wpkt_quanta && (s = bgp_write_packet (peer)) != NULL); @@ -439,8 +441,12 @@ bgp_write (struct thread *thread) bgp_write_proceed_actions (peer); done: - /* Update the last write if some updates were written. */ + /* Update last_update if UPDATEs were written. */ if (peer->update_out > oc) + peer->last_update = bgp_clock (); + + /* If we TXed any flavor of packet update last_write */ + if (update_last_write) peer->last_write = bgp_clock (); sockopt_cork (peer->fd, 0); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 2aa90e4877..cc55fb87d1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -789,7 +789,8 @@ struct peer /* Syncronization list and time. */ struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX]; time_t synctime; - time_t last_write; /* timestamp when the last UPDATE msg was written */ + time_t last_write; /* timestamp when the last msg was written */ + time_t last_update; /* timestamp when the last UPDATE msg was written */ /* Send prefix count. */ unsigned long scount[AFI_MAX][SAFI_MAX]; From 1bf9f0270c889d1131dcc01b1ea20ad054b60b85 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 26 Sep 2016 17:30:30 +0200 Subject: [PATCH 098/136] lib: add "qobj" object-ID infrastructure This adds 64-bit random identifiers as "safe pointers" which are also type-tracked / can have type-specific extension methods. This will be used by both the CLI (to keep safe references while in config editing mode) as well as the Cap'n Proto code (to hand out pointers to the user in a safe way and add per-type handlers) Signed-off-by: David Lamparter --- lib/Makefile.am | 5 +- lib/command.c | 3 + lib/qobj.c | 83 ++++++++++++++++++++++++ lib/qobj.h | 122 ++++++++++++++++++++++++++++++++++++ tests/bgp_capability_test.c | 2 + tests/bgp_mp_attr_test.c | 2 + tests/bgp_mpath_test.c | 2 + 7 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 lib/qobj.c create mode 100644 lib/qobj.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 4401744ab2..b57e9eb4d5 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -15,7 +15,8 @@ libzebra_la_SOURCES = \ zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \ sigevent.c pqueue.c jhash.c workqueue.c nexthop.c json.c \ ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c \ - imsg-buffer.c imsg.c skiplist.c + imsg-buffer.c imsg.c skiplist.c \ + qobj.c BUILT_SOURCES = route_types.h gitversion.h @@ -33,7 +34,7 @@ pkginclude_HEADERS = \ workqueue.h route_types.h libospf.h nexthop.h json.h \ ptm_lib.h csv.h bfd.h vrf.h ns.h systemd.h bitfield.h \ fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h \ - skiplist.h + skiplist.h qobj.h noinst_HEADERS = \ plist_int.h diff --git a/lib/command.c b/lib/command.c index fa3a782a91..9d8bd46580 100644 --- a/lib/command.c +++ b/lib/command.c @@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA. */ #include "command.h" #include "workqueue.h" #include "vrf.h" +#include "qobj.h" DEFINE_MTYPE( LIB, HOST, "Host config") DEFINE_MTYPE( LIB, STRVEC, "String vector") @@ -4179,6 +4180,8 @@ install_default (enum node_type node) void cmd_init (int terminal) { + qobj_init (); + command_cr = XSTRDUP(MTYPE_CMD_TOKENS, ""); token_cr.type = TOKEN_TERMINAL; token_cr.terminal = TERMINAL_LITERAL; diff --git a/lib/qobj.c b/lib/qobj.c new file mode 100644 index 0000000000..65b537f961 --- /dev/null +++ b/lib/qobj.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "thread.h" +#include "memory.h" +#include "hash.h" +#include "log.h" +#include "qobj.h" + +static struct hash *nodes = NULL; + +static unsigned int qobj_key (void *data) +{ + struct qobj_node *node = data; + return (unsigned int)node->nid; +} + +static int qobj_cmp (const void *a, const void *b) +{ + const struct qobj_node *na = a, *nb = b; + return na->nid == nb->nid; +} + +void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) +{ + node->type = type; + do + { + node->nid = (uint64_t)random(); + node->nid ^= (uint64_t)random() << 32; + } + while (hash_get (nodes, node, hash_alloc_intern) != node); +} + +void qobj_unreg(struct qobj_node *node) +{ + hash_release (nodes, node); +} + +struct qobj_node *qobj_get(uint64_t id) +{ + struct qobj_node dummy = { .nid = id }; + return hash_lookup (nodes, &dummy); +} + +void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) +{ + struct qobj_node *node = qobj_get(id); + if (!node || node->type != type) + return NULL; + return (char *)node - node->type->node_member_offset; +} + +void qobj_init (void) +{ + nodes = hash_create (qobj_key, qobj_cmp); +} + +void qobj_finish (void) +{ + hash_free (nodes); + nodes = NULL; +} diff --git a/lib/qobj.h b/lib/qobj.h new file mode 100644 index 0000000000..4a5c0c01ed --- /dev/null +++ b/lib/qobj.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef _QOBJ_H +#define _QOBJ_H + +#include +#include +#include + +/* reserve a specific amount of bytes for a struct, which can grow up to + * that size (or be dummy'd out if not needed) + * + * note the padding's array size will be an error if it gets negative or zero; + * this is intentional to prevent the struct from growing beyond the allocated + * space. + */ +#define RESERVED_SPACE_STRUCT(name, fieldname, size) \ + struct { \ + struct name fieldname; \ + char padding ## fieldname[size - sizeof(struct name)]; \ + }; + +/* don't need struct definitions for these here. code actually using + * these needs to define the struct *before* including this header. + * HAVE_QOBJ_xxx should be defined to +1 in that case, like this: + * + * #if defined(HAVE_QOBJ_NODETYPE_CLI) && HAVE_QOBJ_NODETYPE_CLI < 0 + * #error include files are in wrong order + * #else + * #define HAVE_QOBJ_NODETYPE_CLI 1 + * struct qobj_nodetype_cli { ... } + * #endif + */ +#ifndef HAVE_QOBJ_NODETYPE_CLI +#define HAVE_QOBJ_NODETYPE_CLI -1 +struct qobj_nodetype_cli { int dummy; }; +#endif + +#ifndef HAVE_QOBJ_NODETYPE_CAPNP +#define HAVE_QOBJ_NODETYPE_CAPNP -1 +struct qobj_nodetype_capnp { int dummy; }; +#endif + +/* each different kind of object will have a global variable of this type, + * which can be used by various other pieces to store type-related bits. + * type equality can be tested as pointer equality. (cf. QOBJ_GET_TYPESAFE) + */ +struct qobj_nodetype { + ptrdiff_t node_member_offset; + RESERVED_SPACE_STRUCT(qobj_nodetype_cli, cli, 256) + RESERVED_SPACE_STRUCT(qobj_nodetype_capnp, capnp, 256) +}; + +/* anchor to be embedded somewhere in the object's struct */ +struct qobj_node { + uint64_t nid; + struct qobj_nodetype *type; +}; + +#define QOBJ_FIELDS \ + struct qobj_node qobj_node; + +/* call these at the end of any _create function (QOBJ_REG) + * and beginning of any _destroy function (QOBJ_UNREG) */ +#define QOBJ_REG(n, structname) \ + qobj_reg(&n->qobj_node, &qobj_t_ ## structname) +#define QOBJ_UNREG(n) \ + qobj_unreg(&n->qobj_node) + +/* internals - should not be directly used without a good reason*/ +void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type); +void qobj_unreg(struct qobj_node *node); +struct qobj_node *qobj_get(uint64_t id); +void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type); + +/* type declarations */ +#define DECLARE_QOBJ_TYPE(structname) \ + extern struct qobj_nodetype qobj_t_ ## structname; +#define DEFINE_QOBJ_TYPE(structname) \ + struct qobj_nodetype qobj_t_ ## structname = { \ + .node_member_offset = \ + (ptrdiff_t)offsetof(struct structname, qobj_node) \ + }; +#define DEFINE_QOBJ_TYPE_INIT(structname, ...) \ + struct qobj_nodetype qobj_t_ ## structname = { \ + .node_member_offset = \ + (ptrdiff_t)offsetof(struct structname, qobj_node), \ + __VA_ARGS__ \ + }; + +/* ID dereference with typecheck. + * will return NULL if id not found or wrong type. */ +#define QOBJ_GET_TYPESAFE(id, structname) \ + ((struct structname *)qobj_get_typed((id), &qobj_t_ ## structname)) + +#define QOBJ_ID(ptr) \ + ((ptr)->qobj_node.nid) + +void qobj_init(void); +void qobj_finish(void); + +#endif /* _QOBJ_H */ diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c index 73f46b59a0..7fa4be5611 100644 --- a/tests/bgp_capability_test.c +++ b/tests/bgp_capability_test.c @@ -21,6 +21,7 @@ #include +#include "qobj.h" #include "vty.h" #include "stream.h" #include "privs.h" @@ -643,6 +644,7 @@ main (void) term_bgp_debug_packet = -1UL; term_bgp_debug_as4 = -1UL; + qobj_init (); master = thread_master_create (); bgp_master_init (); vrf_init (); diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c index 928d69752a..cde23d79e4 100644 --- a/tests/bgp_mp_attr_test.c +++ b/tests/bgp_mp_attr_test.c @@ -21,6 +21,7 @@ #include +#include "qobj.h" #include "vty.h" #include "stream.h" #include "privs.h" @@ -532,6 +533,7 @@ main (void) term_bgp_debug_packet = -1UL; term_bgp_debug_as4 = -1UL; + qobj_init (); master = thread_master_create (); bgp_master_init (); vrf_init (); diff --git a/tests/bgp_mpath_test.c b/tests/bgp_mpath_test.c index 66a718cbe2..dbcb00a2ef 100644 --- a/tests/bgp_mpath_test.c +++ b/tests/bgp_mpath_test.c @@ -23,6 +23,7 @@ #include +#include "qobj.h" #include "vty.h" #include "stream.h" #include "privs.h" @@ -374,6 +375,7 @@ int all_tests_count = (sizeof(all_tests)/sizeof(testcase_t *)); static int global_test_init (void) { + qobj_init (); master = thread_master_create (); zclient = zclient_new(master); bgp_master_init (); From e80e7cced350874ce25342d032cd8a08f9fdb1c8 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Sep 2016 14:51:08 +0200 Subject: [PATCH 099/136] lib: qobj: register ifaces, routemaps & keychains This places the appropriate calls so library objects can be used with qobj "pointers", especially in the CLI. Signed-off-by: David Lamparter --- lib/if.c | 6 ++++++ lib/if.h | 4 ++++ lib/keychain.c | 14 ++++++++++++-- lib/keychain.h | 8 ++++++++ lib/routemap.c | 9 +++++++++ lib/routemap.h | 7 +++++++ lib/vrf.c | 6 ++++++ lib/vrf.h | 4 ++++ 8 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/if.c b/lib/if.c index bd6079c0fd..2add03d67c 100644 --- a/lib/if.c +++ b/lib/if.c @@ -43,6 +43,8 @@ DEFINE_MTYPE_STATIC(LIB, NBR_CONNECTED, "Neighbor Connected") DEFINE_MTYPE( LIB, CONNECTED_LABEL, "Connected interface label") DEFINE_MTYPE_STATIC(LIB, IF_LINK_PARAMS, "Informational Link Parameters") +DEFINE_QOBJ_TYPE(interface) + /* List of interfaces in only the default VRF */ int ptm_enable = 0; @@ -149,6 +151,8 @@ if_create_vrf (const char *name, int namelen, vrf_id_t vrf_id) /* Enable Link-detection by default */ SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + QOBJ_REG (ifp, interface); + if (if_master.if_new_hook) (*if_master.if_new_hook) (ifp); @@ -193,6 +197,8 @@ if_delete_retain (struct interface *ifp) if (if_master.if_delete_hook) (*if_master.if_delete_hook) (ifp); + QOBJ_UNREG (ifp); + /* Free connected address list */ list_delete_all_node (ifp->connected); diff --git a/lib/if.h b/lib/if.h index d1875e695a..57062cd3fc 100644 --- a/lib/if.h +++ b/lib/if.h @@ -24,6 +24,7 @@ Boston, MA 02111-1307, USA. */ #include "zebra.h" #include "linklist.h" #include "memory.h" +#include "qobj.h" DECLARE_MTYPE(IF) DECLARE_MTYPE(CONNECTED_LABEL) @@ -267,7 +268,10 @@ struct interface struct route_node *node; vrf_id_t vrf_id; + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(interface) /* Connected address structure. */ struct connected diff --git a/lib/keychain.c b/lib/keychain.c index ac2083cf4b..85af482e1d 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -28,30 +28,40 @@ Boston, MA 02111-1307, USA. */ DEFINE_MTYPE_STATIC(LIB, KEY, "Key") DEFINE_MTYPE_STATIC(LIB, KEYCHAIN, "Key chain") +DEFINE_QOBJ_TYPE(keychain) +DEFINE_QOBJ_TYPE(key) + /* Master list of key chain. */ struct list *keychain_list; static struct keychain * keychain_new (void) { - return XCALLOC (MTYPE_KEYCHAIN, sizeof (struct keychain)); + struct keychain *keychain; + keychain = XCALLOC (MTYPE_KEYCHAIN, sizeof (struct keychain)); + QOBJ_REG (keychain, keychain); + return keychain; } static void keychain_free (struct keychain *keychain) { + QOBJ_UNREG (keychain); XFREE (MTYPE_KEYCHAIN, keychain); } static struct key * key_new (void) { - return XCALLOC (MTYPE_KEY, sizeof (struct key)); + struct key *key = XCALLOC (MTYPE_KEY, sizeof (struct key)); + QOBJ_REG (key, key); + return key; } static void key_free (struct key *key) { + QOBJ_UNREG (key); XFREE (MTYPE_KEY, key); } diff --git a/lib/keychain.h b/lib/keychain.h index f962864c5b..d3f9168a0f 100644 --- a/lib/keychain.h +++ b/lib/keychain.h @@ -22,12 +22,17 @@ #ifndef _ZEBRA_KEYCHAIN_H #define _ZEBRA_KEYCHAIN_H +#include "qobj.h" + struct keychain { char *name; struct list *key; + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(keychain) struct key_range { @@ -45,7 +50,10 @@ struct key struct key_range send; struct key_range accept; + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(key) extern void keychain_init (void); extern struct keychain *keychain_lookup (const char *); diff --git a/lib/routemap.c b/lib/routemap.c index 09afc31bc0..d1fa14fc3d 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -38,6 +38,9 @@ DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str") DEFINE_MTYPE( LIB, ROUTE_MAP_COMPILED, "Route map compiled") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency") +DEFINE_QOBJ_TYPE(route_map_index) +DEFINE_QOBJ_TYPE(route_map) + /* Vector for route match rules. */ static vector route_match_vec; @@ -155,6 +158,7 @@ route_map_new (const char *name) new = XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map)); new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name); + QOBJ_REG (new, route_map); return new; } @@ -215,6 +219,8 @@ route_map_free_map (struct route_map *map) if (map != NULL) { + QOBJ_UNREG (map); + if (map->next) map->next->prev = map->prev; else @@ -482,6 +488,7 @@ route_map_index_new (void) new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index)); new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ + QOBJ_REG (new, route_map_index); return new; } @@ -491,6 +498,8 @@ route_map_index_delete (struct route_map_index *index, int notify) { struct route_map_rule *rule; + QOBJ_UNREG (index); + /* Free route match. */ while ((rule = index->match_list.head) != NULL) route_map_rule_delete (&index->match_list, rule); diff --git a/lib/routemap.h b/lib/routemap.h index 7006e43f66..427a552e7f 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -24,6 +24,7 @@ #include "prefix.h" #include "memory.h" +#include "qobj.h" DECLARE_MTYPE(ROUTE_MAP_NAME) DECLARE_MTYPE(ROUTE_MAP_RULE) DECLARE_MTYPE(ROUTE_MAP_COMPILED) @@ -152,7 +153,10 @@ struct route_map_index /* Make linked list. */ struct route_map_index *next; struct route_map_index *prev; + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(route_map_index) /* Route map list structure. */ struct route_map @@ -171,7 +175,10 @@ struct route_map /* Maintain update info */ int to_be_processed; /* True if modification isn't acted on yet */ int deleted; /* If 1, then this node will be deleted */ + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(route_map) /* Prototypes. */ extern void route_map_init (void); diff --git a/lib/vrf.c b/lib/vrf.c index 7d79b3dc9a..63adea4aec 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -33,6 +33,8 @@ DEFINE_MTYPE_STATIC(LIB, VRF, "VRF") DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map") +DEFINE_QOBJ_TYPE(vrf) + /* * Turn on/off debug code * for vrf. @@ -124,6 +126,7 @@ vrf_get (vrf_id_t vrf_id, const char *name) strcpy (vrf->name, name); listnode_add_sort (vrf_list, vrf); if_init (&vrf->iflist); + QOBJ_REG (vrf, vrf); if (vrf_master.vrf_new_hook) { (*vrf_master.vrf_new_hook) (vrf_id, name, &vrf->info); @@ -212,6 +215,7 @@ vrf_get (vrf_id_t vrf_id, const char *name) strcpy (vrf->name, name); listnode_add_sort (vrf_list, vrf); if_init (&vrf->iflist); + QOBJ_REG (vrf, vrf); if (vrf_master.vrf_new_hook) { (*vrf_master.vrf_new_hook) (vrf_id, name, &vrf->info); @@ -249,6 +253,7 @@ vrf_get (vrf_id_t vrf_id, const char *name) vrf->node = rn; vrf->vrf_id = vrf_id; if_init (&vrf->iflist); + QOBJ_REG (vrf, vrf); if (debug_vrf) zlog_debug("Vrf Created: %p", vrf); return vrf; @@ -275,6 +280,7 @@ vrf_delete (struct vrf *vrf) if (vrf_master.vrf_delete_hook) (*vrf_master.vrf_delete_hook) (vrf->vrf_id, vrf->name, &vrf->info); + QOBJ_UNREG (vrf); if_terminate (&vrf->iflist); if (vrf->node) diff --git a/lib/vrf.h b/lib/vrf.h index dcc115563d..127b7082e9 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -24,6 +24,7 @@ #define _ZEBRA_VRF_H #include "linklist.h" +#include "qobj.h" /* The default NS ID */ #define NS_DEFAULT 0 @@ -85,7 +86,10 @@ struct vrf /* User data */ void *info; + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(vrf) extern struct list *vrf_list; From 676a4ea3ed8d35c25ddda5339d4f43a5b2bf9298 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 5 Apr 2016 18:54:53 -0300 Subject: [PATCH 100/136] isisd: qobj: register everything Wire up all neccessary isisd first-class objects to be able to use qobj safe-pointers on them. Signed-off-by: David Lamparter --- isisd/isis_circuit.c | 7 +++++++ isisd/isis_circuit.h | 4 ++++ isisd/isis_main.c | 1 + isisd/isisd.c | 9 +++++++++ isisd/isisd.h | 7 +++++++ 5 files changed, 28 insertions(+) diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 4f22a5e558..c66fa48263 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -41,6 +41,7 @@ #include "hash.h" #include "prefix.h" #include "stream.h" +#include "qobj.h" #include "isisd/dict.h" #include "isisd/include-netbsd/iso.h" @@ -61,6 +62,8 @@ #include "isisd/isis_events.h" #include "isisd/isis_te.h" +DEFINE_QOBJ_TYPE(isis_circuit) + /* * Prototypes. */ @@ -100,6 +103,8 @@ isis_circuit_new () circuit->mtc = mpls_te_circuit_new(); + QOBJ_REG (circuit, isis_circuit); + return circuit; } @@ -109,6 +114,8 @@ isis_circuit_del (struct isis_circuit *circuit) if (!circuit) return; + QOBJ_UNREG (circuit); + isis_circuit_if_unbind (circuit, circuit->interface); /* and lastly the circuit itself */ diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 9ada1e26a3..efe153f32e 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -25,6 +25,7 @@ #include "vty.h" #include "if.h" +#include "qobj.h" #include "isis_constants.h" #include "isis_common.h" @@ -140,7 +141,10 @@ struct isis_circuit u_int32_t ctrl_pdus_txed; /* controlPDUsSent */ u_int32_t desig_changes[2]; /* lanLxDesignatedIntermediateSystemChanges */ u_int32_t rej_adjacencies; /* rejectedAdjacencies */ + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(isis_circuit) void isis_circuit_init (void); struct isis_circuit *isis_circuit_new (void); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 163a4e051c..6110c4026b 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -38,6 +38,7 @@ #include "plist.h" #include "zclient.h" #include "vrf.h" +#include "qobj.h" #include "isisd/dict.h" #include "include-netbsd/iso.h" diff --git a/isisd/isisd.c b/isisd/isisd.c index 4d7f475616..2e66752ec1 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -34,6 +34,7 @@ #include "stream.h" #include "prefix.h" #include "table.h" +#include "qobj.h" #include "isisd/dict.h" #include "isisd/include-netbsd/iso.h" @@ -63,6 +64,9 @@ u_char DEFAULT_TOPOLOGY_BASEIS[6] = { 0xFE, 0xED, 0xFE, 0xED, 0x00, 0x00 }; struct isis *isis = NULL; +DEFINE_QOBJ_TYPE(isis) +DEFINE_QOBJ_TYPE(isis_area) + /* * Prototypes. */ @@ -100,6 +104,7 @@ isis_new (unsigned long process_id) */ /* isis->debugs = 0xFFFF; */ isisMplsTE.status = disable; /* Only support TE metric */ + QOBJ_REG (isis, isis); } struct isis_area * @@ -169,6 +174,8 @@ isis_area_create (const char *area_tag) listnode_add (isis->area_list, area); area->isis = isis; + QOBJ_REG (area, isis_area); + return area; } @@ -228,6 +235,8 @@ isis_area_destroy (struct vty *vty, const char *area_tag) return CMD_ERR_NO_MATCH; } + QOBJ_UNREG (area); + if (area->circuit_list) { for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit)) diff --git a/isisd/isisd.h b/isisd/isisd.h index ca3e570063..2c303d9304 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -33,6 +33,7 @@ #include "isis_flags.h" #include "dict.h" #include "isis_memory.h" +#include "qobj.h" /* uncomment if you are a developer in bug hunt */ /* #define EXTREME_DEBUG */ @@ -57,9 +58,12 @@ struct isis struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; + + QOBJ_FIELDS }; extern struct isis *isis; +DECLARE_QOBJ_TYPE(isis_area) struct isis_area { @@ -135,7 +139,10 @@ struct isis_area char *topology_basedynh; /* Dynamic hostname base. */ char top_params[200]; /* FIXME: what is reasonable? */ #endif /* TOPOLOGY_GENERATE */ + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(isis_area) void isis_init (void); void isis_new(unsigned long); From 0878c8d423a7e53ac08a73bb47819dc4c9268a31 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 26 Sep 2016 18:36:49 +0200 Subject: [PATCH 101/136] lib: vty: add infrastructure for qobj ID "index" As mentioned in previous commits, this prepares to replace the vty's "void *index" context position with a safe qobj pointer. This will allow concurrent configuration editing by multiple users, as soon as no more code (library included) in the daemon uses vty->index anymore. Signed-off-by: David Lamparter --- lib/vty.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/vty.h b/lib/vty.h index 4f1dbe653e..ba68ecb7e1 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -24,10 +24,18 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "thread.h" #include "log.h" #include "sockunion.h" +#include "qobj.h" #define VTY_BUFSIZ 512 #define VTY_MAXHIST 20 +#if defined(VTY_DEPRECATE_INDEX) && defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define INDEX_WARNING __attribute__((deprecated)) +#else +#define INDEX_WARNING +#endif + /* VTY struct. */ struct vty { @@ -75,7 +83,10 @@ struct vty /* For current referencing point of interface, route-map, access-list etc... */ - void *index; + void *index INDEX_WARNING; + + /* qobj object ID (replacement for "index") */ + uint64_t qobj_index; /* For multiple level index treatment such as key chain and key. */ void *index_sub; @@ -127,6 +138,46 @@ struct vty char address[SU_ADDRSTRLEN]; }; +#undef INDEX_WARNING + +static inline void vty_push_context(struct vty *vty, + int node, uint64_t id, void *idx) +{ + vty->node = node; + vty->qobj_index = id; +#if defined(VTY_DEPRECATE_INDEX) && defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + vty->index = idx; +#pragma GCC diagnostic pop +#else + vty->index = idx; +#endif +} + +#define VTY_PUSH_CONTEXT(nodeval, ptr) \ + vty_push_context(vty, nodeval, QOBJ_ID(ptr), NULL) +#define VTY_PUSH_CONTEXT_COMPAT(nodeval, ptr) \ + vty_push_context(vty, nodeval, QOBJ_ID(ptr), ptr) + +/* can return NULL if context is invalid! */ +#define VTY_GET_CONTEXT(structname) \ + QOBJ_GET_TYPESAFE(vty->qobj_index, structname) + +/* will return if ptr is NULL. */ +#define VTY_CHECK_CONTEXT(ptr) \ + if (!ptr) { \ + vty_out (vty, "Current configuration object was deleted " \ + "by another process.%s", VTY_NEWLINE); \ + return CMD_WARNING; \ + } + +/* struct structname *ptr = ; ptr will never be NULL. */ +#define VTY_DECLVAR_CONTEXT(structname, ptr) \ + struct structname *ptr = VTY_GET_CONTEXT(structname); \ + VTY_CHECK_CONTEXT(ptr); + struct vty_arg { const char *name; From be301cc2566d694d1860b3f23ab5532675c8bbf1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 26 Sep 2016 20:17:12 +0200 Subject: [PATCH 102/136] lib: use qobj for vty->index context position Prepares the library CLI functions for concurrent config access. Note the vty->index pointer is still kept functional for the daemons to use. Signed-off-by: David Lamparter --- lib/if.c | 12 ++++-------- lib/keychain.c | 11 +++-------- lib/routemap.c | 30 +++++++++--------------------- 3 files changed, 16 insertions(+), 37 deletions(-) diff --git a/lib/if.c b/lib/if.c index 2add03d67c..6ae8500291 100644 --- a/lib/if.c +++ b/lib/if.c @@ -683,12 +683,11 @@ DEFUN (interface_desc, "Interface specific description\n" "Characters describing this interface\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); if (argc == 0) return CMD_SUCCESS; - ifp = vty->index; if (ifp->desc) XFREE (MTYPE_TMP, ifp->desc); ifp->desc = argv_concat(argv, argc, 0); @@ -702,9 +701,8 @@ DEFUN (no_interface_desc, NO_STR "Interface specific description\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); - ifp = vty->index; if (ifp->desc) XFREE (MTYPE_TMP, ifp->desc); ifp->desc = NULL; @@ -788,8 +786,7 @@ DEFUN (interface, vty_out (vty, "%% interface %s not in %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } - vty->index = ifp; - vty->node = INTERFACE_NODE; + VTY_PUSH_CONTEXT_COMPAT (INTERFACE_NODE, ifp); return CMD_SUCCESS; } @@ -862,8 +859,7 @@ DEFUN (vrf, vrfp = vrf_get (VRF_UNKNOWN, argv[0]); - vty->index = vrfp; - vty->node = VRF_NODE; + VTY_PUSH_CONTEXT_COMPAT (VRF_NODE, vrfp); return CMD_SUCCESS; } diff --git a/lib/keychain.c b/lib/keychain.c index 85af482e1d..ecc274d4f2 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -250,8 +250,7 @@ DEFUN (key_chain, struct keychain *keychain; keychain = keychain_get (argv[0]); - vty->index = keychain; - vty->node = KEYCHAIN_NODE; + VTY_PUSH_CONTEXT_COMPAT (KEYCHAIN_NODE, keychain); return CMD_SUCCESS; } @@ -285,12 +284,10 @@ DEFUN (key, "Configure a key\n" "Key identifier number\n") { - struct keychain *keychain; + VTY_DECLVAR_CONTEXT (keychain, keychain); struct key *key; u_int32_t index; - keychain = vty->index; - VTY_GET_INTEGER ("key identifier", index, argv[0]); key = key_get (keychain, index); vty->index_sub = key; @@ -306,11 +303,9 @@ DEFUN (no_key, "Delete a key\n" "Key identifier number\n") { - struct keychain *keychain; + VTY_DECLVAR_CONTEXT (keychain, keychain); struct key *key; u_int32_t index; - - keychain = vty->index; VTY_GET_INTEGER ("key identifier", index, argv[0]); key = key_lookup (keychain, index); diff --git a/lib/routemap.c b/lib/routemap.c index d1fa14fc3d..e0a7080bbf 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1450,8 +1450,7 @@ DEFUN (route_map, map = route_map_get (argv[0]); index = route_map_index_get (map, permit, pref); - vty->index = index; - vty->node = RMAP_NODE; + VTY_PUSH_CONTEXT_COMPAT (RMAP_NODE, index); return CMD_SUCCESS; } @@ -1552,9 +1551,7 @@ DEFUN (rmap_onmatch_next, "Exit policy on matches\n" "Next clause\n") { - struct route_map_index *index; - - index = vty->index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); if (index) { @@ -1577,10 +1574,8 @@ DEFUN (no_rmap_onmatch_next, "Exit policy on matches\n" "Next clause\n") { - struct route_map_index *index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); - index = vty->index; - if (index) index->exitpolicy = RMAP_EXIT; @@ -1594,7 +1589,7 @@ DEFUN (rmap_onmatch_goto, "Goto Clause number\n" "Number\n") { - struct route_map_index *index = vty->index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); int d = 0; if (index) @@ -1635,9 +1630,7 @@ DEFUN (no_rmap_onmatch_goto, "Exit policy on matches\n" "Goto Clause number\n") { - struct route_map_index *index; - - index = vty->index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); if (index) index->exitpolicy = RMAP_EXIT; @@ -1696,9 +1689,8 @@ DEFUN (rmap_call, "Jump to another Route-Map after match+set\n" "Target route-map name\n") { - struct route_map_index *index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); - index = vty->index; if (index) { if (index->nextrm) @@ -1724,9 +1716,7 @@ DEFUN (no_rmap_call, NO_STR "Jump to another Route-Map after match+set\n") { - struct route_map_index *index; - - index = vty->index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); if (index->nextrm) { @@ -1746,9 +1736,8 @@ DEFUN (rmap_description, "Route-map comment\n" "Comment describing this route-map rule\n") { - struct route_map_index *index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); - index = vty->index; if (index) { if (index->description) @@ -1764,9 +1753,8 @@ DEFUN (no_rmap_description, NO_STR "Route-map comment\n") { - struct route_map_index *index; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); - index = vty->index; if (index) { if (index->description) From 6a098b3aa757252c803a1c8017c9eefc578321e7 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Sep 2016 16:51:58 +0200 Subject: [PATCH 103/136] lib: replace vty->index_sub with qobj The keychain code is the only user of vty->index_sub; this is also replaced with a qobj safe-pointer. Signed-off-by: David Lamparter --- lib/keychain.c | 77 +++++++++++++------------------------------------- lib/vty.h | 14 +++++++-- 2 files changed, 32 insertions(+), 59 deletions(-) diff --git a/lib/keychain.c b/lib/keychain.c index ecc274d4f2..c090956487 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -290,9 +290,8 @@ DEFUN (key, VTY_GET_INTEGER ("key identifier", index, argv[0]); key = key_get (keychain, index); - vty->index_sub = key; - vty->node = KEYCHAIN_KEY_NODE; - + VTY_PUSH_CONTEXT_SUB (KEYCHAIN_KEY_NODE, key); + return CMD_SUCCESS; } @@ -328,9 +327,7 @@ DEFUN (key_string, "Set key string\n" "The key\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); if (key->string) XFREE(MTYPE_KEY, key->string); @@ -346,9 +343,7 @@ DEFUN (no_key_string, "Unset key string\n" "The key\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); if (key->string) { @@ -557,9 +552,7 @@ DEFUN (accept_lifetime_day_month_day_month, "Month of the year to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); @@ -578,9 +571,7 @@ DEFUN (accept_lifetime_day_month_month_day, "Day of th month to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], argv[3], argv[4], argv[6], argv[5], argv[7]); @@ -599,9 +590,7 @@ DEFUN (accept_lifetime_month_day_day_month, "Month of the year to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], argv[3], argv[4], argv[5], argv[6], argv[7]); @@ -620,9 +609,7 @@ DEFUN (accept_lifetime_month_day_month_day, "Day of th month to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], argv[3], argv[4], argv[6], argv[5], argv[7]); @@ -638,9 +625,7 @@ DEFUN (accept_lifetime_infinite_day_month, "Year to start\n" "Never expires") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[1], argv[2], argv[3]); @@ -656,9 +641,7 @@ DEFUN (accept_lifetime_infinite_month_day, "Year to start\n" "Never expires") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[2], argv[1], argv[3]); @@ -675,9 +658,7 @@ DEFUN (accept_lifetime_duration_day_month, "Duration of the key\n" "Duration seconds\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[1], argv[2], argv[3], argv[4]); @@ -694,9 +675,7 @@ DEFUN (accept_lifetime_duration_month_day, "Duration of the key\n" "Duration seconds\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2], argv[1], argv[3], argv[4]); @@ -715,9 +694,7 @@ DEFUN (send_lifetime_day_month_day_month, "Month of the year to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); @@ -736,9 +713,7 @@ DEFUN (send_lifetime_day_month_month_day, "Day of th month to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], argv[4], argv[6], argv[5], argv[7]); @@ -757,9 +732,7 @@ DEFUN (send_lifetime_month_day_day_month, "Month of the year to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], argv[4], argv[5], argv[6], argv[7]); @@ -778,9 +751,7 @@ DEFUN (send_lifetime_month_day_month_day, "Day of th month to expire\n" "Year to expire\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], argv[4], argv[6], argv[5], argv[7]); @@ -796,9 +767,7 @@ DEFUN (send_lifetime_infinite_day_month, "Year to start\n" "Never expires") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3]); @@ -814,9 +783,7 @@ DEFUN (send_lifetime_infinite_month_day, "Year to start\n" "Never expires") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3]); @@ -833,9 +800,7 @@ DEFUN (send_lifetime_duration_day_month, "Duration of the key\n" "Duration seconds\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_duration_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], argv[4]); @@ -852,9 +817,7 @@ DEFUN (send_lifetime_duration_month_day, "Duration of the key\n" "Duration seconds\n") { - struct key *key; - - key = vty->index_sub; + VTY_DECLVAR_CONTEXT_SUB (key, key); return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], argv[4]); diff --git a/lib/vty.h b/lib/vty.h index ba68ecb7e1..468b29d955 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -88,8 +88,8 @@ struct vty /* qobj object ID (replacement for "index") */ uint64_t qobj_index; - /* For multiple level index treatment such as key chain and key. */ - void *index_sub; + /* qobj second-level object ID (replacement for "index_sub") */ + uint64_t qobj_index_sub; /* For escape character. */ unsigned char escape; @@ -160,10 +160,17 @@ static inline void vty_push_context(struct vty *vty, vty_push_context(vty, nodeval, QOBJ_ID(ptr), NULL) #define VTY_PUSH_CONTEXT_COMPAT(nodeval, ptr) \ vty_push_context(vty, nodeval, QOBJ_ID(ptr), ptr) +#define VTY_PUSH_CONTEXT_SUB(nodeval, ptr) do { \ + vty->node = nodeval; \ + /* qobj_index stays untouched */ \ + vty->qobj_index_sub = QOBJ_ID(ptr); \ + } while (0) /* can return NULL if context is invalid! */ #define VTY_GET_CONTEXT(structname) \ QOBJ_GET_TYPESAFE(vty->qobj_index, structname) +#define VTY_GET_CONTEXT_SUB(structname) \ + QOBJ_GET_TYPESAFE(vty->qobj_index_sub, structname) /* will return if ptr is NULL. */ #define VTY_CHECK_CONTEXT(ptr) \ @@ -177,6 +184,9 @@ static inline void vty_push_context(struct vty *vty, #define VTY_DECLVAR_CONTEXT(structname, ptr) \ struct structname *ptr = VTY_GET_CONTEXT(structname); \ VTY_CHECK_CONTEXT(ptr); +#define VTY_DECLVAR_CONTEXT_SUB(structname, ptr) \ + struct structname *ptr = VTY_GET_CONTEXT_SUB(structname); \ + VTY_CHECK_CONTEXT(ptr); struct vty_arg { From 8ff5a39992b1b8ef53f40b0c49970af4cee07974 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 26 Sep 2016 18:36:13 +0200 Subject: [PATCH 104/136] isisd: use qobj for vty->index context position This converts all uses of vty->index over to qobj. With this, isisd now supports concurrent configuration editing as there are no more unsafe references held anywhere while in config-edit mode. Signed-off-by: David Lamparter --- isisd/isis_redist.c | 9 ++- isisd/isis_routemap.c | 42 +++++++------ isisd/isis_vty.c | 136 ++++++++++-------------------------------- isisd/isisd.c | 42 ++++--------- 4 files changed, 67 insertions(+), 162 deletions(-) diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 1ce6a925e7..21daaa7794 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -553,7 +553,7 @@ DEFUN (isis_redistribute, "Route map reference\n" "Pointer to route-map entries\n") { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int family; int afi; int type; @@ -619,7 +619,7 @@ DEFUN (no_isis_redistribute, "Redistribute into level-1\n" "Redistribute into level-2\n") { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int type; int level; int family; @@ -667,7 +667,7 @@ DEFUN (isis_default_originate, "Route map reference\n" "Pointer to route-map entries\n") { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int family; int originate_type; int level; @@ -734,8 +734,7 @@ DEFUN (no_isis_default_originate, "Distribute default route into level-1\n" "Distribute default route into level-2\n") { - struct isis_area *area = vty->index; - + VTY_DECLVAR_CONTEXT (isis_area, area); int family; int level; diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index fdc0100e77..2bed5d65d2 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -253,9 +253,10 @@ static struct route_map_rule_cmd route_set_metric_cmd = /* ------------------------------------------------------------*/ static int -isis_route_match_add(struct vty *vty, struct route_map_index *index, +isis_route_match_add(struct vty *vty, const char *command, const char *arg) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_add_match (index, command, arg); @@ -275,9 +276,10 @@ isis_route_match_add(struct vty *vty, struct route_map_index *index, } static int -isis_route_match_delete(struct vty *vty, struct route_map_index *index, +isis_route_match_delete(struct vty *vty, const char *command, const char *arg) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_delete_match (index, command, arg); @@ -297,9 +299,10 @@ isis_route_match_delete(struct vty *vty, struct route_map_index *index, } static int -isis_route_set_add(struct vty *vty, struct route_map_index *index, +isis_route_set_add(struct vty *vty, const char *command, const char *arg) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_add_set(index, command, arg); @@ -320,9 +323,10 @@ isis_route_set_add(struct vty *vty, struct route_map_index *index, } static int -isis_route_set_delete (struct vty *vty, struct route_map_index *index, +isis_route_set_delete (struct vty *vty, const char *command, const char *arg) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_delete_set (index, command, arg); @@ -354,7 +358,7 @@ DEFUN (match_ip_address, "IP access-list number (expanded range)\n" "IP Access-list name\n") { - return isis_route_match_add(vty, vty->index, "ip address", argv[0]); + return isis_route_match_add(vty, "ip address", argv[0]); } DEFUN (no_match_ip_address, @@ -369,8 +373,8 @@ DEFUN (no_match_ip_address, "IP Access-list name\n") { if (argc == 0) - return isis_route_match_delete(vty, vty->index, "ip address", NULL); - return isis_route_match_delete(vty, vty->index, "ip address", argv[0]); + return isis_route_match_delete(vty, "ip address", NULL); + return isis_route_match_delete(vty, "ip address", argv[0]); } ALIAS (no_match_ip_address, @@ -392,7 +396,7 @@ DEFUN (match_ip_address_prefix_list, "Match entries of prefix-lists\n" "IP prefix-list name\n") { - return isis_route_match_add(vty, vty->index, "ip address prefix-list", argv[0]); + return isis_route_match_add(vty, "ip address prefix-list", argv[0]); } DEFUN (no_match_ip_address_prefix_list, @@ -405,8 +409,8 @@ DEFUN (no_match_ip_address_prefix_list, "Match entries of prefix-lists\n") { if (argc == 0) - return isis_route_match_delete (vty, vty->index, "ip address prefix-list", NULL); - return isis_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]); + return isis_route_match_delete (vty, "ip address prefix-list", NULL); + return isis_route_match_delete (vty, "ip address prefix-list", argv[0]); } ALIAS (no_match_ip_address_prefix_list, @@ -429,7 +433,7 @@ DEFUN (match_ipv6_address, "Match IPv6 address of route\n" "IPv6 access-list name\n") { - return isis_route_match_add(vty, vty->index, "ipv6 address", argv[0]); + return isis_route_match_add(vty, "ipv6 address", argv[0]); } DEFUN (no_match_ipv6_address, @@ -442,8 +446,8 @@ DEFUN (no_match_ipv6_address, "IPv6 access-list name\n") { if (argc == 0) - return isis_route_match_delete(vty, vty->index, "ipv6 address", NULL); - return isis_route_match_delete(vty, vty->index, "ipv6 address", argv[0]); + return isis_route_match_delete(vty, "ipv6 address", NULL); + return isis_route_match_delete(vty, "ipv6 address", argv[0]); } ALIAS (no_match_ipv6_address, @@ -465,7 +469,7 @@ DEFUN (match_ipv6_address_prefix_list, "Match entries of prefix-lists\n" "IP prefix-list name\n") { - return isis_route_match_add(vty, vty->index, "ipv6 address prefix-list", argv[0]); + return isis_route_match_add(vty, "ipv6 address prefix-list", argv[0]); } DEFUN (no_match_ipv6_address_prefix_list, @@ -478,8 +482,8 @@ DEFUN (no_match_ipv6_address_prefix_list, "Match entries of prefix-lists\n") { if (argc == 0) - return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", NULL); - return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]); + return isis_route_match_delete (vty, "ipv6 address prefix-list", NULL); + return isis_route_match_delete (vty, "ipv6 address prefix-list", argv[0]); } ALIAS (no_match_ipv6_address_prefix_list, @@ -504,7 +508,7 @@ DEFUN (set_metric, "Metric vale for destination routing protocol\n" "Metric value\n") { - return isis_route_set_add(vty, vty->index, "metric", argv[0]); + return isis_route_set_add(vty, "metric", argv[0]); } DEFUN (no_set_metric, @@ -516,8 +520,8 @@ DEFUN (no_set_metric, "Metric value\n") { if (argc == 0) - return isis_route_set_delete(vty, vty->index, "metric", NULL); - return isis_route_set_delete(vty, vty->index, "metric", argv[0]); + return isis_route_set_delete(vty, "metric", NULL); + return isis_route_set_delete(vty, "metric", argv[0]); } ALIAS (no_set_metric, diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c index 53c635ea61..12ef682c17 100644 --- a/isisd/isis_vty.c +++ b/isisd/isis_vty.c @@ -32,10 +32,9 @@ static struct isis_circuit * isis_circuit_lookup (struct vty *vty) { - struct interface *ifp; + struct interface *ifp = VTY_GET_CONTEXT(interface); struct isis_circuit *circuit; - ifp = (struct interface *) vty->index; if (!ifp) { vty_out (vty, "Invalid interface %s", VTY_NEWLINE); @@ -61,15 +60,12 @@ DEFUN (ip_router_isis, "IS-IS Routing for IP\n" "Routing process tag\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct isis_circuit *circuit; struct isis_area *area; const char *af = argv[0]; const char *area_tag = argv[1]; - ifp = (struct interface *) vty->index; - assert (ifp); - /* Prevent more than one area per circuit */ circuit = circuit_scan_by_ifp (ifp); if (circuit && circuit->area) @@ -115,19 +111,12 @@ DEFUN (no_ip_router_isis, "IS-IS Routing for IP\n" "Routing process tag\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct isis_area *area; struct isis_circuit *circuit; const char *af = argv[0]; const char *area_tag = argv[1]; - ifp = (struct interface *) vty->index; - if (!ifp) - { - vty_out (vty, "Invalid interface %s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } - area = isis_area_lookup (area_tag); if (!area) { @@ -1404,11 +1393,9 @@ DEFUN (metric_style, "Send and accept both styles of TLVs during transition\n" "Use new style of TLVs to carry wider metric\n") { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int ret; - assert(area); - if (strncmp (argv[0], "w", 1) == 0) { isis_area_metricstyle_set(area, false, true); @@ -1434,10 +1421,9 @@ DEFUN (no_metric_style, NO_STR "Use old-style (ISO 10589) or new-style packet formats\n") { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int ret; - assert (area); ret = validate_metric_style_narrow (vty, area); if (ret != CMD_SUCCESS) return ret; @@ -1452,8 +1438,7 @@ DEFUN (set_overload_bit, "Set overload bit to avoid any transit traffic\n" "Set overload bit\n") { - struct isis_area *area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); isis_area_overload_bit_set(area, true); return CMD_SUCCESS; @@ -1465,8 +1450,7 @@ DEFUN (no_set_overload_bit, "Reset overload bit to accept transit traffic\n" "Reset overload bit\n") { - struct isis_area *area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); isis_area_overload_bit_set(area, false); return CMD_SUCCESS; @@ -1478,8 +1462,7 @@ DEFUN (set_attached_bit, "Set attached bit to identify as L1/L2 router for inter-area traffic\n" "Set attached bit\n") { - struct isis_area *area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); isis_area_attached_bit_set(area, true); return CMD_SUCCESS; @@ -1490,8 +1473,7 @@ DEFUN (no_set_attached_bit, "no set-attached-bit", "Reset attached bit\n") { - struct isis_area *area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); isis_area_attached_bit_set(area, false); return CMD_SUCCESS; @@ -1503,8 +1485,7 @@ DEFUN (dynamic_hostname, "Dynamic hostname for IS-IS\n" "Dynamic hostname\n") { - struct isis_area *area = vty->index; - assert(area); + VTY_DECLVAR_CONTEXT (isis_area, area); isis_area_dynhostname_set(area, true); return CMD_SUCCESS; @@ -1517,8 +1498,7 @@ DEFUN (no_dynamic_hostname, "Dynamic hostname for IS-IS\n" "Dynamic hostname\n") { - struct isis_area *area = vty->index; - assert(area); + VTY_DECLVAR_CONTEXT (isis_area, area); isis_area_dynhostname_set(area, false); return CMD_SUCCESS; @@ -1526,16 +1506,10 @@ DEFUN (no_dynamic_hostname, static int area_lsp_mtu_set(struct vty *vty, unsigned int lsp_mtu) { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); struct listnode *node; struct isis_circuit *circuit; - if (!area) - { - vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if(circuit->state != C_STATE_INIT && circuit->state != C_STATE_UP) @@ -1590,17 +1564,9 @@ DEFUN (is_type, "Act as both a station router and an area router\n" "Act as an area router only\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); int type; - area = vty->index; - - if (!area) - { - vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } - type = string2circuit_t (argv[0]); if (!type) { @@ -1622,12 +1588,9 @@ DEFUN (no_is_type, "Act as both a station router and an area router\n" "Act as an area router only\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); int type; - area = vty->index; - assert (area); - /* * Put the is-type back to defaults: * - level-1-2 on first area @@ -1679,11 +1642,10 @@ DEFUN (lsp_gen_interval, "Minimum interval between regenerating same LSP\n" "Minimum interval in seconds\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); uint16_t interval; int level; - area = vty->index; interval = atoi (argv[0]); level = IS_LEVEL_1 | IS_LEVEL_2; return set_lsp_gen_interval (vty, area, interval, level); @@ -1695,11 +1657,10 @@ DEFUN (no_lsp_gen_interval, NO_STR "Minimum interval between regenerating same LSP\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); uint16_t interval; int level; - area = vty->index; interval = DEFAULT_MIN_LSP_GEN_INTERVAL; level = IS_LEVEL_1 | IS_LEVEL_2; return set_lsp_gen_interval (vty, area, interval, level); @@ -1719,11 +1680,10 @@ DEFUN (lsp_gen_interval_l1, "Set interval for level 1 only\n" "Minimum interval in seconds\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); uint16_t interval; int level; - area = vty->index; interval = atoi (argv[0]); level = IS_LEVEL_1; return set_lsp_gen_interval (vty, area, interval, level); @@ -1736,11 +1696,10 @@ DEFUN (no_lsp_gen_interval_l1, "Minimum interval between regenerating same LSP\n" "Set interval for level 1 only\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); uint16_t interval; int level; - area = vty->index; interval = DEFAULT_MIN_LSP_GEN_INTERVAL; level = IS_LEVEL_1; return set_lsp_gen_interval (vty, area, interval, level); @@ -1761,11 +1720,10 @@ DEFUN (lsp_gen_interval_l2, "Set interval for level 2 only\n" "Minimum interval in seconds\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); uint16_t interval; int level; - area = vty->index; interval = atoi (argv[0]); level = IS_LEVEL_2; return set_lsp_gen_interval (vty, area, interval, level); @@ -1778,11 +1736,10 @@ DEFUN (no_lsp_gen_interval_l2, "Minimum interval between regenerating same LSP\n" "Set interval for level 2 only\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); uint16_t interval; int level; - area = vty->index; interval = DEFAULT_MIN_LSP_GEN_INTERVAL; level = IS_LEVEL_2; return set_lsp_gen_interval (vty, area, interval, level); @@ -1802,10 +1759,9 @@ DEFUN (spf_interval, "Minimum interval between SPF calculations\n" "Minimum interval between consecutive SPFs in seconds\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); u_int16_t interval; - area = vty->index; interval = atoi (argv[0]); area->min_spf_interval[0] = interval; area->min_spf_interval[1] = interval; @@ -1819,9 +1775,7 @@ DEFUN (no_spf_interval, NO_STR "Minimum interval between SPF calculations\n") { - struct isis_area *area; - - area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; @@ -1843,10 +1797,9 @@ DEFUN (spf_interval_l1, "Set interval for level 1 only\n" "Minimum interval between consecutive SPFs in seconds\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); u_int16_t interval; - area = vty->index; interval = atoi (argv[0]); area->min_spf_interval[0] = interval; @@ -1860,9 +1813,7 @@ DEFUN (no_spf_interval_l1, "Minimum interval between SPF calculations\n" "Set interval for level 1 only\n") { - struct isis_area *area; - - area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; @@ -1884,10 +1835,9 @@ DEFUN (spf_interval_l2, "Set interval for level 2 only\n" "Minimum interval between consecutive SPFs in seconds\n") { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); u_int16_t interval; - area = vty->index; interval = atoi (argv[0]); area->min_spf_interval[1] = interval; @@ -1901,9 +1851,7 @@ DEFUN (no_spf_interval_l2, "Minimum interval between SPF calculations\n" "Set interval for level 2 only\n") { - struct isis_area *area; - - area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; @@ -1922,17 +1870,11 @@ static int area_max_lsp_lifetime_set(struct vty *vty, int level, uint16_t interval) { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int lvl; uint16_t refresh_interval = interval - 300; int set_refresh_interval[ISIS_LEVELS] = {0, 0}; - if (!area) - { - vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) { if (!(lvl & level)) @@ -2049,15 +1991,9 @@ ALIAS (no_max_lsp_lifetime_l2, static int area_lsp_refresh_interval_set(struct vty *vty, int level, uint16_t interval) { - struct isis_area *area = vty->index; + VTY_DECLVAR_CONTEXT (isis_area, area); int lvl; - if (!area) - { - vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { if (!(lvl & level)) @@ -2174,13 +2110,7 @@ area_passwd_set(struct vty *vty, int level, const char *passwd, u_char snp_auth), const char *passwd, u_char snp_auth) { - struct isis_area *area = vty->index; - - if (!area) - { - vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } + VTY_DECLVAR_CONTEXT (isis_area, area); if (passwd && strlen(passwd) > 254) { @@ -2267,14 +2197,8 @@ DEFUN (no_area_passwd, "Configure the authentication password for an area\n" "Set the authentication password for a routing domain\n") { + VTY_DECLVAR_CONTEXT (isis_area, area); int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1; - struct isis_area *area = vty->index; - - if (!area) - { - vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE); - return CMD_ERR_NO_MATCH; - } return isis_area_passwd_unset (area, level); } diff --git a/isisd/isisd.c b/isisd/isisd.c index 2e66752ec1..e84ae7cf00 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -203,8 +203,7 @@ isis_area_get (struct vty *vty, const char *area_tag) if (area) { - vty->node = ISIS_NODE; - vty->index = area; + VTY_PUSH_CONTEXT (ISIS_NODE, area); return CMD_SUCCESS; } @@ -213,8 +212,7 @@ isis_area_get (struct vty *vty, const char *area_tag) if (isis->debugs & DEBUG_EVENTS) zlog_debug ("New IS-IS area instance %s", area->area_tag); - vty->node = ISIS_NODE; - vty->index = area; + VTY_PUSH_CONTEXT (ISIS_NODE, area); return CMD_SUCCESS; } @@ -324,13 +322,12 @@ isis_area_destroy (struct vty *vty, const char *area_tag) int area_net_title (struct vty *vty, const char *net_title) { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); struct area_addr *addr; struct area_addr *addrp; struct listnode *node; u_char buff[255]; - area = vty->index; if (!area) { @@ -427,12 +424,11 @@ area_net_title (struct vty *vty, const char *net_title) int area_clear_net_title (struct vty *vty, const char *net_title) { - struct isis_area *area; + VTY_DECLVAR_CONTEXT (isis_area, area); struct area_addr addr, *addrp = NULL; struct listnode *node; u_char buff[255]; - area = vty->index; if (!area) { vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); @@ -1876,10 +1872,7 @@ DEFUN (log_adj_changes, "log-adjacency-changes", "Log changes in adjacency state\n") { - struct isis_area *area; - - area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); area->log_adj_changes = 1; @@ -1891,10 +1884,7 @@ DEFUN (no_log_adj_changes, "no log-adjacency-changes", "Stop logging changes in adjacency state\n") { - struct isis_area *area; - - area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); area->log_adj_changes = 0; @@ -1918,10 +1908,7 @@ DEFUN (topology_generate_grid, "Optional param 3\n" "Topology\n") { - struct isis_area *area; - - area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); if (!spgrid_check_params (vty, argc, argv)) { @@ -1976,11 +1963,8 @@ DEFUN (topology_baseis, "A Network IS Base for this topology\n" "XXXX.XXXX.XXXX Network entity title (NET)\n") { - struct isis_area *area; u_char buff[ISIS_SYS_ID_LEN]; - - area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); if (sysid2buff (buff, argv[0])) sysid2buff (area->topology_baseis, argv[0]); @@ -1996,10 +1980,7 @@ DEFUN (no_topology_baseis, "A Network IS Base for this topology\n" "XXXX.XXXX.XXXX Network entity title (NET)\n") { - struct isis_area *area; - - area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); memcpy (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS, ISIS_SYS_ID_LEN); return CMD_SUCCESS; @@ -2019,10 +2000,7 @@ DEFUN (topology_basedynh, "Dynamic hostname base for this topology\n" "Dynamic hostname base\n") { - struct isis_area *area; - - area = vty->index; - assert (area); + VTY_DECLVAR_CONTEXT (isis_area, area); /* I hope that it's enough. */ area->topology_basedynh = strndup (argv[0], 16); From cc933ef9f6a6104f287415d97e22bf6e34f193d4 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Sep 2016 16:53:25 +0200 Subject: [PATCH 105/136] lib, isisd: enable concurrent configuration editing Finally, this disables the config editing lock for isisd. It also enables deprecation warnings for the lib/ and isisd/ to catch accidental uses of vty->index. Signed-off-by: David Lamparter --- isisd/Makefile.am | 2 +- isisd/isis_main.c | 1 + lib/Makefile.am | 3 ++- lib/vty.c | 11 +++++++++++ lib/vty.h | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/isisd/Makefile.am b/isisd/Makefile.am index c14351ca3a..69624dced3 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -1,7 +1,7 @@ ## Process this file with automake to produce Makefile.in. AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - @ISIS_TOPOLOGY_INCLUDES@ + @ISIS_TOPOLOGY_INCLUDES@ -DVTY_DEPRECATE_INDEX DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 LIBS = @LIBS@ diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 6110c4026b..44c7840022 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -342,6 +342,7 @@ main (int argc, char **argv, char **envp) */ signal_init (master, array_size (isisd_signals), isisd_signals); cmd_init (1); + vty_config_lockless (); vty_init (master); memory_init (); access_list_init(); diff --git a/lib/Makefile.am b/lib/Makefile.am index b57e9eb4d5..e95e6f34b7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in. -AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + -DVTY_DEPRECATE_INDEX AM_CFLAGS = $(WERROR) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" diff --git a/lib/vty.c b/lib/vty.c index 14ef7e6e4a..cc30def303 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -83,6 +83,7 @@ char *vty_cwd = NULL; /* Configure lock. */ static int vty_config; +static int vty_config_is_lockless = 0; /* Login password check. */ static int no_password_check = 0; @@ -2634,6 +2635,8 @@ vty_log_fixed (char *buf, size_t len) int vty_config_lock (struct vty *vty) { + if (vty_config_is_lockless) + return 1; if (vty_config == 0) { vty->config = 1; @@ -2645,6 +2648,8 @@ vty_config_lock (struct vty *vty) int vty_config_unlock (struct vty *vty) { + if (vty_config_is_lockless) + return 0; if (vty_config == 1 && vty->config == 1) { vty->config = 0; @@ -2653,6 +2658,12 @@ vty_config_unlock (struct vty *vty) return vty->config; } +void +vty_config_lockless (void) +{ + vty_config_is_lockless = 1; +} + /* Master of the threads. */ static struct thread_master *vty_master; diff --git a/lib/vty.h b/lib/vty.h index 468b29d955..3870e59b72 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -353,6 +353,7 @@ extern void vty_log (const char *level, const char *proto, const char *fmt, struct timestamp_control *, va_list); extern int vty_config_lock (struct vty *); extern int vty_config_unlock (struct vty *); +extern void vty_config_lockless (void); extern int vty_shell (struct vty *); extern int vty_shell_serv (struct vty *); extern void vty_hello (struct vty *); From 271327b121ef552336d628f46741e250349d6214 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 29 Sep 2016 14:52:32 -0300 Subject: [PATCH 106/136] ldpd: reset interface configuration when it's disabled This is necessary to prevent the same old configuration to come back when the interface is reactivated later for a given address-family. Signed-off-by: Renato Westphal --- ldpd/ldp_vty_conf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index a3e1b9a250..1cc9bea9dd 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -833,6 +833,8 @@ ldp_vty_interface(struct vty *vty, struct vty_arg *args[]) goto cancel; ia->enabled = 0; + ia->hello_holdtime = 0; + ia->hello_interval = 0; ldp_reload(vty_conf); return (CMD_SUCCESS); } From b67c140b301f66241df15228e6dc35c8cf07ffc1 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 22 Jun 2016 09:59:28 -0300 Subject: [PATCH 107/136] ldpd: merge/dup/reload void **ref support Extend configuration duplication-merge mechanism to allow keeping track of a single specific object. A "void **" pointer is passed in; the "void *" pointer it points to is updated with the new address of the object it points to. Signed-off-by: David Lamparter --- ldpd/ldpd.c | 154 +++++++++++++++++++++++++++++++--------------------- ldpd/ldpd.h | 2 + 2 files changed, 93 insertions(+), 63 deletions(-) diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 1eaa7df4a5..ca503e0d6a 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -50,19 +50,20 @@ static int main_imsg_send_ipc_sockets(struct imsgbuf *, static void main_imsg_send_net_sockets(int); static void main_imsg_send_net_socket(int, enum socket_type); static int main_imsg_send_config(struct ldpd_conf *); -static void ldp_config_normalize(struct ldpd_conf *); -static void ldp_config_reset_main(struct ldpd_conf *); -static void ldp_config_reset_af(struct ldpd_conf *, int); +static void ldp_config_normalize(struct ldpd_conf *, void **); +static void ldp_config_reset_main(struct ldpd_conf *, void **); +static void ldp_config_reset_af(struct ldpd_conf *, int, void **); +static void merge_config_ref(struct ldpd_conf *, struct ldpd_conf *, void **); static void merge_global(struct ldpd_conf *, struct ldpd_conf *); static void merge_af(int, struct ldpd_af_conf *, struct ldpd_af_conf *); -static void merge_ifaces(struct ldpd_conf *, struct ldpd_conf *); +static void merge_ifaces(struct ldpd_conf *, struct ldpd_conf *, void **); static void merge_iface_af(struct iface_af *, struct iface_af *); -static void merge_tnbrs(struct ldpd_conf *, struct ldpd_conf *); -static void merge_nbrps(struct ldpd_conf *, struct ldpd_conf *); -static void merge_l2vpns(struct ldpd_conf *, struct ldpd_conf *); +static void merge_tnbrs(struct ldpd_conf *, struct ldpd_conf *, void **); +static void merge_nbrps(struct ldpd_conf *, struct ldpd_conf *, void **); +static void merge_l2vpns(struct ldpd_conf *, struct ldpd_conf *, void **); static void merge_l2vpn(struct ldpd_conf *, struct l2vpn *, - struct l2vpn *); + struct l2vpn *, void **); struct ldpd_global global; struct ldpd_conf *ldpd_conf; @@ -320,7 +321,7 @@ main(int argc, char *argv[]) /* Get configuration file. */ ldpd_conf = config_new_empty(); - ldp_config_reset_main(ldpd_conf); + ldp_config_reset_main(ldpd_conf, NULL); vty_read_config(config_file, config_default); /* Start execution only if not in dry-run mode */ @@ -884,31 +885,37 @@ main_imsg_send_config(struct ldpd_conf *xconf) } int -ldp_reload(struct ldpd_conf *xconf) +ldp_reload_ref(struct ldpd_conf *xconf, void **ref) { - ldp_config_normalize(xconf); + ldp_config_normalize(xconf, ref); if (main_imsg_send_config(xconf) == -1) return (-1); - merge_config(ldpd_conf, xconf); + merge_config_ref(ldpd_conf, xconf, ref); return (0); } +int +ldp_reload(struct ldpd_conf *xconf) +{ + return ldp_reload_ref(xconf, NULL); +} + static void -ldp_config_normalize(struct ldpd_conf *xconf) +ldp_config_normalize(struct ldpd_conf *xconf, void **ref) { struct l2vpn *l2vpn; struct l2vpn_pw *pw; if (!(xconf->flags & F_LDPD_ENABLED)) - ldp_config_reset_main(xconf); + ldp_config_reset_main(xconf, ref); else { if (!(xconf->ipv4.flags & F_LDPD_AF_ENABLED)) - ldp_config_reset_af(xconf, AF_INET); + ldp_config_reset_af(xconf, AF_INET, ref); if (!(xconf->ipv6.flags & F_LDPD_AF_ENABLED)) - ldp_config_reset_af(xconf, AF_INET6); + ldp_config_reset_af(xconf, AF_INET6, ref); } LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) { @@ -930,24 +937,28 @@ ldp_config_normalize(struct ldpd_conf *xconf) } static void -ldp_config_reset_main(struct ldpd_conf *conf) +ldp_config_reset_main(struct ldpd_conf *conf, void **ref) { struct iface *iface; struct nbr_params *nbrp; while ((iface = LIST_FIRST(&conf->iface_list)) != NULL) { + if (ref && *ref == iface) + *ref = NULL; LIST_REMOVE(iface, entry); free(iface); } while ((nbrp = LIST_FIRST(&conf->nbrp_list)) != NULL) { + if (ref && *ref == nbrp) + *ref = NULL; LIST_REMOVE(nbrp, entry); free(nbrp); } conf->rtr_id.s_addr = INADDR_ANY; - ldp_config_reset_af(conf, AF_INET); - ldp_config_reset_af(conf, AF_INET6); + ldp_config_reset_af(conf, AF_INET, ref); + ldp_config_reset_af(conf, AF_INET6, ref); conf->lhello_holdtime = LINK_DFLT_HOLDTIME; conf->lhello_interval = DEFAULT_HELLO_INTERVAL; conf->thello_holdtime = TARGETED_DFLT_HOLDTIME; @@ -957,7 +968,7 @@ ldp_config_reset_main(struct ldpd_conf *conf) } static void -ldp_config_reset_af(struct ldpd_conf *conf, int af) +ldp_config_reset_af(struct ldpd_conf *conf, int af, void **ref) { struct ldpd_af_conf *af_conf; struct iface *iface; @@ -973,6 +984,8 @@ ldp_config_reset_af(struct ldpd_conf *conf, int af) if (tnbr->af != af) continue; + if (ref && *ref == tnbr) + *ref = NULL; LIST_REMOVE(tnbr, entry); free(tnbr); } @@ -988,7 +1001,7 @@ ldp_config_reset_af(struct ldpd_conf *conf, int af) } struct ldpd_conf * -ldp_dup_config(struct ldpd_conf *conf) +ldp_dup_config_ref(struct ldpd_conf *conf, void **ref) { struct ldpd_conf *xconf; struct iface *iface, *xi; @@ -998,75 +1011,68 @@ ldp_dup_config(struct ldpd_conf *conf) struct l2vpn_if *lif, *xf; struct l2vpn_pw *pw, *xp; - xconf = malloc(sizeof(*xconf)); - *xconf = *conf; +#define COPY(a, b) do { \ + a = calloc(1, sizeof(*a)); \ + if (a == NULL) \ + fatal(__func__); \ + *a = *b; \ + if (ref && *ref == b) *ref = a; \ + } while (0) + + COPY(xconf, conf); LIST_INIT(&xconf->iface_list); LIST_INIT(&xconf->tnbr_list); LIST_INIT(&xconf->nbrp_list); LIST_INIT(&xconf->l2vpn_list); LIST_FOREACH(iface, &conf->iface_list, entry) { - xi = calloc(1, sizeof(*xi)); - if (xi == NULL) - fatal(__func__); - *xi = *iface; + COPY(xi, iface); xi->ipv4.iface = xi; xi->ipv6.iface = xi; LIST_INSERT_HEAD(&xconf->iface_list, xi, entry); } LIST_FOREACH(tnbr, &conf->tnbr_list, entry) { - xt = calloc(1, sizeof(*xt)); - if (xt == NULL) - fatal(__func__); - *xt = *tnbr; + COPY(xt, tnbr); LIST_INSERT_HEAD(&xconf->tnbr_list, xt, entry); } LIST_FOREACH(nbrp, &conf->nbrp_list, entry) { - xn = calloc(1, sizeof(*xn)); - if (xn == NULL) - fatal(__func__); - *xn = *nbrp; + COPY(xn, nbrp); LIST_INSERT_HEAD(&xconf->nbrp_list, xn, entry); } LIST_FOREACH(l2vpn, &conf->l2vpn_list, entry) { - xl = calloc(1, sizeof(*xl)); - if (xl == NULL) - fatal(__func__); - *xl = *l2vpn; + COPY(xl, l2vpn); LIST_INIT(&xl->if_list); LIST_INIT(&xl->pw_list); LIST_INIT(&xl->pw_inactive_list); LIST_INSERT_HEAD(&xconf->l2vpn_list, xl, entry); LIST_FOREACH(lif, &l2vpn->if_list, entry) { - xf = calloc(1, sizeof(*xf)); - if (xf == NULL) - fatal(__func__); - *xf = *lif; + COPY(xf, lif); xf->l2vpn = xl; LIST_INSERT_HEAD(&xl->if_list, xf, entry); } LIST_FOREACH(pw, &l2vpn->pw_list, entry) { - xp = calloc(1, sizeof(*xp)); - if (xp == NULL) - fatal(__func__); - *xp = *pw; + COPY(xp, pw); xp->l2vpn = xl; LIST_INSERT_HEAD(&xl->pw_list, xp, entry); } LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) { - xp = calloc(1, sizeof(*xp)); - if (xp == NULL) - fatal(__func__); - *xp = *pw; + COPY(xp, pw); xp->l2vpn = xl; LIST_INSERT_HEAD(&xl->pw_inactive_list, xp, entry); } } +#undef COPY return (xconf); } +struct ldpd_conf * +ldp_dup_config(struct ldpd_conf *conf) +{ + return ldp_dup_config_ref(conf, NULL); +} + void ldp_clear_config(struct ldpd_conf *xconf) { @@ -1095,19 +1101,27 @@ ldp_clear_config(struct ldpd_conf *xconf) free(xconf); } -void -merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) +static void +merge_config_ref(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) { merge_global(conf, xconf); merge_af(AF_INET, &conf->ipv4, &xconf->ipv4); merge_af(AF_INET6, &conf->ipv6, &xconf->ipv6); - merge_ifaces(conf, xconf); - merge_tnbrs(conf, xconf); - merge_nbrps(conf, xconf); - merge_l2vpns(conf, xconf); + merge_ifaces(conf, xconf, ref); + merge_tnbrs(conf, xconf, ref); + merge_nbrps(conf, xconf, ref); + merge_l2vpns(conf, xconf, ref); + if (ref && *ref == xconf) + *ref = conf; free(xconf); } +void +merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + merge_config_ref(conf, xconf, NULL); +} + static void merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) { @@ -1206,7 +1220,7 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) } static void -merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) +merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) { struct iface *iface, *itmp, *xi; @@ -1235,6 +1249,8 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) merge_iface_af(&iface->ipv4, &xi->ipv4); merge_iface_af(&iface->ipv6, &xi->ipv6); LIST_REMOVE(xi, entry); + if (ref && *ref == xi) + *ref = iface; free(xi); } } @@ -1252,7 +1268,7 @@ merge_iface_af(struct iface_af *ia, struct iface_af *xi) } static void -merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) +merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) { struct tnbr *tnbr, *ttmp, *xt; @@ -1286,12 +1302,14 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) if (!(tnbr->flags & F_TNBR_CONFIGURED)) tnbr->flags |= F_TNBR_CONFIGURED; LIST_REMOVE(xt, entry); + if (ref && *ref == xt) + *ref = tnbr; free(xt); } } static void -merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) +merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) { struct nbr_params *nbrp, *ntmp, *xn; struct nbr *nbr; @@ -1385,12 +1403,14 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) } } LIST_REMOVE(xn, entry); + if (ref && *ref == xn) + *ref = nbrp; free(xn); } } static void -merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf) +merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) { struct l2vpn *l2vpn, *ltmp, *xl; @@ -1432,14 +1452,16 @@ merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf) } /* update existing l2vpns */ - merge_l2vpn(conf, l2vpn, xl); + merge_l2vpn(conf, l2vpn, xl, ref); LIST_REMOVE(xl, entry); + if (ref && *ref == xl) + *ref = l2vpn; free(xl); } } static void -merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) +merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void **ref) { struct l2vpn_if *lif, *ftmp, *xf; struct l2vpn_pw *pw, *ptmp, *xp; @@ -1469,6 +1491,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) } LIST_REMOVE(xf, entry); + if (ref && *ref == xf) + *ref = lif; free(xf); } @@ -1597,6 +1621,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) } LIST_REMOVE(xp, entry); + if (ref && *ref == xp) + *ref = pw; free(xp); } @@ -1645,6 +1671,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) } LIST_REMOVE(xp, entry); + if (ref && *ref == xp) + *ref = pw; free(xp); } diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index b94794208a..d04614d37a 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -602,6 +602,8 @@ struct ldpd_af_global *ldp_af_global_get(struct ldpd_global *, int); int ldp_is_dual_stack(struct ldpd_conf *); in_addr_t ldp_rtr_id_get(struct ldpd_conf *); int ldp_reload(struct ldpd_conf *); +int ldp_reload_ref(struct ldpd_conf *, void **); +struct ldpd_conf *ldp_dup_config_ref(struct ldpd_conf *, void **ref); struct ldpd_conf *ldp_dup_config(struct ldpd_conf *); void ldp_clear_config(struct ldpd_conf *); void merge_config(struct ldpd_conf *, struct ldpd_conf *); From bbee85d2346402f4bf9e30876963fa10a4f9ecf1 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 22 Jun 2016 09:59:28 -0300 Subject: [PATCH 108/136] ldpd: add in-process API for creating/deleting These functions are currently unused but will be used by the Cap'n Proto interface. They're not a particular burden to maintain in-tree, so here they go. Signed-off-by: David Lamparter --- ldpd/ldp_vty_conf.c | 174 ++++++++++++++++++++++++++++++++++++++++++++ ldpd/ldpd.h | 20 +++++ 2 files changed, 194 insertions(+) diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 1cc9bea9dd..ec344f2de5 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -1637,3 +1637,177 @@ ldp_vty_if_init(void) install_element(INTERFACE_NODE, &interface_desc_cmd); install_element(INTERFACE_NODE, &no_interface_desc_cmd); } + +struct iface * +iface_new_api(struct ldpd_conf *conf, const char *name) +{ + const char *ifname = name; + struct iface *iface; + struct interface *ifp; + struct kif kif; + + if (ldp_iface_is_configured(conf, ifname)) + return NULL; + + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + ifp = if_lookup_by_name(ifname); + if (ifp) { + kif.ifindex = ifp->ifindex; + kif.flags = ifp->flags; + } + + iface = if_new(&kif); + LIST_INSERT_HEAD(&conf->iface_list, iface, entry); + return (iface); +} + +void +iface_del_api(struct iface *iface) +{ + LIST_REMOVE(iface, entry); + free(iface); +} + +struct tnbr * +tnbr_new_api(struct ldpd_conf *conf, int af, union ldpd_addr *addr) +{ + struct tnbr *tnbr; + + if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&addr->v6)) + return (NULL); + + if (tnbr_find(conf, af, addr)) + return (NULL); + + tnbr = tnbr_new(af, addr); + tnbr->flags |= F_TNBR_CONFIGURED; + LIST_INSERT_HEAD(&conf->tnbr_list, tnbr, entry); + return (tnbr); +} + +void +tnbr_del_api(struct tnbr *tnbr) +{ + LIST_REMOVE(tnbr, entry); + free(tnbr); +} + +struct nbr_params * +nbrp_new_api(struct ldpd_conf *conf, struct in_addr lsr_id) +{ + struct nbr_params *nbrp; + + if (nbr_params_find(conf, lsr_id)) + return (NULL); + + nbrp = nbr_params_new(lsr_id); + LIST_INSERT_HEAD(&conf->nbrp_list, nbrp, entry); + return (nbrp); +} + +void +nbrp_del_api(struct nbr_params *nbrp) +{ + LIST_REMOVE(nbrp, entry); + free(nbrp); +} + +struct l2vpn * +l2vpn_new_api(struct ldpd_conf *conf, const char *name) +{ + struct l2vpn *l2vpn; + + if (l2vpn_find(conf, name)) + return (NULL); + + l2vpn = l2vpn_new(name); + l2vpn->type = L2VPN_TYPE_VPLS; + LIST_INSERT_HEAD(&conf->l2vpn_list, l2vpn, entry); + return (l2vpn); +} + +void +l2vpn_del_api(struct l2vpn *l2vpn) +{ + struct l2vpn_if *lif; + struct l2vpn_pw *pw; + + while ((lif = LIST_FIRST(&l2vpn->if_list)) != NULL) { + LIST_REMOVE(lif, entry); + free(lif); + } + while ((pw = LIST_FIRST(&l2vpn->pw_list)) != NULL) { + LIST_REMOVE(pw, entry); + free(pw); + } + while ((pw = LIST_FIRST(&l2vpn->pw_inactive_list)) != NULL) { + LIST_REMOVE(pw, entry); + free(pw); + } + LIST_REMOVE(l2vpn, entry); + free(l2vpn); +} + +struct l2vpn_if * +l2vpn_if_new_api(struct ldpd_conf *conf, struct l2vpn *l2vpn, + const char *ifname) +{ + struct l2vpn_if *lif; + struct interface *ifp; + struct kif kif; + + if (ldp_iface_is_configured(conf, ifname)) + return (NULL); + + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + ifp = if_lookup_by_name(ifname); + if (ifp) { + kif.ifindex = ifp->ifindex; + kif.flags = ifp->flags; + } + + lif = l2vpn_if_new(l2vpn, &kif); + LIST_INSERT_HEAD(&l2vpn->if_list, lif, entry); + return (lif); +} + +void +l2vpn_if_del_api(struct l2vpn_if *lif) +{ + LIST_REMOVE(lif, entry); + free(lif); +} + +struct l2vpn_pw * +l2vpn_pw_new_api(struct ldpd_conf *conf, struct l2vpn *l2vpn, + const char *ifname) +{ + struct l2vpn_pw *pw; + struct interface *ifp; + struct kif kif; + + if (ldp_iface_is_configured(conf, ifname)) + return (NULL); + + memset(&kif, 0, sizeof(kif)); + strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); + ifp = if_lookup_by_name(ifname); + if (ifp) { + kif.ifindex = ifp->ifindex; + kif.flags = ifp->flags; + } + + pw = l2vpn_pw_new(l2vpn, &kif); + pw->flags = F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF; + LIST_INSERT_HEAD(&l2vpn->pw_inactive_list, pw, entry); + return (pw); +} + +void +l2vpn_pw_del_api(struct l2vpn_pw *pw) +{ + LIST_REMOVE(pw, entry); + free(pw); +} diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index d04614d37a..67e66ab545 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -610,6 +610,26 @@ void merge_config(struct ldpd_conf *, struct ldpd_conf *); struct ldpd_conf *config_new_empty(void); void config_clear(struct ldpd_conf *); +/* ldp_vty_conf.c */ +/* NOTE: the parameters' names should be preserved because of codegen */ +struct iface *iface_new_api(struct ldpd_conf *cfg, + const char *name); +void iface_del_api(struct iface *iface); +struct tnbr *tnbr_new_api(struct ldpd_conf *cfg, int af, + union ldpd_addr *addr); +void tnbr_del_api(struct tnbr *tnbr); +struct nbr_params *nbrp_new_api(struct ldpd_conf *cfg, + struct in_addr lsr_id); +void nbrp_del_api(struct nbr_params *nbrp); +struct l2vpn *l2vpn_new_api(struct ldpd_conf *cfg, const char *name); +void l2vpn_del_api(struct l2vpn *l2vpn); +struct l2vpn_if *l2vpn_if_new_api(struct ldpd_conf *conf, + struct l2vpn *l2vpn, const char *ifname); +void l2vpn_if_del_api(struct l2vpn_if *lif); +struct l2vpn_pw *l2vpn_pw_new_api(struct ldpd_conf *conf, + struct l2vpn *l2vpn, const char *ifname); +void l2vpn_pw_del_api(struct l2vpn_pw *pw); + /* socket.c */ int ldp_create_socket(int, enum socket_type); void sock_set_nonblock(int); From 4af8997d5594d097abf3978b20843412ac7db1c6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 22 Jun 2016 09:59:28 -0300 Subject: [PATCH 109/136] ldpd: qobj: register everything Place the appropriate QOBJ_* calls. A bit more complicated for ldpd due to the dup-merge config scheme. Signed-off-by: David Lamparter --- ldpd/ldpd.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++------ ldpd/ldpd.h | 15 +++++++++ 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index ca503e0d6a..7120bed110 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -39,6 +39,7 @@ #include "sigevent.h" #include "zclient.h" #include "vrf.h" +#include "qobj.h" static void ldpd_shutdown(void); static pid_t start_child(enum ldpd_process, char *, int, @@ -65,6 +66,14 @@ static void merge_l2vpns(struct ldpd_conf *, struct ldpd_conf *, void **); static void merge_l2vpn(struct ldpd_conf *, struct l2vpn *, struct l2vpn *, void **); +DEFINE_QOBJ_TYPE(iface) +DEFINE_QOBJ_TYPE(tnbr) +DEFINE_QOBJ_TYPE(nbr_params) +DEFINE_QOBJ_TYPE(l2vpn_if) +DEFINE_QOBJ_TYPE(l2vpn_pw) +DEFINE_QOBJ_TYPE(l2vpn) +DEFINE_QOBJ_TYPE(ldpd_conf) + struct ldpd_global global; struct ldpd_conf *ldpd_conf; @@ -328,6 +337,8 @@ main(int argc, char *argv[]) if (dryrun) exit(0); + QOBJ_REG (ldpd_conf, ldpd_conf); + if (daemon_mode && daemon(0, 0) < 0) { log_warn("LDPd daemon failed"); exit(1); @@ -1228,8 +1239,17 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) /* find deleted interfaces */ if ((xi = if_lookup_name(xconf, iface->name)) == NULL) { LIST_REMOVE(iface, entry); - if (ldpd_process == PROC_LDP_ENGINE) + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + break; + case PROC_LDP_ENGINE: if_exit(iface); + break; + case PROC_MAIN: + QOBJ_UNREG (iface); + break; + } free(iface); } } @@ -1239,9 +1259,11 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) LIST_REMOVE(xi, entry); LIST_INSERT_HEAD(&conf->iface_list, xi, entry); - /* resend addresses to activate new interfaces */ - if (ldpd_process == PROC_MAIN) + if (ldpd_process == PROC_MAIN) { + QOBJ_REG (xi, iface); + /* resend addresses to activate new interfaces */ kif_redistribute(xi->name); + } continue; } @@ -1278,12 +1300,20 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) /* find deleted tnbrs */ if ((xt = tnbr_find(xconf, tnbr->af, &tnbr->addr)) == NULL) { - if (ldpd_process == PROC_LDP_ENGINE) { - tnbr->flags &= ~F_TNBR_CONFIGURED; - tnbr_check(tnbr); - } else { + switch (ldpd_process) { + case PROC_LDE_ENGINE: LIST_REMOVE(tnbr, entry); free(tnbr); + break; + case PROC_LDP_ENGINE: + tnbr->flags &= ~F_TNBR_CONFIGURED; + tnbr_check(tnbr); + break; + case PROC_MAIN: + LIST_REMOVE(tnbr, entry); + QOBJ_UNREG (tnbr); + free(tnbr); + break; } } } @@ -1293,8 +1323,16 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) LIST_REMOVE(xt, entry); LIST_INSERT_HEAD(&conf->tnbr_list, xt, entry); - if (ldpd_process == PROC_LDP_ENGINE) + switch (ldpd_process) { + case PROC_LDE_ENGINE: + break; + case PROC_LDP_ENGINE: tnbr_update(xt); + break; + case PROC_MAIN: + QOBJ_REG (xt, tnbr); + break; + } continue; } @@ -1318,7 +1356,10 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) LIST_FOREACH_SAFE(nbrp, &conf->nbrp_list, entry, ntmp) { /* find deleted nbrps */ if ((xn = nbr_params_find(xconf, nbrp->lsr_id)) == NULL) { - if (ldpd_process == PROC_LDP_ENGINE) { + switch (ldpd_process) { + case PROC_LDE_ENGINE: + break; + case PROC_LDP_ENGINE: nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); if (nbr) { session_shutdown(nbr, S_SHUTDOWN, 0, 0); @@ -1333,6 +1374,10 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } + break; + case PROC_MAIN: + QOBJ_UNREG (nbrp); + break; } LIST_REMOVE(nbrp, entry); free(nbrp); @@ -1344,7 +1389,10 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) LIST_REMOVE(xn, entry); LIST_INSERT_HEAD(&conf->nbrp_list, xn, entry); - if (ldpd_process == PROC_LDP_ENGINE) { + switch (ldpd_process) { + case PROC_LDE_ENGINE: + break; + case PROC_LDP_ENGINE: nbr = nbr_find_ldpid(xn->lsr_id.s_addr); if (nbr) { session_shutdown(nbr, S_SHUTDOWN, 0, 0); @@ -1361,6 +1409,10 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) if (nbr_session_active_role(nbr)) nbr_establish_connection(nbr); } + break; + case PROC_MAIN: + QOBJ_REG (xn, nbr_params); + break; } continue; } @@ -1413,6 +1465,8 @@ static void merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) { struct l2vpn *l2vpn, *ltmp, *xl; + struct l2vpn_if *lif; + struct l2vpn_pw *pw; LIST_FOREACH_SAFE(l2vpn, &conf->l2vpn_list, entry, ltmp) { /* find deleted l2vpns */ @@ -1427,6 +1481,13 @@ merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) ldpe_l2vpn_exit(l2vpn); break; case PROC_MAIN: + LIST_FOREACH(lif, &l2vpn->if_list, entry) + QOBJ_UNREG (lif); + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + QOBJ_UNREG (pw); + LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) + QOBJ_UNREG (pw); + QOBJ_UNREG (l2vpn); break; } l2vpn_del(l2vpn); @@ -1446,6 +1507,7 @@ merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf, void **ref) ldpe_l2vpn_init(xl); break; case PROC_MAIN: + QOBJ_REG (xl, l2vpn); break; } continue; @@ -1477,6 +1539,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void LIST_FOREACH_SAFE(lif, &l2vpn->if_list, entry, ftmp) { /* find deleted interfaces */ if ((xf = l2vpn_if_find_name(xl, lif->ifname)) == NULL) { + if (ldpd_process == PROC_MAIN) + QOBJ_UNREG (lif); LIST_REMOVE(lif, entry); free(lif); } @@ -1487,6 +1551,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void LIST_REMOVE(xf, entry); LIST_INSERT_HEAD(&l2vpn->if_list, xf, entry); xf->l2vpn = l2vpn; + if (ldpd_process == PROC_MAIN) + QOBJ_REG (xf, l2vpn_if); continue; } @@ -1509,6 +1575,7 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void ldpe_l2vpn_pw_exit(pw); break; case PROC_MAIN: + QOBJ_UNREG (pw); break; } @@ -1531,6 +1598,7 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void ldpe_l2vpn_pw_init(xp); break; case PROC_MAIN: + QOBJ_REG (xp, l2vpn_pw); break; } continue; @@ -1631,6 +1699,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void /* find deleted inactive pseudowires */ if ((xp = l2vpn_pw_find_name(xl, pw->ifname)) == NULL) { LIST_REMOVE(pw, entry); + if (ldpd_process == PROC_MAIN) + QOBJ_UNREG (pw); free(pw); } } @@ -1640,6 +1710,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl, void LIST_REMOVE(xp, entry); LIST_INSERT_HEAD(&l2vpn->pw_inactive_list, xp, entry); xp->l2vpn = l2vpn; + if (ldpd_process == PROC_MAIN) + QOBJ_REG (xp, l2vpn_pw); continue; } @@ -1723,5 +1795,7 @@ config_clear(struct ldpd_conf *conf) xconf->trans_pref = conf->trans_pref; xconf->flags = conf->flags; merge_config(conf, xconf); + if (ldpd_process == PROC_MAIN) + QOBJ_UNREG (conf); free(conf); } diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 67e66ab545..630b192489 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -26,6 +26,7 @@ #include "openbsd-tree.h" #include "imsg.h" #include "thread.h" +#include "qobj.h" #include "ldp.h" @@ -272,7 +273,9 @@ struct iface { uint16_t flags; struct iface_af ipv4; struct iface_af ipv6; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(iface) /* source of targeted hellos */ struct tnbr { @@ -284,7 +287,9 @@ struct tnbr { int state; uint16_t pw_count; uint8_t flags; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(tnbr) #define F_TNBR_CONFIGURED 0x01 #define F_TNBR_DYNAMIC 0x02 @@ -306,7 +311,9 @@ struct nbr_params { uint8_t md5key_len; } auth; uint8_t flags; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(nbr_params) #define F_NBRP_KEEPALIVE 0x01 #define F_NBRP_GTSM 0x02 #define F_NBRP_GTSM_HOPS 0x04 @@ -317,7 +324,9 @@ struct l2vpn_if { char ifname[IF_NAMESIZE]; unsigned int ifindex; uint16_t flags; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(l2vpn_if) struct l2vpn_pw { LIST_ENTRY(l2vpn_pw) entry; @@ -332,7 +341,9 @@ struct l2vpn_pw { uint16_t remote_mtu; uint32_t remote_status; uint8_t flags; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(l2vpn_pw) #define F_PW_STATUSTLV_CONF 0x01 /* status tlv configured */ #define F_PW_STATUSTLV 0x02 /* status tlv negotiated */ #define F_PW_CWORD_CONF 0x04 /* control word configured */ @@ -351,7 +362,9 @@ struct l2vpn { LIST_HEAD(, l2vpn_if) if_list; LIST_HEAD(, l2vpn_pw) pw_list; LIST_HEAD(, l2vpn_pw) pw_inactive_list; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(l2vpn) #define L2VPN_TYPE_VPWS 1 #define L2VPN_TYPE_VPLS 2 @@ -401,7 +414,9 @@ struct ldpd_conf { uint16_t thello_interval; uint16_t trans_pref; int flags; + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(ldpd_conf) #define F_LDPD_NO_FIB_UPDATE 0x0001 #define F_LDPD_DS_CISCO_INTEROP 0x0002 #define F_LDPD_ENABLED 0x0004 From 0b47280e45ce89e7dbdc7c0124d21ae722e6da59 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 29 Sep 2016 14:16:50 -0300 Subject: [PATCH 110/136] ldpd: replace global vars w/ qobj for vty context ldpd was keeping track of the vty session's position in config editing with 3 global static variables. This worked because only one vty could be in configuration-editing mode before. Replace with vty->qobj_index infrastructure and enable vty_config_lockless. Signed-off-by: David Lamparter --- ldpd/Makefile.am | 3 +- ldpd/ldp_vty_conf.c | 148 +++++++++++++++++++++----------------------- ldpd/ldpd.c | 1 + 3 files changed, 72 insertions(+), 80 deletions(-) diff --git a/ldpd/Makefile.am b/ldpd/Makefile.am index c292adf6fc..1f4d910192 100644 --- a/ldpd/Makefile.am +++ b/ldpd/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in. -AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + -DVTY_DEPRECATE_INDEX DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index ec344f2de5..e5acada180 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -42,10 +42,6 @@ static int ldp_iface_is_configured(struct ldpd_conf *, const char *); static int ldp_vty_nbr_session_holdtime(struct vty *, struct vty_arg *[]); static int ldp_vty_af_session_holdtime(struct vty *, struct vty_arg *[]); -static char vty_ifname[IF_NAMESIZE]; -static char vty_l2vpn_name[L2VPN_NAME_LEN]; -static char vty_pw_ifname[IF_NAMESIZE]; - static struct cmd_node interface_node = { INTERFACE_NODE, @@ -504,10 +500,9 @@ ldp_vty_disc_holdtime(struct vty *vty, struct vty_arg *args[]) else hello_type = HELLO_TARGETED; - vty_conf = ldp_dup_config(ldpd_conf); - switch (vty->node) { case LDP_NODE: + vty_conf = ldp_dup_config(ldpd_conf); if (disable) { switch (hello_type) { case HELLO_LINK: @@ -528,9 +523,11 @@ ldp_vty_disc_holdtime(struct vty *vty, struct vty_arg *args[]) break; } } + ldp_reload(vty_conf); break; case LDP_IPV4_NODE: case LDP_IPV6_NODE: + vty_conf = ldp_dup_config(ldpd_conf); af = ldp_vty_get_af(vty); af_conf = ldp_af_conf_get(vty_conf, af); @@ -553,24 +550,25 @@ ldp_vty_disc_holdtime(struct vty *vty, struct vty_arg *args[]) break; } } + ldp_reload(vty_conf); break; case LDP_IPV4_IFACE_NODE: case LDP_IPV6_IFACE_NODE: af = ldp_vty_get_af(vty); - iface = if_lookup_name(vty_conf, vty_ifname); - ia = iface_af_get(iface, af); + iface = VTY_GET_CONTEXT(iface); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&iface); + ia = iface_af_get(iface, af); if (disable) ia->hello_holdtime = 0; else ia->hello_holdtime = secs; + ldp_reload_ref(vty_conf, (void **)&iface); break; default: fatalx("ldp_vty_disc_holdtime: unexpected node"); } - ldp_reload(vty_conf); - return (CMD_SUCCESS); } @@ -605,10 +603,9 @@ ldp_vty_disc_interval(struct vty *vty, struct vty_arg *args[]) else hello_type = HELLO_TARGETED; - vty_conf = ldp_dup_config(ldpd_conf); - switch (vty->node) { case LDP_NODE: + vty_conf = ldp_dup_config(ldpd_conf); if (disable) { switch (hello_type) { case HELLO_LINK: @@ -629,9 +626,11 @@ ldp_vty_disc_interval(struct vty *vty, struct vty_arg *args[]) break; } } + ldp_reload(vty_conf); break; case LDP_IPV4_NODE: case LDP_IPV6_NODE: + vty_conf = ldp_dup_config(ldpd_conf); af = ldp_vty_get_af(vty); af_conf = ldp_af_conf_get(vty_conf, af); @@ -654,24 +653,25 @@ ldp_vty_disc_interval(struct vty *vty, struct vty_arg *args[]) break; } } + ldp_reload(vty_conf); break; case LDP_IPV4_IFACE_NODE: case LDP_IPV6_IFACE_NODE: af = ldp_vty_get_af(vty); - iface = if_lookup_name(vty_conf, vty_ifname); - ia = iface_af_get(iface, af); + iface = VTY_GET_CONTEXT(iface); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&iface); + ia = iface_af_get(iface, af); if (disable) ia->hello_interval = 0; else ia->hello_interval = secs; + ldp_reload_ref(vty_conf, (void **)&iface); break; default: fatalx("ldp_vty_disc_interval: unexpected node"); } - ldp_reload(vty_conf); - return (CMD_SUCCESS); } @@ -839,18 +839,6 @@ ldp_vty_interface(struct vty *vty, struct vty_arg *args[]) return (CMD_SUCCESS); } - switch (af) { - case AF_INET: - vty->node = LDP_IPV4_IFACE_NODE; - break; - case AF_INET6: - vty->node = LDP_IPV6_IFACE_NODE; - break; - default: - break; - } - strlcpy(vty_ifname, ifname, sizeof(vty_ifname)); - if (iface == NULL) { if (ldp_iface_is_configured(vty_conf, ifname)) { vty_out(vty, "%% Interface is already in use%s", @@ -870,17 +858,29 @@ ldp_vty_interface(struct vty *vty, struct vty_arg *args[]) ia = iface_af_get(iface, af); ia->enabled = 1; LIST_INSERT_HEAD(&vty_conf->iface_list, iface, entry); + ldp_reload_ref(vty_conf, (void **)&iface); } else { memset(&kif, 0, sizeof(kif)); strlcpy(kif.ifname, ifname, sizeof(kif.ifname)); ia = iface_af_get(iface, af); - if (ia->enabled) - goto cancel; - ia->enabled = 1; + if (!ia->enabled) { + ia->enabled = 1; + ldp_reload_ref(vty_conf, (void **)&iface); + } + } + + switch (af) { + case AF_INET: + VTY_PUSH_CONTEXT(LDP_IPV4_IFACE_NODE, iface); + break; + case AF_INET6: + VTY_PUSH_CONTEXT(LDP_IPV6_IFACE_NODE, iface); + break; + default: + break; } - ldp_reload(vty_conf); return (CMD_SUCCESS); cancel: @@ -1241,16 +1241,17 @@ ldp_vty_l2vpn(struct vty *vty, struct vty_arg *args[]) return (CMD_SUCCESS); } - vty->node = LDP_L2VPN_NODE; - strlcpy(vty_l2vpn_name, name_str, sizeof(vty_l2vpn_name)); - if (l2vpn) + if (l2vpn) { + VTY_PUSH_CONTEXT(LDP_L2VPN_NODE, l2vpn); goto cancel; + } l2vpn = l2vpn_new(name_str); l2vpn->type = L2VPN_TYPE_VPLS; LIST_INSERT_HEAD(&vty_conf->l2vpn_list, l2vpn, entry); ldp_reload(vty_conf); + VTY_PUSH_CONTEXT(LDP_L2VPN_NODE, l2vpn); return (CMD_SUCCESS); @@ -1270,15 +1271,15 @@ ldp_vty_l2vpn_bridge(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; ifname = vty_get_arg_value(args, "ifname"); - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + l2vpn = VTY_GET_CONTEXT(l2vpn); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&l2vpn); if (disable) memset(l2vpn->br_ifname, 0, sizeof(l2vpn->br_ifname)); else strlcpy(l2vpn->br_ifname, ifname, sizeof(l2vpn->br_ifname)); - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&l2vpn); return (CMD_SUCCESS); } @@ -1302,15 +1303,15 @@ ldp_vty_l2vpn_mtu(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + l2vpn = VTY_GET_CONTEXT(l2vpn); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&l2vpn); if (disable) l2vpn->mtu = DEFAULT_L2VPN_MTU; else l2vpn->mtu = mtu; - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&l2vpn); return (CMD_SUCCESS); } @@ -1332,15 +1333,15 @@ ldp_vty_l2vpn_pwtype(struct vty *vty, struct vty_arg *args[]) else pw_type = PW_TYPE_ETHERNET_TAGGED; - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + l2vpn = VTY_GET_CONTEXT(l2vpn); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&l2vpn); if (disable) l2vpn->pw_type = DEFAULT_PW_TYPE; else l2vpn->pw_type = pw_type; - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&l2vpn); return (CMD_SUCCESS); } @@ -1359,8 +1360,9 @@ ldp_vty_l2vpn_interface(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; ifname = vty_get_arg_value(args, "ifname"); - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + l2vpn = VTY_GET_CONTEXT(l2vpn); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&l2vpn); + l2vpn = l2vpn_find(vty_conf, l2vpn->name); lif = l2vpn_if_find_name(l2vpn, ifname); if (disable) { @@ -1392,7 +1394,7 @@ ldp_vty_l2vpn_interface(struct vty *vty, struct vty_arg *args[]) lif = l2vpn_if_new(l2vpn, &kif); LIST_INSERT_HEAD(&l2vpn->if_list, lif, entry); - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&l2vpn); return (CMD_SUCCESS); @@ -1415,8 +1417,8 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; ifname = vty_get_arg_value(args, "ifname"); - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); + l2vpn = VTY_GET_CONTEXT(l2vpn); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&l2vpn); pw = l2vpn_pw_find_name(l2vpn, ifname); if (disable) { @@ -1430,8 +1432,7 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) } if (pw) { - vty->node = LDP_PSEUDOWIRE_NODE; - strlcpy(vty_pw_ifname, ifname, sizeof(vty_pw_ifname)); + VTY_PUSH_CONTEXT(LDP_PSEUDOWIRE_NODE, pw); goto cancel; } @@ -1452,10 +1453,9 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) pw->flags = F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF; LIST_INSERT_HEAD(&l2vpn->pw_inactive_list, pw, entry); - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&pw); + VTY_PUSH_CONTEXT(LDP_PSEUDOWIRE_NODE, pw); - vty->node = LDP_PSEUDOWIRE_NODE; - strlcpy(vty_pw_ifname, ifname, sizeof(vty_pw_ifname)); return (CMD_SUCCESS); cancel: @@ -1467,7 +1467,6 @@ int ldp_vty_l2vpn_pw_cword(struct vty *vty, struct vty_arg *args[]) { struct ldpd_conf *vty_conf; - struct l2vpn *l2vpn; struct l2vpn_pw *pw; const char *preference_str; int disable; @@ -1475,9 +1474,8 @@ ldp_vty_l2vpn_pw_cword(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; preference_str = vty_get_arg_value(args, "preference"); - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); - pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + pw = VTY_GET_CONTEXT(l2vpn_pw); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&pw); if (disable) pw->flags |= F_PW_CWORD_CONF; @@ -1488,7 +1486,7 @@ ldp_vty_l2vpn_pw_cword(struct vty *vty, struct vty_arg *args[]) pw->flags |= F_PW_CWORD_CONF; } - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&pw); return (CMD_SUCCESS); } @@ -1497,7 +1495,6 @@ int ldp_vty_l2vpn_pw_nbr_addr(struct vty *vty, struct vty_arg *args[]) { struct ldpd_conf *vty_conf; - struct l2vpn *l2vpn; struct l2vpn_pw *pw; int af; union ldpd_addr addr; @@ -1513,9 +1510,8 @@ ldp_vty_l2vpn_pw_nbr_addr(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); - pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + pw = VTY_GET_CONTEXT(l2vpn_pw); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&pw); if (disable) { pw->af = AF_UNSPEC; @@ -1527,7 +1523,7 @@ ldp_vty_l2vpn_pw_nbr_addr(struct vty *vty, struct vty_arg *args[]) pw->flags |= F_PW_STATIC_NBR_ADDR; } - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&pw); return (CMD_SUCCESS); } @@ -1536,7 +1532,6 @@ int ldp_vty_l2vpn_pw_nbr_id(struct vty *vty, struct vty_arg *args[]) { struct ldpd_conf *vty_conf; - struct l2vpn *l2vpn; struct l2vpn_pw *pw; struct in_addr lsr_id; const char *lsr_id_str; @@ -1551,16 +1546,15 @@ ldp_vty_l2vpn_pw_nbr_id(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); - pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + pw = VTY_GET_CONTEXT(l2vpn_pw); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&pw); if (disable) pw->lsr_id.s_addr = INADDR_ANY; else pw->lsr_id = lsr_id; - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&pw); return (CMD_SUCCESS); } @@ -1569,7 +1563,6 @@ int ldp_vty_l2vpn_pw_pwid(struct vty *vty, struct vty_arg *args[]) { struct ldpd_conf *vty_conf; - struct l2vpn *l2vpn; struct l2vpn_pw *pw; char *ep; uint32_t pwid; @@ -1585,16 +1578,15 @@ ldp_vty_l2vpn_pw_pwid(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); - pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + pw = VTY_GET_CONTEXT(l2vpn_pw); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&pw); if (disable) pw->pwid = 0; else pw->pwid = pwid; - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&pw); return (CMD_SUCCESS); } @@ -1603,22 +1595,20 @@ int ldp_vty_l2vpn_pw_pwstatus(struct vty *vty, struct vty_arg *args[]) { struct ldpd_conf *vty_conf; - struct l2vpn *l2vpn; struct l2vpn_pw *pw; int disable; disable = (vty_get_arg_value(args, "no")) ? 1 : 0; - vty_conf = ldp_dup_config(ldpd_conf); - l2vpn = l2vpn_find(vty_conf, vty_l2vpn_name); - pw = l2vpn_pw_find_name(l2vpn, vty_pw_ifname); + pw = VTY_GET_CONTEXT(l2vpn_pw); + vty_conf = ldp_dup_config_ref(ldpd_conf, (void **)&pw); if (disable) pw->flags |= F_PW_STATUSTLV_CONF; else pw->flags &= ~F_PW_STATUSTLV_CONF; - ldp_reload(vty_conf); + ldp_reload_ref(vty_conf, (void **)&pw); return (CMD_SUCCESS); } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 7120bed110..aa1dc57a7b 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -323,6 +323,7 @@ main(int argc, char *argv[]) master = thread_master_create(); cmd_init(1); + vty_config_lockless (); vty_init(master); vrf_init(); ldp_vty_init(); From 3ddccf1805208b3fba7098ea7ab1725e23329e41 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 30 Sep 2016 15:38:03 +0200 Subject: [PATCH 111/136] zebra: use qobj and enable concurrent config edits This puts all the proper VTY_DECLVAR_CONTEXT calls in place, removing all vty->index uses in the process. With that, vty_config_lockless can be enabled in zebra. [v2: fix ordering screw-up in "interface XXX" command] Signed-off-by: David Lamparter --- zebra/Makefile.am | 3 +- zebra/interface.c | 122 +++++++++++++++----------------- zebra/irdp_interface.c | 97 ++++---------------------- zebra/main.c | 1 + zebra/rtadv.c | 153 +++++++++++++---------------------------- zebra/test_main.c | 6 +- zebra/zebra_ptm.c | 7 +- zebra/zebra_routemap.c | 70 ++++++++++--------- 8 files changed, 164 insertions(+), 295 deletions(-) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index e22dea0998..52766f37ba 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -2,7 +2,8 @@ include ../common.am ## Process this file with automake to produce Makefile.in. -AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + -DVTY_DEPRECATE_INDEX DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/zebra/interface.c b/zebra/interface.c index 9be97e2214..6b575f3c83 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1233,13 +1233,12 @@ DEFUN_NOSH (zebra_interface, "Interface's name\n") { int ret; - struct interface *ifp; - + /* Call lib interface() */ if ((ret = interface_cmd.func (self, vty, argc, argv)) != CMD_SUCCESS) return ret; - ifp = vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); if (ifp->ifindex == IFINDEX_INTERNAL) /* Is this really necessary? Shouldn't status be initialized to 0 @@ -1286,14 +1285,13 @@ DEFUN_NOSH (zebra_vrf, "Select a VRF to configure\n" "VRF's name\n") { + // VTY_DECLVAR_CONTEXT (vrf, vrfp); int ret; /* Call lib vrf() */ if ((ret = vrf_cmd.func (self, vty, argc, argv)) != CMD_SUCCESS) return ret; - // vrfp = vty->index; - return ret; } @@ -1518,11 +1516,10 @@ DEFUN (multicast, "multicast", "Set multicast flag to interface\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); int ret; - struct interface *ifp; struct zebra_if *if_data; - ifp = (struct interface *) vty->index; if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { ret = if_set_flags (ifp, IFF_MULTICAST); @@ -1545,11 +1542,10 @@ DEFUN (no_multicast, NO_STR "Unset multicast flag to interface\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); int ret; - struct interface *ifp; struct zebra_if *if_data; - ifp = (struct interface *) vty->index; if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { ret = if_unset_flags (ifp, IFF_MULTICAST); @@ -1571,10 +1567,9 @@ DEFUN (linkdetect, "link-detect", "Enable link detection on interface\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); int if_was_operative; - - ifp = (struct interface *) vty->index; + if_was_operative = if_is_no_ptm_operative(ifp); SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); @@ -1594,10 +1589,9 @@ DEFUN (no_linkdetect, NO_STR "Disable link detection on interface\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); int if_was_operative; - ifp = (struct interface *) vty->index; if_was_operative = if_is_no_ptm_operative(ifp); UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); @@ -1614,11 +1608,10 @@ DEFUN (shutdown_if, "shutdown", "Shutdown the selected interface\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); int ret; - struct interface *ifp; struct zebra_if *if_data; - ifp = (struct interface *) vty->index; if (ifp->ifindex != IFINDEX_INTERNAL) { ret = if_unset_flags (ifp, IFF_UP); @@ -1641,12 +1634,10 @@ DEFUN (no_shutdown_if, NO_STR "Shutdown the selected interface\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); int ret; - struct interface *ifp; struct zebra_if *if_data; - ifp = (struct interface *) vty->index; - if (ifp->ifindex != IFINDEX_INTERNAL) { ret = if_set_flags (ifp, IFF_UP | IFF_RUNNING); @@ -1675,10 +1666,9 @@ DEFUN (bandwidth_if, "Set bandwidth informational parameter\n" "Bandwidth in megabits\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); unsigned int bandwidth; - - ifp = (struct interface *) vty->index; + bandwidth = strtol(argv[0], NULL, 10); /* bandwidth range is <1-100000> */ @@ -1703,9 +1693,7 @@ DEFUN (no_bandwidth_if, NO_STR "Set bandwidth informational parameter\n") { - struct interface *ifp; - - ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); ifp->bandwidth = 0; @@ -1779,6 +1767,7 @@ DEFUN (link_params, "link-params", LINK_PARAMS_STR) { + /* vty->qobj_index stays the same @ interface pointer */ vty->node = LINK_PARAMS_NODE; return CMD_SUCCESS; @@ -1790,7 +1779,7 @@ DEFUN (link_params_enable, "enable", "Activate link parameters on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* This command could be issue at startup, when activate MPLS TE */ /* on a new interface or after a ON / OFF / ON toggle */ @@ -1819,7 +1808,7 @@ DEFUN (no_link_params_enable, NO_STR "Disable link parameters on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); zlog_debug ("MPLS-TE: disable TE link parameters on interface %s", ifp->name); @@ -1839,7 +1828,7 @@ DEFUN (link_params_metric, "Link metric for MPLS-TE purpose\n" "Metric value in decimal\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); u_int32_t metric; @@ -1855,9 +1844,9 @@ DEFUN (no_link_params_metric, no_link_params_metric_cmd, "no metric", NO_STR - "Disbale Link Metric on this interface\n") + "Disable Link Metric on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset TE Metric */ link_param_cmd_unset(ifp, LP_TE); @@ -1871,7 +1860,7 @@ DEFUN (link_params_maxbw, "Maximum bandwidth that can be used\n" "Bytes/second (IEEE floating point format)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); float bw; @@ -1915,7 +1904,7 @@ DEFUN (link_params_max_rsv_bw, "Maximum bandwidth that may be reserved\n" "Bytes/second (IEEE floating point format)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); float bw; @@ -1948,7 +1937,7 @@ DEFUN (link_params_unrsv_bw, "Priority\n" "Bytes/second (IEEE floating point format)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); int priority; float bw; @@ -1989,7 +1978,7 @@ DEFUN (link_params_admin_grp, "Administrative group membership\n" "32-bit Hexadecimal value (e.g. 0xa1)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); unsigned long value; @@ -2010,9 +1999,9 @@ DEFUN (no_link_params_admin_grp, no_link_params_admin_grp_cmd, "no admin-grp", NO_STR - "Disbale Administrative group membership on this interface\n") + "Disable Administrative group membership on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset Admin Group */ link_param_cmd_unset(ifp, LP_ADM_GRP); @@ -2029,8 +2018,7 @@ DEFUN (link_params_inter_as, "Remote AS number\n" "AS number in the range <1-4294967295>\n") { - - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); struct in_addr addr; u_int32_t as; @@ -2066,8 +2054,7 @@ DEFUN (no_link_params_inter_as, NO_STR "Remove Neighbor IP address and AS number for Inter-AS TE\n") { - - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); /* Reset Remote IP and AS neighbor */ @@ -2089,8 +2076,7 @@ DEFUN (link_params_delay, "Unidirectional Average Link Delay\n" "Average delay in micro-second as decimal (0...16777215)\n") { - - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); u_int32_t delay = 0, low = 0, high = 0; u_int8_t update = 0; @@ -2180,9 +2166,9 @@ DEFUN (no_link_params_delay, no_link_params_delay_cmd, "no delay", NO_STR - "Disbale Unidirectional Average, Min & Max Link Delay on this interface\n") + "Disable Unidirectional Average, Min & Max Link Delay on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); /* Unset Delays */ @@ -2205,7 +2191,7 @@ DEFUN (link_params_delay_var, "Unidirectional Link Delay Variation\n" "delay variation in micro-second as decimal (0...16777215)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); u_int32_t value; @@ -2221,9 +2207,9 @@ DEFUN (no_link_params_delay_var, no_link_params_delay_var_cmd, "no delay-variation", NO_STR - "Disbale Unidirectional Delay Variation on this interface\n") + "Disable Unidirectional Delay Variation on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset Delay Variation */ link_param_cmd_unset(ifp, LP_DELAY_VAR); @@ -2237,7 +2223,7 @@ DEFUN (link_params_pkt_loss, "Unidirectional Link Packet Loss\n" "percentage of total traffic by 0.000003% step and less than 50.331642%\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); float fval; @@ -2261,9 +2247,9 @@ DEFUN (no_link_params_pkt_loss, no_link_params_pkt_loss_cmd, "no packet-loss", NO_STR - "Disbale Unidirectional Link Packet Loss on this interface\n") + "Disable Unidirectional Link Packet Loss on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset Packet Loss */ link_param_cmd_unset(ifp, LP_PKT_LOSS); @@ -2277,7 +2263,7 @@ DEFUN (link_params_res_bw, "Unidirectional Residual Bandwidth\n" "Bytes/second (IEEE floating point format)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); float bw; @@ -2307,9 +2293,9 @@ DEFUN (no_link_params_res_bw, no_link_params_res_bw_cmd, "no res-bw", NO_STR - "Disbale Unidirectional Residual Bandwidth on this interface\n") + "Disable Unidirectional Residual Bandwidth on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset Residual Bandwidth */ link_param_cmd_unset(ifp, LP_RES_BW); @@ -2323,7 +2309,7 @@ DEFUN (link_params_ava_bw, "Unidirectional Available Bandwidth\n" "Bytes/second (IEEE floating point format)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); float bw; @@ -2353,9 +2339,9 @@ DEFUN (no_link_params_ava_bw, no_link_params_ava_bw_cmd, "no ava-bw", NO_STR - "Disbale Unidirectional Available Bandwidth on this interface\n") + "Disable Unidirectional Available Bandwidth on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset Available Bandwidth */ link_param_cmd_unset(ifp, LP_AVA_BW); @@ -2369,7 +2355,7 @@ DEFUN (link_params_use_bw, "Unidirectional Utilised Bandwidth\n" "Bytes/second (IEEE floating point format)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct if_link_params *iflp = if_link_params_get (ifp); float bw; @@ -2399,9 +2385,9 @@ DEFUN (no_link_params_use_bw, no_link_params_use_bw_cmd, "no use-bw", NO_STR - "Disbale Unidirectional Utilised Bandwidth on this interface\n") + "Disable Unidirectional Utilised Bandwidth on this interface\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); /* Unset Utilised Bandwidth */ link_param_cmd_unset(ifp, LP_USE_BW); @@ -2557,7 +2543,8 @@ DEFUN (ip_address, "Set the IP address of an interface\n" "IP address (e.g. 10.0.0.1/8)\n") { - return ip_address_install (vty, vty->index, argv[0], NULL, NULL); + VTY_DECLVAR_CONTEXT (interface, ifp); + return ip_address_install (vty, ifp, argv[0], NULL, NULL); } DEFUN (no_ip_address, @@ -2568,7 +2555,8 @@ DEFUN (no_ip_address, "Set the IP address of an interface\n" "IP Address (e.g. 10.0.0.1/8)") { - return ip_address_uninstall (vty, vty->index, argv[0], NULL, NULL); + VTY_DECLVAR_CONTEXT (interface, ifp); + return ip_address_uninstall (vty, ifp, argv[0], NULL, NULL); } @@ -2582,7 +2570,8 @@ DEFUN (ip_address_label, "Label of this address\n" "Label\n") { - return ip_address_install (vty, vty->index, argv[0], NULL, argv[1]); + VTY_DECLVAR_CONTEXT (interface, ifp); + return ip_address_install (vty, ifp, argv[0], NULL, argv[1]); } DEFUN (no_ip_address_label, @@ -2595,7 +2584,8 @@ DEFUN (no_ip_address_label, "Label of this address\n" "Label\n") { - return ip_address_uninstall (vty, vty->index, argv[0], NULL, argv[1]); + VTY_DECLVAR_CONTEXT (interface, ifp); + return ip_address_uninstall (vty, ifp, argv[0], NULL, argv[1]); } #endif /* HAVE_NETLINK */ @@ -2758,7 +2748,8 @@ DEFUN (ipv6_address, "Set the IP address of an interface\n" "IPv6 address (e.g. 3ffe:506::1/48)\n") { - return ipv6_address_install (vty, vty->index, argv[0], NULL, NULL, 0); + VTY_DECLVAR_CONTEXT (interface, ifp); + return ipv6_address_install (vty, ifp, argv[0], NULL, NULL, 0); } DEFUN (no_ipv6_address, @@ -2769,7 +2760,8 @@ DEFUN (no_ipv6_address, "Set the IP address of an interface\n" "IPv6 address (e.g. 3ffe:506::1/48)\n") { - return ipv6_address_uninstall (vty, vty->index, argv[0], NULL, NULL, 0); + VTY_DECLVAR_CONTEXT (interface, ifp); + return ipv6_address_uninstall (vty, ifp, argv[0], NULL, NULL, 0); } #endif /* HAVE_IPV6 */ diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 8fb4fcad10..9d8c2e67bf 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -380,12 +380,7 @@ DEFUN (ip_irdp_multicast, IP_STR "ICMP Router discovery on this interface using multicast\n") { - struct interface *ifp; - - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } + VTY_DECLVAR_CONTEXT (interface, ifp); irdp_if_start(ifp, TRUE, TRUE); return CMD_SUCCESS; @@ -397,12 +392,7 @@ DEFUN (ip_irdp_broadcast, IP_STR "ICMP Router discovery on this interface using broadcast\n") { - struct interface *ifp; - - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } + VTY_DECLVAR_CONTEXT (interface, ifp); irdp_if_start(ifp, FALSE, TRUE); return CMD_SUCCESS; @@ -415,12 +405,7 @@ DEFUN (no_ip_irdp, IP_STR "Disable ICMP Router discovery on this interface\n") { - struct interface *ifp; - - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } + VTY_DECLVAR_CONTEXT (interface, ifp); irdp_if_stop(ifp); return CMD_SUCCESS; @@ -432,12 +417,7 @@ DEFUN (ip_irdp_shutdown, IP_STR "ICMP Router discovery shutdown on this interface\n") { - struct interface *ifp; - - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } + VTY_DECLVAR_CONTEXT (interface, ifp); irdp_if_shutdown(ifp); return CMD_SUCCESS; @@ -450,12 +430,7 @@ DEFUN (no_ip_irdp_shutdown, IP_STR "ICMP Router discovery no shutdown on this interface\n") { - struct interface *ifp; - - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } + VTY_DECLVAR_CONTEXT (interface, ifp); irdp_if_no_shutdown(ifp); return CMD_SUCCESS; @@ -469,13 +444,9 @@ DEFUN (ip_irdp_holdtime, "Set holdtime value\n" "Holdtime value in seconds. Default is 1800 seconds\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -492,13 +463,9 @@ DEFUN (ip_irdp_minadvertinterval, "Set minimum time between advertisement\n" "Minimum advertisement interval in seconds\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -525,13 +492,9 @@ DEFUN (ip_irdp_maxadvertinterval, "Set maximum time between advertisement\n" "Maximum advertisement interval in seconds\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -564,13 +527,9 @@ DEFUN (ip_irdp_preference, "Set default preference level for this interface\n" "Preference level\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -588,20 +547,15 @@ DEFUN (ip_irdp_address_preference, "Set IRDP address for advertise\n" "Preference level\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); struct listnode *node; struct in_addr ip; int pref; int ret; - struct interface *ifp; struct zebra_if *zi; struct irdp_interface *irdp; struct Adv *adv; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } - zi=ifp->info; irdp=&zi->irdp; @@ -633,19 +587,14 @@ DEFUN (no_ip_irdp_address_preference, "Select IRDP address\n" "Old preference level\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); struct listnode *node, *nnode; struct in_addr ip; int ret; - struct interface *ifp; struct zebra_if *zi; struct irdp_interface *irdp; struct Adv *adv; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } - zi=ifp->info; irdp=&zi->irdp; @@ -671,13 +620,9 @@ DEFUN (ip_irdp_debug_messages, IP_STR "ICMP Router discovery debug Averts. and Solicits (short)\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -693,13 +638,9 @@ DEFUN (ip_irdp_debug_misc, IP_STR "ICMP Router discovery debug Averts. and Solicits (short)\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -715,13 +656,9 @@ DEFUN (ip_irdp_debug_packet, IP_STR "ICMP Router discovery debug Averts. and Solicits (short)\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; @@ -738,13 +675,9 @@ DEFUN (ip_irdp_debug_disable, IP_STR "ICMP Router discovery debug Averts. and Solicits (short)\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zi; struct irdp_interface *irdp; - ifp = (struct interface *) vty->index; - if(!ifp) { - return CMD_WARNING; - } zi=ifp->info; irdp=&zi->irdp; diff --git a/zebra/main.c b/zebra/main.c index da7e6b6fb8..9247d43507 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -346,6 +346,7 @@ main (int argc, char **argv) /* Vty related initialize. */ signal_init (zebrad.master, array_size(zebra_signals), zebra_signals); cmd_init (1); + vty_config_lockless (); vty_init (zebrad.master); memory_init (); diff --git a/zebra/rtadv.c b/zebra/rtadv.c index ac297890a5..3e0a198702 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -871,10 +871,8 @@ DEFUN (ipv6_nd_suppress_ra, "Neighbor discovery\n" "Suppress Router Advertisement\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; if (if_is_loopback (ifp) || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) @@ -884,7 +882,6 @@ DEFUN (ipv6_nd_suppress_ra, } ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); - zif = ifp->info; zif->rtadv.configured = 0; return CMD_SUCCESS; } @@ -897,10 +894,8 @@ DEFUN (no_ipv6_nd_suppress_ra, "Neighbor discovery\n" "Suppress Router Advertisement\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; if (if_is_loopback (ifp) || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) @@ -910,7 +905,6 @@ DEFUN (no_ipv6_nd_suppress_ra, } ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); - zif = ifp->info; zif->rtadv.configured = 1; return CMD_SUCCESS; } @@ -923,8 +917,8 @@ DEFUN (ipv6_nd_ra_interval_msec, "Router Advertisement interval\n" "Router Advertisement interval in milliseconds\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); unsigned interval; - struct interface *ifp = (struct interface *) vty->index; struct zebra_if *zif = ifp->info; struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); struct zebra_ns *zns; @@ -958,8 +952,8 @@ DEFUN (ipv6_nd_ra_interval, "Router Advertisement interval\n" "Router Advertisement interval in seconds\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); unsigned interval; - struct interface *ifp = (struct interface *) vty->index; struct zebra_if *zif = ifp->info; struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); struct zebra_ns *zns; @@ -993,13 +987,11 @@ DEFUN (no_ipv6_nd_ra_interval, "Neighbor discovery\n" "Router Advertisement interval\n") { - struct interface *ifp; - struct zebra_if *zif; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; struct zebra_vrf *zvrf; struct zebra_ns *zns; - ifp = (struct interface *) vty->index; - zif = ifp->info; zvrf = vrf_info_lookup (ifp->vrf_id); zns = zvrf->zns; @@ -1038,12 +1030,9 @@ DEFUN (ipv6_nd_ra_lifetime, "Router lifetime\n" "Router lifetime in seconds (0 stands for a non-default gw)\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; int lifetime; - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; VTY_GET_INTEGER_RANGE ("router lifetime", lifetime, argv[0], 0, 9000); @@ -1070,11 +1059,8 @@ DEFUN (no_ipv6_nd_ra_lifetime, "Neighbor discovery\n" "Router lifetime\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvDefaultLifetime = -1; @@ -1098,7 +1084,7 @@ DEFUN (ipv6_nd_reachable_time, "Reachable time\n" "Reachable time in milliseconds\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zif = ifp->info; VTY_GET_INTEGER_RANGE ("reachable time", zif->rtadv.AdvReachableTime, argv[0], 1, RTADV_MAX_REACHABLE_TIME); return CMD_SUCCESS; @@ -1112,11 +1098,8 @@ DEFUN (no_ipv6_nd_reachable_time, "Neighbor discovery\n" "Reachable time\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvReachableTime = 0; @@ -1140,7 +1123,7 @@ DEFUN (ipv6_nd_homeagent_preference, "Home Agent preference\n" "preference value (default is 0, least preferred)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zif = ifp->info; VTY_GET_INTEGER_RANGE ("home agent preference", zif->rtadv.HomeAgentPreference, argv[0], 0, 65535); return CMD_SUCCESS; @@ -1154,11 +1137,8 @@ DEFUN (no_ipv6_nd_homeagent_preference, "Neighbor discovery\n" "Home Agent preference\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.HomeAgentPreference = 0; @@ -1182,7 +1162,7 @@ DEFUN (ipv6_nd_homeagent_lifetime, "Home Agent lifetime\n" "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zif = ifp->info; VTY_GET_INTEGER_RANGE ("home agent lifetime", zif->rtadv.HomeAgentLifetime, argv[0], 0, RTADV_MAX_HALIFETIME); return CMD_SUCCESS; @@ -1196,11 +1176,8 @@ DEFUN (no_ipv6_nd_homeagent_lifetime, "Neighbor discovery\n" "Home Agent lifetime\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.HomeAgentLifetime = -1; @@ -1223,11 +1200,8 @@ DEFUN (ipv6_nd_managed_config_flag, "Neighbor discovery\n" "Managed address configuration flag\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvManagedFlag = 1; @@ -1242,11 +1216,8 @@ DEFUN (no_ipv6_nd_managed_config_flag, "Neighbor discovery\n" "Managed address configuration flag\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvManagedFlag = 0; @@ -1260,11 +1231,8 @@ DEFUN (ipv6_nd_homeagent_config_flag, "Neighbor discovery\n" "Home Agent configuration flag\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvHomeAgentFlag = 1; @@ -1279,11 +1247,8 @@ DEFUN (no_ipv6_nd_homeagent_config_flag, "Neighbor discovery\n" "Home Agent configuration flag\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvHomeAgentFlag = 0; @@ -1297,11 +1262,8 @@ DEFUN (ipv6_nd_adv_interval_config_option, "Neighbor discovery\n" "Advertisement Interval Option\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvIntervalOption = 1; @@ -1316,11 +1278,8 @@ DEFUN (no_ipv6_nd_adv_interval_config_option, "Neighbor discovery\n" "Advertisement Interval Option\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvIntervalOption = 0; @@ -1334,11 +1293,8 @@ DEFUN (ipv6_nd_other_config_flag, "Neighbor discovery\n" "Other statefull configuration flag\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvOtherConfigFlag = 1; @@ -1353,11 +1309,8 @@ DEFUN (no_ipv6_nd_other_config_flag, "Neighbor discovery\n" "Other statefull configuration flag\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.AdvOtherConfigFlag = 0; @@ -1380,16 +1333,13 @@ DEFUN (ipv6_nd_prefix, "Do not use prefix for autoconfiguration\n" "Set Router Address flag\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zebra_if = ifp->info; int i; int ret; int cursor = 1; - struct interface *ifp; - struct zebra_if *zebra_if; struct rtadv_prefix rp; - ifp = (struct interface *) vty->index; - zebra_if = ifp->info; - ret = str2prefix_ipv6 (argv[0], &rp.prefix); if (!ret) { @@ -1611,14 +1561,11 @@ DEFUN (no_ipv6_nd_prefix, "Prefix information\n" "IPv6 prefix\n") { + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zebra_if = ifp->info; int ret; - struct interface *ifp; - struct zebra_if *zebra_if; struct rtadv_prefix rp; - ifp = (struct interface *) vty->index; - zebra_if = ifp->info; - ret = str2prefix_ipv6 (argv[0], &rp.prefix); if (!ret) { @@ -1801,13 +1748,10 @@ DEFUN (ipv6_nd_router_preference, "Low default router preference\n" "Medium default router preference (default)\n") { - struct interface *ifp; - struct zebra_if *zif; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; int i = 0; - ifp = (struct interface *) vty->index; - zif = ifp->info; - while (0 != rtadv_pref_strs[i]) { if (strncmp (argv[0], rtadv_pref_strs[i], 1) == 0) @@ -1829,11 +1773,8 @@ DEFUN (no_ipv6_nd_router_preference, "Neighbor discovery\n" "Default router preference\n") { - struct interface *ifp; - struct zebra_if *zif; - - ifp = (struct interface *) vty->index; - zif = ifp->info; + VTY_DECLVAR_CONTEXT (interface, ifp); + struct zebra_if *zif = ifp->info; zif->rtadv.DefaultPreference = RTADV_PREF_MEDIUM; /* Default per RFC4191. */ @@ -1859,7 +1800,7 @@ DEFUN (ipv6_nd_mtu, "Advertised MTU\n" "MTU in bytes\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zif = ifp->info; VTY_GET_INTEGER_RANGE ("MTU", zif->rtadv.AdvLinkMTU, argv[0], 1, 65535); return CMD_SUCCESS; @@ -1873,7 +1814,7 @@ DEFUN (no_ipv6_nd_mtu, "Neighbor discovery\n" "Advertised MTU\n") { - struct interface *ifp = (struct interface *) vty->index; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *zif = ifp->info; zif->rtadv.AdvLinkMTU = 0; return CMD_SUCCESS; diff --git a/zebra/test_main.c b/zebra/test_main.c index bbaf450282..828b61af91 100644 --- a/zebra/test_main.c +++ b/zebra/test_main.c @@ -124,11 +124,10 @@ DEFUN (test_interface_state, "up\n" "down\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); if (argc < 1) return CMD_WARNING; - - ifp = vty->index; + if (ifp->ifindex == IFINDEX_INTERNAL) { ifp->ifindex = ++test_ifindex; @@ -294,6 +293,7 @@ main (int argc, char **argv) /* Vty related initialize. */ signal_init (zebrad.master, array_size(zebra_signals), zebra_signals); cmd_init (1); + vty_config_lockless (); vty_init (zebrad.master); memory_init (); zebra_debug_init (); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 133b0fc2e9..ebae1bd4b9 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -298,12 +298,11 @@ DEFUN (zebra_ptm_enable_if, "ptm-enable", "Enable neighbor check with specified topology\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); struct zebra_if *if_data; int old_ptm_enable; int send_linkdown = 0; - ifp = (struct interface *) vty->index; if (ifp->ifindex == IFINDEX_INTERNAL) { return CMD_SUCCESS; @@ -338,12 +337,10 @@ DEFUN (no_zebra_ptm_enable_if, NO_STR "Enable neighbor check with specified topology\n") { - struct interface *ifp; + VTY_DECLVAR_CONTEXT (interface, ifp); int send_linkup = 0; struct zebra_if *if_data; - ifp = (struct interface *) vty->index; - if ((ifp->ifindex != IFINDEX_INTERNAL) && (ifp->ptm_enable)) { if (!if_is_operative(ifp)) diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index e6c5a3e917..685f5cc4b9 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -58,10 +58,11 @@ static void zebra_route_map_set_delay_timer(u_int32_t value); /* Add zebra route map rule */ static int -zebra_route_match_add(struct vty *vty, struct route_map_index *index, +zebra_route_match_add(struct vty *vty, const char *command, const char *arg, route_map_event_t type) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_add_match (index, command, arg); @@ -87,10 +88,11 @@ zebra_route_match_add(struct vty *vty, struct route_map_index *index, /* Delete zebra route map rule. */ static int -zebra_route_match_delete (struct vty *vty, struct route_map_index *index, +zebra_route_match_delete (struct vty *vty, const char *command, const char *arg, route_map_event_t type) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; char *dep_name = NULL; const char *tmpstr; @@ -138,9 +140,10 @@ zebra_route_match_delete (struct vty *vty, struct route_map_index *index, /* Add zebra route map rule. */ static int -zebra_route_set_add (struct vty *vty, struct route_map_index *index, +zebra_route_set_add (struct vty *vty, const char *command, const char *arg) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_add_set (index, command, arg); @@ -161,9 +164,10 @@ zebra_route_set_add (struct vty *vty, struct route_map_index *index, /* Delete zebra route map rule. */ static int -zebra_route_set_delete (struct vty *vty, struct route_map_index *index, +zebra_route_set_delete (struct vty *vty, const char *command, const char *arg) { + VTY_DECLVAR_CONTEXT (route_map_index, index); int ret; ret = route_map_delete_set (index, command, arg); @@ -301,7 +305,7 @@ DEFUN (match_interface, "match first hop interface of route\n" "Interface name\n") { - return zebra_route_match_add (vty, vty->index, "interface", argv[0], + return zebra_route_match_add (vty, "interface", argv[0], RMAP_EVENT_MATCH_ADDED); } @@ -313,9 +317,9 @@ DEFUN (no_match_interface, "Match first hop interface of route\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, "interface", NULL, RMAP_EVENT_MATCH_DELETED); + return zebra_route_match_delete (vty, "interface", NULL, RMAP_EVENT_MATCH_DELETED); - return zebra_route_match_delete (vty, vty->index, "interface", argv[0], RMAP_EVENT_MATCH_DELETED); + return zebra_route_match_delete (vty, "interface", argv[0], RMAP_EVENT_MATCH_DELETED); } ALIAS (no_match_interface, @@ -333,7 +337,7 @@ DEFUN (match_tag, "Match tag of route\n" "Tag value\n") { - return zebra_route_match_add (vty, vty->index, "tag", argv[0], + return zebra_route_match_add (vty, "tag", argv[0], RMAP_EVENT_MATCH_ADDED); } @@ -345,10 +349,10 @@ DEFUN (no_match_tag, "Match tag of route\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, "tag", NULL, + return zebra_route_match_delete (vty, "tag", NULL, RMAP_EVENT_MATCH_DELETED); - return zebra_route_match_delete (vty, vty->index, "tag", argv[0], + return zebra_route_match_delete (vty, "tag", argv[0], RMAP_EVENT_MATCH_DELETED); } @@ -369,7 +373,7 @@ DEFUN (match_ip_next_hop, "IP access-list number (expanded range)\n" "IP Access-list name\n") { - return zebra_route_match_add (vty, vty->index, "ip next-hop", argv[0], RMAP_EVENT_FILTER_ADDED); + return zebra_route_match_add (vty, "ip next-hop", argv[0], RMAP_EVENT_FILTER_ADDED); } DEFUN (no_match_ip_next_hop, @@ -381,10 +385,10 @@ DEFUN (no_match_ip_next_hop, "Match next-hop address of route\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, "ip next-hop", NULL, + return zebra_route_match_delete (vty, "ip next-hop", NULL, RMAP_EVENT_FILTER_DELETED); - return zebra_route_match_delete (vty, vty->index, "ip next-hop", argv[0], + return zebra_route_match_delete (vty, "ip next-hop", argv[0], RMAP_EVENT_FILTER_DELETED); } @@ -408,7 +412,7 @@ DEFUN (match_ip_next_hop_prefix_list, "Match entries of prefix-lists\n" "IP prefix-list name\n") { - return zebra_route_match_add (vty, vty->index, "ip next-hop prefix-list", + return zebra_route_match_add (vty, "ip next-hop prefix-list", argv[0], RMAP_EVENT_PLIST_ADDED); } @@ -422,11 +426,11 @@ DEFUN (no_match_ip_next_hop_prefix_list, "Match entries of prefix-lists\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip next-hop prefix-list", NULL, RMAP_EVENT_PLIST_DELETED); - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip next-hop prefix-list", argv[0], RMAP_EVENT_PLIST_DELETED); } @@ -452,7 +456,7 @@ DEFUN (match_ip_address, "IP Access-list name\n") { - return zebra_route_match_add (vty, vty->index, "ip address", argv[0], + return zebra_route_match_add (vty, "ip address", argv[0], RMAP_EVENT_FILTER_ADDED); } @@ -465,10 +469,10 @@ DEFUN (no_match_ip_address, "Match address of route\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, "ip address", NULL, + return zebra_route_match_delete (vty, "ip address", NULL, RMAP_EVENT_FILTER_DELETED); - return zebra_route_match_delete (vty, vty->index, "ip address", argv[0], + return zebra_route_match_delete (vty, "ip address", argv[0], RMAP_EVENT_FILTER_DELETED); } @@ -492,7 +496,7 @@ DEFUN (match_ip_address_prefix_list, "Match entries of prefix-lists\n" "IP prefix-list name\n") { - return zebra_route_match_add (vty, vty->index, "ip address prefix-list", + return zebra_route_match_add (vty, "ip address prefix-list", argv[0], RMAP_EVENT_PLIST_ADDED); } @@ -506,11 +510,11 @@ DEFUN (no_match_ip_address_prefix_list, "Match entries of prefix-lists\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip address prefix-list", NULL, RMAP_EVENT_PLIST_DELETED); - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip address prefix-list", argv[0], RMAP_EVENT_PLIST_DELETED); } @@ -534,7 +538,7 @@ DEFUN (match_ip_address_prefix_len, "Match prefix length of ip address\n" "Prefix length\n") { - return zebra_route_match_add (vty, vty->index, "ip address prefix-len", + return zebra_route_match_add (vty, "ip address prefix-len", argv[0], RMAP_EVENT_MATCH_ADDED); } @@ -548,11 +552,11 @@ DEFUN (no_match_ip_address_prefix_len, "prefix length of ip address\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip address prefix-len", NULL, RMAP_EVENT_MATCH_DELETED); - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip address prefix-len", argv[0], RMAP_EVENT_MATCH_DELETED); } @@ -575,7 +579,7 @@ DEFUN (match_ip_nexthop_prefix_len, "Match prefixlen of given nexthop\n" "Prefix length\n") { - return zebra_route_match_add (vty, vty->index, "ip next-hop prefix-len", + return zebra_route_match_add (vty, "ip next-hop prefix-len", argv[0], RMAP_EVENT_MATCH_ADDED); } @@ -589,11 +593,11 @@ DEFUN (no_match_ip_nexthop_prefix_len, "Match prefix length of nexthop\n") { if (argc == 0) - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip next-hop prefix-len", NULL, RMAP_EVENT_MATCH_DELETED); - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "ip next-hop prefix-len", argv[0], RMAP_EVENT_MATCH_DELETED); } @@ -620,7 +624,7 @@ DEFUN (match_source_protocol, VTY_NEWLINE); return CMD_WARNING; } - return zebra_route_match_add (vty, vty->index, "source-protocol", + return zebra_route_match_add (vty, "source-protocol", argv[0], RMAP_EVENT_MATCH_ADDED); } @@ -643,7 +647,7 @@ DEFUN (no_match_source_protocol, return CMD_WARNING; } } - return zebra_route_match_delete (vty, vty->index, + return zebra_route_match_delete (vty, "source-protocol", argv[0] ? argv[0] : NULL, RMAP_EVENT_MATCH_DELETED); } @@ -706,7 +710,7 @@ DEFUN (set_src, vty_out (vty, "%% not a local address%s", VTY_NEWLINE); return CMD_WARNING; } - return zebra_route_set_add (vty, vty->index, "src", argv[0]); + return zebra_route_set_add (vty, "src", argv[0]); } DEFUN (no_set_src, @@ -717,9 +721,9 @@ DEFUN (no_set_src, "Source address for route\n") { if (argc == 0) - return zebra_route_set_delete (vty, vty->index, "src", NULL); + return zebra_route_set_delete (vty, "src", NULL); - return zebra_route_set_delete (vty, vty->index, "src", argv[0]); + return zebra_route_set_delete (vty, "src", argv[0]); } DEFUN (zebra_route_map_timer, From 83418577afb992deed55296008debaf3fcad74d5 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Fri, 7 Oct 2016 14:13:26 +0000 Subject: [PATCH 112/136] bgpd: remove the "exit" at the end of "router bgp" Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp --- bgpd/bgpd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index c2af6852f9..7d47f18e54 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -7359,8 +7359,6 @@ bgp_config_write (struct vty *vty) write += bgp_rfapi_cfg_write(vty, bgp); #endif - vty_out (vty, " exit%s", VTY_NEWLINE); - write++; } return write; From e2e210ddf07c843199e24d8ff28381e23c926136 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Oct 2016 12:20:33 -0400 Subject: [PATCH 113/136] lib: Fix ordering issue in protocol names The protocol names and enum must be kept in the same order. Signed-off-by: Donald Sharp --- lib/log.c | 4 ++++ lib/log.h | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/log.c b/lib/log.c index 49c69efc8a..192e569d96 100644 --- a/lib/log.c +++ b/lib/log.c @@ -41,6 +41,10 @@ static int logfile_fd = -1; /* Used in signal handler. */ struct zlog *zlog_default = NULL; +/* + * This must be kept in the same order as the + * zlog_proto_t enum + */ const char *zlog_proto_names[] = { "NONE", diff --git a/lib/log.h b/lib/log.h index 91cab3f96a..551f12f3fe 100644 --- a/lib/log.h +++ b/lib/log.h @@ -41,6 +41,10 @@ * please use LOG_ERR instead. */ +/* + * This must be kept in the same order as + * zlog_proto_names[] + */ typedef enum { ZLOG_NONE, @@ -53,9 +57,9 @@ typedef enum ZLOG_OSPF6, ZLOG_LDP, ZLOG_ISIS, - ZLOG_RFP, ZLOG_PIM, - ZLOG_MASC + ZLOG_MASC, + ZLOG_RFP, } zlog_proto_t; /* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent From b10c5b07022e404dc36a9b50d592b131f356c54a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Oct 2016 12:35:06 -0400 Subject: [PATCH 114/136] lib: Remove unknown protocol. What is MASC anyways? Signed-off-by: Donald Sharp --- lib/log.c | 1 - lib/log.h | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/log.c b/lib/log.c index 192e569d96..cd1f0bb771 100644 --- a/lib/log.c +++ b/lib/log.c @@ -58,7 +58,6 @@ const char *zlog_proto_names[] = "LDP", "ISIS", "PIM", - "MASC", "RFP", NULL, }; diff --git a/lib/log.h b/lib/log.h index 551f12f3fe..d71b85df1a 100644 --- a/lib/log.h +++ b/lib/log.h @@ -58,7 +58,6 @@ typedef enum ZLOG_LDP, ZLOG_ISIS, ZLOG_PIM, - ZLOG_MASC, ZLOG_RFP, } zlog_proto_t; From 822835927b36cf84677e9081a1a7eaf84db89b8a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 22 Dec 2015 15:24:25 -0500 Subject: [PATCH 115/136] zebra: Fix some warnings found during compile. This commit fixes some warnings found in Martin's Testbed that compiles sun solaris and freebsd images. Signed-off-by: Donald Sharp --- zebra/if_ioctl.c | 1 + zebra/if_ioctl_solaris.c | 1 + zebra/ioctl_solaris.c | 1 + zebra/rtread_getmsg.c | 1 + 4 files changed, 4 insertions(+) diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index 101529c321..5b7b5863e5 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -31,6 +31,7 @@ #include "zebra_memory.h" #include "log.h" #include "vrf.h" +#include "vty.h" #include "zebra/interface.h" #include "zebra/rib.h" diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index 45a45f3e81..0e727b9dc4 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -32,6 +32,7 @@ #include "log.h" #include "privs.h" #include "vrf.h" +#include "vty.h" #include "zebra/interface.h" #include "zebra/ioctl_solaris.h" diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index 12737cbf06..b5bf1ccb0a 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -28,6 +28,7 @@ #include "ioctl.h" #include "log.h" #include "privs.h" +#include "vty.h" #include "zebra/rib.h" #include "zebra/rt.h" diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 0facc1a19f..c6eee75174 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -26,6 +26,7 @@ #include "log.h" #include "if.h" #include "vrf.h" +#include "vty.h" #include "zebra/rib.h" #include "zebra/zserv.h" From e7331dea737788c1e7590eac104430aa98cf9f38 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 1 Oct 2016 04:06:03 +0200 Subject: [PATCH 116/136] ospfd: Update route in zebra when tag changes Signed-off-by: Christian Franke --- ospfd/ospf_ase.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 74c1711ef1..fe40b10171 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -598,6 +598,10 @@ ospf_ase_route_match_same (struct route_table *rt, struct prefix *prefix, if (op->ifindex != newop->ifindex) return 0; } + + if (or->u.ext.tag != newor->u.ext.tag) + return 0; + return 1; } From dc9ffce87868441da9653d1476a4307eb2ecd996 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 1 Oct 2016 20:42:34 +0200 Subject: [PATCH 117/136] *: Consistently support 32-bit route tags This patch improves zebra,ripd,ripngd,ospfd and bgpd so that they can make use of 32-bit route tags in the case of zebra,ospf,bgp or 16-bit route-tags in the case of ripd,ripngd. It is based on the following patch: commit d25764028829a3a30cdbabe85f32408a63cccadf Author: Paul Jakma Date: Fri Jul 1 14:23:45 2016 +0100 *: Widen width of Zserv routing tag field. But also contains the changes which make this actually useful for all the daemons. Signed-off-by: Christian Franke --- bgpd/bgp_attr.h | 2 +- bgpd/bgp_route.c | 4 +- bgpd/bgp_route.h | 2 +- bgpd/bgp_routemap.c | 91 +++-------------- bgpd/bgp_zebra.c | 24 ++--- lib/routemap.c | 26 +++++ lib/routemap.h | 3 + lib/zclient.c | 8 +- lib/zclient.h | 4 +- lib/zebra.h | 4 + ospfd/ospf_asbr.c | 2 +- ospfd/ospf_asbr.h | 4 +- ospfd/ospf_dump.c | 2 +- ospfd/ospf_lsa.c | 2 +- ospfd/ospf_routemap.c | 89 +++-------------- ospfd/ospf_vty.c | 16 +-- ospfd/ospf_zebra.c | 8 +- ospfd/ospfd.h | 2 +- pimd/pim_zebra.c | 2 +- ripd/rip_routemap.c | 78 +++------------ ripd/ripd.c | 15 +-- ripd/ripd.h | 2 +- ripngd/ripng_route.h | 4 +- ripngd/ripng_routemap.c | 66 +++--------- ripngd/ripngd.c | 20 ++-- ripngd/ripngd.h | 4 +- zebra/rib.h | 7 +- zebra/zebra_mpls_vty.c | 32 +++--- zebra/zebra_routemap.c | 54 ++-------- zebra/zebra_routemap.h | 4 +- zebra/zebra_static.c | 4 +- zebra/zebra_static.h | 6 +- zebra/zebra_vty.c | 216 ++++++++++++++++++++-------------------- zebra/zserv.c | 12 +-- 34 files changed, 301 insertions(+), 518 deletions(-) diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 002bdfa081..d4f45ba60a 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -118,7 +118,7 @@ struct attr_extra u_char mp_nexthop_prefer_global; /* route tag */ - u_short tag; + route_tag_t tag; uint16_t encap_tunneltype; /* grr */ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index bace5ab31e..923acfda67 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5820,7 +5820,7 @@ ALIAS (no_ipv6_aggregate_address_summary_only, void bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *nexthop, const struct in6_addr *nexthop6, unsigned int ifindex, - u_int32_t metric, u_char type, u_short instance, u_short tag) + u_int32_t metric, u_char type, u_short instance, route_tag_t tag) { struct bgp_info *new; struct bgp_info *bi; @@ -7185,7 +7185,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (json_paths) json_object_int_add(json_path, "tag", attr->extra->tag); else - vty_out (vty, ", tag %d", attr->extra->tag); + vty_out (vty, ", tag %"ROUTE_TAG_PRI, attr->extra->tag); } if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID)) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 3d65b4b0a9..0dce5da572 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -271,7 +271,7 @@ extern int bgp_maximum_prefix_overflow (struct peer *, afi_t, safi_t, int); extern void bgp_redistribute_add (struct bgp *, struct prefix *, const struct in_addr *, const struct in6_addr *, unsigned int ifindex, - u_int32_t, u_char, u_short, u_short); + u_int32_t, u_char, u_short, route_tag_t); extern void bgp_redistribute_delete (struct bgp *, struct prefix *, u_char, u_short); extern void bgp_redistribute_withdraw (struct bgp *, afi_t, int, u_short); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index beab1969b6..acb7449a09 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1070,7 +1070,7 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct bgp_info *bgp_info; if (type == RMAP_BGP) @@ -1088,46 +1088,13 @@ route_match_tag (void *rule, struct prefix *prefix, } -/* Route map `match tag' match statement. `arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - u_short tmp; - - /* tag value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - tmp = atoi(arg); - if (tmp < 1) - return NULL; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - - if (!tag) - return tag; - - *tag = tmp; - - return tag; -} - - -/* Free route map's compiled 'match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag matching. */ -struct route_map_rule_cmd route_match_tag_cmd = +static struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, - route_match_tag_compile, - route_match_tag_free, + route_map_rule_tag_compile, + route_map_rule_tag_free, }; @@ -1924,7 +1891,7 @@ static route_map_result_t route_set_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct bgp_info *bgp_info; struct attr_extra *ae; @@ -1942,47 +1909,13 @@ route_set_tag (void *rule, struct prefix *prefix, return RMAP_OKAY; } -/* Route map `tag' compile function. Given string is converted to u_short. */ -static void * -route_set_tag_compile (const char *arg) -{ - u_short *tag; - u_short tmp; - - /* tag value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - tmp = atoi(arg); - - if (tmp < 1) - return NULL; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - - if (!tag) - return tag; - - *tag = tmp; - - return tag; -} - -/* Free route map's tag value. */ -static void -route_set_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - - /* Route map commands for tag set. */ -struct route_map_rule_cmd route_set_tag_cmd = +static struct route_map_rule_cmd route_set_tag_cmd = { "tag", route_set_tag, - route_set_tag_compile, - route_set_tag_free, + route_map_rule_tag_compile, + route_map_rule_tag_free, }; @@ -3605,7 +3538,7 @@ ALIAS (no_match_interface, DEFUN (match_tag, match_tag_cmd, - "match tag <1-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Tag value\n") @@ -3631,7 +3564,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <1-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n" @@ -4313,7 +4246,7 @@ ALIAS (no_set_aggregator_as, DEFUN (set_tag, set_tag_cmd, - "set tag <1-65535>", + "set tag <1-4294967295>", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -4336,7 +4269,7 @@ DEFUN (no_set_tag, ALIAS (no_set_tag, no_set_tag_val_cmd, - "no set tag <1-65535>", + "no set tag <1-4294967295>", NO_STR SET_STR "Tag value for routing protocol\n" diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index d524c41a28..789dba53f8 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -640,7 +640,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, api.metric = 0; if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) - api.tag = stream_getw (s); + api.tag = stream_getl (s); else api.tag = 0; @@ -649,7 +649,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, if (bgp_debug_zebra((struct prefix *)&p)) { char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Rx IPv4 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %d", + zlog_debug("Rx IPv4 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %"ROUTE_TAG_PRI, vrf_id, zebra_route_string(api.type), api.instance, inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])), @@ -681,7 +681,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, { char buf[2][INET_ADDRSTRLEN]; zlog_debug("Rx IPv4 route delete VRF %u %s[%d] %s/%d " - "nexthop %s metric %u tag %d", + "nexthop %s metric %u tag %"ROUTE_TAG_PRI, vrf_id, zebra_route_string(api.type), api.instance, inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])), @@ -757,7 +757,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, api.metric = 0; if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) - api.tag = stream_getw (s); + api.tag = stream_getl (s); else api.tag = 0; @@ -770,7 +770,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, if (bgp_debug_zebra((struct prefix *)&p)) { char buf[2][INET6_ADDRSTRLEN]; - zlog_debug("Rx IPv6 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %d", + zlog_debug("Rx IPv6 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %"ROUTE_TAG_PRI, vrf_id, zebra_route_string(api.type), api.instance, inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])), @@ -801,7 +801,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, { char buf[2][INET6_ADDRSTRLEN]; zlog_debug("Rx IPv6 route delete VRF %u %s[%d] %s/%d " - "nexthop %s metric %u tag %d", + "nexthop %s metric %u tag %"ROUTE_TAG_PRI, vrf_id, zebra_route_string(api.type), api.instance, inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])), @@ -1211,7 +1211,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, u_int32_t nhcount, metric; struct bgp_info local_info; struct bgp_info *info_cp = &local_info; - u_short tag; + route_tag_t tag; /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. @@ -1376,7 +1376,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if (bgp_debug_zebra(p)) { int i; - zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %d" + zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI " count %d", (valid_nh_count ? "add":"delete"), bgp->vrf_id, inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), @@ -1557,7 +1557,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if (bgp_debug_zebra(p)) { int i; - zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %d", + zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI, valid_nh_count ? "add" : "delete", bgp->vrf_id, inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag); @@ -1579,7 +1579,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if (bgp_debug_zebra(p)) { int i; - zlog_debug("Tx IPv6 route %s VRF %u %s/%d metric %u tag %d", + zlog_debug("Tx IPv6 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI, valid_nh_count ? "add" : "delete", bgp->vrf_id, inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag); @@ -1683,7 +1683,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) if (bgp_debug_zebra(p)) { char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Tx IPv4 route delete VRF %u %s/%d metric %u tag %d", + zlog_debug("Tx IPv4 route delete VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI, peer->bgp->vrf_id, inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag); @@ -1723,7 +1723,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) if (bgp_debug_zebra(p)) { char buf[2][INET6_ADDRSTRLEN]; - zlog_debug("Tx IPv6 route delete VRF %u %s/%d metric %u tag %d", + zlog_debug("Tx IPv6 route delete VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI, peer->bgp->vrf_id, inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag); diff --git a/lib/routemap.c b/lib/routemap.c index e0a7080bbf..6f93087ae9 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1829,6 +1829,32 @@ route_map_init_dep_hashes (void) route_map_dep_hash_cmp); } +/* Common route map rules */ + +void * +route_map_rule_tag_compile (const char *arg) +{ + unsigned long int tmp; + char *endptr; + route_tag_t *tag; + + errno = 0; + tmp = strtoul(arg, &endptr, 0); + if (arg[0] == '\0' || *endptr != '\0' || errno || tmp > ROUTE_TAG_MAX) + return NULL; + + tag = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*tag)); + *tag = tmp; + + return tag; +} + +void +route_map_rule_tag_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + /* Initialization of route map vector. */ void route_map_init_vty (void) diff --git a/lib/routemap.h b/lib/routemap.h index 427a552e7f..0f888897b2 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -235,4 +235,7 @@ extern void route_map_upd8_dependency (route_map_event_t type, const char *arg, extern void route_map_notify_dependencies (const char *affected_name, route_map_event_t event); +extern void *route_map_rule_tag_compile (const char *arg); +extern void route_map_rule_tag_free (void *rule); + #endif /* _ZEBRA_ROUTEMAP_H */ diff --git a/lib/zclient.c b/lib/zclient.c index 24d9b589df..fa8150c5a1 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -722,7 +722,7 @@ zclient_connect (struct thread *t) * If ZAPI_MESSAGE_METRIC is set, the metric value is written as an 8 * byte value. * - * If ZAPI_MESSAGE_TAG is set, the tag value is written as a 2 byte value + * If ZAPI_MESSAGE_TAG is set, the tag value is written as a 4 byte value * * If ZAPI_MESSAGE_MTU is set, the mtu value is written as a 4 byte value * @@ -785,7 +785,7 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG)) - stream_putw (s, api->tag); + stream_putl (s, api->tag); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU)) stream_putl (s, api->mtu); @@ -852,7 +852,7 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG)) - stream_putw (s, api->tag); + stream_putl (s, api->tag); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU)) stream_putl (s, api->mtu); @@ -918,7 +918,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG)) - stream_putw (s, api->tag); + stream_putl (s, api->tag); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU)) stream_putl (s, api->mtu); diff --git a/lib/zclient.h b/lib/zclient.h index 4edbd7636e..f122b233b9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -150,7 +150,7 @@ struct zapi_ipv4 u_int32_t metric; - u_short tag; + route_tag_t tag; u_int32_t mtu; @@ -238,7 +238,7 @@ struct zapi_ipv6 u_int32_t metric; - u_short tag; + route_tag_t tag; u_int32_t mtu; diff --git a/lib/zebra.h b/lib/zebra.h index e625b4051b..08c50c68bc 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -528,4 +528,8 @@ typedef u_int16_t zebra_command_t; /* VRF ID type. */ typedef u_int16_t vrf_id_t; +typedef uint32_t route_tag_t; +#define ROUTE_TAG_MAX UINT32_MAX +#define ROUTE_TAG_PRI PRIu32 + #endif /* _ZEBRA_H */ diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 21f99af128..284c564688 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -137,7 +137,7 @@ ospf_route_map_set_compare (struct route_map_set_values *values1, struct external_info * ospf_external_info_add (u_char type, u_short instance, struct prefix_ipv4 p, ifindex_t ifindex, struct in_addr nexthop, - u_short tag) + route_tag_t tag) { struct external_info *new; struct route_node *rn; diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h index 25a53aad4f..e2fa367455 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -47,7 +47,7 @@ struct external_info struct in_addr nexthop; /* Additional Route tag. */ - u_int32_t tag; + route_tag_t tag; struct route_map_set_values route_map_set; #define ROUTEMAP_METRIC(E) (E)->route_map_set.metric @@ -65,7 +65,7 @@ extern struct external_info *ospf_external_info_add (u_char, u_short, struct prefix_ipv4, ifindex_t, struct in_addr, - u_short); + route_tag_t); extern void ospf_external_info_delete (u_char, u_short, struct prefix_ipv4); extern struct external_info *ospf_external_info_lookup (u_char, u_short, struct prefix_ipv4 *); diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index a53c726853..8fa2258f4f 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -493,7 +493,7 @@ ospf_as_external_lsa_dump (struct stream *s, u_int16_t length) IS_EXTERNAL_METRIC (al->e[i].tos) ? "E" : "-", al->e[i].tos & 0x7f, GET_METRIC (al->e[i].metric)); zlog_debug (" Forwarding address %s", inet_ntoa (al->e[i].fwd_addr)); - zlog_debug (" External Route Tag %d", al->e[i].route_tag); + zlog_debug (" External Route Tag %"ROUTE_TAG_PRI, al->e[i].route_tag); } } diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index b96ed452c1..916d4d01c9 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1673,7 +1673,7 @@ ospf_external_lsa_body_set (struct stream *s, struct external_info *ei, /* Put forwarding address. */ stream_put_ipv4 (s, fwd_addr.s_addr); - /* Put route tag -- only first 16bits are used for compatibility */ + /* Put route tag */ stream_putl (s, ei->tag); } diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index b2f1c67126..6bd853bf89 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -443,7 +443,7 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct external_info *ei; if (type == RMAP_OSPF) @@ -457,45 +457,13 @@ route_match_tag (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map `match tag' match statement. `arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - u_short tmp; - - /* tag value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - tmp = atoi(arg); - if (tmp < 1) - return NULL; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - - if (!tag) - return tag; - - *tag = tmp; - - return tag; -} - -/* Free route map's compiled 'match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag matching. */ -struct route_map_rule_cmd route_match_tag_cmd = +static struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, - route_match_tag_compile, - route_match_tag_free, + route_map_rule_tag_compile, + route_map_rule_tag_free, }; @@ -633,7 +601,7 @@ static route_map_result_t route_set_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct external_info *ei; if (type == RMAP_OSPF) @@ -648,46 +616,13 @@ route_set_tag (void *rule, struct prefix *prefix, return RMAP_OKAY; } -/* Route map `tag' compile function. Given string is converted to u_short. */ -static void * -route_set_tag_compile (const char *arg) -{ - u_short *tag; - u_short tmp; - - /* tag value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - tmp = atoi(arg); - - if (tmp < 1) - return NULL; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - - if (!tag) - return tag; - - *tag = tmp; - - return tag; -} - -/* Free route map's tag value. */ -static void -route_set_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag set. */ -struct route_map_rule_cmd route_set_tag_cmd = +static struct route_map_rule_cmd route_set_tag_cmd = { "tag", route_set_tag, - route_set_tag_compile, - route_set_tag_free, + route_map_rule_tag_compile, + route_map_rule_tag_free, }; DEFUN (match_ip_nexthop, @@ -877,7 +812,7 @@ ALIAS (no_match_interface, DEFUN (match_tag, match_tag_cmd, - "match tag <1-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Tag value\n") @@ -900,7 +835,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <1-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n" @@ -977,7 +912,7 @@ ALIAS (no_set_metric_type, DEFUN (set_tag, set_tag_cmd, - "set tag <1-65535>", + "set tag <1-4294967295>", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -1000,7 +935,7 @@ DEFUN (no_set_tag, ALIAS (no_set_tag, no_set_tag_val_cmd, - "no set tag <1-65535>", + "no set tag <1-4294967295>", NO_STR SET_STR "Tag value for routing protocol\n" diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index d9a4289edf..e76d9d7065 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -5350,8 +5350,8 @@ show_as_external_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) vty_out (vty, " Forward Address: %s%s", inet_ntoa (al->e[0].fwd_addr), VTY_NEWLINE); - vty_out (vty, " External Route Tag: %lu%s%s", - (u_long)ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, " External Route Tag: %"ROUTE_TAG_PRI"%s%s", + (route_tag_t)ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE); } return 0; @@ -5376,8 +5376,8 @@ show_as_external_lsa_stdvty (struct ospf_lsa *lsa) zlog_debug( " Forward Address: %s%s", inet_ntoa (al->e[0].fwd_addr), "\n"); - zlog_debug( " External Route Tag: %u%s%s", - ntohl (al->e[0].route_tag), "\n", "\n"); + zlog_debug( " External Route Tag: %"ROUTE_TAG_PRI"%s%s", + (route_tag_t)ntohl (al->e[0].route_tag), "\n", "\n"); return 0; } @@ -5404,8 +5404,8 @@ show_as_nssa_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) vty_out (vty, " NSSA: Forward Address: %s%s", inet_ntoa (al->e[0].fwd_addr), VTY_NEWLINE); - vty_out (vty, " External Route Tag: %u%s%s", - ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, " External Route Tag: %"ROUTE_TAG_PRI"%s%s", + (route_tag_t)ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE); } return 0; @@ -9067,11 +9067,11 @@ show_ip_ospf_route_external (struct vty *vty, struct route_table *rt) switch (er->path_type) { case OSPF_PATH_TYPE1_EXTERNAL: - vty_out (vty, "N E1 %-18s [%d] tag: %u%s", buf1, + vty_out (vty, "N E1 %-18s [%d] tag: %"ROUTE_TAG_PRI"%s", buf1, er->cost, er->u.ext.tag, VTY_NEWLINE); break; case OSPF_PATH_TYPE2_EXTERNAL: - vty_out (vty, "N E2 %-18s [%d/%d] tag: %u%s", buf1, er->cost, + vty_out (vty, "N E2 %-18s [%d/%d] tag: %"ROUTE_TAG_PRI"%s", buf1, er->cost, er->u.ext.type2_cost, er->u.ext.tag, VTY_NEWLINE); break; } diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 4458e95ba0..b0ff5d0e3c 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -379,10 +379,10 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) if (distance) SET_FLAG (message, ZAPI_MESSAGE_DISTANCE); - /* Check if path type is ASE and use only 16bit tags */ + /* Check if path type is ASE */ if (((or->path_type == OSPF_PATH_TYPE1_EXTERNAL) || (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) && - (or->u.ext.tag > 0) && (or->u.ext.tag < UINT16_MAX)) + (or->u.ext.tag > 0) && (or->u.ext.tag <= ROUTE_TAG_MAX)) SET_FLAG (message, ZAPI_MESSAGE_TAG); /* Make packet. */ @@ -479,7 +479,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) } if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) - stream_putw (s, (u_short)or->u.ext.tag); + stream_putl (s, or->u.ext.tag); stream_putw_at (s, 0, stream_get_endp (s)); @@ -1093,7 +1093,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) api.metric = stream_getl (s); if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) - api.tag = stream_getw (s); + api.tag = stream_getl (s); else api.tag = 0; diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index bc4acf993b..93b5ab766a 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -269,7 +269,7 @@ struct ospf struct list *redist[ZEBRA_ROUTE_MAX + 1]; /* Redistribute tag info. */ - u_short dtag[ZEBRA_ROUTE_MAX + 1]; //Pending: cant configure as of now + route_tag_t dtag[ZEBRA_ROUTE_MAX + 1]; //Pending: cant configure as of now int default_metric; /* Default metric for redistribute. */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 1a8d5f22bc..1822de2299 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -610,7 +610,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, 0; if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) - api.tag = stream_getw (s); + api.tag = stream_getl (s); else api.tag = 0; diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index e7263ad7be..666ccf0844 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -463,8 +463,9 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct rip_info *rinfo; + route_tag_t rinfo_tag; if (type == RMAP_RIP) { @@ -472,7 +473,8 @@ route_match_tag (void *rule, struct prefix *prefix, rinfo = object; /* The information stored by rinfo is host ordered. */ - if (rinfo->tag == *tag) + rinfo_tag = rinfo->tag; + if (rinfo_tag == *tag) return RMAP_MATCH; else return RMAP_NOMATCH; @@ -480,45 +482,13 @@ route_match_tag (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map `match tag' match statement. `arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - u_short tmp; - - /* tag value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - tmp = atoi(arg); - if (tmp < 1) - return NULL; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - - if (!tag) - return tag; - - *tag = tmp; - - return tag; -} - -/* Free route map's compiled `match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag matching. */ -struct route_map_rule_cmd route_match_tag_cmd = +static struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, - route_match_tag_compile, - route_match_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free, }; /* `set metric METRIC' */ @@ -687,7 +657,7 @@ static route_map_result_t route_set_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct rip_info *rinfo; if(type == RMAP_RIP) @@ -703,33 +673,13 @@ route_set_tag (void *rule, struct prefix *prefix, return RMAP_OKAY; } -/* Route map `tag' compile function. Given string is converted - to u_short. */ -static void * -route_set_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `ip nexthop' value. */ -static void -route_set_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag set. */ static struct route_map_rule_cmd route_set_tag_cmd = { "tag", route_set_tag, - route_set_tag_compile, - route_set_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free }; #define MATCH_STR "Match values from routing table\n" @@ -950,7 +900,7 @@ ALIAS (no_match_ip_address_prefix_list, DEFUN (match_tag, match_tag_cmd, - "match tag <1-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -973,7 +923,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <1-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n" @@ -1080,7 +1030,7 @@ ALIAS (no_set_ip_nexthop, DEFUN (set_tag, set_tag_cmd, - "set tag <1-65535>", + "set tag <1-4294967295>", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -1103,7 +1053,7 @@ DEFUN (no_set_tag, ALIAS (no_set_tag, no_set_tag_val_cmd, - "no set tag <1-65535>", + "no set tag <1-4294967295>", NO_STR SET_STR "Tag value for routing protocol\n" diff --git a/ripd/ripd.c b/ripd/ripd.c index 22ba2fe998..220297e835 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -760,17 +760,18 @@ rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv) } } else - zlog_debug (" %s/%d -> %s family %d tag %d metric %ld", + zlog_debug (" %s/%d -> %s family %d tag %"ROUTE_TAG_PRI" metric %ld", inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ), netmask, inet_ntop (AF_INET, &rte->nexthop, nbuf, BUFSIZ), ntohs (rte->family), - ntohs (rte->tag), (u_long) ntohl (rte->metric)); + (route_tag_t)ntohs (rte->tag), + (u_long) ntohl (rte->metric)); } else { - zlog_debug (" %s family %d tag %d metric %ld", + zlog_debug (" %s family %d tag %"ROUTE_TAG_PRI" metric %ld", inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ), - ntohs (rte->family), ntohs (rte->tag), + ntohs (rte->family), (route_tag_t)ntohs (rte->tag), (u_long)ntohl (rte->metric)); } } @@ -3551,13 +3552,13 @@ DEFUN (show_ip_rip, (rinfo->sub_type == RIP_ROUTE_RTE)) { vty_out (vty, "%-15s ", inet_ntoa (rinfo->from)); - vty_out (vty, "%3d ", rinfo->tag); + vty_out (vty, "%3"ROUTE_TAG_PRI" ", (route_tag_t)rinfo->tag); rip_vty_out_uptime (vty, rinfo); } else if (rinfo->metric == RIP_METRIC_INFINITY) { vty_out (vty, "self "); - vty_out (vty, "%3d ", rinfo->tag); + vty_out (vty, "%3"ROUTE_TAG_PRI" ", (route_tag_t)rinfo->tag); rip_vty_out_uptime (vty, rinfo); } else @@ -3573,7 +3574,7 @@ DEFUN (show_ip_rip, } else vty_out (vty, "self "); - vty_out (vty, "%3d", rinfo->tag); + vty_out (vty, "%3"ROUTE_TAG_PRI, (route_tag_t)rinfo->tag); } vty_out (vty, "%s", VTY_NEWLINE); diff --git a/ripd/ripd.h b/ripd/ripd.h index 588da1d5f7..2d5bd98de8 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -225,7 +225,7 @@ struct rip_info struct in_addr nexthop_out; u_char metric_set; u_int32_t metric_out; - u_short tag_out; + u_int16_t tag_out; ifindex_t ifindex_out; struct route_node *rp; diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h index fe65c88363..9ff90aa8d0 100644 --- a/ripngd/ripng_route.h +++ b/ripngd/ripng_route.h @@ -35,13 +35,13 @@ struct ripng_aggregate u_char metric; /* Tag field of RIPng packet.*/ - u_short tag; + u_int16_t tag; /* Route-map futures - this variables can be changed. */ struct in6_addr nexthop_out; u_char metric_set; u_char metric_out; - u_short tag_out; + u_int16_t tag_out; }; extern void ripng_aggregate_increment (struct route_node *rp, diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 9bda2e260d..ee4f4afcec 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -240,8 +240,9 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct ripng_info *rinfo; + route_tag_t rinfo_tag; if (type == RMAP_RIPNG) { @@ -249,7 +250,8 @@ route_match_tag (void *rule, struct prefix *prefix, rinfo = object; /* The information stored by rinfo is host ordered. */ - if (rinfo->tag == *tag) + rinfo_tag = rinfo->tag; + if (rinfo_tag == *tag) return RMAP_MATCH; else return RMAP_NOMATCH; @@ -257,32 +259,12 @@ route_match_tag (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map `match tag' match statement. `arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - -/* Route map commands for tag matching. */ static struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, - route_match_tag_compile, - route_match_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free, }; /* `set metric METRIC' */ @@ -452,7 +434,7 @@ static route_map_result_t route_set_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct ripng_info *rinfo; if(type == RMAP_RIPNG) @@ -460,7 +442,7 @@ route_set_tag (void *rule, struct prefix *prefix, /* Fetch routemap's rule information. */ tag = rule; rinfo = object; - + /* Set next hop value. */ rinfo->tag_out = *tag; } @@ -468,33 +450,13 @@ route_set_tag (void *rule, struct prefix *prefix, return RMAP_OKAY; } -/* Route map `tag' compile function. Given string is converted - to u_short. */ -static void * -route_set_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `ip nexthop' value. */ -static void -route_set_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag set. */ static struct route_map_rule_cmd route_set_tag_cmd = { "tag", route_set_tag, - route_set_tag_compile, - route_set_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free }; #define MATCH_STR "Match values from routing table\n" @@ -564,7 +526,7 @@ ALIAS (no_match_interface, DEFUN (match_tag, match_tag_cmd, - "match tag <1-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -587,7 +549,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <1-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n" @@ -681,7 +643,7 @@ ALIAS (no_set_ipv6_nexthop_local, DEFUN (set_tag, set_tag_cmd, - "set tag <1-65535>", + "set tag <1-4294967295>", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -704,7 +666,7 @@ DEFUN (no_set_tag, ALIAS (no_set_tag, no_set_tag_val_cmd, - "no set tag <1-65535>", + "no set tag <1-4294967295>", NO_STR SET_STR "Tag value for routing protocol\n" diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 7d2c66b0b2..0c9606e69c 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -321,9 +321,9 @@ ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv) if (rte->metric == RIPNG_METRIC_NEXTHOP) zlog_debug (" nexthop %s/%d", inet6_ntoa (rte->addr), rte->prefixlen); else - zlog_debug (" %s/%d metric %d tag %d", + zlog_debug (" %s/%d metric %d tag %"ROUTE_TAG_PRI, inet6_ntoa (rte->addr), rte->prefixlen, - rte->metric, ntohs (rte->tag)); + rte->metric, (route_tag_t)ntohs (rte->tag)); } } @@ -337,15 +337,15 @@ ripng_nexthop_rte (struct rte *rte, /* Logging before checking RTE. */ if (IS_RIPNG_DEBUG_RECV) - zlog_debug ("RIPng nexthop RTE address %s tag %d prefixlen %d", - inet6_ntoa (rte->addr), ntohs (rte->tag), rte->prefixlen); + zlog_debug ("RIPng nexthop RTE address %s tag %"ROUTE_TAG_PRI" prefixlen %d", + inet6_ntoa (rte->addr), (route_tag_t)ntohs (rte->tag), rte->prefixlen); /* RFC2080 2.1.1 Next Hop: The route tag and prefix length in the next hop RTE must be set to zero on sending and ignored on receiption. */ if (ntohs (rte->tag) != 0) - zlog_warn ("RIPng nexthop RTE with non zero tag value %d from %s", - ntohs (rte->tag), inet6_ntoa (from->sin6_addr)); + zlog_warn ("RIPng nexthop RTE with non zero tag value %"ROUTE_TAG_PRI" from %s", + (route_tag_t)ntohs (rte->tag), inet6_ntoa (from->sin6_addr)); if (rte->prefixlen != 0) zlog_warn ("RIPng nexthop RTE with non zero prefixlen value %d from %s", @@ -2017,8 +2017,8 @@ DEFUN (show_ipv6_ripng, vty_out (vty, "%*s", 18, " "); vty_out (vty, "%*s", 28, " "); - vty_out (vty, "self %2d %3d%s", aggregate->metric, - aggregate->tag, + vty_out (vty, "self %2d %3"ROUTE_TAG_PRI"%s", aggregate->metric, + (route_tag_t)aggregate->tag, VTY_NEWLINE); } @@ -2062,8 +2062,8 @@ DEFUN (show_ipv6_ripng, if (len > 0) vty_out (vty, "%*s", len, " "); - vty_out (vty, " %2d %3d ", - rinfo->metric, rinfo->tag); + vty_out (vty, " %2d %3"ROUTE_TAG_PRI" ", + rinfo->metric, (route_tag_t)rinfo->tag); /* time */ if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index 5337eb88f3..c4b34b348c 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -148,7 +148,7 @@ struct ripng struct rte { struct in6_addr addr; /* RIPng destination prefix */ - u_short tag; /* RIPng tag */ + u_int16_t tag; /* RIPng tag */ u_char prefixlen; /* Length of the RIPng prefix */ u_char metric; /* Metric of the RIPng route */ /* The nexthop is stored by the structure @@ -202,7 +202,7 @@ struct ripng_info struct in6_addr nexthop_out; u_char metric_set; u_char metric_out; - u_short tag_out; + u_int16_t tag_out; struct route_node *rp; }; diff --git a/zebra/rib.h b/zebra/rib.h index 96301a8af4..c95a9ba0c3 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -23,6 +23,7 @@ #ifndef _ZEBRA_RIB_H #define _ZEBRA_RIB_H +#include "zebra.h" #include "linklist.h" #include "prefix.h" #include "table.h" @@ -47,6 +48,9 @@ struct rib /* Refrence count. */ unsigned long refcnt; + /* Tag */ + route_tag_t tag; + /* Uptime. */ time_t uptime; @@ -72,9 +76,6 @@ struct rib /* Distance. */ u_char distance; - /* Tag */ - u_short tag; - /* Flags of this route. * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed * to clients via Zserv diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index e7338a10c2..6136e92cdd 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -221,7 +221,7 @@ DEFUN (ip_route_label, DEFUN (ip_route_tag_label, ip_route_tag_label_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> label WORD", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -257,7 +257,7 @@ DEFUN (ip_route_mask_label, DEFUN (ip_route_mask_tag_label, ip_route_mask_tag_label_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> label WORD", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -295,7 +295,7 @@ DEFUN (ip_route_distance_label, DEFUN (ip_route_tag_distance_label, ip_route_tag_distance_label_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> label WORD", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -333,7 +333,7 @@ DEFUN (ip_route_mask_distance_label, DEFUN (ip_route_mask_tag_distance_label, ip_route_mask_tag_distance_label_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> label WORD", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -370,7 +370,7 @@ DEFUN (no_ip_route_label, DEFUN (no_ip_route_tag_label, no_ip_route_tag_label_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -407,7 +407,7 @@ DEFUN (no_ip_route_mask_label, DEFUN (no_ip_route_mask_tag_label, no_ip_route_mask_tag_label_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> label WORD", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -445,7 +445,7 @@ DEFUN (no_ip_route_distance_label, DEFUN (no_ip_route_tag_distance_label, no_ip_route_tag_distance_label_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -484,7 +484,7 @@ DEFUN (no_ip_route_mask_distance_label, DEFUN (no_ip_route_mask_tag_distance_label, no_ip_route_mask_tag_distance_label_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> label WORD", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -519,7 +519,7 @@ DEFUN (ipv6_route_label, DEFUN (ipv6_route_tag_label, ipv6_route_tag_label_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> label WORD", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> label WORD", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -548,7 +548,7 @@ DEFUN (ipv6_route_ifname_label, } DEFUN (ipv6_route_ifname_tag_label, ipv6_route_ifname_tag_label_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> label WORD", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> label WORD", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -579,7 +579,7 @@ DEFUN (ipv6_route_pref_label, DEFUN (ipv6_route_pref_tag_label, ipv6_route_pref_tag_label_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> label WORD", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255> label WORD", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -611,7 +611,7 @@ DEFUN (ipv6_route_ifname_pref_label, DEFUN (ipv6_route_ifname_pref_tag_label, ipv6_route_ifname_pref_tag_label_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> label WORD", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255> label WORD", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -643,7 +643,7 @@ DEFUN (no_ipv6_route_label, DEFUN (no_ipv6_route_tag_label, no_ipv6_route_tag_label_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> label WORD", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -675,7 +675,7 @@ DEFUN (no_ipv6_route_ifname_label, DEFUN (no_ipv6_route_ifname_tag_label, no_ipv6_route_ifname_tag_label_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> label WORD", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -708,7 +708,7 @@ DEFUN (no_ipv6_route_pref_label, DEFUN (no_ipv6_route_pref_tag_label, no_ipv6_route_pref_tag_label_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> label WORD", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255> label WORD", NO_STR IP_STR "Establish static routes\n" @@ -742,7 +742,7 @@ DEFUN (no_ipv6_route_ifname_pref_label, DEFUN (no_ipv6_route_ifname_pref_tag_label, no_ipv6_route_ifname_pref_tag_label_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> label WORD", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255> label WORD", NO_STR IP_STR "Establish static routes\n" diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 685f5cc4b9..9cfae6e705 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -51,7 +51,7 @@ struct nh_rmap_obj vrf_id_t vrf_id; u_int32_t source_protocol; int metric; - u_short tag; + route_tag_t tag; }; static void zebra_route_map_set_delay_timer(u_int32_t value); @@ -193,7 +193,7 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct nh_rmap_obj *nh_data; if (type == RMAP_ZEBRA) @@ -207,45 +207,13 @@ route_match_tag (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map 'match tag' match statement. 'arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - u_short tmp; - - /* tag value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - tmp = atoi(arg); - if (tmp < 1) - return NULL; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - - if (!tag) - return tag; - - *tag = tmp; - - return tag; -} - -/* Free route map's compiled 'match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag matching */ -struct route_map_rule_cmd route_match_tag_cmd = +static struct route_map_rule_cmd route_match_tag_cmd = { - "tag", - route_match_tag, - route_match_tag_compile, - route_match_tag_free + "tag", + route_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, }; @@ -332,7 +300,7 @@ ALIAS (no_match_interface, DEFUN (match_tag, match_tag_cmd, - "match tag <1-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Tag value\n") @@ -358,7 +326,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <1-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n") @@ -1649,7 +1617,7 @@ zebra_route_map_write_delay_timer (struct vty *vty) route_map_result_t zebra_route_map_check (int family, int rib_type, struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, u_short tag) + struct nexthop *nexthop, vrf_id_t vrf_id, route_tag_t tag) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_MATCH; @@ -1692,7 +1660,7 @@ zebra_del_import_table_route_map (afi_t afi, uint32_t table) route_map_result_t zebra_import_table_route_map_check (int family, int rib_type, struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, u_short tag, const char *rmap_name) + struct nexthop *nexthop, vrf_id_t vrf_id, route_tag_t tag, const char *rmap_name) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_DENYMATCH; diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index 5eb3740909..bf418ccacc 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -34,13 +34,13 @@ extern route_map_result_t zebra_import_table_route_map_check (int family, int ri struct prefix *p, struct nexthop *nexthop, vrf_id_t vrf_id, - u_short tag, + route_tag_t tag, const char *rmap_name); extern route_map_result_t zebra_route_map_check (int family, int rib_type, struct prefix *p, struct nexthop *nexthop, vrf_id_t vrf_id, - u_short tag); + route_tag_t tag); extern route_map_result_t zebra_nht_route_map_check (int family, int client_proto, struct prefix *p, diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index dfe196c4b2..a691048b2c 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -362,7 +362,7 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ int static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, union g_addr *gate, ifindex_t ifindex, - const char *ifname, u_char flags, u_short tag, + const char *ifname, u_char flags, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label) { @@ -481,7 +481,7 @@ static_add_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, int static_delete_route (afi_t afi, safi_t safi, u_char type, struct prefix *p, union g_addr *gate, ifindex_t ifindex, - u_short tag, u_char distance, struct zebra_vrf *zvrf, + route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label) { struct route_node *rn; diff --git a/zebra/zebra_static.h b/zebra/zebra_static.h index 8c55ea84c0..5b6f429761 100644 --- a/zebra/zebra_static.h +++ b/zebra/zebra_static.h @@ -45,7 +45,7 @@ struct static_route u_char distance; /* Tag */ - u_short tag; + route_tag_t tag; /* Flag for this static route's type. */ u_char type; @@ -87,13 +87,13 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ extern int static_add_route (afi_t, safi_t safi, u_char type, struct prefix *p, union g_addr *gate, ifindex_t ifindex, - const char *ifname, u_char flags, u_short tag, + const char *ifname, u_char flags, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label); extern int static_delete_route (afi_t, safi_t safi, u_char type, struct prefix *p, - union g_addr *gate, ifindex_t ifindex, u_short tag, + union g_addr *gate, ifindex_t ifindex, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 054979fd0f..0aef681f33 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -66,7 +66,7 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, struct in_addr gate; struct in_addr mask; u_char flag = 0; - u_short tag = 0; + route_tag_t tag = 0; struct zebra_vrf *zvrf = NULL; unsigned int ifindex = 0; const char *ifname = NULL; @@ -104,7 +104,7 @@ zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd, /* tag */ if (tag_str) - tag = atoi(tag_str); + tag = atol(tag_str); /* VRF id */ zvrf = zebra_vrf_list_lookup_by_name (vrf_id_str); @@ -365,7 +365,7 @@ DEFUN (ip_route, DEFUN (ip_route_tag, ip_route_tag_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -396,7 +396,7 @@ DEFUN (ip_route_flags, DEFUN (ip_route_flags_tag, ip_route_flags_tag_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -427,7 +427,7 @@ DEFUN (ip_route_flags2, DEFUN (ip_route_flags2_tag, ip_route_flags2_tag_cmd, - "ip route A.B.C.D/M (reject|blackhole) tag <1-65535>", + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -459,7 +459,7 @@ DEFUN (ip_route_mask, DEFUN (ip_route_mask_tag, ip_route_mask_tag_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -493,7 +493,7 @@ DEFUN (ip_route_mask_flags, DEFUN (ip_route_mask_flags_tag, ip_route_mask_flags_tag_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -526,7 +526,7 @@ DEFUN (ip_route_mask_flags2, DEFUN (ip_route_mask_flags2_tag, ip_route_mask_flags2_tag_cmd, - "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535>", + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -558,7 +558,7 @@ DEFUN (ip_route_distance, DEFUN (ip_route_tag_distance, ip_route_tag_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -592,7 +592,7 @@ DEFUN (ip_route_flags_distance, DEFUN (ip_route_flags_tag_distance, ip_route_flags_tag_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255>", + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -624,7 +624,7 @@ DEFUN (ip_route_flags_distance2, DEFUN (ip_route_flags_tag_distance2, ip_route_flags_tag_distance2_cmd, - "ip route A.B.C.D/M (reject|blackhole) tag <1-65535> <1-255>", + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -656,7 +656,7 @@ DEFUN (ip_route_mask_distance, DEFUN (ip_route_mask_tag_distance, ip_route_mask_tag_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -674,7 +674,7 @@ DEFUN (ip_route_mask_tag_distance, DEFUN (ip_route_mask_flags_tag_distance, ip_route_mask_flags_tag_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255>", + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -726,7 +726,7 @@ DEFUN (ip_route_mask_flags_distance2, DEFUN (ip_route_mask_flags_tag_distance2, ip_route_mask_flags_tag_distance2_cmd, - "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535> <1-255>", + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -758,7 +758,7 @@ DEFUN (no_ip_route, DEFUN (no_ip_route_tag, no_ip_route_tag_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -787,7 +787,7 @@ ALIAS (no_ip_route, ALIAS (no_ip_route_tag, no_ip_route_flags_tag_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -815,7 +815,7 @@ DEFUN (no_ip_route_flags2, DEFUN (no_ip_route_flags2_tag, no_ip_route_flags2_tag_cmd, - "no ip route A.B.C.D/M (reject|blackhole) tag <1-65535>", + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -847,7 +847,7 @@ DEFUN (no_ip_route_mask, DEFUN (no_ip_route_mask_tag, no_ip_route_mask_tag_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -878,7 +878,7 @@ ALIAS (no_ip_route_mask, ALIAS (no_ip_route_mask_tag, no_ip_route_mask_flags_tag_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -908,7 +908,7 @@ DEFUN (no_ip_route_mask_flags2, DEFUN (no_ip_route_mask_flags2_tag, no_ip_route_mask_flags2_tag_cmd, - "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535>", + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -941,7 +941,7 @@ DEFUN (no_ip_route_distance, DEFUN (no_ip_route_tag_distance, no_ip_route_tag_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -976,7 +976,7 @@ DEFUN (no_ip_route_flags_distance, DEFUN (no_ip_route_flags_tag_distance, no_ip_route_flags_tag_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255>", + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1010,7 +1010,7 @@ DEFUN (no_ip_route_flags_distance2, DEFUN (no_ip_route_flags_tag_distance2, no_ip_route_flags_tag_distance2_cmd, - "no ip route A.B.C.D/M (reject|blackhole) tag <1-65535> <1-255>", + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1044,7 +1044,7 @@ DEFUN (no_ip_route_mask_distance, DEFUN (no_ip_route_mask_tag_distance, no_ip_route_mask_tag_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1081,7 +1081,7 @@ DEFUN (no_ip_route_mask_flags_distance, DEFUN (no_ip_route_mask_flags_tag_distance, no_ip_route_mask_flags_tag_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255>", + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1117,7 +1117,7 @@ DEFUN (no_ip_route_mask_flags_distance2, DEFUN (no_ip_route_mask_flags_tag_distance2, no_ip_route_mask_flags_tag_distance2_cmd, - "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535> <1-255>", + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -1151,7 +1151,7 @@ DEFUN (ip_route_vrf, DEFUN (ip_route_tag_vrf, ip_route_tag_vrf_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> " VRF_CMD_STR, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -1184,7 +1184,7 @@ DEFUN (ip_route_flags_vrf, DEFUN (ip_route_flags_tag_vrf, ip_route_flags_tag_vrf_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -1217,7 +1217,7 @@ DEFUN (ip_route_flags2_vrf, DEFUN (ip_route_flags2_tag_vrf, ip_route_flags2_tag_vrf_cmd, - "ip route A.B.C.D/M (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -1251,7 +1251,7 @@ DEFUN (ip_route_mask_vrf, DEFUN (ip_route_mask_tag_vrf, ip_route_mask_tag_vrf_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> " VRF_CMD_STR, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -1287,7 +1287,7 @@ DEFUN (ip_route_mask_flags_vrf, DEFUN (ip_route_mask_flags_tag_vrf, ip_route_mask_flags_tag_vrf_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -1322,7 +1322,7 @@ DEFUN (ip_route_mask_flags2_vrf, DEFUN (ip_route_mask_flags2_tag_vrf, ip_route_mask_flags2_tag_vrf_cmd, - "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -1356,7 +1356,7 @@ DEFUN (ip_route_distance_vrf, DEFUN (ip_route_tag_distance_vrf, ip_route_tag_distance_vrf_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> " VRF_CMD_STR, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -1392,7 +1392,7 @@ DEFUN (ip_route_flags_distance_vrf, DEFUN (ip_route_flags_tag_distance_vrf, ip_route_flags_tag_distance_vrf_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -1426,7 +1426,7 @@ DEFUN (ip_route_flags_distance2_vrf, DEFUN (ip_route_flags_tag_distance2_vrf, ip_route_flags_tag_distance2_vrf_cmd, - "ip route A.B.C.D/M (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -1460,7 +1460,7 @@ DEFUN (ip_route_mask_distance_vrf, DEFUN (ip_route_mask_tag_distance_vrf, ip_route_mask_tag_distance_vrf_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> " VRF_CMD_STR, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -1479,7 +1479,7 @@ DEFUN (ip_route_mask_tag_distance_vrf, DEFUN (ip_route_mask_flags_tag_distance_vrf, ip_route_mask_flags_tag_distance_vrf_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -1534,7 +1534,7 @@ DEFUN (ip_route_mask_flags_distance2_vrf, DEFUN (ip_route_mask_flags_tag_distance2_vrf, ip_route_mask_flags_tag_distance2_vrf_cmd, - "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -1585,7 +1585,7 @@ DEFUN (no_ip_route_flags_vrf, DEFUN (no_ip_route_tag_vrf, no_ip_route_tag_vrf_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> " VRF_CMD_STR, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1603,7 +1603,7 @@ DEFUN (no_ip_route_tag_vrf, DEFUN (no_ip_route_flags_tag_vrf, no_ip_route_flags_tag_vrf_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1637,7 +1637,7 @@ DEFUN (no_ip_route_flags2_vrf, DEFUN (no_ip_route_flags2_tag_vrf, no_ip_route_flags2_tag_vrf_cmd, - "no ip route A.B.C.D/M (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1689,7 +1689,7 @@ DEFUN (no_ip_route_mask_flags_vrf, DEFUN (no_ip_route_mask_tag_vrf, no_ip_route_mask_tag_vrf_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> " VRF_CMD_STR, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1708,7 +1708,7 @@ DEFUN (no_ip_route_mask_tag_vrf, DEFUN (no_ip_route_mask_flags_tag_vrf, no_ip_route_mask_flags_tag_vrf_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1744,7 +1744,7 @@ DEFUN (no_ip_route_mask_flags2_vrf, DEFUN (no_ip_route_mask_flags2_tag_vrf, no_ip_route_mask_flags2_tag_vrf_cmd, - "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1780,7 +1780,7 @@ DEFUN (no_ip_route_distance_vrf, DEFUN (no_ip_route_tag_distance_vrf, no_ip_route_tag_distance_vrf_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1817,7 +1817,7 @@ DEFUN (no_ip_route_flags_distance_vrf, DEFUN (no_ip_route_flags_tag_distance_vrf, no_ip_route_flags_tag_distance_vrf_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1853,7 +1853,7 @@ DEFUN (no_ip_route_flags_distance2_vrf, DEFUN (no_ip_route_flags_tag_distance2_vrf, no_ip_route_flags_tag_distance2_vrf_cmd, - "no ip route A.B.C.D/M (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1889,7 +1889,7 @@ DEFUN (no_ip_route_mask_distance_vrf, DEFUN (no_ip_route_mask_tag_distance_vrf, no_ip_route_mask_tag_distance_vrf_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1928,7 +1928,7 @@ DEFUN (no_ip_route_mask_flags_distance_vrf, DEFUN (no_ip_route_mask_flags_tag_distance_vrf, no_ip_route_mask_flags_tag_distance_vrf_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1966,7 +1966,7 @@ DEFUN (no_ip_route_mask_flags_distance2_vrf, DEFUN (no_ip_route_mask_flags_tag_distance2_vrf, no_ip_route_mask_flags_tag_distance2_vrf_cmd, - "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -2013,7 +2013,7 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) vty_out (vty, "\""); vty_out (vty, ", distance %u, metric %u", rib->distance, rib->metric); if (rib->tag) - vty_out (vty, ", tag %d", rib->tag); + vty_out (vty, ", tag %"ROUTE_TAG_PRI, rib->tag); if (rib->mtu) vty_out (vty, ", mtu %u", rib->mtu); if (rib->vrf_id != VRF_DEFAULT) @@ -2685,7 +2685,7 @@ DEFUN (no_ipv6_nht_default_route, DEFUN (show_ip_route_tag, show_ip_route_tag_cmd, - "show ip route tag <1-65535>", + "show ip route tag <1-4294967295>", SHOW_STR IP_STR "IP routing table\n" @@ -2696,16 +2696,16 @@ DEFUN (show_ip_route_tag, struct route_node *rn; struct rib *rib; int first = 1; - u_short tag = 0; + route_tag_t tag = 0; vrf_id_t vrf_id = VRF_DEFAULT; - if (argc > 1) - { - tag = atoi(argv[1]); - VRF_GET_ID (vrf_id, argv[0]); - } - else - tag = atoi(argv[0]); + if (argc > 1) + { + tag = atol(argv[1]); + VRF_GET_ID (vrf_id, argv[0]); + } + else + tag = atol(argv[0]); table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); if (! table) @@ -2730,7 +2730,7 @@ DEFUN (show_ip_route_tag, ALIAS (show_ip_route_tag, show_ip_route_vrf_tag_cmd, - "show ip route " VRF_CMD_STR " tag <1-65535>", + "show ip route " VRF_CMD_STR " tag <1-4294967295>", SHOW_STR IP_STR "IP routing table\n" @@ -3317,7 +3317,7 @@ DEFUN (show_ip_route_vrf_all, DEFUN (show_ip_route_vrf_all_tag, show_ip_route_vrf_all_tag_cmd, - "show ip route " VRF_ALL_CMD_STR " tag <1-65535>", + "show ip route " VRF_ALL_CMD_STR " tag <1-4294967295>", SHOW_STR IP_STR "IP routing table\n" @@ -3332,10 +3332,10 @@ DEFUN (show_ip_route_vrf_all_tag, vrf_iter_t iter; int first = 1; int vrf_header = 1; - u_short tag = 0; + route_tag_t tag = 0; if (argv[0]) - tag = atoi(argv[0]); + tag = atol(argv[0]); for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) { @@ -3706,7 +3706,7 @@ static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) } if (si->tag) - vty_out (vty, " tag %d", si->tag); + vty_out (vty, " tag %"ROUTE_TAG_PRI, si->tag); if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) vty_out (vty, " %d", si->distance); @@ -3744,7 +3744,7 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, struct in6_addr gate_addr; u_char type = 0; u_char flag = 0; - u_short tag = 0; + route_tag_t tag = 0; unsigned int ifindex = 0; struct interface *ifp = NULL; struct zebra_vrf *zvrf; @@ -3785,7 +3785,7 @@ static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str, /* tag */ if (tag_str) - tag = atoi(tag_str); + tag = atol(tag_str); /* When gateway is valid IPv6 addrees, then gate is treated as nexthop address other case gate is treated as interface name. */ @@ -3877,7 +3877,7 @@ DEFUN (ipv6_route, DEFUN (ipv6_route_tag, ipv6_route_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -3905,7 +3905,7 @@ DEFUN (ipv6_route_flags, DEFUN (ipv6_route_flags_tag, ipv6_route_flags_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -3932,7 +3932,7 @@ DEFUN (ipv6_route_ifname, } DEFUN (ipv6_route_ifname_tag, ipv6_route_ifname_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -3960,7 +3960,7 @@ DEFUN (ipv6_route_ifname_flags, DEFUN (ipv6_route_ifname_flags_tag, ipv6_route_ifname_flags_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -3989,7 +3989,7 @@ DEFUN (ipv6_route_pref, DEFUN (ipv6_route_pref_tag, ipv6_route_pref_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4019,7 +4019,7 @@ DEFUN (ipv6_route_flags_pref, DEFUN (ipv6_route_flags_pref_tag, ipv6_route_flags_pref_tag_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535> <1-255>", + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4049,7 +4049,7 @@ DEFUN (ipv6_route_ifname_pref, DEFUN (ipv6_route_ifname_pref_tag, ipv6_route_ifname_pref_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4079,7 +4079,7 @@ DEFUN (ipv6_route_ifname_flags_pref, DEFUN (ipv6_route_ifname_flags_pref_tag, ipv6_route_ifname_flags_pref_tag_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535> <1-255>", + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4109,7 +4109,7 @@ DEFUN (no_ipv6_route, DEFUN (no_ipv6_route_tag, no_ipv6_route_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -4139,7 +4139,7 @@ DEFUN (no_ipv6_route_flags, DEFUN (no_ipv6_route_flags_tag, no_ipv6_route_flags_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -4169,7 +4169,7 @@ DEFUN (no_ipv6_route_ifname, DEFUN (no_ipv6_route_ifname_tag, no_ipv6_route_ifname_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -4199,7 +4199,7 @@ DEFUN (no_ipv6_route_ifname_flags, DEFUN (no_ipv6_route_ifname_flags_tag, no_ipv6_route_ifname_flags_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>", NO_STR IP_STR "Establish static routes\n" @@ -4230,7 +4230,7 @@ DEFUN (no_ipv6_route_pref, DEFUN (no_ipv6_route_pref_tag, no_ipv6_route_pref_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -4263,7 +4263,7 @@ DEFUN (no_ipv6_route_flags_pref, DEFUN (no_ipv6_route_flags_pref_tag, no_ipv6_route_flags_pref_tag_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535> <1-255>", + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -4296,7 +4296,7 @@ DEFUN (no_ipv6_route_ifname_pref, DEFUN (no_ipv6_route_ifname_pref_tag, no_ipv6_route_ifname_pref_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -4328,7 +4328,7 @@ DEFUN (no_ipv6_route_ifname_flags_pref, DEFUN (no_ipv6_route_ifname_flags_pref_tag, no_ipv6_route_ifname_flags_pref_tag_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535> <1-255>", + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>", NO_STR IP_STR "Establish static routes\n" @@ -4359,7 +4359,7 @@ DEFUN (ipv6_route_vrf, DEFUN (ipv6_route_tag_vrf, ipv6_route_tag_vrf_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4389,7 +4389,7 @@ DEFUN (ipv6_route_flags_vrf, DEFUN (ipv6_route_flags_tag_vrf, ipv6_route_flags_tag_vrf_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4418,7 +4418,7 @@ DEFUN (ipv6_route_ifname_vrf, } DEFUN (ipv6_route_ifname_tag_vrf, ipv6_route_ifname_tag_vrf_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4448,7 +4448,7 @@ DEFUN (ipv6_route_ifname_flags_vrf, DEFUN (ipv6_route_ifname_flags_tag_vrf, ipv6_route_ifname_flags_tag_vrf_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4479,7 +4479,7 @@ DEFUN (ipv6_route_pref_vrf, DEFUN (ipv6_route_pref_tag_vrf, ipv6_route_pref_tag_vrf_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4511,7 +4511,7 @@ DEFUN (ipv6_route_flags_pref_vrf, DEFUN (ipv6_route_flags_pref_tag_vrf, ipv6_route_flags_pref_tag_vrf_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4543,7 +4543,7 @@ DEFUN (ipv6_route_ifname_pref_vrf, DEFUN (ipv6_route_ifname_pref_tag_vrf, ipv6_route_ifname_pref_tag_vrf_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4575,7 +4575,7 @@ DEFUN (ipv6_route_ifname_flags_pref_vrf, DEFUN (ipv6_route_ifname_flags_pref_tag_vrf, ipv6_route_ifname_flags_pref_tag_vrf_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -4607,7 +4607,7 @@ DEFUN (no_ipv6_route_vrf, DEFUN (no_ipv6_route_tag_vrf, no_ipv6_route_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4639,7 +4639,7 @@ DEFUN (no_ipv6_route_flags_vrf, DEFUN (no_ipv6_route_flags_tag_vrf, no_ipv6_route_flags_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4671,7 +4671,7 @@ DEFUN (no_ipv6_route_ifname_vrf, DEFUN (no_ipv6_route_ifname_tag_vrf, no_ipv6_route_ifname_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4703,7 +4703,7 @@ DEFUN (no_ipv6_route_ifname_flags_vrf, DEFUN (no_ipv6_route_ifname_flags_tag_vrf, no_ipv6_route_ifname_flags_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4736,7 +4736,7 @@ DEFUN (no_ipv6_route_pref_vrf, DEFUN (no_ipv6_route_pref_tag_vrf, no_ipv6_route_pref_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4771,7 +4771,7 @@ DEFUN (no_ipv6_route_flags_pref_vrf, DEFUN (no_ipv6_route_flags_pref_tag_vrf, no_ipv6_route_flags_pref_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4806,7 +4806,7 @@ DEFUN (no_ipv6_route_ifname_pref_vrf, DEFUN (no_ipv6_route_ifname_pref_tag_vrf, no_ipv6_route_ifname_pref_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-65535> <1-255> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4840,7 +4840,7 @@ DEFUN (no_ipv6_route_ifname_flags_pref_vrf, DEFUN (no_ipv6_route_ifname_flags_pref_tag_vrf, no_ipv6_route_ifname_flags_pref_tag_vrf_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-65535> <1-255> " VRF_CMD_STR, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -4961,7 +4961,7 @@ ALIAS (show_ipv6_route, DEFUN (show_ipv6_route_tag, show_ipv6_route_tag_cmd, - "show ipv6 route tag <1-65535>", + "show ipv6 route tag <1-4294967295>", SHOW_STR IP_STR "IPv6 routing table\n" @@ -4972,16 +4972,16 @@ DEFUN (show_ipv6_route_tag, struct route_node *rn; struct rib *rib; int first = 1; - u_short tag = 0; + route_tag_t tag = 0; vrf_id_t vrf_id = VRF_DEFAULT; if (argc > 1) { VRF_GET_ID (vrf_id, argv[0]); - tag = atoi(argv[1]); + tag = atol(argv[1]); } else - tag = atoi(argv[0]); + tag = atol(argv[0]); table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) @@ -5006,7 +5006,7 @@ DEFUN (show_ipv6_route_tag, ALIAS (show_ipv6_route_tag, show_ipv6_route_vrf_tag_cmd, - "show ipv6 route " VRF_CMD_STR " tag <1-65535>", + "show ipv6 route " VRF_CMD_STR " tag <1-4294967295>", SHOW_STR IP_STR "IPv6 routing table\n" @@ -5403,7 +5403,7 @@ DEFUN (show_ipv6_route_vrf_all, DEFUN (show_ipv6_route_vrf_all_tag, show_ipv6_route_vrf_all_tag_cmd, - "show ipv6 route " VRF_ALL_CMD_STR " tag <1-65535>", + "show ipv6 route " VRF_ALL_CMD_STR " tag <1-4294967295>", SHOW_STR IP_STR "IPv6 routing table\n" @@ -5418,10 +5418,10 @@ DEFUN (show_ipv6_route_vrf_all_tag, vrf_iter_t iter; int first = 1; int vrf_header = 1; - u_short tag = 0; + route_tag_t tag = 0; if (argv[0]) - tag = atoi(argv[0]); + tag = atol(argv[0]); for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) { @@ -5773,7 +5773,7 @@ static_config_ipv6 (struct vty *vty) vty_out (vty, " %s", "blackhole"); if (si->tag) - vty_out (vty, " tag %d", si->tag); + vty_out (vty, " tag %"ROUTE_TAG_PRI, si->tag); if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) vty_out (vty, " %d", si->distance); diff --git a/zebra/zserv.c b/zebra/zserv.c index 135cd88a10..530410c268 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -732,7 +732,7 @@ zsend_redistribute_route (int cmd, struct zserv *client, struct prefix *p, if (rib->tag) { SET_FLAG(zapi_flags, ZAPI_MESSAGE_TAG); - stream_putw(s, rib->tag); + stream_putl(s, rib->tag); } /* MTU */ @@ -1114,7 +1114,7 @@ zread_ipv4_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Tag */ if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) - rib->tag = stream_getw (s); + rib->tag = stream_getl (s); else rib->tag = 0; @@ -1213,7 +1213,7 @@ zread_ipv4_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* tag */ if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) - api.tag = stream_getw (s); + api.tag = stream_getl (s); else api.tag = 0; @@ -1343,7 +1343,7 @@ zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length, struct /* Tag */ if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) - rib->tag = stream_getw (s); + rib->tag = stream_getl (s); else rib->tag = 0; @@ -1464,7 +1464,7 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* Tag */ if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) - rib->tag = stream_getw (s); + rib->tag = stream_getl (s); else rib->tag = 0; @@ -1553,7 +1553,7 @@ zread_ipv6_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) /* tag */ if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) - api.tag = stream_getw (s); + api.tag = stream_getl (s); else api.tag = 0; From 464015fa32f89e7f128cb19917c9e347b3284d69 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 1 Oct 2016 06:41:40 +0200 Subject: [PATCH 118/136] ospf6d: add support for route tags Signed-off-by: Christian Franke --- ospf6d/ospf6_asbr.c | 191 +++++++++++++++++++++++++++++++++++++++++-- ospf6d/ospf6_asbr.h | 6 +- ospf6d/ospf6_route.h | 1 + ospf6d/ospf6_zebra.c | 16 +++- 4 files changed, 203 insertions(+), 11 deletions(-) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 208d3b8c4c..09e6420ffe 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -93,7 +93,10 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); /* external route tag */ - UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + if (info->tag) + SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + else + UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); /* Set metric */ OSPF6_ASBR_METRIC_SET (as_external_lsa, route->path.cost); @@ -123,7 +126,10 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) /* External Route Tag */ if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) { - /* xxx */ + route_tag_t network_order = htonl(info->tag); + + memcpy (p, &network_order, sizeof(network_order)); + p += sizeof(network_order); } /* Fill LSA Header */ @@ -146,6 +152,29 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) ospf6_lsa_originate_process (lsa, ospf6); } +static route_tag_t +ospf6_as_external_lsa_get_tag (struct ospf6_lsa *lsa) +{ + struct ospf6_as_external_lsa *external; + ptrdiff_t tag_offset; + route_tag_t network_order; + + if (!lsa) + return 0; + + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + if (!CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T)) + return 0; + + tag_offset = sizeof(*external) + OSPF6_PREFIX_SPACE(external->prefix.prefix_length); + if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F)) + tag_offset += sizeof(struct in6_addr); + + memcpy(&network_order, (caddr_t)external + tag_offset, sizeof(network_order)); + return ntohl(network_order); +} void ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) @@ -222,6 +251,8 @@ ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) route->path.u.cost_e2 = 0; } + route->path.tag = ospf6_as_external_lsa_get_tag (lsa); + ospf6_route_copy_nexthops (route, asbr_entry); if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) @@ -427,7 +458,7 @@ ospf6_asbr_send_externals_to_area (struct ospf6_area *oa) void ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, - u_int nexthop_num, struct in6_addr *nexthop) + u_int nexthop_num, struct in6_addr *nexthop, route_tag_t tag) { int ret; struct ospf6_route troute; @@ -469,6 +500,7 @@ ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, memset (&tinfo, 0, sizeof (tinfo)); troute.route_option = &tinfo; tinfo.ifindex = ifindex; + tinfo.tag = tag; ret = route_map_apply (ospf6->rmap[type].map, prefix, RMAP_OSPF6, &troute); @@ -495,6 +527,12 @@ ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding)) memcpy (&info->forwarding, &tinfo.forwarding, sizeof (struct in6_addr)); + info->tag = tinfo.tag; + } + else + { + /* If there is no route-map, simply update the tag */ + info->tag = tag; } info->type = type; @@ -542,6 +580,12 @@ ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding)) memcpy (&info->forwarding, &tinfo.forwarding, sizeof (struct in6_addr)); + info->tag = tinfo.tag; + } + else + { + /* If there is no route-map, simply set the tag */ + info->tag = tag; } info->type = type; @@ -861,6 +905,30 @@ ospf6_routemap_rule_match_interface_cmd = ospf6_routemap_rule_match_interface_free }; +/* Match function for matching route tags */ +static route_map_result_t +ospf6_routemap_rule_match_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (type == RMAP_OSPF6 && info->tag == *tag) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static struct route_map_rule_cmd +ospf6_routemap_rule_match_tag_cmd = +{ + "tag", + ospf6_routemap_rule_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + static route_map_result_t ospf6_routemap_rule_set_metric_type (void *rule, struct prefix *prefix, route_map_object_t type, void *object) @@ -986,6 +1054,30 @@ ospf6_routemap_rule_set_forwarding_cmd = ospf6_routemap_rule_set_forwarding_free, }; +static route_map_result_t +ospf6_routemap_rule_set_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (type != RMAP_OSPF6) + return RMAP_OKAY; + + info->tag = *tag; + return RMAP_OKAY; +} + +static struct route_map_rule_cmd +ospf6_routemap_rule_set_tag_cmd = +{ + "tag", + ospf6_routemap_rule_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + static int route_map_command_status (struct vty *vty, int ret) { @@ -1054,8 +1146,8 @@ DEFUN (ospf6_routemap_match_interface, DEFUN (ospf6_routemap_no_match_interface, ospf6_routemap_no_match_interface_cmd, "no match interface", - MATCH_STR NO_STR + MATCH_STR "Match first hop interface of route\n") { int ret = route_map_delete_match ((struct route_map_index *) vty->index, @@ -1066,11 +1158,45 @@ DEFUN (ospf6_routemap_no_match_interface, ALIAS (ospf6_routemap_no_match_interface, ospf6_routemap_no_match_interface_val_cmd, "no match interface WORD", - MATCH_STR NO_STR + MATCH_STR "Match first hop interface of route\n" "Interface name\n") +/* add "match tag" */ +DEFUN (ospf6_routemap_match_tag, + ospf6_routemap_match_tag_cmd, + "match tag <1-4294967295>", + MATCH_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + int ret = route_map_add_match ((struct route_map_index *) vty->index, + "tag", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "match tag" */ +DEFUN (ospf6_routemap_no_match_tag, + ospf6_routemap_no_match_tag_cmd, + "no match tag", + NO_STR + MATCH_STR + "Tag value for routing protocol\n") +{ + int ret = route_map_delete_match ((struct route_map_index *) vty->index, + "tag", argc ? argv[0] : NULL); + return route_map_command_status (vty, ret); +} + +ALIAS (ospf6_routemap_no_match_tag, + ospf6_routemap_no_match_tag_val_cmd, + "no match tag <1-4294967295>", + NO_STR + MATCH_STR + "Tag value for routing protocol\n" + "Tag value\n") + /* add "set metric-type" */ DEFUN (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd, @@ -1167,6 +1293,40 @@ DEFUN (ospf6_routemap_no_set_forwarding, return route_map_command_status (vty, ret); } +/* add "set tag" */ +DEFUN (ospf6_routemap_set_tag, + ospf6_routemap_set_tag_cmd, + "set tag <1-4294967295>", + "Set value\n" + "Tag value for routing protocol\n" + "Tag value\n") +{ + int ret = route_map_add_set ((struct route_map_index *) vty->index, + "tag", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "set tag" */ +DEFUN (ospf6_routemap_no_set_tag, + ospf6_routemap_no_set_tag_cmd, + "no set tag", + NO_STR + "Set value\n" + "Tag value for routing protocol\n") +{ + int ret = route_map_delete_set ((struct route_map_index *) vty->index, + "tag", argc ? argv[0] : NULL); + return route_map_command_status (vty, ret); +} + +ALIAS (ospf6_routemap_no_set_tag, + ospf6_routemap_no_set_tag_val_cmd, + "no set tag <1-4294967295>", + NO_STR + "Set value\n" + "Tag value for routing protocol\n" + "Tag value\n") + static void ospf6_routemap_init (void) { @@ -1177,10 +1337,12 @@ ospf6_routemap_init (void) route_map_install_match (&ospf6_routemap_rule_match_address_prefixlist_cmd); route_map_install_match (&ospf6_routemap_rule_match_interface_cmd); + route_map_install_match (&ospf6_routemap_rule_match_tag_cmd); route_map_install_set (&ospf6_routemap_rule_set_metric_type_cmd); route_map_install_set (&ospf6_routemap_rule_set_metric_cmd); route_map_install_set (&ospf6_routemap_rule_set_forwarding_cmd); + route_map_install_set (&ospf6_routemap_rule_set_tag_cmd); /* Match address prefix-list */ install_element (RMAP_NODE, &ospf6_routemap_match_address_prefixlist_cmd); @@ -1191,6 +1353,11 @@ ospf6_routemap_init (void) install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_cmd); install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_val_cmd); + /* Match tag */ + install_element (RMAP_NODE, &ospf6_routemap_match_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_val_cmd); + /* ASE Metric Type (e.g. Type-1/Type-2) */ install_element (RMAP_NODE, &ospf6_routemap_set_metric_type_cmd); install_element (RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd); @@ -1200,9 +1367,14 @@ ospf6_routemap_init (void) install_element (RMAP_NODE, &no_set_metric_cmd); install_element (RMAP_NODE, &no_set_metric_val_cmd); - /* ASE Metric */ + /* Forwarding address */ install_element (RMAP_NODE, &ospf6_routemap_set_forwarding_cmd); install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd); + + /* Tag */ + install_element (RMAP_NODE, &ospf6_routemap_set_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_val_cmd); } @@ -1280,6 +1452,13 @@ ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) VNL); } + /* Tag */ + if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T)) + { + vty_out (vty, " Tag: %"ROUTE_TAG_PRI"%s", + ospf6_as_external_lsa_get_tag (lsa), VNL); + } + return 0; } diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 645e8fd9cf..da6bbdd9c3 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -47,7 +47,8 @@ struct ospf6_external_info u_int32_t id; struct in6_addr forwarding; - /* u_int32_t tag; */ + + route_tag_t tag; ifindex_t ifindex; }; @@ -82,7 +83,8 @@ extern int ospf6_asbr_is_asbr (struct ospf6 *o); extern void ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, u_int nexthop_num, - struct in6_addr *nexthop); + struct in6_addr *nexthop, + route_tag_t tag); extern void ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex, struct prefix *prefix); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 610b0970b0..7ecc066602 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -100,6 +100,7 @@ struct ospf6_path u_int32_t cost_e2; u_int32_t cost_config; } u; + u_int32_t tag; }; #define OSPF6_PATH_TYPE_NONE 0 diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 45165fdf2e..d24b808aaa 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -260,6 +260,11 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) { char prefixstr[PREFIX2STR_BUFFER], nexthopstr[128]; @@ -269,14 +274,14 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, else snprintf (nexthopstr, sizeof (nexthopstr), "::"); - zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld", + zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %"ROUTE_TAG_PRI, (command == ZEBRA_IPV6_ROUTE_ADD ? "add" : "delete"), - zebra_route_string(api.type), prefixstr, nexthopstr, ifindex); + zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag); } if (command == ZEBRA_REDISTRIBUTE_IPV6_ADD) ospf6_asbr_redistribute_add (api.type, ifindex, (struct prefix *) &p, - api.nexthop_num, nexthop); + api.nexthop_num, nexthop, api.tag); else ospf6_asbr_redistribute_remove (api.type, ifindex, (struct prefix *) &p); @@ -468,6 +473,11 @@ ospf6_zebra_route_update (int type, struct ospf6_route *request) SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = (request->path.metric_type == 2 ? request->path.u.cost_e2 : request->path.cost); + if (request->path.tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = request->path.tag; + } dest = (struct prefix_ipv6 *) &request->prefix; if (type == REM) From 9471675f21f3f7d9cec41c32854477f96a5ce323 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 1 Oct 2016 21:43:17 +0200 Subject: [PATCH 119/136] ripd: add support for route tags Signed-off-by: Christian Franke --- ripd/rip_interface.c | 6 +++--- ripd/rip_zebra.c | 15 +++++++++++++-- ripd/ripd.c | 7 +++++-- ripd/ripd.h | 3 ++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 604343be89..359549ed80 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -640,7 +640,7 @@ rip_apply_address_add (struct connected *ifc) if ((rip_enable_if_lookup(ifc->ifp->name) >= 0) || (rip_enable_network_lookup2(ifc) >= 0)) rip_redistribute_add(ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, - &address, ifc->ifp->ifindex, NULL, 0, 0); + &address, ifc->ifp->ifindex, NULL, 0, 0, 0); } @@ -951,7 +951,7 @@ rip_connect_set (struct interface *ifp, int set) (rip_enable_network_lookup2(connected) >= 0)) rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, &address, connected->ifp->ifindex, - NULL, 0, 0); + NULL, 0, 0, 0); } else { rip_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, @@ -959,7 +959,7 @@ rip_connect_set (struct interface *ifp, int set) if (rip_redistribute_check (ZEBRA_ROUTE_CONNECT)) rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_REDISTRIBUTE, &address, connected->ifp->ifindex, - NULL, 0, 0); + NULL, 0, 0, 0); } } } diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 23f2adf82c..3f7c7a3e4d 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -91,6 +91,12 @@ rip_zebra_ipv4_send (struct route_node *rp, u_char cmd) api.distance = rinfo->distance; } + if (rinfo->tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = rinfo->tag; + } + zapi_ipv4_route (cmd, zclient, (struct prefix_ipv4 *)&rp->p, &api); @@ -176,10 +182,15 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + /* Then fetch IPv4 prefixes. */ if (command == ZEBRA_REDISTRIBUTE_IPV4_ADD) rip_redistribute_add (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex, - &nexthop, api.metric, api.distance); + &nexthop, api.metric, api.distance, api.tag); else if (command == ZEBRA_REDISTRIBUTE_IPV4_DEL) rip_redistribute_delete (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex); @@ -614,7 +625,7 @@ DEFUN (rip_default_information_originate, rip->default_information = 1; rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, &p, 0, - NULL, 0, 0); + NULL, 0, 0, 0); } return CMD_SUCCESS; diff --git a/ripd/ripd.c b/ripd/ripd.c index 220297e835..395a7f7822 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1513,7 +1513,8 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, void rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, ifindex_t ifindex, struct in_addr *nexthop, - unsigned int metric, unsigned char distance) + unsigned int metric, unsigned char distance, + route_tag_t tag) { int ret; struct route_node *rp = NULL; @@ -1534,6 +1535,8 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, newinfo.metric = 1; newinfo.external_metric = metric; newinfo.distance = distance; + if (tag <= UINT16_MAX) /* RIP only supports 16 bit tags */ + newinfo.tag = tag; newinfo.rp = rp; if (nexthop) newinfo.nexthop = *nexthop; @@ -2945,7 +2948,7 @@ DEFUN (rip_route, node->info = (void *)1; - rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0); + rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0, 0); return CMD_SUCCESS; } diff --git a/ripd/ripd.h b/ripd/ripd.h index 2d5bd98de8..3de23ec334 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -403,7 +403,8 @@ extern int rip_neighbor_lookup (struct sockaddr_in *); extern int rip_redistribute_check (int); extern void rip_redistribute_add (int, int, struct prefix_ipv4 *, ifindex_t, - struct in_addr *, unsigned int, unsigned char); + struct in_addr *, unsigned int, unsigned char, + route_tag_t); extern void rip_redistribute_delete (int, int, struct prefix_ipv4 *, ifindex_t); extern void rip_redistribute_withdraw (int); extern void rip_zebra_ipv4_add (struct route_node *); From 1796a585f07b76a6855e32f339bfd7346432dd2d Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 1 Oct 2016 22:35:32 +0200 Subject: [PATCH 120/136] ripngd: add support for route tags Signed-off-by: Christian Franke --- ripngd/ripng_interface.c | 6 +++--- ripngd/ripng_zebra.c | 13 ++++++++++++- ripngd/ripngd.c | 9 ++++++--- ripngd/ripngd.h | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 0061c0e803..c4dec7e7b1 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -383,7 +383,7 @@ ripng_apply_address_add (struct connected *ifc) { if ((ripng_enable_if_lookup(ifc->ifp->name) >= 0) || (ripng_enable_network_lookup2(ifc) >= 0)) ripng_redistribute_add(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, - &address, ifc->ifp->ifindex, NULL); + &address, ifc->ifp->ifindex, NULL, 0); } @@ -704,13 +704,13 @@ ripng_connect_set (struct interface *ifp, int set) if ((ripng_enable_if_lookup(connected->ifp->name) >= 0) || (ripng_enable_network_lookup2(connected) >= 0)) ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, - &address, connected->ifp->ifindex, NULL); + &address, connected->ifp->ifindex, NULL, 0); } else { ripng_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, &address, connected->ifp->ifindex); if (ripng_redistribute_check (ZEBRA_ROUTE_CONNECT)) ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_REDISTRIBUTE, - &address, connected->ifp->ifindex, NULL); + &address, connected->ifp->ifindex, NULL, 0); } } } diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index c4ed0c52c6..d05b5dbad8 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -92,6 +92,12 @@ ripng_zebra_ipv6_send (struct route_node *rp, u_char cmd) SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = rinfo->metric; + if (rinfo->tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = rinfo->tag; + } + zapi_ipv6_route (cmd, zclient, (struct prefix_ipv6 *)&rp->p, &api); @@ -172,8 +178,13 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + if (command == ZEBRA_REDISTRIBUTE_IPV6_ADD) - ripng_redistribute_add (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex, &nexthop); + ripng_redistribute_add (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex, &nexthop, api.tag); else ripng_redistribute_delete (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex); diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 0c9606e69c..e8aad7774a 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -907,7 +907,8 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* Add redistributed route to RIPng table. */ void ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, - ifindex_t ifindex, struct in6_addr *nexthop) + ifindex_t ifindex, struct in6_addr *nexthop, + route_tag_t tag) { struct route_node *rp; struct ripng_info *rinfo = NULL, newinfo; @@ -926,6 +927,8 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, newinfo.sub_type = sub_type; newinfo.ifindex = ifindex; newinfo.metric = 1; + if (tag <= UINT16_MAX) /* RIPng only supports 16 bit tags */ + newinfo.tag = tag; newinfo.rp = rp; if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop)) newinfo.nexthop = *nexthop; @@ -2216,7 +2219,7 @@ DEFUN (ripng_route, } rp->info = (void *)1; - ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL); + ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL, 0); return CMD_SUCCESS; } @@ -2553,7 +2556,7 @@ DEFUN (ripng_default_information_originate, ripng->default_information = 1; str2prefix_ipv6 ("::/0", &p); - ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL); + ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0); } return CMD_SUCCESS; diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index c4b34b348c..e340eeecc2 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -383,7 +383,7 @@ extern void ripng_info_free (struct ripng_info *rinfo); extern void ripng_event (enum ripng_event, int); extern int ripng_request (struct interface *ifp); extern void ripng_redistribute_add (int, int, struct prefix_ipv6 *, - ifindex_t, struct in6_addr *); + ifindex_t, struct in6_addr *, route_tag_t); extern void ripng_redistribute_delete (int, int, struct prefix_ipv6 *, ifindex_t); extern void ripng_redistribute_withdraw (int type); From 3c8ab49fd1edd6c3f6f83abd9d4b8ae97c11ecde Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 11 Mar 2016 14:27:11 -0500 Subject: [PATCH 121/136] bgpd, lib: Remove RESTRICTED_NODE from code base The RESTRICTED_NODE command is not used, introduces code complexity and provides no additional levels of security. The only way to get into RESTRICTED_NODE is to add, under vty configuration the command 'anonymous restricted', and then telnet to a daemon, provide a password, then type 'enable' and fail to enter the password three times. Then the user can enter a very limited set of commands to monitor bgp and only bgp behavior. This commit removes both the RESTRICTED_NODE usage as well as the lib/* usage of the code Signed-off-by: Donald Sharp --- bgpd/bgp_route.c | 83 ----------------------------------------------- bgpd/bgp_vty.c | 52 ----------------------------- confdefs.h | 73 +++++++++++++++++++++++++++++++++++++++++ conftest | Bin 0 -> 9680 bytes conftest.err | 0 lib/command.c | 20 ------------ lib/command.h | 1 - lib/memory_vty.c | 2 -- lib/vty.c | 44 ++----------------------- 9 files changed, 75 insertions(+), 200 deletions(-) create mode 100644 confdefs.h create mode 100755 conftest create mode 100644 conftest.err diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 923acfda67..096718c982 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -14970,49 +14970,6 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ip_bgp_damp_flap_route_map_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_flap_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_damp_cmd); - - /* Restricted node: VIEW_NODE - (set of dangerous commands) */ - install_element (RESTRICTED_NODE, &show_ip_bgp_route_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_route_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_route_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_route_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_prefix_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community2_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community3_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community4_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_afi_safi_community_all_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_afi_safi_community_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_afi_safi_community2_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_afi_safi_community3_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_afi_safi_community4_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community2_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community3_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_community4_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_exact_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_exact_cmd); install_element (ENABLE_NODE, &show_ip_bgp_cmd); install_element (ENABLE_NODE, &show_ip_bgp_instance_cmd); @@ -15272,46 +15229,6 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbor_flap_cmd); install_element (VIEW_NODE, &show_bgp_instance_neighbor_damp_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbor_damp_cmd); - - /* Restricted: - * VIEW_NODE - (set of dangerous commands) - (commands dependent on prev) - */ - install_element (RESTRICTED_NODE, &show_bgp_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community2_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community3_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community4_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community2_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community3_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_community4_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_route_pathtype_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_neighbor_received_prefix_filter_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_neighbor_received_prefix_filter_cmd); install_element (ENABLE_NODE, &show_bgp_cmd); install_element (ENABLE_NODE, &show_bgp_ipv6_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index c605e186d6..44388bea34 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -15964,46 +15964,6 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_bgp_ipv6_safi_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); -#endif /* HAVE_IPV6 */ - install_element (RESTRICTED_NODE, &show_ip_bgp_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_all_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_bgp_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_all_ipv6_updgrps_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_updgrps_s_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_updgrps_s_cmd); - install_element (RESTRICTED_NODE, &show_bgp_updgrps_s_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_updgrps_s_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_updgrps_s_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_updgrps_adj_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_updgrps_adj_cmd); - install_element (RESTRICTED_NODE, &show_bgp_updgrps_adj_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_updgrps_adj_cmd); - install_element (RESTRICTED_NODE, &show_bgp_updgrps_afi_adj_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_updgrps_adj_s_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_updgrps_adj_s_cmd); - install_element (RESTRICTED_NODE, &show_bgp_updgrps_adj_s_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_updgrps_adj_s_cmd); - install_element (RESTRICTED_NODE, &show_bgp_updgrps_afi_adj_s_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_all_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); -#ifdef HAVE_IPV6 - install_element (RESTRICTED_NODE, &show_bgp_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_all_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); #endif /* HAVE_IPV6 */ install_element (ENABLE_NODE, &show_ip_bgp_summary_cmd); install_element (ENABLE_NODE, &show_ip_bgp_updgrps_cmd); @@ -16064,11 +16024,6 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_all_neighbors_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); install_element (ENABLE_NODE, &show_ip_bgp_neighbors_cmd); install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbors_cmd); install_element (ENABLE_NODE, &show_ip_bgp_neighbors_peer_cmd); @@ -16090,10 +16045,6 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_cmd); install_element (VIEW_NODE, &show_bgp_instance_neighbors_peer_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_bgp_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); install_element (ENABLE_NODE, &show_bgp_neighbors_cmd); install_element (ENABLE_NODE, &show_bgp_ipv6_neighbors_cmd); install_element (ENABLE_NODE, &show_bgp_neighbors_peer_cmd); @@ -16194,17 +16145,14 @@ bgp_vty_init (void) /* "show bgp memory" commands. */ install_element (VIEW_NODE, &show_bgp_memory_cmd); - install_element (RESTRICTED_NODE, &show_bgp_memory_cmd); install_element (ENABLE_NODE, &show_bgp_memory_cmd); /* "show bgp views" commands. */ install_element (VIEW_NODE, &show_bgp_views_cmd); - install_element (RESTRICTED_NODE, &show_bgp_views_cmd); install_element (ENABLE_NODE, &show_bgp_views_cmd); /* "show bgp vrfs" commands. */ install_element (VIEW_NODE, &show_bgp_vrfs_cmd); - install_element (RESTRICTED_NODE, &show_bgp_vrfs_cmd); install_element (ENABLE_NODE, &show_bgp_vrfs_cmd); /* Community-list. */ diff --git a/confdefs.h b/confdefs.h new file mode 100644 index 0000000000..0787a51477 --- /dev/null +++ b/confdefs.h @@ -0,0 +1,73 @@ +/* confdefs.h */ +#define PACKAGE_NAME "Quagga" +#define PACKAGE_TARNAME "quagga" +#define PACKAGE_VERSION "0.99.24+cl3u4" +#define PACKAGE_STRING "Quagga 0.99.24+cl3u4" +#define PACKAGE_BUGREPORT "https://bugzilla.quagga.net" +#define PACKAGE_URL "" +#define PACKAGE "quagga" +#define VERSION "0.99.24+cl3u4" +#define STDC_HEADERS 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_UNISTD_H 1 +#define __EXTENSIONS__ 1 +#define _ALL_SOURCE 1 +#define _GNU_SOURCE 1 +#define _POSIX_PTHREAD_SEMANTICS 1 +#define _TANDEM_SOURCE 1 +#define HAVE_DLFCN_H 1 +#define LT_OBJDIR ".libs/" +#define HAVE_JSON_C_JSON_H 1 +#define CONSUMED_TIME_CHECK 5000000 +#define HAVE_V6_RR_SEMANTICS /**/ +#define HAVE_RTADV /**/ +#define QUAGGA_USER "quagga" +#define QUAGGA_GROUP "quagga" +#define CONFIGFILE_MASK 0600 +#define LOGFILE_MASK 0600 +#define MULTIPATH_NUM 4 +#define restrict __restrict +#define STDC_HEADERS 1 +#define TIME_WITH_SYS_TIME 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE__BOOL 1 +#define HAVE_STDBOOL_H 1 +#define HAVE_STROPTS_H 1 +#define HAVE_SYS_TIMES_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_LINUX_VERSION_H 1 +#define HAVE_NETDB_H 1 +#define HAVE_ASM_TYPES_H 1 +#define HAVE_SYS_CDEFS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_SIGNAL_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_TIME_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_NET_IF_H 1 +#define HAVE_SYS_UN_H 1 +#define HAVE_NETINET_IN_SYSTM_H 1 +#define HAVE_NET_ROUTE_H 1 +#define HAVE_ARPA_INET_H 1 +#define HAVE_NETINET_IP_ICMP_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_STDDEF_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYSLOG_H 1 +#define HAVE_WCHAR_H 1 +#define HAVE_WCTYPE_H 1 +#define HAVE_SYS_SYSCTL_H 1 +#define HAVE_UCONTEXT_H 1 +#define HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS 1 +#define GNU_LINUX /**/ +#define VTYSH /**/ diff --git a/conftest b/conftest new file mode 100755 index 0000000000000000000000000000000000000000..04df7b19b5cd8ac7858e7c8175862774123b5a21 GIT binary patch literal 9680 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 = 0; vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); - vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; + vty->status = VTY_CLOSE; } } } @@ -735,7 +731,6 @@ vty_end_config (struct vty *vty) { case VIEW_NODE: case ENABLE_NODE: - case RESTRICTED_NODE: /* Nothing to do. */ break; case CONFIG_NODE: @@ -1157,7 +1152,6 @@ vty_stop_input (struct vty *vty) { case VIEW_NODE: case ENABLE_NODE: - case RESTRICTED_NODE: /* Nothing to do. */ break; case CONFIG_NODE: @@ -1717,9 +1711,7 @@ vty_create (int vty_sock, union sockunion *su) strcpy (vty->address, buf); if (no_password_check) { - if (restricted_mode) - vty->node = RESTRICTED_NODE; - else if (host.advanced) + if (host.advanced) vty->node = ENABLE_NODE; else vty->node = VIEW_NODE; @@ -2899,26 +2891,6 @@ DEFUN (no_vty_login, return CMD_SUCCESS; } -/* initial mode. */ -DEFUN (vty_restricted_mode, - vty_restricted_mode_cmd, - "anonymous restricted", - "Restrict view commands available in anonymous, unauthenticated vty\n") -{ - restricted_mode = 1; - return CMD_SUCCESS; -} - -DEFUN (vty_no_restricted_mode, - vty_no_restricted_mode_cmd, - "no anonymous restricted", - NO_STR - "Enable password checking\n") -{ - restricted_mode = 0; - return CMD_SUCCESS; -} - DEFUN (service_advanced_vty, service_advanced_vty_cmd, "service advanced-vty", @@ -3027,14 +2999,6 @@ vty_config_write (struct vty *vty) /* login */ if (no_password_check) vty_out (vty, " no login%s", VTY_NEWLINE); - - if (restricted_mode != restricted_mode_default) - { - if (restricted_mode_default) - vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE); - else - vty_out (vty, " anonymous restricted%s", VTY_NEWLINE); - } if (do_log_commands) vty_out (vty, "log commands%s", VTY_NEWLINE); @@ -3164,8 +3128,6 @@ vty_init (struct thread_master *master_thread) /* Install bgp top node. */ install_node (&vty_node, vty_config_write); - install_element (RESTRICTED_NODE, &config_who_cmd); - install_element (RESTRICTED_NODE, &show_history_cmd); install_element (VIEW_NODE, &config_who_cmd); install_element (VIEW_NODE, &show_history_cmd); install_element (ENABLE_NODE, &config_who_cmd); @@ -3187,8 +3149,6 @@ vty_init (struct thread_master *master_thread) install_element (VTY_NODE, &no_vty_access_class_cmd); install_element (VTY_NODE, &vty_login_cmd); install_element (VTY_NODE, &no_vty_login_cmd); - install_element (VTY_NODE, &vty_restricted_mode_cmd); - install_element (VTY_NODE, &vty_no_restricted_mode_cmd); #ifdef HAVE_IPV6 install_element (VTY_NODE, &vty_ipv6_access_class_cmd); install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd); From 735e62a0f28b375b184e1bfeb97f7cbe5673a497 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 11 Mar 2016 14:27:12 -0500 Subject: [PATCH 122/136] lib: Consolidate VIEW_NODE to be ENABLE_NODE as well If you are in VIEW_NODE, the command should exist in ENABLE_NODE as well. This is being done to reduce chances of code being added to one but not the other NODE. Signed-off-by: Donald Sharp --- lib/command.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/command.c b/lib/command.c index 5ebab1450d..f72733ab27 100644 --- a/lib/command.c +++ b/lib/command.c @@ -718,6 +718,9 @@ install_element (enum node_type ntype, struct cmd_element *cmd) vector_set (cnode->cmd_vector, cmd); if (cmd->tokens == NULL) cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); + + if (ntype == VIEW_NODE) + install_element (ENABLE_NODE, cmd); } static const unsigned char itoa64[] = From 0b1442e37b5278c604f9e5210d00c5a4b201ac61 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 11 Mar 2016 14:27:13 -0500 Subject: [PATCH 123/136] *: Consolidate all double VIEW_NODE and ENABLE_NODE's If a command is put into the VIEW_NODE, it is going into the ENABLE_NODE as well. This is especially true for show commands. As such if a command is in both consolidate it down to VIEW_NODE. Signed-off-by: Donald Sharp --- bgpd/bgp_encap.c | 23 ---- bgpd/bgp_filter.c | 2 - bgpd/bgp_mplsvpn.c | 13 -- bgpd/bgp_nexthop.c | 5 - bgpd/bgp_route.c | 256 --------------------------------------- bgpd/bgp_vty.c | 78 ------------ isisd/isis_spf.c | 4 - isisd/isis_te.c | 2 - isisd/isisd.c | 19 --- ldpd/ldp_vty.xml | 1 - ldpd/ldp_vty_cmds.c | 12 -- lib/command.c | 9 -- lib/memory_vty.c | 2 - lib/plist.c | 22 ---- lib/vty.c | 2 - ospf6d/ospf6_area.c | 4 - ospf6d/ospf6_asbr.c | 1 - ospf6d/ospf6_interface.c | 8 -- ospf6d/ospf6_neighbor.c | 2 - ospf6d/ospf6_top.c | 9 -- ospf6d/ospf6_zebra.c | 1 - ospf6d/ospf6d.c | 7 -- ospfd/ospf_ri.c | 2 - ospfd/ospf_te.c | 2 - ospfd/ospf_vty.c | 36 ------ pimd/pim_cmd.c | 35 +----- ripd/ripd.c | 2 - ripngd/ripng_debug.c | 1 - ripngd/ripngd.c | 3 - zebra/interface.c | 6 - zebra/zebra_vty.c | 64 ---------- zebra/zserv.c | 3 - 32 files changed, 2 insertions(+), 634 deletions(-) diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c index 136bab13b6..ac99169e7f 100644 --- a/bgpd/bgp_encap.c +++ b/bgpd/bgp_encap.c @@ -932,27 +932,4 @@ bgp_encap_init (void) install_element (VIEW_NODE, &show_bgp_ipv6_encap_neighbor_advertised_routes_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_neighbor_advertised_routes_cmd); #endif - - - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_rd_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_tags_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_rd_tags_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_rd_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_neighbor_advertised_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_encap_rd_neighbor_advertised_routes_cmd); - -#ifdef HAVE_IPV6 - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_rd_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_tags_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_rd_tags_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_rd_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_neighbor_advertised_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_encap_rd_neighbor_advertised_routes_cmd); -#endif - - } diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index 33877e7258..a95a7564ec 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -695,8 +695,6 @@ bgp_filter_init (void) install_element (VIEW_NODE, &show_ip_as_path_access_list_cmd); install_element (VIEW_NODE, &show_ip_as_path_access_list_all_cmd); - install_element (ENABLE_NODE, &show_ip_as_path_access_list_cmd); - install_element (ENABLE_NODE, &show_ip_as_path_access_list_all_cmd); } void diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 2fc5d28087..86e8788351 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1279,17 +1279,4 @@ bgp_mplsvpn_init (void) install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbor_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbor_advertised_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbor_advertised_routes_cmd); - - install_element (ENABLE_NODE, &show_bgp_ipv4_vpn_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_vpn_rd_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_vpn_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_vpn_rd_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_tags_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_tags_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbor_advertised_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbor_advertised_routes_cmd); } diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 19f5428d88..0d6203b2a7 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -567,16 +567,11 @@ bgp_scan_init (struct bgp *bgp) void bgp_scan_vty_init (void) { - install_element (ENABLE_NODE, &show_ip_bgp_nexthop_cmd); install_element (VIEW_NODE, &show_ip_bgp_nexthop_cmd); install_element (VIEW_NODE, &show_ip_bgp_nexthop_detail_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_nexthop_detail_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_nexthop_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_all_nexthop_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_nexthop_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_all_nexthop_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_nexthop_detail_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_nexthop_detail_cmd); } void diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 096718c982..e53941172b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -14971,131 +14971,12 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ip_bgp_neighbor_flap_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd); - - install_element (ENABLE_NODE, &show_ip_bgp_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community2_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community3_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community4_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community2_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community3_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community4_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_afi_safi_community_all_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_afi_safi_community_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_afi_safi_community2_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_afi_safi_community3_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_afi_safi_community4_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_advertised_route_rmap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_advertised_route_rmap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_rmap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_routes_rmap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_received_routes_rmap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_rmap_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_afi_safi_neighbor_adv_recd_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_dampening_params_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_dampened_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_dampening_parameters_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_dampening_dampd_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_dampening_flap_stats_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_dampened_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_statistics_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_statistics_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_address_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_address_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_damp_flap_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_damp_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_prefix_cmd); install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_vpn_rd_route_cmd); install_element (VIEW_NODE, &show_bgp_ipv4_vpn_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_vpn_route_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_vpn_rd_route_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_vpn_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_vpn_route_cmd); /* BGP dampening clear commands */ install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd); @@ -15230,99 +15111,6 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_bgp_instance_neighbor_damp_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_cmd); - install_element (ENABLE_NODE, &show_bgp_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_route_cmd); - install_element (ENABLE_NODE, &show_bgp_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_regexp_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_regexp_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_list_cmd); - install_element (ENABLE_NODE, &show_bgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_filter_list_cmd); - install_element (ENABLE_NODE, &show_bgp_route_map_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_route_map_cmd); - install_element (ENABLE_NODE, &show_bgp_community_all_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_all_cmd); - install_element (ENABLE_NODE, &show_bgp_community_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_cmd); - install_element (ENABLE_NODE, &show_bgp_community2_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community2_cmd); - install_element (ENABLE_NODE, &show_bgp_community3_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community3_cmd); - install_element (ENABLE_NODE, &show_bgp_community4_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community4_cmd); - install_element (ENABLE_NODE, &show_bgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community2_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community3_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community4_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community_list_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_list_cmd); - install_element (ENABLE_NODE, &show_bgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_all_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_route_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_route_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_route_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_prefix_pathtype_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_prefix_list_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_prefix_list_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_filter_list_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_filter_list_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_route_map_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_route_map_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_community_list_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_community_list_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_damp_cmd); - /* Statistics */ install_element (ENABLE_NODE, &show_bgp_statistics_cmd); //install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd); @@ -15367,61 +15155,17 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_exact_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_longer_cmd); - /* old command */ - install_element (ENABLE_NODE, &show_ipv6_bgp_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_regexp_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community2_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community3_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community4_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_regexp_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community2_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community3_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community4_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_prefix_longer_cmd); - /* old command */ install_element (VIEW_NODE, &ipv6_bgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &ipv6_bgp_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &ipv6_mbgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &ipv6_mbgp_neighbor_advertised_route_cmd); /* old command */ install_element (VIEW_NODE, &ipv6_bgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &ipv6_bgp_neighbor_received_routes_cmd); install_element (VIEW_NODE, &ipv6_mbgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &ipv6_mbgp_neighbor_received_routes_cmd); /* old command */ install_element (VIEW_NODE, &ipv6_bgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &ipv6_bgp_neighbor_routes_cmd); install_element (VIEW_NODE, &ipv6_mbgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &ipv6_mbgp_neighbor_routes_cmd); #endif /* HAVE_IPV6 */ install_element (BGP_NODE, &bgp_distance_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 44388bea34..7c2bcd8bd7 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -15965,52 +15965,10 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_bgp_instance_ipv6_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); #endif /* HAVE_IPV6 */ - install_element (ENABLE_NODE, &show_ip_bgp_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_updgrps_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_updgrps_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_all_updgrps_cmd); - install_element (ENABLE_NODE, &show_bgp_updgrps_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_updgrps_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_updgrps_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_all_ipv6_updgrps_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_updgrps_s_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_updgrps_s_cmd); - install_element (ENABLE_NODE, &show_bgp_updgrps_s_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_updgrps_s_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_updgrps_s_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_updgrps_adj_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_updgrps_adj_cmd); - install_element (ENABLE_NODE, &show_bgp_updgrps_adj_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_updgrps_adj_cmd); - install_element (ENABLE_NODE, &show_bgp_updgrps_afi_adj_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_updgrps_adj_s_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_updgrps_adj_s_cmd); - install_element (ENABLE_NODE, &show_bgp_updgrps_adj_s_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_updgrps_adj_s_cmd); - install_element (ENABLE_NODE, &show_bgp_updgrps_afi_adj_s_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_all_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv4_safi_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); -#ifdef HAVE_IPV6 - install_element (ENABLE_NODE, &show_bgp_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_all_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); -#endif /* HAVE_IPV6 */ install_element (VIEW_NODE, &show_bgp_ipv4_vpn_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_vpn_summary_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_vpn_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_vpn_summary_cmd); /* "show ip bgp neighbors" commands. */ install_element (VIEW_NODE, &show_ip_bgp_neighbors_cmd); @@ -16024,17 +15982,6 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_all_neighbors_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_all_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); #ifdef HAVE_IPV6 install_element (VIEW_NODE, &show_bgp_neighbors_cmd); @@ -16045,20 +15992,10 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_cmd); install_element (VIEW_NODE, &show_bgp_instance_neighbors_peer_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); /* Old commands. */ install_element (VIEW_NODE, &show_ipv6_bgp_summary_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_summary_cmd); #endif /* HAVE_IPV6 */ /* "show ip bgp peer-group" commands. */ @@ -16066,24 +16003,16 @@ bgp_vty_init (void) install_element (VIEW_NODE, &show_ip_bgp_instance_peer_groups_cmd); install_element (VIEW_NODE, &show_ip_bgp_peer_group_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_peer_group_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_peer_groups_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_peer_groups_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_peer_group_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_peer_group_cmd); /* "show ip bgp paths" commands. */ install_element (VIEW_NODE, &show_ip_bgp_paths_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_paths_cmd); /* "show ip bgp community" commands. */ install_element (VIEW_NODE, &show_ip_bgp_community_info_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_info_cmd); /* "show ip bgp attribute-info" commands. */ install_element (VIEW_NODE, &show_ip_bgp_attr_info_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_attr_info_cmd); /* "redistribute" commands. */ install_element (BGP_NODE, &bgp_redistribute_ipv4_cmd); @@ -16145,15 +16074,12 @@ bgp_vty_init (void) /* "show bgp memory" commands. */ install_element (VIEW_NODE, &show_bgp_memory_cmd); - install_element (ENABLE_NODE, &show_bgp_memory_cmd); /* "show bgp views" commands. */ install_element (VIEW_NODE, &show_bgp_views_cmd); - install_element (ENABLE_NODE, &show_bgp_views_cmd); /* "show bgp vrfs" commands. */ install_element (VIEW_NODE, &show_bgp_vrfs_cmd); - install_element (ENABLE_NODE, &show_bgp_vrfs_cmd); /* Community-list. */ community_list_vty (); @@ -17068,8 +16994,6 @@ community_list_vty (void) install_element (CONFIG_NODE, &no_ip_community_list_name_expanded_cmd); install_element (VIEW_NODE, &show_ip_community_list_cmd); install_element (VIEW_NODE, &show_ip_community_list_arg_cmd); - install_element (ENABLE_NODE, &show_ip_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_community_list_arg_cmd); /* Extcommunity-list. */ install_element (CONFIG_NODE, &ip_extcommunity_list_standard_cmd); @@ -17090,6 +17014,4 @@ community_list_vty (void) install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_expanded_cmd); install_element (VIEW_NODE, &show_ip_extcommunity_list_cmd); install_element (VIEW_NODE, &show_ip_extcommunity_list_arg_cmd); - install_element (ENABLE_NODE, &show_ip_extcommunity_list_cmd); - install_element (ENABLE_NODE, &show_ip_extcommunity_list_arg_cmd); } diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index fd5af4a2b8..041f2ed3f6 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1680,8 +1680,4 @@ isis_spf_cmds_init () install_element (VIEW_NODE, &show_isis_topology_cmd); install_element (VIEW_NODE, &show_isis_topology_l1_cmd); install_element (VIEW_NODE, &show_isis_topology_l2_cmd); - - install_element (ENABLE_NODE, &show_isis_topology_cmd); - install_element (ENABLE_NODE, &show_isis_topology_l1_cmd); - install_element (ENABLE_NODE, &show_isis_topology_l2_cmd); } diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 022722f760..34300669b0 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -1356,8 +1356,6 @@ isis_mpls_te_init (void) /* Register new VTY commands */ install_element (VIEW_NODE, &show_isis_mpls_te_router_cmd); install_element (VIEW_NODE, &show_isis_mpls_te_interface_cmd); - install_element (ENABLE_NODE, &show_isis_mpls_te_router_cmd); - install_element (ENABLE_NODE, &show_isis_mpls_te_interface_cmd); install_element (ISIS_NODE, &isis_mpls_te_on_cmd); install_element (ISIS_NODE, &no_isis_mpls_te_on_cmd); diff --git a/isisd/isisd.c b/isisd/isisd.c index e84ae7cf00..d6ce627b93 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2310,24 +2310,6 @@ isis_init () install_element (VIEW_NODE, &show_database_detail_cmd); install_element (VIEW_NODE, &show_database_detail_arg_cmd); - install_element (ENABLE_NODE, &show_isis_summary_cmd); - - install_element (ENABLE_NODE, &show_isis_interface_cmd); - install_element (ENABLE_NODE, &show_isis_interface_detail_cmd); - install_element (ENABLE_NODE, &show_isis_interface_arg_cmd); - - install_element (ENABLE_NODE, &show_isis_neighbor_cmd); - install_element (ENABLE_NODE, &show_isis_neighbor_detail_cmd); - install_element (ENABLE_NODE, &show_isis_neighbor_arg_cmd); - install_element (ENABLE_NODE, &clear_isis_neighbor_cmd); - install_element (ENABLE_NODE, &clear_isis_neighbor_arg_cmd); - - install_element (ENABLE_NODE, &show_hostname_cmd); - install_element (ENABLE_NODE, &show_database_cmd); - install_element (ENABLE_NODE, &show_database_arg_cmd); - install_element (ENABLE_NODE, &show_database_arg_detail_cmd); - install_element (ENABLE_NODE, &show_database_detail_cmd); - install_element (ENABLE_NODE, &show_database_detail_arg_cmd); install_element (ENABLE_NODE, &show_debugging_isis_cmd); install_node (&debug_node, config_write_debug); @@ -2408,6 +2390,5 @@ isis_init () install_element (ISIS_NODE, &no_topology_baseis_cmd); install_element (ISIS_NODE, &no_topology_baseis_noid_cmd); install_element (VIEW_NODE, &show_isis_generated_topology_cmd); - install_element (ENABLE_NODE, &show_isis_generated_topology_cmd); #endif /* TOPOLOGY_GENERATE */ } diff --git a/ldpd/ldp_vty.xml b/ldpd/ldp_vty.xml index 8742f9c1be..ee5c6e4df2 100644 --- a/ldpd/ldp_vty.xml +++ b/ldpd/ldp_vty.xml @@ -370,7 +370,6 @@ - diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index 7160835726..b55c7fc8a9 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -1695,18 +1695,6 @@ ldp_vty_init (void) install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_pw_id_pwid_cmd); install_element (LDP_PSEUDOWIRE_NODE, &ldp_no_pw_status_disable_cmd); install_node (&ldp_debug_node, ldp_debug_config_write); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_neighbor_cmd); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_binding_cmd); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_discovery_cmd); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_interface_cmd); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_address_family_binding_cmd); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_address_family_discovery_cmd); - install_element (ENABLE_NODE, &ldp_show_mpls_ldp_address_family_interface_cmd); - install_element (ENABLE_NODE, &ldp_show_l2vpn_atom_binding_cmd); - install_element (ENABLE_NODE, &ldp_show_l2vpn_atom_vc_cmd); - install_element (ENABLE_NODE, &ldp_show_debugging_mpls_ldp_cmd); - install_element (ENABLE_NODE, &ldp_clear_mpls_ldp_neighbor_cmd); - install_element (ENABLE_NODE, &ldp_clear_mpls_ldp_neighbor_addr_cmd); install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_discovery_hello_dir_cmd); install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_errors_cmd); install_element (ENABLE_NODE, &ldp_debug_mpls_ldp_event_cmd); diff --git a/lib/command.c b/lib/command.c index f72733ab27..be243d44ea 100644 --- a/lib/command.c +++ b/lib/command.c @@ -4226,17 +4226,10 @@ cmd_init (int terminal) install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); } install_element (ENABLE_NODE, &show_startup_config_cmd); - install_element (ENABLE_NODE, &show_version_cmd); - install_element (ENABLE_NODE, &show_commandtree_cmd); if (terminal) { - install_element (ENABLE_NODE, &config_terminal_length_cmd); - install_element (ENABLE_NODE, &config_terminal_no_length_cmd); - install_element (ENABLE_NODE, &show_logging_cmd); - install_element (ENABLE_NODE, &echo_cmd); install_element (ENABLE_NODE, &config_logmsg_cmd); - install_default (CONFIG_NODE); } @@ -4283,11 +4276,9 @@ cmd_init (int terminal) install_element (CONFIG_NODE, &no_service_terminal_length_cmd); install_element (VIEW_NODE, &show_thread_cpu_cmd); - install_element (ENABLE_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &clear_thread_cpu_cmd); install_element (VIEW_NODE, &show_work_queues_cmd); - install_element (ENABLE_NODE, &show_work_queues_cmd); vrf_install_commands (); } diff --git a/lib/memory_vty.c b/lib/memory_vty.c index be8735dfc4..ff0363d45a 100644 --- a/lib/memory_vty.c +++ b/lib/memory_vty.c @@ -114,8 +114,6 @@ void memory_init (void) { install_element (VIEW_NODE, &show_memory_cmd); - - install_element (ENABLE_NODE, &show_memory_cmd); } /* Stats querying from users */ diff --git a/lib/plist.c b/lib/plist.c index cab5d0dba9..2b93d880f6 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -2976,17 +2976,6 @@ prefix_list_init_ipv4 (void) install_element (VIEW_NODE, &show_ip_prefix_list_detail_cmd); install_element (VIEW_NODE, &show_ip_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_name_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_name_seq_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_first_match_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_summary_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_summary_name_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_detail_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &clear_ip_prefix_list_cmd); install_element (ENABLE_NODE, &clear_ip_prefix_list_name_cmd); install_element (ENABLE_NODE, &clear_ip_prefix_list_name_prefix_cmd); @@ -3053,17 +3042,6 @@ prefix_list_init_ipv6 (void) install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_cmd); install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_name_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_name_seq_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_first_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_summary_name_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &clear_ipv6_prefix_list_cmd); install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_cmd); install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_prefix_cmd); diff --git a/lib/vty.c b/lib/vty.c index 15bc683c14..171aca1739 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -3130,7 +3130,6 @@ vty_init (struct thread_master *master_thread) install_element (VIEW_NODE, &config_who_cmd); install_element (VIEW_NODE, &show_history_cmd); - install_element (ENABLE_NODE, &config_who_cmd); install_element (CONFIG_NODE, &line_vty_cmd); install_element (CONFIG_NODE, &service_advanced_vty_cmd); install_element (CONFIG_NODE, &no_service_advanced_vty_cmd); @@ -3139,7 +3138,6 @@ vty_init (struct thread_master *master_thread) install_element (ENABLE_NODE, &terminal_monitor_cmd); install_element (ENABLE_NODE, &terminal_no_monitor_cmd); install_element (ENABLE_NODE, &no_terminal_monitor_cmd); - install_element (ENABLE_NODE, &show_history_cmd); install_default (VTY_NODE); install_element (VTY_NODE, &exec_timeout_min_cmd); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 490d4d2510..685be58324 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -1089,10 +1089,6 @@ ospf6_area_init (void) install_element (VIEW_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_simulate_spf_tree_root_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_spf_tree_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_simulate_spf_tree_root_cmd); - install_element (OSPF6_NODE, &area_range_cmd); install_element (OSPF6_NODE, &area_range_advertise_cmd); install_element (OSPF6_NODE, &area_range_cost_cmd); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 09e6420ffe..e3626c25d8 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1525,7 +1525,6 @@ ospf6_asbr_init (void) ospf6_install_lsa_handler (&as_external_handler); install_element (VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_redistribute_cmd); install_element (OSPF6_NODE, &ospf6_redistribute_cmd); install_element (OSPF6_NODE, &ospf6_redistribute_routemap_cmd); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index f4835d0532..cd3b171a61 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1907,14 +1907,6 @@ ospf6_interface_init (void) install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_detail_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_prefix_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_prefix_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_prefix_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_prefix_match_cmd); install_element (CONFIG_NODE, &interface_cmd); install_default (INTERFACE_NODE); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 9b0285eee9..bb265274f5 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -921,8 +921,6 @@ ospf6_neighbor_init (void) { install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_neighbor_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_neighbor_detail_cmd); } DEFUN (debug_ospf6_neighbor, diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 7feced007c..5def2acf69 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -1003,7 +1003,6 @@ ospf6_top_init (void) install_node (&ospf6_node, config_write_ospf6); install_element (VIEW_NODE, &show_ipv6_ospf6_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_cmd); install_element (CONFIG_NODE, &router_ospf6_cmd); install_element (CONFIG_NODE, &no_router_ospf6_cmd); @@ -1015,14 +1014,6 @@ ospf6_top_init (void) install_element (VIEW_NODE, &show_ipv6_ospf6_route_longer_detail_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_route_type_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_route_type_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_match_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_longer_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_type_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_type_detail_cmd); install_default (OSPF6_NODE); install_element (OSPF6_NODE, &ospf6_router_id_cmd); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index d24b808aaa..4d658ed1b8 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -700,7 +700,6 @@ ospf6_zebra_init (struct thread_master *master) /* Install command element for zebra node. */ install_element (VIEW_NODE, &show_zebra_cmd); - install_element (ENABLE_NODE, &show_zebra_cmd); install_element (CONFIG_NODE, &router_zebra_cmd); install_element (CONFIG_NODE, &no_router_zebra_cmd); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index a60c5b0821..5ae7e60e8b 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -1799,21 +1799,14 @@ ospf6_init (void) install_element_ospf6_clear_interface (); install_element (VIEW_NODE, &show_version_ospf6_cmd); - install_element (ENABLE_NODE, &show_version_ospf6_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_border_routers_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_border_routers_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_border_routers_detail_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_router_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_network_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_router_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_network_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_detail_cmd); #define INSTALL(n,c) \ install_element (n ## _NODE, &show_ipv6_ospf6_ ## c) diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index 8fefd2bdb8..be06cc0a88 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -1621,8 +1621,6 @@ ospf_router_info_register_vty (void) { install_element (VIEW_NODE, &show_ip_ospf_router_info_cmd); install_element (VIEW_NODE, &show_ip_ospf_router_info_pce_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_router_info_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_router_info_pce_cmd); install_element (OSPF_NODE, &router_info_area_cmd); install_element (OSPF_NODE, &router_info_as_cmd); diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 12d589cd99..c672500368 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -2650,8 +2650,6 @@ ospf_mpls_te_register_vty (void) { install_element (VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd); install_element (VIEW_NODE, &show_ip_ospf_mpls_te_link_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_mpls_te_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_mpls_te_link_cmd); install_element (OSPF_NODE, &ospf_mpls_te_on_cmd); install_element (OSPF_NODE, &no_ospf_mpls_te_cmd); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index e76d9d7065..9da36f6a6c 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -9976,10 +9976,8 @@ ospf_vty_show_init (void) { /* "show ip ospf" commands. */ install_element (VIEW_NODE, &show_ip_ospf_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_cmd); /* "show ip ospf database" commands. */ install_element (VIEW_NODE, &show_ip_ospf_database_type_cmd); @@ -9989,13 +9987,6 @@ ospf_vty_show_init (void) install_element (VIEW_NODE, &show_ip_ospf_database_type_id_self_cmd); install_element (VIEW_NODE, &show_ip_ospf_database_type_self_cmd); install_element (VIEW_NODE, &show_ip_ospf_database_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_id_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_id_adv_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_adv_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_id_self_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_self_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_database_type_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_database_type_id_cmd); @@ -10004,20 +9995,11 @@ ospf_vty_show_init (void) install_element (VIEW_NODE, &show_ip_ospf_instance_database_type_id_self_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_database_type_self_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_database_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_type_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_type_id_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_type_id_adv_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_type_adv_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_type_id_self_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_type_self_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_database_cmd); /* "show ip ospf interface" commands. */ install_element (VIEW_NODE, &show_ip_ospf_interface_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_interface_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_interface_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_interface_cmd); /* "show ip ospf neighbor" commands. */ install_element (VIEW_NODE, &show_ip_ospf_neighbor_int_detail_cmd); @@ -10027,13 +10009,6 @@ ospf_vty_show_init (void) install_element (VIEW_NODE, &show_ip_ospf_neighbor_detail_cmd); install_element (VIEW_NODE, &show_ip_ospf_neighbor_cmd); install_element (VIEW_NODE, &show_ip_ospf_neighbor_all_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_int_detail_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_int_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_id_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_detail_all_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_detail_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_all_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_neighbor_int_detail_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_neighbor_int_cmd); @@ -10042,24 +10017,13 @@ ospf_vty_show_init (void) install_element (VIEW_NODE, &show_ip_ospf_instance_neighbor_detail_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_neighbor_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_neighbor_all_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_int_detail_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_int_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_id_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_detail_all_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_detail_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_neighbor_all_cmd); /* "show ip ospf route" commands. */ install_element (VIEW_NODE, &show_ip_ospf_route_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_route_cmd); install_element (VIEW_NODE, &show_ip_ospf_border_routers_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_border_routers_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_route_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_route_cmd); install_element (VIEW_NODE, &show_ip_ospf_instance_border_routers_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_instance_border_routers_cmd); } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 6cfed0f2c6..430fc738d5 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -4942,45 +4942,14 @@ void pim_cmd_init() install_element (VIEW_NODE, &show_ip_ssmpingd_cmd); install_element (VIEW_NODE, &show_debugging_pim_cmd); + install_element (ENABLE_NODE, &show_ip_pim_address_cmd); + install_element (ENABLE_NODE, &clear_ip_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_mroute_cmd); install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_join_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_parameters_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_groups_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_sources_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_sources_retransmissions_cmd); - install_element (ENABLE_NODE, &show_ip_igmp_querier_cmd); - install_element (ENABLE_NODE, &show_ip_pim_address_cmd); - install_element (ENABLE_NODE, &show_ip_pim_assert_cmd); - install_element (ENABLE_NODE, &show_ip_pim_assert_internal_cmd); - install_element (ENABLE_NODE, &show_ip_pim_assert_metric_cmd); - install_element (ENABLE_NODE, &show_ip_pim_assert_winner_metric_cmd); - install_element (ENABLE_NODE, &show_ip_pim_dr_cmd); - install_element (ENABLE_NODE, &show_ip_pim_hello_cmd); - install_element (ENABLE_NODE, &show_ip_pim_interface_cmd); - install_element (ENABLE_NODE, &show_ip_pim_join_cmd); - install_element (ENABLE_NODE, &show_ip_pim_jp_override_interval_cmd); - install_element (ENABLE_NODE, &show_ip_pim_lan_prune_delay_cmd); - install_element (ENABLE_NODE, &show_ip_pim_local_membership_cmd); - install_element (ENABLE_NODE, &show_ip_pim_neighbor_cmd); - install_element (ENABLE_NODE, &show_ip_pim_rpf_cmd); - install_element (ENABLE_NODE, &show_ip_pim_secondary_cmd); - install_element (ENABLE_NODE, &show_ip_pim_upstream_cmd); - install_element (ENABLE_NODE, &show_ip_pim_upstream_join_desired_cmd); - install_element (ENABLE_NODE, &show_ip_pim_upstream_rpf_cmd); - install_element (ENABLE_NODE, &show_ip_multicast_cmd); - install_element (ENABLE_NODE, &show_ip_mroute_cmd); - install_element (ENABLE_NODE, &show_ip_mroute_count_cmd); - install_element (ENABLE_NODE, &show_ip_rib_cmd); - install_element (ENABLE_NODE, &show_ip_ssmpingd_cmd); - install_element (ENABLE_NODE, &show_debugging_pim_cmd); - install_element (ENABLE_NODE, &test_igmp_receive_report_cmd); install_element (ENABLE_NODE, &test_pim_receive_assert_cmd); install_element (ENABLE_NODE, &test_pim_receive_dump_cmd); diff --git a/ripd/ripd.c b/ripd/ripd.c index 395a7f7822..ab81996640 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -4084,8 +4084,6 @@ rip_init (void) /* Install rip commands. */ install_element (VIEW_NODE, &show_ip_rip_cmd); install_element (VIEW_NODE, &show_ip_rip_status_cmd); - install_element (ENABLE_NODE, &show_ip_rip_cmd); - install_element (ENABLE_NODE, &show_ip_rip_status_cmd); install_element (CONFIG_NODE, &router_rip_cmd); install_element (CONFIG_NODE, &no_router_rip_cmd); diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c index c1eb39ba24..eb8ff5dc6d 100644 --- a/ripngd/ripng_debug.c +++ b/ripngd/ripng_debug.c @@ -266,7 +266,6 @@ ripng_debug_init () install_element (VIEW_NODE, &show_debugging_ripng_cmd); - install_element (ENABLE_NODE, &show_debugging_ripng_cmd); install_element (ENABLE_NODE, &debug_ripng_events_cmd); install_element (ENABLE_NODE, &debug_ripng_packet_cmd); install_element (ENABLE_NODE, &debug_ripng_packet_direct_cmd); diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index e8aad7774a..2a32b934f2 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -3032,9 +3032,6 @@ ripng_init () install_element (VIEW_NODE, &show_ipv6_ripng_cmd); install_element (VIEW_NODE, &show_ipv6_ripng_status_cmd); - install_element (ENABLE_NODE, &show_ipv6_ripng_cmd); - install_element (ENABLE_NODE, &show_ipv6_ripng_status_cmd); - install_element (CONFIG_NODE, &router_ripng_cmd); install_element (CONFIG_NODE, &no_router_ripng_cmd); diff --git a/zebra/interface.c b/zebra/interface.c index 6b575f3c83..0f4d2ee9e4 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -2940,12 +2940,6 @@ zebra_if_init (void) install_element (VIEW_NODE, &show_interface_name_cmd); install_element (VIEW_NODE, &show_interface_name_vrf_cmd); install_element (VIEW_NODE, &show_interface_name_vrf_all_cmd); - install_element (ENABLE_NODE, &show_interface_cmd); - install_element (ENABLE_NODE, &show_interface_vrf_cmd); - install_element (ENABLE_NODE, &show_interface_vrf_all_cmd); - install_element (ENABLE_NODE, &show_interface_name_cmd); - install_element (ENABLE_NODE, &show_interface_name_vrf_cmd); - install_element (ENABLE_NODE, &show_interface_name_vrf_all_cmd); install_element (ENABLE_NODE, &show_interface_desc_cmd); install_element (ENABLE_NODE, &show_interface_desc_vrf_cmd); install_element (ENABLE_NODE, &show_interface_desc_vrf_all_cmd); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 0aef681f33..12084968b7 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -6116,28 +6116,9 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_route_supernets_cmd); install_element (VIEW_NODE, &show_ip_route_summary_cmd); install_element (VIEW_NODE, &show_ip_route_summary_prefix_cmd); - install_element (ENABLE_NODE, &show_vrf_cmd); - install_element (ENABLE_NODE, &show_ip_route_cmd); - install_element (ENABLE_NODE, &show_ip_route_ospf_instance_cmd); - install_element (ENABLE_NODE, &show_ip_route_tag_cmd); - install_element (ENABLE_NODE, &show_ip_nht_cmd); - install_element (ENABLE_NODE, &show_ip_nht_vrf_cmd); - install_element (ENABLE_NODE, &show_ip_nht_vrf_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_nht_cmd); - install_element (ENABLE_NODE, &show_ipv6_nht_vrf_cmd); - install_element (ENABLE_NODE, &show_ipv6_nht_vrf_all_cmd); - install_element (ENABLE_NODE, &show_ip_route_addr_cmd); - install_element (ENABLE_NODE, &show_ip_route_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_route_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_route_protocol_cmd); - install_element (ENABLE_NODE, &show_ip_route_supernets_cmd); - install_element (ENABLE_NODE, &show_ip_route_summary_cmd); - install_element (ENABLE_NODE, &show_ip_route_summary_prefix_cmd); install_element (VIEW_NODE, &show_ip_rpf_cmd); - install_element (ENABLE_NODE, &show_ip_rpf_cmd); install_element (VIEW_NODE, &show_ip_rpf_addr_cmd); - install_element (ENABLE_NODE, &show_ip_rpf_addr_cmd); /* Commands for VRF */ @@ -6199,15 +6180,6 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_route_vrf_supernets_cmd); install_element (VIEW_NODE, &show_ip_route_vrf_summary_cmd); install_element (VIEW_NODE, &show_ip_route_vrf_summary_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_addr_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_tag_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_protocol_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_supernets_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_summary_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_summary_prefix_cmd); install_element (VIEW_NODE, &show_ip_route_vrf_all_cmd); install_element (VIEW_NODE, &show_ip_route_vrf_all_tag_cmd); @@ -6218,15 +6190,6 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_route_vrf_all_supernets_cmd); install_element (VIEW_NODE, &show_ip_route_vrf_all_summary_cmd); install_element (VIEW_NODE, &show_ip_route_vrf_all_summary_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_tag_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_addr_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_protocol_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_supernets_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_summary_cmd); - install_element (ENABLE_NODE, &show_ip_route_vrf_all_summary_prefix_cmd); #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &ipv6_route_cmd); @@ -6273,17 +6236,8 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ipv6_route_addr_cmd); install_element (VIEW_NODE, &show_ipv6_route_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_route_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_tag_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_protocol_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_addr_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_summary_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_mroute_cmd); - install_element (ENABLE_NODE, &show_ipv6_mroute_cmd); /* Commands for VRF */ @@ -6329,14 +6283,6 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ipv6_route_vrf_addr_cmd); install_element (VIEW_NODE, &show_ipv6_route_vrf_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_route_vrf_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_tag_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_protocol_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_addr_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_summary_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_route_vrf_all_cmd); install_element (VIEW_NODE, &show_ipv6_route_vrf_all_tag_cmd); @@ -6346,19 +6292,9 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ipv6_route_vrf_all_addr_cmd); install_element (VIEW_NODE, &show_ipv6_route_vrf_all_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_route_vrf_all_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_tag_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_protocol_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_addr_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_route_vrf_all_summary_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_mroute_vrf_cmd); - install_element (ENABLE_NODE, &show_ipv6_mroute_vrf_cmd); install_element (VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_mroute_vrf_all_cmd); #endif /* HAVE_IPV6 */ } diff --git a/zebra/zserv.c b/zebra/zserv.c index 530410c268..da52f93a28 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2616,7 +2616,6 @@ zebra_init (void) #endif install_element (VIEW_NODE, &show_ip_forwarding_cmd); - install_element (ENABLE_NODE, &show_ip_forwarding_cmd); install_element (CONFIG_NODE, &ip_forwarding_cmd); install_element (CONFIG_NODE, &no_ip_forwarding_cmd); install_element (ENABLE_NODE, &show_zebra_client_cmd); @@ -2624,14 +2623,12 @@ zebra_init (void) #ifdef HAVE_NETLINK install_element (VIEW_NODE, &show_table_cmd); - install_element (ENABLE_NODE, &show_table_cmd); install_element (CONFIG_NODE, &config_table_cmd); install_element (CONFIG_NODE, &no_config_table_cmd); #endif /* HAVE_NETLINK */ #ifdef HAVE_IPV6 install_element (VIEW_NODE, &show_ipv6_forwarding_cmd); - install_element (ENABLE_NODE, &show_ipv6_forwarding_cmd); install_element (CONFIG_NODE, &ipv6_forwarding_cmd); install_element (CONFIG_NODE, &no_ipv6_forwarding_cmd); #endif /* HAVE_IPV6 */ From c77272a1c6b59f25ef58caab2a2c1db01c0d76df Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 8 Oct 2016 16:37:48 -0400 Subject: [PATCH 124/136] bgpd: Fix changes caused by index_sub change. The qobj changes changed how index_sub was handled. This commit leaves some issues still. Signed-off-by: Donald Sharp --- bgpd/rfapi/bgp_rfapi_cfg.c | 111 ++++++++----------------------------- bgpd/rfapi/bgp_rfapi_cfg.h | 6 ++ 2 files changed, 30 insertions(+), 87 deletions(-) diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 2a8ba3dbdd..9e4eafa6cf 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -18,8 +18,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ - - #include "lib/zebra.h" #include "lib/command.h" @@ -81,6 +79,8 @@ DEFINE_MTYPE(RFAPI, RFAPI_L2ADDR_OPT, "RFAPI L2 Address Option") DEFINE_MTYPE(RFAPI, RFAPI_AP, "RFAPI Advertised Prefix") DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ETH, "RFAPI Monitor Ethernet") +DEFINE_QOBJ_TYPE(rfapi_nve_group_cfg) +DEFINE_QOBJ_TYPE(rfapi_l2_group_cfg) /*********************************************************************** * RFAPI Support ***********************************************************************/ @@ -1407,7 +1407,7 @@ DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist, "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg) afi_t afi; if (!bgp) @@ -1422,9 +1422,6 @@ DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -1465,7 +1462,7 @@ DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist, "prefix list name\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); afi_t afi; if (!bgp) @@ -1480,9 +1477,6 @@ DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -1523,7 +1517,7 @@ DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap, "Route-map for filtering redistributed routes\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); if (!bgp) { @@ -1537,9 +1531,6 @@ DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -1568,7 +1559,7 @@ DEFUN (vnc_nve_group_redist_bgpdirect_routemap, "Route-map for filtering exported routes\n" "route map name\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); if (!bgp) { @@ -1582,9 +1573,6 @@ DEFUN (vnc_nve_group_redist_bgpdirect_routemap, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -1928,7 +1916,7 @@ DEFUN (vnc_nve_group_export_no_prefixlist, "Prefix-list for filtering exported routes\n" "prefix list name\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); afi_t afi; if (!bgp) @@ -1943,9 +1931,6 @@ DEFUN (vnc_nve_group_export_no_prefixlist, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -2005,7 +1990,7 @@ DEFUN (vnc_nve_group_export_prefixlist, "Prefix-list for filtering exported routes\n" "prefix list name\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); afi_t afi; if (!bgp) @@ -2020,9 +2005,6 @@ DEFUN (vnc_nve_group_export_prefixlist, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -2072,7 +2054,7 @@ DEFUN (vnc_nve_group_export_no_routemap, "Route-map for filtering exported routes\n" "route map name\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); if (!bgp) { @@ -2086,9 +2068,6 @@ DEFUN (vnc_nve_group_export_no_routemap, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -2138,7 +2117,7 @@ DEFUN (vnc_nve_group_export_routemap, "Route-map for filtering exported routes\n" "route map name\n") { struct bgp *bgp = vty->index; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); if (!bgp) { @@ -2152,9 +2131,6 @@ DEFUN (vnc_nve_group_export_routemap, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -2721,9 +2697,8 @@ DEFUN (vnc_nve_group, * XXX subsequent calls will need to make sure this item is still * in the linked list and has the same name */ - vty->index_sub = rfg; + VTY_PUSH_CONTEXT_SUB (BGP_VNC_NVE_GROUP_NODE, rfg); - vty->node = BGP_VNC_NVE_GROUP_NODE; return CMD_SUCCESS; } @@ -2935,7 +2910,7 @@ DEFUN (vnc_nve_group_prefix, "IPv4 prefix\n" "IPv6 prefix\n") { - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct prefix p; int afi; struct route_table *rt; @@ -2950,9 +2925,6 @@ DEFUN (vnc_nve_group_prefix, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3064,7 +3036,7 @@ DEFUN (vnc_nve_group_rt_import, "Import filter\n" "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") { - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; int rc; struct listnode *node; @@ -3078,9 +3050,6 @@ DEFUN (vnc_nve_group_rt_import, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3143,7 +3112,7 @@ DEFUN (vnc_nve_group_rt_export, "Export filter\n" "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") { - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; int rc; @@ -3153,9 +3122,6 @@ DEFUN (vnc_nve_group_rt_export, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3186,7 +3152,7 @@ DEFUN (vnc_nve_group_rt_both, "Export+import filters\n" "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") { - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; int rc; int is_export_bgp = 0; @@ -3200,9 +3166,6 @@ DEFUN (vnc_nve_group_rt_both, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3281,7 +3244,7 @@ DEFUN (vnc_nve_group_l2rd, "Fixed value 1-255\n" "use the low-order octet of the NVE's VN address\n") { - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; if (!bgp) @@ -3290,9 +3253,6 @@ DEFUN (vnc_nve_group_l2rd, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3338,7 +3298,7 @@ DEFUN (vnc_nve_group_no_l2rd, NO_STR "Specify default Local Nve ID value to use in RD for L2 routes\n") { - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; if (!bgp) @@ -3347,9 +3307,6 @@ DEFUN (vnc_nve_group_no_l2rd, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3372,7 +3329,7 @@ DEFUN (vnc_nve_group_rd, { int ret; struct prefix_rd prd; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; if (!bgp) @@ -3381,9 +3338,6 @@ DEFUN (vnc_nve_group_rd, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3455,7 +3409,7 @@ DEFUN (vnc_nve_group_responselifetime, "Response lifetime in seconds\n" "Infinite response lifetime\n") { unsigned int rspint; - struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); struct bgp *bgp = vty->index; struct rfapi_descriptor *rfd; struct listnode *hdnode; @@ -3466,9 +3420,6 @@ DEFUN (vnc_nve_group_responselifetime, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) { @@ -3566,9 +3517,7 @@ DEFUN (vnc_l2_group, * XXX subsequent calls will need to make sure this item is still * in the linked list and has the same name */ - vty->index_sub = rfg; - - vty->node = BGP_VNC_L2_GROUP_NODE; + VTY_PUSH_CONTEXT_SUB (BGP_VNC_L2_GROUP_NODE, rfg); return CMD_SUCCESS; } @@ -3648,7 +3597,7 @@ DEFUN (vnc_l2_group_lni, "Specify Logical Network ID associated with group\n" "value\n") { - struct rfapi_l2_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); struct bgp *bgp = vty->index; if (!bgp) @@ -3657,9 +3606,6 @@ DEFUN (vnc_l2_group_lni, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) { @@ -3679,7 +3625,7 @@ DEFUN (vnc_l2_group_labels, "Specify label values associated with group\n" "Space separated list of label values <0-1048575>\n") { - struct rfapi_l2_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); struct bgp *bgp = vty->index; struct list *ll; @@ -3689,9 +3635,6 @@ DEFUN (vnc_l2_group_labels, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) { @@ -3725,7 +3668,7 @@ DEFUN (vnc_l2_group_no_labels, "Specify label values associated with L2 group\n" "Space separated list of label values <0-1048575>\n") { - struct rfapi_l2_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); struct bgp *bgp = vty->index; struct list *ll; @@ -3735,9 +3678,6 @@ DEFUN (vnc_l2_group_no_labels, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) { @@ -3772,7 +3712,7 @@ DEFUN (vnc_l2_group_rt, "Import filters\n" "A route target\n") { - struct rfapi_l2_group_cfg *rfg; + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); struct bgp *bgp = vty->index; int rc = CMD_SUCCESS; int do_import = 0; @@ -3803,9 +3743,6 @@ DEFUN (vnc_l2_group_rt, return CMD_WARNING; } - /* get saved pointer */ - rfg = vty->index_sub; - /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) { diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h index 12cedd56f2..50ab3e27aa 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.h +++ b/bgpd/rfapi/bgp_rfapi_cfg.h @@ -36,7 +36,10 @@ struct rfapi_l2_group_cfg struct ecommunity *rt_import_list; struct ecommunity *rt_export_list; void *rfp_cfg; /* rfp owned group config */ + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(rfapi_l2_group_cfg) struct rfapi_nve_group_cfg { @@ -95,7 +98,10 @@ struct rfapi_nve_group_cfg char *routemap_redist_name[ZEBRA_ROUTE_MAX]; struct route_map *routemap_redist[ZEBRA_ROUTE_MAX]; + + QOBJ_FIELDS }; +DECLARE_QOBJ_TYPE(rfapi_nve_group_cfg) struct rfapi_rfg_name { From d93f7ffcb195c600109fe7d4febaef72a2d83715 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Mon, 10 Oct 2016 07:53:34 -0700 Subject: [PATCH 125/136] bgpd: 'neighbor x.x.x.x weight' should be per address-family Ticket: CM-13053 Reviewed By: dslice@cumulusnetworks.com 'neighbor x.x.x.x weight' was implemented as a per-peer knob instead of a per-peer per-afi-safi option. This makes it configurable per-peer per-afi-safi so that we can do things like soft clear that afi/safi when weight is modified. --- bgpd/bgp_route.c | 8 +-- bgpd/bgp_updgrp.c | 1 - bgpd/bgp_vty.c | 45 ++++++++---- bgpd/bgpd.c | 179 +++++++++++++++++++++++++++++----------------- bgpd/bgpd.h | 16 +++-- 5 files changed, 157 insertions(+), 92 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index e53941172b..764bb6c438 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1018,8 +1018,8 @@ bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, filter = &peer->filter[afi][safi]; /* Apply default weight value. */ - if (peer->weight) - (bgp_attr_extra_get (attr))->weight = peer->weight; + if (peer->weight[afi][safi]) + (bgp_attr_extra_get (attr))->weight = peer->weight[afi][safi]; if (rmap_name) { @@ -1075,8 +1075,8 @@ bgp_output_modifier (struct peer *peer, struct prefix *p, struct attr *attr, filter = &peer->filter[afi][safi]; /* Apply default weight value. */ - if (peer->weight) - (bgp_attr_extra_get (attr))->weight = peer->weight; + if (peer->weight[afi][safi]) + (bgp_attr_extra_get (attr))->weight = peer->weight[afi][safi]; if (rmap_name) { diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 29e6243a1a..77b979e56e 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -140,7 +140,6 @@ conf_copy (struct peer *dst, struct peer *src, afi_t afi, safi_t safi) dst->bgp = src->bgp; dst->sort = src->sort; dst->as = src->as; - dst->weight = src->weight; dst->v_routeadv = src->v_routeadv; dst->flags = src->flags; dst->af_flags[afi][safi] = src->af_flags[afi][safi]; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 7c2bcd8bd7..258a709837 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -4881,6 +4881,7 @@ ALIAS (no_neighbor_port, /* neighbor weight. */ static int peer_weight_set_vty (struct vty *vty, const char *ip_str, + afi_t afi, safi_t safi, const char *weight_str) { int ret; @@ -4893,12 +4894,13 @@ peer_weight_set_vty (struct vty *vty, const char *ip_str, VTY_GET_INTEGER_RANGE("weight", weight, weight_str, 0, 65535); - ret = peer_weight_set (peer, weight); + ret = peer_weight_set (peer, afi, safi, weight); return bgp_vty_return (vty, ret); } static int -peer_weight_unset_vty (struct vty *vty, const char *ip_str) +peer_weight_unset_vty (struct vty *vty, const char *ip_str, + afi_t afi, safi_t safi) { int ret; struct peer *peer; @@ -4907,7 +4909,7 @@ peer_weight_unset_vty (struct vty *vty, const char *ip_str) if (! peer) return CMD_WARNING; - ret = peer_weight_unset (peer); + ret = peer_weight_unset (peer, afi, safi); return bgp_vty_return (vty, ret); } @@ -4919,7 +4921,7 @@ DEFUN (neighbor_weight, "Set default weight for routes from this neighbor\n" "default weight\n") { - return peer_weight_set_vty (vty, argv[0], argv[1]); + return peer_weight_set_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[1]); } DEFUN (no_neighbor_weight, @@ -4930,7 +4932,7 @@ DEFUN (no_neighbor_weight, NEIGHBOR_ADDR_STR2 "Set default weight for routes from this neighbor\n") { - return peer_weight_unset_vty (vty, argv[0]); + return peer_weight_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty)); } ALIAS (no_neighbor_weight, @@ -12315,11 +12317,6 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js else if (p->update_source) json_object_string_add(json_neigh, "updateSource", sockunion2str (p->update_source, buf1, SU_ADDRSTRLEN)); } - - /* Default weight */ - if (CHECK_FLAG (p->config, PEER_CONFIG_WEIGHT)) - json_object_int_add(json_neigh, "defaultWeight", p->weight); - } else { @@ -12338,10 +12335,6 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js vty_out (vty, "%s", VTY_NEWLINE); } - /* Default weight */ - if (CHECK_FLAG (p->config, PEER_CONFIG_WEIGHT)) - vty_out (vty, " Default weight %d%s", p->weight, VTY_NEWLINE); - vty_out (vty, "%s", VTY_NEWLINE); } @@ -15315,6 +15308,30 @@ bgp_vty_init (void) install_element (BGP_NODE, &neighbor_weight_cmd); install_element (BGP_NODE, &no_neighbor_weight_cmd); install_element (BGP_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_IPV4_NODE, &neighbor_weight_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_weight_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_weight_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_weight_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_IPV6_NODE, &neighbor_weight_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_weight_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_weight_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_weight_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_weight_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_weight_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_weight_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_weight_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_weight_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_weight_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_weight_val_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_weight_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_weight_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_weight_val_cmd); /* "neighbor override-capability" commands. */ install_element (BGP_NODE, &neighbor_override_capability_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 7d47f18e54..9ad68fbc67 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -890,7 +890,6 @@ peer_global_config_reset (struct peer *peer) int v6only; - peer->weight = 0; peer->change_local_as = 0; peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? MAXTTL : 1); if (peer->update_source) @@ -1157,7 +1156,6 @@ peer_new (struct bgp *bgp) peer->bgp = bgp; peer = peer_lock (peer); /* initial reference */ bgp_lock (bgp); - peer->weight = 0; peer->password = NULL; /* Set default flags. */ @@ -1253,6 +1251,7 @@ peer_xfer_config (struct peer *peer_dst, struct peer *peer_src) peer_dst->afc[afi][safi] = peer_src->afc[afi][safi]; peer_dst->af_flags[afi][safi] = peer_src->af_flags[afi][safi]; peer_dst->allowas_in[afi][safi] = peer_src->allowas_in[afi][safi]; + peer_dst->weight[afi][safi] = peer_src->weight[afi][safi]; } for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) @@ -2199,9 +2198,6 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer) /* GTSM hops */ peer->gtsm_hops = conf->gtsm_hops; - /* Weight */ - peer->weight = conf->weight; - /* this flag is per-neighbor and so has to be preserved */ v6only = CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); @@ -2291,6 +2287,9 @@ peer_group2peer_config_copy_af (struct peer_group *group, struct peer *peer, /* allowas-in */ peer->allowas_in[afi][safi] = conf->allowas_in[afi][safi]; + /* weight */ + peer->weight[afi][safi] = conf->weight[afi][safi]; + /* default-originate route-map */ if (conf->default_rmap[afi][safi].name) { @@ -3678,6 +3677,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE,1, peer_change_reset_out }, { PEER_FLAG_ADDPATH_TX_ALL_PATHS, 1, peer_change_reset }, { PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS, 1, peer_change_reset }, + { PEER_FLAG_WEIGHT, 0, peer_change_reset_in }, { 0, 0, 0 } }; @@ -4513,15 +4513,47 @@ peer_port_unset (struct peer *peer) return 0; } +/* + * Helper function that is called after the name of the policy + * being used by a peer has changed (AF specific). Automatically + * initiates inbound or outbound processing as needed. + */ +static void +peer_on_policy_change (struct peer *peer, afi_t afi, safi_t safi, int outbound) +{ + if (outbound) + { + update_group_adjust_peer (peer_af_find (peer, afi, safi)); + if (peer->status == Established) + bgp_announce_route(peer, afi, safi); + } + else + { + if (peer->status != Established) + return; + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + bgp_soft_reconfig_in (peer, afi, safi); + else if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV) + || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + } +} + + /* neighbor weight. */ int -peer_weight_set (struct peer *peer, u_int16_t weight) +peer_weight_set (struct peer *peer, afi_t afi, safi_t safi, u_int16_t weight) { struct peer_group *group; struct listnode *node, *nnode; - SET_FLAG (peer->config, PEER_CONFIG_WEIGHT); - peer->weight = weight; + if (peer->weight[afi][safi] != weight) + { + peer->weight[afi][safi] = weight; + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_WEIGHT); + peer_on_policy_change (peer, afi, safi, 0); + } if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) return 0; @@ -4530,35 +4562,71 @@ peer_weight_set (struct peer *peer, u_int16_t weight) group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - peer->weight = group->conf->weight; + if (peer->weight[afi][safi] != weight) + { + peer->weight[afi][safi] = weight; + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_WEIGHT); + peer_on_policy_change (peer, afi, safi, 0); + } } - return 1; + return 0; } int -peer_weight_unset (struct peer *peer) +peer_weight_unset (struct peer *peer, afi_t afi, safi_t safi) { struct peer_group *group; struct listnode *node, *nnode; - /* Set default weight. */ - if (peer_group_active (peer)) - peer->weight = peer->group->conf->weight; - else - peer->weight = 0; - - UNSET_FLAG (peer->config, PEER_CONFIG_WEIGHT); - - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - return 0; - - /* peer-group member updates. */ - group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + /* not the peer-group itself but a peer in a peer-group */ + if (peer_group_active(peer)) { - peer->weight = 0; + group = peer->group; + + /* inherit weight from the peer-group */ + if (CHECK_FLAG (group->conf->af_flags[afi][safi], PEER_FLAG_WEIGHT)) + { + peer->weight[afi][safi] = group->conf->weight[afi][safi]; + peer_af_flag_set (peer, afi, safi, PEER_FLAG_WEIGHT); + peer_on_policy_change (peer, afi, safi, 0); + } + else + { + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_WEIGHT)) + { + peer->weight[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_WEIGHT); + peer_on_policy_change (peer, afi, safi, 0); + } + } } - return 1; + + else + { + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_WEIGHT)) + { + peer->weight[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_WEIGHT); + peer_on_policy_change (peer, afi, safi, 0); + } + + /* peer-group member updates. */ + group = peer->group; + + if (group) + { + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_WEIGHT)) + { + peer->weight[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_WEIGHT); + peer_on_policy_change (peer, afi, safi, 0); + } + } + } + } + return 0; } int @@ -4788,7 +4856,7 @@ peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num) { peer->allowas_in[afi][safi] = allow_num; SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); - peer_change_action (peer, afi, safi, peer_change_reset_in); + peer_on_policy_change (peer, afi, safi, 0); } if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) @@ -4801,7 +4869,7 @@ peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num) { peer->allowas_in[afi][safi] = allow_num; SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); - peer_change_action (peer, afi, safi, peer_change_reset_in); + peer_on_policy_change (peer, afi, safi, 0); } } @@ -4818,6 +4886,7 @@ peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) { peer->allowas_in[afi][safi] = 0; peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + peer_on_policy_change (peer, afi, safi, 0); } if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) @@ -4830,6 +4899,7 @@ peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) { peer->allowas_in[afi][safi] = 0; peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + peer_on_policy_change (peer, afi, safi, 0); } } return 0; @@ -5064,33 +5134,6 @@ peer_password_unset (struct peer *peer) return 0; } -/* - * Helper function that is called after the name of the policy - * being used by a peer has changed (AF specific). Automatically - * initiates inbound or outbound processing as needed. - */ -static void -peer_on_policy_change (struct peer *peer, afi_t afi, safi_t safi, int outbound) -{ - if (outbound) - { - update_group_adjust_peer (peer_af_find (peer, afi, safi)); - if (peer->status == Established) - bgp_announce_route(peer, afi, safi); - } - else - { - if (peer->status != Established) - return; - - if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) - bgp_soft_reconfig_in (peer, afi, safi); - else if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV) - || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) - bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); - } -} - /* Set distribute list to the peer. */ int @@ -6623,16 +6666,6 @@ bgp_config_write_peer_global (struct vty *vty, struct bgp *bgp, peer->connect, VTY_NEWLINE); } - /* weight */ - if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT)) - { - if (! peer_group_active (peer) || g_peer->weight != peer->weight) - { - vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight, - VTY_NEWLINE); - } - } - /* capability dynamic */ if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) { @@ -6995,6 +7028,20 @@ bgp_config_write_peer_af (struct vty *vty, struct bgp *bgp, } } + /* weight */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_WEIGHT)) + if (! peer_group_active (peer) + || ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_WEIGHT) + || peer->weight[afi][safi] != g_peer->weight[afi][safi]) + { + if (peer->weight[afi][safi]) + { + afi_header_vty_out (vty, afi, safi, write, + " neighbor %s weight %d%s", + addr, peer->weight[afi][safi], VTY_NEWLINE); + } + } + /* Filter. */ bgp_config_write_filter (vty, peer, afi, safi, write); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index cc55fb87d1..ee105201e8 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -697,6 +697,7 @@ struct peer #define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1 << 21) /* remove-private-as all replace-as */ #define PEER_FLAG_ADDPATH_TX_ALL_PATHS (1 << 22) /* addpath-tx-all-paths */ #define PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS (1 << 23) /* addpath-tx-bestpath-per-AS */ +#define PEER_FLAG_WEIGHT (1 << 24) /* weight */ /* MD5 password */ char *password; @@ -729,12 +730,10 @@ struct peer /* Default attribute value for the peer. */ u_int32_t config; -#define PEER_CONFIG_WEIGHT (1 << 0) /* Default weight. */ -#define PEER_CONFIG_TIMER (1 << 1) /* keepalive & holdtime */ -#define PEER_CONFIG_CONNECT (1 << 2) /* connect */ -#define PEER_CONFIG_ROUTEADV (1 << 3) /* route advertise */ +#define PEER_CONFIG_TIMER (1 << 0) /* keepalive & holdtime */ +#define PEER_CONFIG_CONNECT (1 << 1) /* connect */ +#define PEER_CONFIG_ROUTEADV (1 << 2) /* route advertise */ - u_int32_t weight; u_int32_t holdtime; u_int32_t keepalive; u_int32_t connect; @@ -828,6 +827,9 @@ struct peer /* allowas-in. */ char allowas_in[AFI_MAX][SAFI_MAX]; + /* weight */ + unsigned long weight[AFI_MAX][SAFI_MAX]; + /* peer reset cause */ char last_reset; #define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */ @@ -1292,8 +1294,8 @@ extern int peer_default_originate_unset (struct peer *, afi_t, safi_t); extern int peer_port_set (struct peer *, u_int16_t); extern int peer_port_unset (struct peer *); -extern int peer_weight_set (struct peer *, u_int16_t); -extern int peer_weight_unset (struct peer *); +extern int peer_weight_set (struct peer *, afi_t, safi_t, u_int16_t); +extern int peer_weight_unset (struct peer *, afi_t, safi_t); extern int peer_timers_set (struct peer *, u_int32_t keepalive, u_int32_t holdtime); extern int peer_timers_unset (struct peer *); From 4fede82a13392be8f02bf7c4aacb5ad69e92392c Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 10 Oct 2016 19:35:43 -0400 Subject: [PATCH 126/136] ospfd: Fix arm compile issue size_t printf formatter is %zd! Signed-off-by: Donald Sharp --- ospfd/ospf_spf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 5f8ef6f993..5dfd41dd1e 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -1283,7 +1283,7 @@ ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table, area->ts_spf = area->ospf->ts_spf; if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_spf_calculate: Stop. %ld vertices", + zlog_debug ("ospf_spf_calculate: Stop. %zd vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); /* Free SPF vertices, but not the list. List has ospf_vertex_free From 093391128fa1af3c4c72b38b3e75a8f244fe85cf Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 11 Oct 2016 13:08:10 +0200 Subject: [PATCH 127/136] build: remove accidentally-added configure outputs these files do not belong in the git tree. (They're temporaries during a ./configure run and normally removed at the end; let's add them to .gitignore anyway.) Signed-off-by: David Lamparter --- .gitignore | 3 +++ confdefs.h | 73 --------------------------------------------------- conftest | Bin 9680 -> 0 bytes conftest.err | 0 4 files changed, 3 insertions(+), 73 deletions(-) delete mode 100644 confdefs.h delete mode 100755 conftest delete mode 100644 conftest.err diff --git a/.gitignore b/.gitignore index 9a55091fe6..ddaf6e240e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,9 @@ autom4te*.cache configure.lineno configure config.h.in +confdefs.h +conftest +conftest.err aclocal.m4 Makefile.in zebra-[0-9.][0-9.][0-9.]*.tar.gz diff --git a/confdefs.h b/confdefs.h deleted file mode 100644 index 0787a51477..0000000000 --- a/confdefs.h +++ /dev/null @@ -1,73 +0,0 @@ -/* confdefs.h */ -#define PACKAGE_NAME "Quagga" -#define PACKAGE_TARNAME "quagga" -#define PACKAGE_VERSION "0.99.24+cl3u4" -#define PACKAGE_STRING "Quagga 0.99.24+cl3u4" -#define PACKAGE_BUGREPORT "https://bugzilla.quagga.net" -#define PACKAGE_URL "" -#define PACKAGE "quagga" -#define VERSION "0.99.24+cl3u4" -#define STDC_HEADERS 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STRING_H 1 -#define HAVE_MEMORY_H 1 -#define HAVE_STRINGS_H 1 -#define HAVE_INTTYPES_H 1 -#define HAVE_STDINT_H 1 -#define HAVE_UNISTD_H 1 -#define __EXTENSIONS__ 1 -#define _ALL_SOURCE 1 -#define _GNU_SOURCE 1 -#define _POSIX_PTHREAD_SEMANTICS 1 -#define _TANDEM_SOURCE 1 -#define HAVE_DLFCN_H 1 -#define LT_OBJDIR ".libs/" -#define HAVE_JSON_C_JSON_H 1 -#define CONSUMED_TIME_CHECK 5000000 -#define HAVE_V6_RR_SEMANTICS /**/ -#define HAVE_RTADV /**/ -#define QUAGGA_USER "quagga" -#define QUAGGA_GROUP "quagga" -#define CONFIGFILE_MASK 0600 -#define LOGFILE_MASK 0600 -#define MULTIPATH_NUM 4 -#define restrict __restrict -#define STDC_HEADERS 1 -#define TIME_WITH_SYS_TIME 1 -#define HAVE_SYS_WAIT_H 1 -#define HAVE__BOOL 1 -#define HAVE_STDBOOL_H 1 -#define HAVE_STROPTS_H 1 -#define HAVE_SYS_TIMES_H 1 -#define HAVE_SYS_SELECT_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_LINUX_VERSION_H 1 -#define HAVE_NETDB_H 1 -#define HAVE_ASM_TYPES_H 1 -#define HAVE_SYS_CDEFS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_LIMITS_H 1 -#define HAVE_SIGNAL_H 1 -#define HAVE_SYS_SOCKET_H 1 -#define HAVE_NETINET_IN_H 1 -#define HAVE_TIME_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_NET_IF_H 1 -#define HAVE_SYS_UN_H 1 -#define HAVE_NETINET_IN_SYSTM_H 1 -#define HAVE_NET_ROUTE_H 1 -#define HAVE_ARPA_INET_H 1 -#define HAVE_NETINET_IP_ICMP_H 1 -#define HAVE_FCNTL_H 1 -#define HAVE_STDDEF_H 1 -#define HAVE_SYS_IOCTL_H 1 -#define HAVE_SYSLOG_H 1 -#define HAVE_WCHAR_H 1 -#define HAVE_WCTYPE_H 1 -#define HAVE_SYS_SYSCTL_H 1 -#define HAVE_UCONTEXT_H 1 -#define HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS 1 -#define GNU_LINUX /**/ -#define VTYSH /**/ diff --git a/conftest b/conftest deleted file mode 100755 index 04df7b19b5cd8ac7858e7c8175862774123b5a21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9680 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 Date: Tue, 11 Oct 2016 13:37:26 +0200 Subject: [PATCH 128/136] bgpd/rfp: fix remaining index_sub uses Signed-off-by: David Lamparter --- bgpd/rfapi/rfapi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 3e292716de..8ff95e9ad3 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -4110,10 +4110,10 @@ rfapi_rfp_get_or_init_group_config_nve ( struct vty *vty, uint32_t size) { - struct rfapi_nve_group_cfg *rfg = vty->index_sub; + struct rfapi_nve_group_cfg *rfg = VTY_GET_CONTEXT_SUB(rfapi_nve_group_cfg); /* make sure group is still in list */ - if (!listnode_lookup (rfc->nve_groups_sequential, rfg)) + if (!rfg || !listnode_lookup (rfc->nve_groups_sequential, rfg)) { /* Not in list anymore */ vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); @@ -4135,10 +4135,10 @@ rfapi_rfp_get_or_init_group_config_l2 ( struct vty *vty, uint32_t size) { - struct rfapi_l2_group_cfg *rfg = vty->index_sub; + struct rfapi_l2_group_cfg *rfg = VTY_GET_CONTEXT_SUB(rfapi_l2_group_cfg); /* make sure group is still in list */ - if (!listnode_lookup (rfc->l2_groups, rfg)) + if (!rfg || !listnode_lookup (rfc->l2_groups, rfg)) { /* Not in list anymore */ vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); @@ -4188,7 +4188,7 @@ rfapi_rfp_init_group_config_ptr_vty ( return NULL; bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val); - if (!bgp || !bgp->rfapi_cfg || !vty->index_sub) + if (!bgp || !bgp->rfapi_cfg) return NULL; switch (type) From 31107a358a8ba38491507a8edc76dd7e1657449e Mon Sep 17 00:00:00 2001 From: Lou Berger Date: Wed, 5 Oct 2016 20:44:57 -0400 Subject: [PATCH 129/136] rfapi: cleanup some warnings --- bgpd/rfapi/rfapi.c | 7 ------- bgpd/rfapi/rfapi_rib.c | 4 ++-- bgpd/rfapi/rfapi_vty.c | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 8ff95e9ad3..0cf3bdfb57 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -3191,14 +3191,7 @@ DEFUN (debug_rfapi_close_rfd, int rc; char *endptr = NULL; -#if (UINTPTR_MAX == ULONG_MAX) - handle = (void *) (uintptr_t) (strtoul (argv[0], &endptr, 16)); -#elif (UINTPTR_MAX == ULLONG_MAX) handle = (rfapi_handle) (uintptr_t) (strtoull (argv[0], &endptr, 16)); -#else - /* give up */ - assert (0); -#endif if (*endptr != '\0' || (uintptr_t) handle == UINTPTR_MAX) { diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 62ffd7b820..896b5f50a8 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -1013,8 +1013,8 @@ process_pending_node ( zlog_debug ("%s: lPendCost->count=%d, slRibPt->count=%d", __func__, - (lPendCost ? lPendCost->count : -1), - (slRibPt ? slRibPt->count : -1)); + (lPendCost ? (int) lPendCost->count : -1), + (slRibPt ? (int) slRibPt->count : -1)); /* * Iterate over routes at RIB Node. diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 3c76bb4223..90c0e63a7a 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -566,7 +566,7 @@ rfapiPrintBi (void *stream, struct bgp_info *bi) int has_macaddr = 0; struct ethaddr macaddr; struct rfapi_l2address_option l2o_buf; - uint8_t l2hid; /* valid if has_macaddr */ + uint8_t l2hid=0; /* valid if has_macaddr */ #define REMAIN (BUFSIZ - (p-line)) #define INCP {p += (r > REMAIN)? REMAIN: r;} From 353190c1c801e725472c79e12f924f0c1639661a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 11 Oct 2016 08:21:41 -0400 Subject: [PATCH 130/136] zebra: Fix signed / unsigned comparisons clang is complaining about signed/unsigned comparisons for rta_addattr_l. Signed-off-by: Donald Sharp --- zebra/rt_netlink.c | 3 ++- zebra/rt_netlink.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 95c8892979..825ad00100 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1719,7 +1719,8 @@ addattr_l (struct nlmsghdr *n, unsigned int maxlen, int type, void *data, int al } int -rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) +rta_addattr_l (struct rtattr *rta, unsigned int maxlen, int type, + void *data, int alen) { int len; struct rtattr *subrta; diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 7d8b2e7040..55af237b64 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -35,7 +35,7 @@ extern int addattr_l (struct nlmsghdr *n, unsigned int maxlen, int type, void *data, int alen); extern int -rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen); +rta_addattr_l (struct rtattr *rta, unsigned int maxlen, int type, void *data, int alen); extern const char * nl_msg_type_to_str (uint16_t msg_type); From d8e012a89e822f23d75361c24ba19324b3685381 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 11 Oct 2016 08:24:03 -0400 Subject: [PATCH 131/136] bgpd: Fix peer->weight usage When setting the weight make sure to use the correct afi safi to get what we need. Signed-off-by: Donald Sharp Reviewed-by: Lou Berger --- bgpd/rfapi/rfapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 0cf3bdfb57..985bcaf942 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -784,7 +784,7 @@ add_vnc_route ( } /* override default weight assigned by bgp_attr_default_set() */ - attr.extra->weight = (rfd->peer ? rfd->peer->weight : 0); + attr.extra->weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0; /* * NB: ticket 81: do not reset attr.aspath here because it would From b6bf1505df0d8fa7d566126e6ea2aa9b9f9c8f54 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 11 Oct 2016 08:47:32 -0400 Subject: [PATCH 132/136] bgp: Fix bi->extra->tag if statement bi->extra->tag is a 3 byte array, the statement as written will always be true. Modify code to see if we actually have any data in the tag and then print the label. Signed-off-by: Donald Sharp Reviewed-by: Lou Berger --- bgpd/rfapi/rfapi_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 90c0e63a7a..1978bec280 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -487,7 +487,7 @@ rfapi_vty_out_vncinfo ( XFREE (MTYPE_ECOMMUNITY_STR, s); } - if (bi->extra != NULL && bi->extra->tag != NULL) + if (bi->extra != NULL) vty_out (vty, " label=%u", decode_label (bi->extra->tag)); if (rfapiGetVncLifetime (bi->attr, &lifetime)) From 039eaca36717fb9bfe415cc47b4d36848c54b51f Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 26 Sep 2016 22:01:37 +0200 Subject: [PATCH 133/136] vtysh: infer integrated config usage from existence of Quagga.conf Only write to integrated config if integrated config is configured explicitly or it is already in use. Signed-off-by: Christian Franke --- vtysh/vtysh.c | 26 +++++++++++++++++++++----- vtysh/vtysh.h | 8 ++++++++ vtysh/vtysh_config.c | 6 +++--- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 4f64283a0e..9663a58528 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -75,8 +75,7 @@ struct vtysh_client vtysh_client[] = { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH, .next = NULL}, }; -/* Using integrated config from Quagga.conf. Default is no. */ -int vtysh_writeconfig_integrated = 1; +enum vtysh_write_integrated vtysh_write_integrated = WRITE_INTEGRATED_UNSPECIFIED; extern char config_default[]; @@ -2482,7 +2481,7 @@ DEFUN (vtysh_integrated_config, "Set up miscellaneous service\n" "Write configuration into integrated file\n") { - vtysh_writeconfig_integrated = 1; + vtysh_write_integrated = WRITE_INTEGRATED_YES; return CMD_SUCCESS; } @@ -2493,7 +2492,7 @@ DEFUN (no_vtysh_integrated_config, "Set up miscellaneous service\n" "Write configuration into integrated file\n") { - vtysh_writeconfig_integrated = 0; + vtysh_write_integrated = WRITE_INTEGRATED_NO; return CMD_SUCCESS; } @@ -2573,6 +2572,23 @@ write_config_integrated(void) return CMD_SUCCESS; } +static bool vtysh_writeconfig_integrated(void) +{ + struct stat s; + + switch (vtysh_write_integrated) + { + case WRITE_INTEGRATED_UNSPECIFIED: + if (stat(integrate_default, &s) && errno == ENOENT) + return false; + return true; + case WRITE_INTEGRATED_NO: + return false; + case WRITE_INTEGRATED_YES: + return true; + } +} + DEFUN (vtysh_write_memory, vtysh_write_memory_cmd, "write memory", @@ -2585,7 +2601,7 @@ DEFUN (vtysh_write_memory, FILE *fp; /* If integrated Quagga.conf explicitely set. */ - if (vtysh_writeconfig_integrated) + if (vtysh_writeconfig_integrated()) return write_config_integrated(); else backup_config_file(integrate_default); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index e82aba6b6f..3aa7b8dc83 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -45,6 +45,14 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_DEFAULT_CONFIG "vtysh.conf" #define QUAGGA_DEFAULT_CONFIG "Quagga.conf" +enum vtysh_write_integrated { + WRITE_INTEGRATED_UNSPECIFIED, + WRITE_INTEGRATED_NO, + WRITE_INTEGRATED_YES +}; + +extern enum vtysh_write_integrated vtysh_write_integrated; + void vtysh_init_vty (void); void vtysh_init_cmd (void); extern int vtysh_connect_all (const char *optional_daemon_name); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index eb58497310..4b0a390843 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -33,8 +33,6 @@ DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG_LINE, "Vtysh configuration line") vector configvec; -extern int vtysh_writeconfig_integrated; - struct config { /* Configuration node name. */ @@ -458,8 +456,10 @@ vtysh_config_write () sprintf (line, "hostname %s", host.name); vtysh_config_parse_line(line); } - if (!vtysh_writeconfig_integrated) + if (vtysh_write_integrated == WRITE_INTEGRATED_NO) vtysh_config_parse_line ("no service integrated-vtysh-config"); + if (vtysh_write_integrated == WRITE_INTEGRATED_YES) + vtysh_config_parse_line ("service integrated-vtysh-config"); user_config_write (); } From 6694f68c3f641fc96ff844ec344a3e8f01998fa8 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 12 Oct 2016 12:39:02 -0300 Subject: [PATCH 134/136] lib/vtysh: fix duplicate installation of some vty commands This is a followup to commits 735e62 and 0b1442, where we forgot to apply the same VIEW/ENABLE consolidation logic to vtysh. Also, we can't call install_default() for the ENABLE node because some of the vty commands installed by this function were already installed in the VIEW node before. Signed-off-by: Renato Westphal --- lib/command.c | 7 ++++++- vtysh/vtysh.c | 22 ---------------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/lib/command.c b/lib/command.c index be243d44ea..fd4b2e2427 100644 --- a/lib/command.c +++ b/lib/command.c @@ -4220,10 +4220,15 @@ cmd_init (int terminal) if (terminal) { - install_default (ENABLE_NODE); + install_element (ENABLE_NODE, &config_end_cmd); install_element (ENABLE_NODE, &config_disable_cmd); install_element (ENABLE_NODE, &config_terminal_cmd); install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + install_element (ENABLE_NODE, &config_write_terminal_cmd); + install_element (ENABLE_NODE, &config_write_file_cmd); + install_element (ENABLE_NODE, &config_write_memory_cmd); + install_element (ENABLE_NODE, &config_write_cmd); + install_element (ENABLE_NODE, &show_running_config_cmd); } install_element (ENABLE_NODE, &show_startup_config_cmd); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 9663a58528..f8c45ed1fb 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -3186,7 +3186,6 @@ vtysh_init_vty (void) install_node (&vty_node, NULL); vtysh_install_default (VIEW_NODE); - vtysh_install_default (ENABLE_NODE); vtysh_install_default (CONFIG_NODE); vtysh_install_default (BGP_NODE); vtysh_install_default (RIP_NODE); @@ -3233,8 +3232,6 @@ vtysh_init_vty (void) install_element (VIEW_NODE, &vtysh_quit_all_cmd); install_element (CONFIG_NODE, &vtysh_exit_all_cmd); /* install_element (CONFIG_NODE, &vtysh_quit_all_cmd); */ - install_element (ENABLE_NODE, &vtysh_exit_all_cmd); - install_element (ENABLE_NODE, &vtysh_quit_all_cmd); install_element (RIP_NODE, &vtysh_exit_ripd_cmd); install_element (RIP_NODE, &vtysh_quit_ripd_cmd); install_element (RIPNG_NODE, &vtysh_exit_ripngd_cmd); @@ -3419,11 +3416,8 @@ vtysh_init_vty (void) install_element (ENABLE_NODE, &vtysh_write_memory_cmd); install_element (VIEW_NODE, &vtysh_terminal_length_cmd); - install_element (ENABLE_NODE, &vtysh_terminal_length_cmd); install_element (VIEW_NODE, &vtysh_terminal_no_length_cmd); - install_element (ENABLE_NODE, &vtysh_terminal_no_length_cmd); install_element (VIEW_NODE, &vtysh_show_daemons_cmd); - install_element (ENABLE_NODE, &vtysh_show_daemons_cmd); install_element (VIEW_NODE, &vtysh_ping_cmd); install_element (VIEW_NODE, &vtysh_ping_ip_cmd); @@ -3437,37 +3431,21 @@ vtysh_init_vty (void) install_element (VIEW_NODE, &vtysh_telnet_cmd); install_element (VIEW_NODE, &vtysh_telnet_port_cmd); install_element (VIEW_NODE, &vtysh_ssh_cmd); -#endif - install_element (ENABLE_NODE, &vtysh_ping_cmd); - install_element (ENABLE_NODE, &vtysh_ping_ip_cmd); - install_element (ENABLE_NODE, &vtysh_traceroute_cmd); - install_element (ENABLE_NODE, &vtysh_traceroute_ip_cmd); -#ifdef HAVE_IPV6 - install_element (ENABLE_NODE, &vtysh_ping6_cmd); - install_element (ENABLE_NODE, &vtysh_traceroute6_cmd); #endif #if defined(HAVE_SHELL_ACCESS) - install_element (ENABLE_NODE, &vtysh_telnet_cmd); - install_element (ENABLE_NODE, &vtysh_telnet_port_cmd); - install_element (ENABLE_NODE, &vtysh_ssh_cmd); install_element (ENABLE_NODE, &vtysh_start_shell_cmd); install_element (ENABLE_NODE, &vtysh_start_bash_cmd); install_element (ENABLE_NODE, &vtysh_start_zsh_cmd); #endif install_element (VIEW_NODE, &vtysh_show_memory_cmd); - install_element (ENABLE_NODE, &vtysh_show_memory_cmd); install_element (VIEW_NODE, &vtysh_show_work_queues_cmd); - install_element (ENABLE_NODE, &vtysh_show_work_queues_cmd); - install_element (ENABLE_NODE, &vtysh_show_work_queues_daemon_cmd); install_element (VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); install_element (VIEW_NODE, &vtysh_show_thread_cmd); - install_element (ENABLE_NODE, &vtysh_show_thread_cmd); /* Logging */ - install_element (ENABLE_NODE, &vtysh_show_logging_cmd); install_element (VIEW_NODE, &vtysh_show_logging_cmd); install_element (CONFIG_NODE, &vtysh_log_stdout_cmd); install_element (CONFIG_NODE, &vtysh_log_stdout_level_cmd); From 590ef6197077d64652766287ce5d5300b3b6d1e2 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Thu, 13 Oct 2016 13:43:12 +0000 Subject: [PATCH 135/136] zebra: Fix signed / unsigned build error Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp --- zebra/rt_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 825ad00100..19f453b150 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1722,7 +1722,7 @@ int rta_addattr_l (struct rtattr *rta, unsigned int maxlen, int type, void *data, int alen) { - int len; + unsigned int len; struct rtattr *subrta; len = RTA_LENGTH (alen); From 3d3c3cbd115a3c38b490d656002af777d002743c Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Thu, 13 Oct 2016 13:48:02 +0000 Subject: [PATCH 136/136] vtysh: fix build failure in vtysh_writeconfig_integrated() Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp --- vtysh/vtysh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index f8c45ed1fb..39ad8d81d6 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2587,6 +2587,7 @@ static bool vtysh_writeconfig_integrated(void) case WRITE_INTEGRATED_YES: return true; } + return true; } DEFUN (vtysh_write_memory,