frr/lib/prefix.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1492 lines
34 KiB
C
Raw Normal View History

2002-12-13 21:15:29 +01:00
/*
* Prefix related functions.
* Copyright (C) 1997, 98, 99 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 this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2002-12-13 21:15:29 +01:00
*/
#include <zebra.h>
#include "prefix.h"
#include "ipaddr.h"
2002-12-13 21:15:29 +01:00
#include "vty.h"
#include "sockunion.h"
#include "memory.h"
#include "log.h"
#include "jhash.h"
#include "lib_errors.h"
#include "printfrr.h"
bgpd: support for DF election in EVPN-MH DF (Designated forwarder) election is used for picking a single BUM-traffic forwarded per-ES. RFC7432 specifies a mechanism called service carving for DF election. However that mechanism has many disadvantages - 1. LBs poorly. 2. Doesn't allow for a controlled failover needed in upgrade scenarios. 3. Not easy to hw accelerate. To fix the poor performance of service carving alternate DF mechanisms have been proposed via the following drafts - draft-ietf-bess-evpn-df-election-framework draft-ietf-bess-evpn-pref-df This commit adds support for the pref-df election mechanism which is used as the default. Other mechanisms including service-carving may be added later. In this mechanism one switch on an ES is elected as DF based on the preference value; higher preference wins with IP address acting as the tie-breaker (lower-IP wins if pref value is the same). Sample output ============= >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> torm-11# sh bgp l2vpn evpn es 03:00:00:00:00:01:11:00:00:01 ESI: 03:00:00:00:00:01:11:00:00:01 Type: LR RD: 27.0.0.15:6 Originator-IP: 27.0.0.15 Local ES DF preference: 100 VNI Count: 10 Remote VNI Count: 10 Inconsistent VNI VTEP Count: 0 Inconsistencies: - VTEPs: 27.0.0.16 flags: EA df_alg: preference df_pref: 32767 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> torm-11# sh bgp l2vpn evpn route esi 03:00:00:00:00:01:11:00:00:01 *> [4]:[03:00:00:00:00:01:11:00:00:01]:[32]:[27.0.0.15] 27.0.0.15 32768 i ET:8 ES-Import-Rt:00:00:00:00:01:11 DF: (alg: 2, pref: 100) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
2020-05-09 01:35:09 +02:00
#include "vxlan.h"
DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix");
DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec");
2002-12-13 21:15:29 +01:00
/* Maskbit. */
static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
0xf8, 0xfc, 0xfe, 0xff};
2002-12-13 21:15:29 +01:00
/* Number of bits in prefix type. */
#ifndef PNBBY
#define PNBBY 8
#endif /* PNBBY */
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
int is_zero_mac(const struct ethaddr *mac)
{
int i = 0;
for (i = 0; i < ETH_ALEN; i++) {
if (mac->octet[i])
return 0;
}
return 1;
}
bool is_bcast_mac(const struct ethaddr *mac)
{
int i = 0;
for (i = 0; i < ETH_ALEN; i++)
if (mac->octet[i] != 0xFF)
return false;
return true;
}
bool is_mcast_mac(const struct ethaddr *mac)
{
if ((mac->octet[0] & 0x01) == 0x01)
return true;
return false;
}
unsigned int prefix_bit(const uint8_t *prefix, const uint16_t bit_index)
{
unsigned int offset = bit_index / 8;
unsigned int shift = 7 - (bit_index % 8);
return (prefix[offset] >> shift) & 1;
}
int str2family(const char *string)
{
if (!strcmp("ipv4", string))
return AF_INET;
else if (!strcmp("ipv6", string))
return AF_INET6;
2016-04-19 22:08:59 +02:00
else if (!strcmp("ethernet", string))
return AF_ETHERNET;
else if (!strcmp("evpn", string))
return AF_EVPN;
2016-04-19 22:08:59 +02:00
return -1;
}
const char *family2str(int family)
{
switch (family) {
case AF_INET:
return "IPv4";
case AF_INET6:
return "IPv6";
case AF_ETHERNET:
return "Ethernet";
case AF_EVPN:
return "Evpn";
}
return "?";
}
/* Address Family Identifier to Address Family converter. */
int afi2family(afi_t afi)
2002-12-13 21:15:29 +01:00
{
if (afi == AFI_IP)
return AF_INET;
else if (afi == AFI_IP6)
return AF_INET6;
else if (afi == AFI_L2VPN)
2016-04-19 22:08:59 +02:00
return AF_ETHERNET;
/* NOTE: EVPN code should NOT use this interface. */
2002-12-13 21:15:29 +01:00
return 0;
}
afi_t family2afi(int family)
{
if (family == AF_INET)
return AFI_IP;
else if (family == AF_INET6)
return AFI_IP6;
else if (family == AF_ETHERNET || family == AF_EVPN)
return AFI_L2VPN;
2002-12-13 21:15:29 +01:00
return 0;
}
2016-04-19 22:08:59 +02:00
const char *afi2str(afi_t afi)
{
switch (afi) {
case AFI_IP:
return "IPv4";
case AFI_IP6:
return "IPv6";
case AFI_L2VPN:
return "l2vpn";
bgpd: add L3/L2VPN Virtual Network Control feature This feature adds an L3 & L2 VPN application that makes use of the VPN and Encap SAFIs. This code is currently used to support IETF NVO3 style operation. In NVO3 terminology it provides the Network Virtualization Authority (NVA) and the ability to import/export IP prefixes and MAC addresses from Network Virtualization Edges (NVEs). The code supports per-NVE tables. The NVE-NVA protocol used to communicate routing and Ethernet / Layer 2 (L2) forwarding information between NVAs and NVEs is referred to as the Remote Forwarder Protocol (RFP). OpenFlow is an example RFP. For general background on NVO3 and RFP concepts see [1]. For information on Openflow see [2]. RFPs are integrated with BGP via the RF API contained in the new "rfapi" BGP sub-directory. Currently, only a simple example RFP is included in Quagga. Developers may use this example as a starting point to integrate Quagga with an RFP of their choosing, e.g., OpenFlow. The RFAPI code also supports the ability import/export of routing information between VNC and customer edge routers (CEs) operating within a virtual network. Import/export may take place between BGP views or to the default zebera VRF. BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VPN information between NVAs. BGP based IP VPN support is defined in RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs), and RFC4659, BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN . Use of both the Encapsulation Subsequent Address Family Identifier (SAFI) and the Tunnel Encapsulation Attribute, RFC5512, The BGP Encapsulation Subsequent Address Family Identifier (SAFI) and the BGP Tunnel Encapsulation Attribute, are supported. MAC address distribution does not follow any standard BGB encoding, although it was inspired by the early IETF EVPN concepts. The feature is conditionally compiled and disabled by default. Use the --enable-bgp-vnc configure option to enable. The majority of this code was authored by G. Paul Ziemba <paulz@labn.net>. [1] http://tools.ietf.org/html/draft-ietf-nvo3-nve-nva-cp-req [2] https://www.opennetworking.org/sdn-resources/technical-library Now includes changes needed to merge with cmaster-next.
2016-05-07 20:18:56 +02:00
case AFI_MAX:
return "bad-value";
2016-04-19 22:08:59 +02:00
default:
break;
}
return NULL;
}
const char *safi2str(safi_t safi)
{
switch (safi) {
case SAFI_UNICAST:
return "unicast";
case SAFI_MULTICAST:
return "multicast";
case SAFI_MPLS_VPN:
return "vpn";
case SAFI_ENCAP:
return "encap";
case SAFI_EVPN:
return "evpn";
case SAFI_LABELED_UNICAST:
return "labeled-unicast";
case SAFI_FLOWSPEC:
return "flowspec";
default:
return "unknown";
}
}
2002-12-13 21:15:29 +01:00
/* If n includes p prefix then return 1 else return 0. */
int prefix_match(const struct prefix *n, const struct prefix *p)
2002-12-13 21:15:29 +01:00
{
int offset;
int shift;
const uint8_t *np, *pp;
2002-12-13 21:15:29 +01:00
/* If n's prefix is longer than p's one return 0. */
if (n->prefixlen > p->prefixlen)
return 0;
if (n->family == AF_FLOWSPEC) {
/* prefixlen is unused. look at fs prefix len */
if (n->u.prefix_flowspec.family !=
p->u.prefix_flowspec.family)
return 0;
if (n->u.prefix_flowspec.prefixlen >
p->u.prefix_flowspec.prefixlen)
return 0;
/* Set both prefix's head pointer. */
np = (const uint8_t *)&n->u.prefix_flowspec.ptr;
pp = (const uint8_t *)&p->u.prefix_flowspec.ptr;
offset = n->u.prefix_flowspec.prefixlen;
while (offset--)
if (np[offset] != pp[offset])
return 0;
return 1;
}
/* Set both prefix's head pointer. */
np = n->u.val;
pp = p->u.val;
2002-12-13 21:15:29 +01:00
offset = n->prefixlen / PNBBY;
shift = n->prefixlen % PNBBY;
2002-12-13 21:15:29 +01:00
if (shift)
if (maskbit[shift] & (np[offset] ^ pp[offset]))
return 0;
while (offset--)
if (np[offset] != pp[offset])
return 0;
return 1;
}
/*
* n is a type5 evpn prefix. This function tries to see if there is an
* ip-prefix within n which matches prefix p
* If n includes p prefix then return 1 else return 0.
*/
int evpn_type5_prefix_match(const struct prefix *n, const struct prefix *p)
{
int offset;
int shift;
int prefixlen;
const uint8_t *np, *pp;
struct prefix_evpn *evp;
if (n->family != AF_EVPN)
return 0;
evp = (struct prefix_evpn *)n;
pp = p->u.val;
if ((evp->prefix.route_type != 5) ||
(p->family == AF_INET6 && !is_evpn_prefix_ipaddr_v6(evp)) ||
(p->family == AF_INET && !is_evpn_prefix_ipaddr_v4(evp)) ||
(is_evpn_prefix_ipaddr_none(evp)))
return 0;
prefixlen = evp->prefix.prefix_addr.ip_prefix_length;
np = &evp->prefix.prefix_addr.ip.ip.addr;
/* If n's prefix is longer than p's one return 0. */
if (prefixlen > p->prefixlen)
return 0;
offset = prefixlen / PNBBY;
shift = prefixlen % PNBBY;
if (shift)
if (maskbit[shift] & (np[offset] ^ pp[offset]))
return 0;
while (offset--)
if (np[offset] != pp[offset])
return 0;
return 1;
}
/* If n includes p then return 1 else return 0. Prefix mask is not considered */
int prefix_match_network_statement(const struct prefix *n,
const struct prefix *p)
{
int offset;
int shift;
const uint8_t *np, *pp;
/* Set both prefix's head pointer. */
np = n->u.val;
pp = p->u.val;
offset = n->prefixlen / PNBBY;
shift = n->prefixlen % PNBBY;
if (shift)
if (maskbit[shift] & (np[offset] ^ pp[offset]))
return 0;
2002-12-13 21:15:29 +01:00
while (offset--)
if (np[offset] != pp[offset])
return 0;
return 1;
}
#ifdef __clang_analyzer__
#undef prefix_copy /* cf. prefix.h */
#endif
void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
2002-12-13 21:15:29 +01:00
{
struct prefix *dest = udest.p;
const struct prefix *src = usrc.p;
2002-12-13 21:15:29 +01:00
dest->family = src->family;
dest->prefixlen = src->prefixlen;
2002-12-13 21:15:29 +01:00
if (src->family == AF_INET)
dest->u.prefix4 = src->u.prefix4;
else if (src->family == AF_INET6)
dest->u.prefix6 = src->u.prefix6;
else if (src->family == AF_ETHERNET) {
memcpy(&dest->u.prefix_eth, &src->u.prefix_eth,
sizeof(struct ethaddr));
} else if (src->family == AF_EVPN) {
memcpy(&dest->u.prefix_evpn, &src->u.prefix_evpn,
sizeof(struct evpn_addr));
2002-12-13 21:15:29 +01:00
} else if (src->family == AF_UNSPEC) {
dest->u.lp.id = src->u.lp.id;
dest->u.lp.adv_router = src->u.lp.adv_router;
} else if (src->family == AF_FLOWSPEC) {
void *temp;
int len;
len = src->u.prefix_flowspec.prefixlen;
dest->u.prefix_flowspec.prefixlen =
src->u.prefix_flowspec.prefixlen;
dest->u.prefix_flowspec.family =
src->u.prefix_flowspec.family;
dest->family = src->family;
temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len);
dest->u.prefix_flowspec.ptr = (uintptr_t)temp;
memcpy((void *)dest->u.prefix_flowspec.ptr,
(void *)src->u.prefix_flowspec.ptr, len);
2002-12-13 21:15:29 +01:00
} else {
flog_err(EC_LIB_DEVELOPMENT,
"prefix_copy(): Unknown address family %d",
src->family);
2002-12-13 21:15:29 +01:00
assert(0);
}
}
/*
* Return 1 if the address/netmask contained in the prefix structure
* is the same, and else return 0. For this routine, 'same' requires
* that not only the prefix length and the network part be the same,
* but also the host part. Thus, 10.0.0.1/8 and 10.0.0.2/8 are not
* the same. Note that this routine has the same return value sense
* as '==' (which is different from prefix_cmp).
*/
int prefix_same(union prefixconstptr up1, union prefixconstptr up2)
2002-12-13 21:15:29 +01:00
{
const struct prefix *p1 = up1.p;
const struct prefix *p2 = up2.p;
if ((p1 && !p2) || (!p1 && p2))
return 0;
if (!p1 && !p2)
return 1;
2002-12-13 21:15:29 +01:00
if (p1->family == p2->family && p1->prefixlen == p2->prefixlen) {
if (p1->family == AF_INET)
if (IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4))
2002-12-13 21:15:29 +01:00
return 1;
if (p1->family == AF_INET6)
if (IPV6_ADDR_SAME(&p1->u.prefix6.s6_addr,
&p2->u.prefix6.s6_addr))
2002-12-13 21:15:29 +01:00
return 1;
if (p1->family == AF_ETHERNET)
if (!memcmp(&p1->u.prefix_eth, &p2->u.prefix_eth,
sizeof(struct ethaddr)))
return 1;
if (p1->family == AF_EVPN)
if (!memcmp(&p1->u.prefix_evpn, &p2->u.prefix_evpn,
sizeof(struct evpn_addr)))
return 1;
if (p1->family == AF_FLOWSPEC) {
if (p1->u.prefix_flowspec.family !=
p2->u.prefix_flowspec.family)
return 0;
if (p1->u.prefix_flowspec.prefixlen !=
p2->u.prefix_flowspec.prefixlen)
return 0;
if (!memcmp(&p1->u.prefix_flowspec.ptr,
&p2->u.prefix_flowspec.ptr,
p2->u.prefix_flowspec.prefixlen))
return 1;
}
2002-12-13 21:15:29 +01:00
}
return 0;
}
/*
* Return -1/0/1 comparing the prefixes in a way that gives a full/linear
* order.
*
* Network prefixes are considered the same if the prefix lengths are equal
* and the network parts are the same. Host bits (which are considered masked
* by the prefix length) are not significant. Thus, 10.0.0.1/8 and
* 10.0.0.2/8 are considered equivalent by this routine. Note that
* this routine has the same return sense as strcmp (which is different
* from prefix_same).
*/
int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2)
2002-12-13 21:15:29 +01:00
{
const struct prefix *p1 = up1.p;
const struct prefix *p2 = up2.p;
2002-12-13 21:15:29 +01:00
int offset;
int shift;
int i;
2002-12-13 21:15:29 +01:00
/* Set both prefix's head pointer. */
const uint8_t *pp1;
const uint8_t *pp2;
2002-12-13 21:15:29 +01:00
if (p1->family != p2->family)
return numcmp(p1->family, p2->family);
if (p1->family == AF_FLOWSPEC) {
pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr;
pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr;
2002-12-13 21:15:29 +01:00
if (p1->u.prefix_flowspec.family !=
p2->u.prefix_flowspec.family)
return 1;
if (p1->u.prefix_flowspec.prefixlen !=
p2->u.prefix_flowspec.prefixlen)
return numcmp(p1->u.prefix_flowspec.prefixlen,
p2->u.prefix_flowspec.prefixlen);
offset = p1->u.prefix_flowspec.prefixlen;
while (offset--)
if (pp1[offset] != pp2[offset])
return numcmp(pp1[offset], pp2[offset]);
return 0;
}
pp1 = p1->u.val;
pp2 = p2->u.val;
if (p1->prefixlen != p2->prefixlen)
return numcmp(p1->prefixlen, p2->prefixlen);
2011-10-24 16:45:05 +02:00
offset = p1->prefixlen / PNBBY;
shift = p1->prefixlen % PNBBY;
2002-12-13 21:15:29 +01:00
i = memcmp(pp1, pp2, offset);
if (i)
return i;
2002-12-13 21:15:29 +01:00
lib: Fix read beyond end of data structure Our Address Sanitizer CI is finding this issue: error 09-Oct-2019 19:28:33 r4: bgpd triggered an exception by AddressSanitizer error 09-Oct-2019 19:28:33 ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdd425b060 at pc 0x00000068575f bp 0x7ffdd4258550 sp 0x7ffdd4258540 error 09-Oct-2019 19:28:33 READ of size 1 at 0x7ffdd425b060 thread T0 error 09-Oct-2019 19:28:33 #0 0x68575e in prefix_cmp lib/prefix.c:776 error 09-Oct-2019 19:28:33 #1 0x5889f5 in rfapiItBiIndexSearch bgpd/rfapi/rfapi_import.c:2230 error 09-Oct-2019 19:28:33 #2 0x5889f5 in rfapiBgpInfoFilteredImportVPN bgpd/rfapi/rfapi_import.c:3520 error 09-Oct-2019 19:28:33 #3 0x58b909 in rfapiProcessWithdraw bgpd/rfapi/rfapi_import.c:4071 error 09-Oct-2019 19:28:33 #4 0x4c459b in bgp_withdraw bgpd/bgp_route.c:3736 error 09-Oct-2019 19:28:33 #5 0x484122 in bgp_nlri_parse_vpn bgpd/bgp_mplsvpn.c:237 error 09-Oct-2019 19:28:33 #6 0x497f52 in bgp_nlri_parse bgpd/bgp_packet.c:315 error 09-Oct-2019 19:28:33 #7 0x49d06d in bgp_update_receive bgpd/bgp_packet.c:1598 error 09-Oct-2019 19:28:33 #8 0x49d06d in bgp_process_packet bgpd/bgp_packet.c:2274 error 09-Oct-2019 19:28:33 #9 0x6b9f54 in thread_call lib/thread.c:1531 error 09-Oct-2019 19:28:33 #10 0x657037 in frr_run lib/libfrr.c:1052 error 09-Oct-2019 19:28:33 #11 0x42d268 in main bgpd/bgp_main.c:486 error 09-Oct-2019 19:28:33 #12 0x7f806032482f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) error 09-Oct-2019 19:28:33 #13 0x42bcc8 in _start (/usr/lib/frr/bgpd+0x42bcc8) error 09-Oct-2019 19:28:33 error 09-Oct-2019 19:28:33 Address 0x7ffdd425b060 is located in stack of thread T0 at offset 240 in frame error 09-Oct-2019 19:28:33 #0 0x483945 in bgp_nlri_parse_vpn bgpd/bgp_mplsvpn.c:103 error 09-Oct-2019 19:28:33 error 09-Oct-2019 19:28:33 This frame has 5 object(s): error 09-Oct-2019 19:28:33 [32, 36) 'label' error 09-Oct-2019 19:28:33 [96, 108) 'rd_as' error 09-Oct-2019 19:28:33 [160, 172) 'rd_ip' error 09-Oct-2019 19:28:33 [224, 240) 'prd' <== Memory access at offset 240 overflows this variable error 09-Oct-2019 19:28:33 [288, 336) 'p' error 09-Oct-2019 19:28:33 HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext error 09-Oct-2019 19:28:33 (longjmp and C++ exceptions *are* supported) error 09-Oct-2019 19:28:33 SUMMARY: AddressSanitizer: stack-buffer-overflow lib/prefix.c:776 prefix_cmp error 09-Oct-2019 19:28:33 Shadow bytes around the buggy address: error 09-Oct-2019 19:28:33 0x10003a8435b0: 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00 00 00 error 09-Oct-2019 19:28:33 0x10003a8435c0: 00 00 00 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3 error 09-Oct-2019 19:28:33 0x10003a8435d0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 error 09-Oct-2019 19:28:33 0x10003a8435e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 error 09-Oct-2019 19:28:33 0x10003a8435f0: f1 f1 04 f4 f4 f4 f2 f2 f2 f2 00 04 f4 f4 f2 f2 error 09-Oct-2019 19:28:33 =>0x10003a843600: f2 f2 00 04 f4 f4 f2 f2 f2 f2 00 00[f4]f4 f2 f2 error 09-Oct-2019 19:28:33 0x10003a843610: f2 f2 00 00 00 00 00 00 f4 f4 f3 f3 f3 f3 00 00 error 09-Oct-2019 19:28:33 0x10003a843620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 error 09-Oct-2019 19:28:33 0x10003a843630: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 02 f4 error 09-Oct-2019 19:28:33 0x10003a843640: f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2 00 00 error 09-Oct-2019 19:28:33 0x10003a843650: f4 f4 f2 f2 f2 f2 00 00 00 00 f2 f2 f2 f2 00 00 error 09-Oct-2019 19:28:33 Shadow byte legend (one shadow byte represents 8 application bytes): error 09-Oct-2019 19:28:33 Addressable: 00 error 09-Oct-2019 19:28:33 Partially addressable: 01 02 03 04 05 06 07 error 09-Oct-2019 19:28:33 Heap left redzone: fa error 09-Oct-2019 19:28:33 Heap right redzone: fb error 09-Oct-2019 19:28:33 Freed heap region: fd error 09-Oct-2019 19:28:33 Stack left redzone: f1 error 09-Oct-2019 19:28:33 Stack mid redzone: f2 error 09-Oct-2019 19:28:33 Stack right redzone: f3 error 09-Oct-2019 19:28:33 Stack partial redzone: f4 error 09-Oct-2019 19:28:33 Stack after return: f5 error 09-Oct-2019 19:28:33 Stack use after scope: f8 error 09-Oct-2019 19:28:33 Global redzone: f9 error 09-Oct-2019 19:28:33 Global init order: f6 error 09-Oct-2019 19:28:33 Poisoned by user: f7 error 09-Oct-2019 19:28:33 Container overflow: fc error 09-Oct-2019 19:28:33 Array cookie: ac error 09-Oct-2019 19:28:33 Intra object redzone: bb error 09-Oct-2019 19:28:33 ASan internal: fe error 09-Oct-2019 19:28:36 r3: Daemon bgpd not running This is the result of this code pattern in rfapi/rfapi_import.c: prefix_cmp((struct prefix *)&bpi_result->extra->vnc.import.rd, (struct prefix *)prd)) Effectively prd or vnc.import.rd are `struct prefix_rd` which are being typecast to a `struct prefix`. Not a big deal except commit 1315d74de97be2944d7b005b2f9a50e9ae5eff4d modified the prefix_cmp function to allow for a sorted prefix_cmp. In prefix_cmp we were looking at the offset and shift. In the case of vnc we were passing a prefix length of 64 which is the exact length of the remaining data structure for struct prefix_rd. So we calculated a offset of 8 and a shift of 0. The data structures for the prefix portion happened to be equal to 64 bits of data. So we checked that with the memcmp got a 0 and promptly read off the end of the data structure for the numcmp. The fix is if shift is 0 that means thei the memcmp has checked everything and there is nothing to do. Please note: We will still crash if we set the prefixlen > then ~312 bits currently( ie if the prefixlen specifies a bit length longer than the prefix length ). I do not think there is anything to do here( nor am I sure how to correct this either ) as that we are going to have some severe problems when we muck up the prefixlen. Fixes: #5025 Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
2019-10-10 14:52:54 +02:00
/*
* At this point offset was the same, if we have shift
* that means we still have data to compare, if shift is
* 0 then we are at the end of the data structure
* and should just return, as that we will be accessing
* memory beyond the end of the party zone
*/
if (shift)
return numcmp(pp1[offset] & maskbit[shift],
pp2[offset] & maskbit[shift]);
return 0;
2002-12-13 21:15:29 +01:00
}
/*
* Count the number of common bits in 2 prefixes. The prefix length is
* ignored for this function; the whole prefix is compared. If the prefix
* address families don't match, return -1; otherwise the return value is
* in range 0 ... maximum prefix length for the address family.
*/
int prefix_common_bits(const struct prefix *p1, const struct prefix *p2)
{
int pos, bit;
int length = 0;
uint8_t xor ;
/* Set both prefix's head pointer. */
const uint8_t *pp1 = p1->u.val;
const uint8_t *pp2 = p2->u.val;
if (p1->family == AF_INET)
length = IPV4_MAX_BYTELEN;
if (p1->family == AF_INET6)
length = IPV6_MAX_BYTELEN;
if (p1->family == AF_ETHERNET)
length = ETH_ALEN;
if (p1->family == AF_EVPN)
length = 8 * sizeof(struct evpn_addr);
if (p1->family != p2->family || !length)
return -1;
for (pos = 0; pos < length; pos++)
if (pp1[pos] != pp2[pos])
break;
if (pos == length)
return pos * 8;
xor = pp1[pos] ^ pp2[pos];
for (bit = 0; bit < 8; bit++)
if (xor&(1 << (7 - bit)))
break;
return pos * 8 + bit;
}
2002-12-13 21:15:29 +01:00
/* Return prefix family type string. */
const char *prefix_family_str(const struct prefix *p)
2002-12-13 21:15:29 +01:00
{
if (p->family == AF_INET)
return "inet";
if (p->family == AF_INET6)
return "inet6";
2016-04-19 22:08:59 +02:00
if (p->family == AF_ETHERNET)
return "ether";
if (p->family == AF_EVPN)
return "evpn";
2002-12-13 21:15:29 +01:00
return "unspec";
}
/* Allocate new prefix_ipv4 structure. */
struct prefix_ipv4 *prefix_ipv4_new(void)
2002-12-13 21:15:29 +01:00
{
struct prefix_ipv4 *p;
/* Call prefix_new to allocate a full-size struct prefix to avoid
problems
where the struct prefix_ipv4 is cast to struct prefix and unallocated
bytes were being referenced (e.g. in structure assignments). */
p = (struct prefix_ipv4 *)prefix_new();
2002-12-13 21:15:29 +01:00
p->family = AF_INET;
return p;
}
/* Free prefix_ipv4 structure. */
void prefix_ipv4_free(struct prefix_ipv4 **p)
2002-12-13 21:15:29 +01:00
{
prefix_free((struct prefix **)p);
2002-12-13 21:15:29 +01:00
}
/* If given string is valid return 1 else return 0 */
int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p)
2002-12-13 21:15:29 +01:00
{
int ret;
int plen;
char *pnt;
char *cp;
2002-12-13 21:15:29 +01:00
/* Find slash inside string. */
pnt = strchr(str, '/');
2002-12-13 21:15:29 +01:00
/* String doesn't contail slash. */
if (pnt == NULL) {
/* Convert string to prefix. */
ret = inet_pton(AF_INET, str, &p->prefix);
2002-12-13 21:15:29 +01:00
if (ret == 0)
return 0;
2002-12-13 21:15:29 +01:00
/* If address doesn't contain slash we assume it host address.
*/
p->family = AF_INET;
p->prefixlen = IPV4_MAX_BITLEN;
2002-12-13 21:15:29 +01:00
return ret;
} else {
cp = XMALLOC(MTYPE_TMP, (pnt - str) + 1);
memcpy(cp, str, pnt - str);
2002-12-13 21:15:29 +01:00
*(cp + (pnt - str)) = '\0';
ret = inet_pton(AF_INET, cp, &p->prefix);
2002-12-13 21:15:29 +01:00
XFREE(MTYPE_TMP, cp);
if (ret == 0)
return 0;
2002-12-13 21:15:29 +01:00
/* Get prefix length. */
plen = (uint8_t)atoi(++pnt);
if (plen > IPV4_MAX_BITLEN)
2002-12-13 21:15:29 +01:00
return 0;
2002-12-13 21:15:29 +01:00
p->family = AF_INET;
p->prefixlen = plen;
}
return ret;
}
2016-04-19 22:08:59 +02:00
/* When string format is invalid return 0. */
int str2prefix_eth(const char *str, struct prefix_eth *p)
{
int ret = 0;
int plen = 48;
char *pnt;
char *cp = NULL;
const char *str_addr = str;
unsigned int a[6];
int i;
bool slash = false;
if (!strcmp(str, "any")) {
memset(p, 0, sizeof(*p));
p->family = AF_ETHERNET;
return 1;
}
2016-04-19 22:08:59 +02:00
/* Find slash inside string. */
pnt = strchr(str, '/');
2016-04-19 22:08:59 +02:00
if (pnt) {
/* Get prefix length. */
plen = (uint8_t)atoi(++pnt);
2016-04-19 22:08:59 +02:00
if (plen > 48) {
ret = 0;
goto done;
}
cp = XMALLOC(MTYPE_TMP, (pnt - str) + 1);
memcpy(cp, str, pnt - str);
2002-12-13 21:15:29 +01:00
*(cp + (pnt - str)) = '\0';
2016-04-19 22:08:59 +02:00
str_addr = cp;
slash = true;
2016-04-19 22:08:59 +02:00
}
/* Convert string to prefix. */
if (sscanf(str_addr, "%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, a + 2,
a + 3, a + 4, a + 5)
!= 6) {
ret = 0;
goto done;
}
for (i = 0; i < 6; ++i) {
p->eth_addr.octet[i] = a[i] & 0xff;
}
p->prefixlen = plen;
p->family = AF_ETHERNET;
/*
* special case to allow old configurations to work
* Since all zero's is implicitly meant to allow
* a comparison to zero, let's assume
*/
if (!slash && is_zero_mac(&(p->eth_addr)))
p->prefixlen = 0;
2016-04-19 22:08:59 +02:00
ret = 1;
done:
XFREE(MTYPE_TMP, cp);
2016-04-19 22:08:59 +02:00
return ret;
}
2011-10-11 13:17:45 +02:00
/* Convert masklen into IP address's netmask (network byte order). */
void masklen2ip(const int masklen, struct in_addr *netmask)
2002-12-13 21:15:29 +01:00
{
2011-10-24 16:45:05 +02:00
assert(masklen >= 0 && masklen <= IPV4_MAX_BITLEN);
/* left shift is only defined for less than the size of the type.
* we unconditionally use long long in case the target platform
* has defined behaviour for << 32 (or has a 64-bit left shift) */
if (sizeof(unsigned long long) > 4)
netmask->s_addr = htonl(0xffffffffULL << (32 - masklen));
else
netmask->s_addr =
htonl(masklen ? 0xffffffffU << (32 - masklen) : 0);
2002-12-13 21:15:29 +01:00
}
/* Convert IP address's netmask into integer. We assume netmask is
* sequential one. Argument netmask should be network byte order. */
uint8_t ip_masklen(struct in_addr netmask)
2002-12-13 21:15:29 +01:00
{
uint32_t tmp = ~ntohl(netmask.s_addr);
/*
* clz: count leading zeroes. sadly, the behaviour of this builtin is
* undefined for a 0 argument, even though most CPUs give 32
*/
return tmp ? __builtin_clz(tmp) : 32;
2002-12-13 21:15:29 +01:00
}
2011-10-18 16:33:53 +02:00
/* Apply mask to IPv4 prefix (network byte order). */
2002-12-13 21:15:29 +01:00
void apply_mask_ipv4(struct prefix_ipv4 *p)
{
struct in_addr mask;
masklen2ip(p->prefixlen, &mask);
p->prefix.s_addr &= mask.s_addr;
2002-12-13 21:15:29 +01:00
}
/* If prefix is 0.0.0.0/0 then return 1 else return 0. */
int prefix_ipv4_any(const struct prefix_ipv4 *p)
2002-12-13 21:15:29 +01:00
{
return (p->prefix.s_addr == INADDR_ANY && p->prefixlen == 0);
2002-12-13 21:15:29 +01:00
}
2002-12-13 21:15:29 +01:00
/* Allocate a new ip version 6 route */
struct prefix_ipv6 *prefix_ipv6_new(void)
2002-12-13 21:15:29 +01:00
{
struct prefix_ipv6 *p;
/* Allocate a full-size struct prefix to avoid problems with structure
size mismatches. */
p = (struct prefix_ipv6 *)prefix_new();
2002-12-13 21:15:29 +01:00
p->family = AF_INET6;
return p;
}
/* Free prefix for IPv6. */
void prefix_ipv6_free(struct prefix_ipv6 **p)
2002-12-13 21:15:29 +01:00
{
prefix_free((struct prefix **)p);
2002-12-13 21:15:29 +01:00
}
/* If given string is valid return 1 else return 0 */
int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p)
2002-12-13 21:15:29 +01:00
{
char *pnt;
char *cp;
int ret;
2002-12-13 21:15:29 +01:00
pnt = strchr(str, '/');
2002-12-13 21:15:29 +01:00
/* If string doesn't contain `/' treat it as host route. */
if (pnt == NULL) {
ret = inet_pton(AF_INET6, str, &p->prefix);
if (ret == 0)
2002-12-13 21:15:29 +01:00
return 0;
p->prefixlen = IPV6_MAX_BITLEN;
} else {
int plen;
cp = XMALLOC(MTYPE_TMP, (pnt - str) + 1);
memcpy(cp, str, pnt - str);
2002-12-13 21:15:29 +01:00
*(cp + (pnt - str)) = '\0';
ret = inet_pton(AF_INET6, cp, &p->prefix);
XFREE(MTYPE_TMP, cp);
if (ret == 0)
2002-12-13 21:15:29 +01:00
return 0;
plen = (uint8_t)atoi(++pnt);
2011-10-24 16:45:05 +02:00
if (plen > IPV6_MAX_BITLEN)
2002-12-13 21:15:29 +01:00
return 0;
p->prefixlen = plen;
}
p->family = AF_INET6;
return ret;
}
/* Convert struct in6_addr netmask into integer.
* FIXME return uint8_t as ip_maskleni() does. */
2002-12-13 21:15:29 +01:00
int ip6_masklen(struct in6_addr netmask)
{
if (netmask.s6_addr32[0] != 0xffffffffU)
return __builtin_clz(~ntohl(netmask.s6_addr32[0]));
if (netmask.s6_addr32[1] != 0xffffffffU)
return __builtin_clz(~ntohl(netmask.s6_addr32[1])) + 32;
if (netmask.s6_addr32[2] != 0xffffffffU)
return __builtin_clz(~ntohl(netmask.s6_addr32[2])) + 64;
if (netmask.s6_addr32[3] != 0xffffffffU)
return __builtin_clz(~ntohl(netmask.s6_addr32[3])) + 96;
/* note __builtin_clz(0) is undefined */
return 128;
2002-12-13 21:15:29 +01:00
}
void masklen2ip6(const int masklen, struct in6_addr *netmask)
2002-12-13 21:15:29 +01:00
{
2011-10-24 16:45:05 +02:00
assert(masklen >= 0 && masklen <= IPV6_MAX_BITLEN);
if (masklen == 0) {
/* note << 32 is undefined */
memset(netmask, 0, sizeof(*netmask));
} else if (masklen <= 32) {
netmask->s6_addr32[0] = htonl(0xffffffffU << (32 - masklen));
netmask->s6_addr32[1] = 0;
netmask->s6_addr32[2] = 0;
netmask->s6_addr32[3] = 0;
} else if (masklen <= 64) {
netmask->s6_addr32[0] = 0xffffffffU;
netmask->s6_addr32[1] = htonl(0xffffffffU << (64 - masklen));
netmask->s6_addr32[2] = 0;
netmask->s6_addr32[3] = 0;
} else if (masklen <= 96) {
netmask->s6_addr32[0] = 0xffffffffU;
netmask->s6_addr32[1] = 0xffffffffU;
netmask->s6_addr32[2] = htonl(0xffffffffU << (96 - masklen));
netmask->s6_addr32[3] = 0;
} else {
netmask->s6_addr32[0] = 0xffffffffU;
netmask->s6_addr32[1] = 0xffffffffU;
netmask->s6_addr32[2] = 0xffffffffU;
netmask->s6_addr32[3] = htonl(0xffffffffU << (128 - masklen));
}
2002-12-13 21:15:29 +01:00
}
void apply_mask_ipv6(struct prefix_ipv6 *p)
{
uint8_t *pnt;
int index;
int offset;
index = p->prefixlen / 8;
if (index < 16) {
pnt = (uint8_t *)&p->prefix;
offset = p->prefixlen % 8;
pnt[index] &= maskbit[offset];
index++;
while (index < 16)
pnt[index++] = 0;
}
2002-12-13 21:15:29 +01:00
}
void apply_mask(struct prefix *p)
{
switch (p->family) {
case AF_INET:
apply_mask_ipv4((struct prefix_ipv4 *)p);
break;
case AF_INET6:
apply_mask_ipv6((struct prefix_ipv6 *)p);
break;
default:
break;
}
return;
}
/* Utility function of convert between struct prefix <=> union sockunion. */
struct prefix *sockunion2hostprefix(const union sockunion *su,
struct prefix *prefix)
2002-12-13 21:15:29 +01:00
{
if (su->sa.sa_family == AF_INET) {
struct prefix_ipv4 *p;
p = prefix ? (struct prefix_ipv4 *)prefix : prefix_ipv4_new();
2002-12-13 21:15:29 +01:00
p->family = AF_INET;
p->prefix = su->sin.sin_addr;
p->prefixlen = IPV4_MAX_BITLEN;
return (struct prefix *)p;
}
if (su->sa.sa_family == AF_INET6) {
struct prefix_ipv6 *p;
p = prefix ? (struct prefix_ipv6 *)prefix : prefix_ipv6_new();
2002-12-13 21:15:29 +01:00
p->family = AF_INET6;
p->prefixlen = IPV6_MAX_BITLEN;
memcpy(&p->prefix, &su->sin6.sin6_addr,
sizeof(struct in6_addr));
return (struct prefix *)p;
}
return NULL;
}
void prefix2sockunion(const struct prefix *p, union sockunion *su)
{
memset(su, 0, sizeof(*su));
su->sa.sa_family = p->family;
if (p->family == AF_INET)
su->sin.sin_addr = p->u.prefix4;
if (p->family == AF_INET6)
memcpy(&su->sin6.sin6_addr, &p->u.prefix6,
sizeof(struct in6_addr));
}
int prefix_blen(const struct prefix *p)
2002-12-13 21:15:29 +01:00
{
switch (p->family) {
case AF_INET:
return IPV4_MAX_BYTELEN;
case AF_INET6:
return IPV6_MAX_BYTELEN;
2016-04-19 22:08:59 +02:00
case AF_ETHERNET:
return ETH_ALEN;
2002-12-13 21:15:29 +01:00
}
return 0;
}
/* Generic function for conversion string to struct prefix. */
int str2prefix(const char *str, struct prefix *p)
2002-12-13 21:15:29 +01:00
{
int ret;
if (!str || !p)
return 0;
2002-12-13 21:15:29 +01:00
/* First we try to convert string to struct prefix_ipv4. */
ret = str2prefix_ipv4(str, (struct prefix_ipv4 *)p);
if (ret)
return ret;
/* Next we try to convert string to struct prefix_ipv6. */
ret = str2prefix_ipv6(str, (struct prefix_ipv6 *)p);
if (ret)
return ret;
2016-04-19 22:08:59 +02:00
/* Next we try to convert string to struct prefix_eth. */
ret = str2prefix_eth(str, (struct prefix_eth *)p);
if (ret)
return ret;
2002-12-13 21:15:29 +01:00
return 0;
}
static const char *prefixevpn_ead2str(const struct prefix_evpn *p, char *str,
int size)
{
uint8_t family;
char buf[ESI_STR_LEN];
char buf1[INET6_ADDRSTRLEN];
family = IS_IPADDR_V4(&p->prefix.ead_addr.ip) ? AF_INET : AF_INET6;
snprintf(str, size, "[%d]:[%u]:[%s]:[%d]:[%s]", p->prefix.route_type,
p->prefix.ead_addr.eth_tag,
esi_to_str(&p->prefix.ead_addr.esi, buf, sizeof(buf)),
(family == AF_INET) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN,
inet_ntop(family, &p->prefix.ead_addr.ip.ipaddr_v4, buf1,
sizeof(buf1)));
return str;
}
static const char *prefixevpn_macip2str(const struct prefix_evpn *p, char *str,
int size)
{
uint8_t family;
char buf1[ETHER_ADDR_STRLEN];
char buf2[PREFIX2STR_BUFFER];
if (is_evpn_prefix_ipaddr_none(p))
snprintf(str, size, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
p->prefix.macip_addr.eth_tag, 8 * ETH_ALEN,
prefix_mac2str(&p->prefix.macip_addr.mac, buf1,
sizeof(buf1)));
else {
family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6;
snprintf(str, size, "[%d]:[%d]:[%d]:[%s]:[%d]:[%s]",
p->prefix.route_type, p->prefix.macip_addr.eth_tag,
8 * ETH_ALEN,
prefix_mac2str(&p->prefix.macip_addr.mac, buf1,
sizeof(buf1)),
family == AF_INET ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN,
inet_ntop(family, &p->prefix.macip_addr.ip.ip.addr,
buf2, PREFIX2STR_BUFFER));
}
return str;
}
static const char *prefixevpn_imet2str(const struct prefix_evpn *p, char *str,
int size)
{
uint8_t family;
char buf[INET6_ADDRSTRLEN];
family = IS_IPADDR_V4(&p->prefix.imet_addr.ip) ? AF_INET : AF_INET6;
snprintf(str, size, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
p->prefix.imet_addr.eth_tag,
(family == AF_INET) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN,
inet_ntop(family, &p->prefix.imet_addr.ip.ipaddr_v4, buf,
sizeof(buf)));
return str;
}
static const char *prefixevpn_es2str(const struct prefix_evpn *p, char *str,
int size)
{
uint8_t family;
char buf[ESI_STR_LEN];
char buf1[INET6_ADDRSTRLEN];
family = IS_IPADDR_V4(&p->prefix.es_addr.ip) ? AF_INET : AF_INET6;
snprintf(str, size, "[%d]:[%s]:[%d]:[%s]", p->prefix.route_type,
esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)),
(family == AF_INET) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN,
inet_ntop(family, &p->prefix.es_addr.ip.ipaddr_v4, buf1,
sizeof(buf1)));
return str;
}
static const char *prefixevpn_prefix2str(const struct prefix_evpn *p, char *str,
int size)
{
uint8_t family;
char buf[INET6_ADDRSTRLEN];
family = IS_IPADDR_V4(&p->prefix.prefix_addr.ip) ? AF_INET : AF_INET6;
snprintf(str, size, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
p->prefix.prefix_addr.eth_tag,
p->prefix.prefix_addr.ip_prefix_length,
inet_ntop(family, &p->prefix.prefix_addr.ip.ipaddr_v4, buf,
sizeof(buf)));
return str;
}
static const char *prefixevpn2str(const struct prefix_evpn *p, char *str,
int size)
{
switch (p->prefix.route_type) {
case BGP_EVPN_AD_ROUTE:
return prefixevpn_ead2str(p, str, size);
case BGP_EVPN_MAC_IP_ROUTE:
return prefixevpn_macip2str(p, str, size);
case BGP_EVPN_IMET_ROUTE:
return prefixevpn_imet2str(p, str, size);
case BGP_EVPN_ES_ROUTE:
return prefixevpn_es2str(p, str, size);
case BGP_EVPN_IP_PREFIX_ROUTE:
return prefixevpn_prefix2str(p, str, size);
default:
snprintf(str, size, "Unsupported EVPN prefix");
break;
}
return str;
}
const char *prefix2str(union prefixconstptr pu, char *str, int size)
2002-12-13 21:15:29 +01:00
{
const struct prefix *p = pu.p;
char buf[PREFIX2STR_BUFFER];
int byte, tmp, a, b;
bool z = false;
size_t l;
switch (p->family) {
case AF_INET:
case AF_INET6:
inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf));
l = strlen(buf);
buf[l++] = '/';
byte = p->prefixlen;
tmp = p->prefixlen - 100;
if (tmp >= 0) {
buf[l++] = '1';
z = true;
byte = tmp;
}
b = byte % 10;
a = byte / 10;
if (a || z)
buf[l++] = '0' + a;
buf[l++] = '0' + b;
buf[l] = '\0';
strlcpy(str, buf, size);
break;
case AF_ETHERNET:
snprintf(str, size, "%s/%d",
prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf)),
p->prefixlen);
break;
case AF_EVPN:
prefixevpn2str((const struct prefix_evpn *)p, str, size);
break;
case AF_FLOWSPEC:
strlcpy(str, "FS prefix", size);
break;
default:
strlcpy(str, "UNK prefix", size);
break;
2016-04-19 22:08:59 +02:00
}
return str;
2002-12-13 21:15:29 +01:00
}
void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr,
char *buf, int buf_size)
{
int save_errno = errno;
if (addr.s_addr == INADDR_ANY)
strlcpy(buf, "*", buf_size);
else {
if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
if (onfail)
snprintf(buf, buf_size, "%s", onfail);
}
}
errno = save_errno;
}
const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str)
{
char src_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
prefix_mcast_inet4_dump("<src?>", sg->src, src_str, sizeof(src_str));
prefix_mcast_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str));
snprintf(sg_str, PREFIX_SG_STR_LEN, "(%s,%s)", src_str, grp_str);
return sg_str;
}
struct prefix *prefix_new(void)
2002-12-13 21:15:29 +01:00
{
struct prefix *p;
p = XCALLOC(MTYPE_PREFIX, sizeof(*p));
2002-12-13 21:15:29 +01:00
return p;
}
void prefix_free_lists(void *arg)
{
struct prefix *p = arg;
prefix_free(&p);
}
2002-12-13 21:15:29 +01:00
/* Free prefix structure. */
void prefix_free(struct prefix **p)
2002-12-13 21:15:29 +01:00
{
XFREE(MTYPE_PREFIX, *p);
2002-12-13 21:15:29 +01:00
}
/* Utility function to convert ipv4 prefixes to Classful prefixes */
void apply_classful_mask_ipv4(struct prefix_ipv4 *p)
{
uint32_t destination;
2002-12-13 21:15:29 +01:00
destination = ntohl(p->prefix.s_addr);
if (p->prefixlen == IPV4_MAX_BITLEN)
;
2002-12-13 21:15:29 +01:00
/* do nothing for host routes */
else if (IN_CLASSC(destination)) {
p->prefixlen = 24;
apply_mask_ipv4(p);
} else if (IN_CLASSB(destination)) {
p->prefixlen = 16;
apply_mask_ipv4(p);
} else {
p->prefixlen = 8;
apply_mask_ipv4(p);
}
}
in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen)
{
struct in_addr mask;
masklen2ip(masklen, &mask);
return (masklen != IPV4_MAX_BITLEN - 1)
?
/* normal case */
(hostaddr | ~mask.s_addr)
:
/* For prefix 31 return 255.255.255.255 (RFC3021) */
htonl(0xFFFFFFFF);
}
2002-12-13 21:15:29 +01:00
/* Utility function to convert ipv4 netmask to prefixes
ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16"
ex.) "1.0.0.0" NULL => "1.0.0.0/8" */
int netmask_str2prefix_str(const char *net_str, const char *mask_str,
char *prefix_str, size_t prefix_str_len)
2002-12-13 21:15:29 +01:00
{
struct in_addr network;
struct in_addr mask;
uint8_t prefixlen;
uint32_t destination;
2002-12-13 21:15:29 +01:00
int ret;
2002-12-13 21:15:29 +01:00
ret = inet_aton(net_str, &network);
if (!ret)
return 0;
2002-12-13 21:15:29 +01:00
if (mask_str) {
ret = inet_aton(mask_str, &mask);
if (!ret)
return 0;
2002-12-13 21:15:29 +01:00
prefixlen = ip_masklen(mask);
} else {
destination = ntohl(network.s_addr);
if (network.s_addr == INADDR_ANY)
2002-12-13 21:15:29 +01:00
prefixlen = 0;
else if (IN_CLASSC(destination))
prefixlen = 24;
else if (IN_CLASSB(destination))
prefixlen = 16;
else if (IN_CLASSA(destination))
prefixlen = 8;
else
return 0;
}
snprintf(prefix_str, prefix_str_len, "%s/%d", net_str, prefixlen);
2002-12-13 21:15:29 +01:00
return 1;
}
/* converts to internal representation of mac address
* returns 1 on success, 0 otherwise
* format accepted: AA:BB:CC:DD:EE:FF
* if mac parameter is null, then check only
*/
int prefix_str2mac(const char *str, struct ethaddr *mac)
{
unsigned int a[6];
int i;
if (!str)
return 0;
if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, a + 2, a + 3,
a + 4, a + 5)
!= 6) {
/* error in incoming str length */
return 0;
}
/* valid mac address */
if (!mac)
return 1;
for (i = 0; i < 6; ++i)
mac->octet[i] = a[i] & 0xff;
return 1;
}
char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size)
{
char *ptr;
if (!mac)
return NULL;
if (!buf)
ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char));
else {
assert(size >= ETHER_ADDR_STRLEN);
ptr = buf;
}
snprintf(ptr, (ETHER_ADDR_STRLEN), "%02x:%02x:%02x:%02x:%02x:%02x",
(uint8_t)mac->octet[0], (uint8_t)mac->octet[1],
(uint8_t)mac->octet[2], (uint8_t)mac->octet[3],
(uint8_t)mac->octet[4], (uint8_t)mac->octet[5]);
return ptr;
}
unsigned prefix_hash_key(const void *pp)
{
struct prefix copy;
if (((struct prefix *)pp)->family == AF_FLOWSPEC) {
uint32_t len;
void *temp;
/* make sure *all* unused bits are zero,
* particularly including alignment /
* padding and unused prefix bytes.
*/
memset(&copy, 0, sizeof(copy));
prefix_copy(&copy, (struct prefix *)pp);
len = jhash((void *)copy.u.prefix_flowspec.ptr,
copy.u.prefix_flowspec.prefixlen,
0x55aa5a5a);
temp = (void *)copy.u.prefix_flowspec.ptr;
XFREE(MTYPE_PREFIX_FLOWSPEC, temp);
copy.u.prefix_flowspec.ptr = (uintptr_t)NULL;
return len;
}
/* make sure *all* unused bits are zero, particularly including
* alignment /
* padding and unused prefix bytes. */
memset(&copy, 0, sizeof(copy));
prefix_copy(&copy, (struct prefix *)pp);
return jhash(&copy,
offsetof(struct prefix, u.prefix) + PSIZE(copy.prefixlen),
0x55aa5a5a);
}
/* converts to internal representation of esi
* returns 1 on success, 0 otherwise
* format accepted: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa
* if esi parameter is null, then check only
*/
int str_to_esi(const char *str, esi_t *esi)
{
int i;
unsigned int a[ESI_BYTES];
if (!str)
return 0;
if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x",
a + 0, a + 1, a + 2, a + 3,
a + 4, a + 5, a + 6, a + 7,
a + 8, a + 9)
!= ESI_BYTES) {
/* error in incoming str length */
return 0;
}
/* valid ESI */
if (!esi)
return 1;
for (i = 0; i < ESI_BYTES; ++i)
esi->val[i] = a[i] & 0xff;
return 1;
}
char *esi_to_str(const esi_t *esi, char *buf, int size)
{
char *ptr;
if (!esi)
return NULL;
if (!buf)
ptr = XMALLOC(MTYPE_TMP, ESI_STR_LEN * sizeof(char));
else {
assert(size >= ESI_STR_LEN);
ptr = buf;
}
snprintf(ptr, ESI_STR_LEN,
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
esi->val[0], esi->val[1], esi->val[2],
esi->val[3], esi->val[4], esi->val[5],
esi->val[6], esi->val[7], esi->val[8],
esi->val[9]);
return ptr;
}
bgpd: support for DF election in EVPN-MH DF (Designated forwarder) election is used for picking a single BUM-traffic forwarded per-ES. RFC7432 specifies a mechanism called service carving for DF election. However that mechanism has many disadvantages - 1. LBs poorly. 2. Doesn't allow for a controlled failover needed in upgrade scenarios. 3. Not easy to hw accelerate. To fix the poor performance of service carving alternate DF mechanisms have been proposed via the following drafts - draft-ietf-bess-evpn-df-election-framework draft-ietf-bess-evpn-pref-df This commit adds support for the pref-df election mechanism which is used as the default. Other mechanisms including service-carving may be added later. In this mechanism one switch on an ES is elected as DF based on the preference value; higher preference wins with IP address acting as the tie-breaker (lower-IP wins if pref value is the same). Sample output ============= >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> torm-11# sh bgp l2vpn evpn es 03:00:00:00:00:01:11:00:00:01 ESI: 03:00:00:00:00:01:11:00:00:01 Type: LR RD: 27.0.0.15:6 Originator-IP: 27.0.0.15 Local ES DF preference: 100 VNI Count: 10 Remote VNI Count: 10 Inconsistent VNI VTEP Count: 0 Inconsistencies: - VTEPs: 27.0.0.16 flags: EA df_alg: preference df_pref: 32767 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> torm-11# sh bgp l2vpn evpn route esi 03:00:00:00:00:01:11:00:00:01 *> [4]:[03:00:00:00:00:01:11:00:00:01]:[32]:[27.0.0.15] 27.0.0.15 32768 i ET:8 ES-Import-Rt:00:00:00:00:01:11 DF: (alg: 2, pref: 100) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
2020-05-09 01:35:09 +02:00
char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len)
{
switch (df_alg) {
case EVPN_MH_DF_ALG_SERVICE_CARVING:
snprintf(buf, buf_len, "service-carving");
break;
case EVPN_MH_DF_ALG_HRW:
snprintf(buf, buf_len, "HRW");
break;
case EVPN_MH_DF_ALG_PREF:
snprintf(buf, buf_len, "preference");
break;
default:
snprintf(buf, buf_len, "unknown %u", df_alg);
break;
}
return buf;
}
printfrr_ext_autoreg_p("EA", printfrr_ea)
static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
const struct ethaddr *mac = ptr;
char cbuf[ETHER_ADDR_STRLEN];
if (!mac)
return bputs(buf, "(null)");
/* need real length even if buffer is too short */
prefix_mac2str(mac, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("IA", printfrr_ia)
static ssize_t printfrr_ia(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
const struct ipaddr *ipa = ptr;
char cbuf[INET6_ADDRSTRLEN];
bool use_star = false;
if (ea->fmt[0] == 's') {
use_star = true;
ea->fmt++;
}
if (!ipa)
return bputs(buf, "(null)");
if (use_star) {
struct in_addr zero4 = {};
struct in6_addr zero6 = {};
switch (ipa->ipa_type) {
case IPADDR_V4:
if (!memcmp(&ipa->ip.addr, &zero4, sizeof(zero4)))
return bputch(buf, '*');
break;
case IPADDR_V6:
if (!memcmp(&ipa->ip.addr, &zero6, sizeof(zero6)))
return bputch(buf, '*');
break;
default:
break;
}
}
ipaddr2str(ipa, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("I4", printfrr_i4)
static ssize_t printfrr_i4(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
char cbuf[INET_ADDRSTRLEN];
bool use_star = false;
struct in_addr zero = {};
if (ea->fmt[0] == 's') {
use_star = true;
ea->fmt++;
}
if (!ptr)
return bputs(buf, "(null)");
if (use_star && !memcmp(ptr, &zero, sizeof(zero)))
return bputch(buf, '*');
inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("I6", printfrr_i6)
static ssize_t printfrr_i6(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
char cbuf[INET6_ADDRSTRLEN];
bool use_star = false;
struct in6_addr zero = {};
if (ea->fmt[0] == 's') {
use_star = true;
ea->fmt++;
}
if (!ptr)
return bputs(buf, "(null)");
if (use_star && !memcmp(ptr, &zero, sizeof(zero)))
return bputch(buf, '*');
inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("FX", printfrr_pfx)
static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
char cbuf[PREFIX_STRLEN];
if (!ptr)
return bputs(buf, "(null)");
prefix2str(ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("PSG4", printfrr_psg)
static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
const struct prefix_sg *sg = ptr;
ssize_t ret = 0;
if (!sg)
return bputs(buf, "(null)");
if (sg->src.s_addr == INADDR_ANY)
ret += bputs(buf, "(*,");
else
ret += bprintfrr(buf, "(%pI4,", &sg->src);
if (sg->grp.s_addr == INADDR_ANY)
ret += bputs(buf, "*)");
else
ret += bprintfrr(buf, "%pI4)", &sg->grp);
return ret;
}