diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index 7518f02acf..feda0328bd 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -29,6 +29,7 @@ #include "skiplist.h" #include "workqueue.h" #include "zclient.h" +#include "mpls.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_labelpool.h" @@ -391,7 +392,8 @@ void bgp_lp_get( if (lp_fifo_count(&lp->requests) > lp->pending_count) { if (!zclient || zclient->sock < 0) return; - if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) + if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE, + MPLS_LABEL_BASE_ANY)) lp->pending_count += LP_CHUNK_SIZE; } } @@ -552,7 +554,8 @@ void bgp_lp_event_zebra_up(void) return; } - zclient_send_get_label_chunk(zclient, 0, labels_needed); + zclient_send_get_label_chunk(zclient, 0, labels_needed, + MPLS_LABEL_BASE_ANY); lp->pending_count = labels_needed; /* diff --git a/ldpd/lde.c b/ldpd/lde.c index 2aa96546ec..0ddf4f07d9 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -1660,7 +1660,8 @@ lde_get_label_chunk(void) uint32_t start, end; debug_labels("getting label chunk (size %u)", CHUNK_SIZE); - ret = lm_get_label_chunk(zclient_sync, 0, CHUNK_SIZE, &start, &end); + ret = lm_get_label_chunk(zclient_sync, 0, MPLS_LABEL_BASE_ANY, + CHUNK_SIZE, &start, &end); if (ret < 0) { log_warnx("Error getting label chunk!"); return -1; diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 9fccb085dd..a6f0519bd7 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -236,6 +236,9 @@ main(int argc, char *argv[]) " --ctl_socket Override ctl socket path\n" " -n, --instance Instance id\n"); + /* set default instance (to differentiate ldpd socket from lde one */ + init.instance = 1; + while (1) { int opt; diff --git a/lib/mpls.h b/lib/mpls.h index b140c8e317..d7b56c47bd 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -54,6 +54,7 @@ extern "C" { #define MPLS_LABEL_RESERVED_MAX 15 #define MPLS_LABEL_UNRESERVED_MIN 16 #define MPLS_LABEL_UNRESERVED_MAX 1048575 +#define MPLS_LABEL_BASE_ANY 0 /* Default min and max SRGB label range */ /* Even if the SRGB allows to manage different Label space between routers, diff --git a/lib/zclient.c b/lib/zclient.c index e9b4f5a58b..6937700199 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1995,10 +1995,11 @@ int lm_label_manager_connect(struct zclient *zclient, int async) * @param zclient Zclient used to connect to label manager (zebra) * @param keep Avoid garbage collection * @param chunk_size Amount of labels requested + * @param base Base for the label chunk. if MPLS_LABEL_BASE_ANY we do not care * @result 0 on success, -1 otherwise */ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, - uint32_t chunk_size) + uint32_t chunk_size, uint32_t base) { struct stream *s; @@ -2018,6 +2019,7 @@ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, stream_putw(s, zclient->instance); stream_putc(s, keep); stream_putl(s, chunk_size); + stream_putl(s, base); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -2038,7 +2040,7 @@ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, * @param end To write last assigned chunk label to * @result 0 on success, -1 otherwise */ -int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, +int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base, uint32_t chunk_size, uint32_t *start, uint32_t *end) { int ret; @@ -2063,6 +2065,8 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, stream_putc(s, keep); /* chunk size */ stream_putl(s, chunk_size); + /* requested chunk base */ + stream_putl(s, base); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -2103,6 +2107,15 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, "Wrong instId (%u) in get chunk response Should be %u", instance, zclient->instance); + /* if we requested a specific chunk and it could not be allocated, the + * response message will end here + */ + if (!STREAM_READABLE(s)) { + zlog_info("Unable to assign Label Chunk to %s instance %u", + zebra_route_string(proto), instance); + return -1; + } + /* keep */ response_keep = stream_getc(s); /* start and end labels */ diff --git a/lib/zclient.h b/lib/zclient.h index d651738687..be2ef69dc1 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -609,15 +609,13 @@ extern struct interface *zebra_interface_link_params_read(struct stream *s, vrf_id_t vrf_id); extern size_t zebra_interface_link_params_write(struct stream *, struct interface *); -extern int zclient_send_get_label_chunk( - struct zclient *zclient, - uint8_t keep, - uint32_t chunk_size); +extern int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, + uint32_t chunk_size, uint32_t base); extern int lm_label_manager_connect(struct zclient *zclient, int async); extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, - uint32_t chunk_size, uint32_t *start, - uint32_t *end); + uint32_t base, uint32_t chunk_size, + uint32_t *start, uint32_t *end); extern int lm_release_label_chunk(struct zclient *zclient, uint32_t start, uint32_t end); extern int tm_table_manager_connect(struct zclient *zclient); diff --git a/tests/test_lblmgr.c b/tests/test_lblmgr.c deleted file mode 100644 index e71e680fad..0000000000 --- a/tests/test_lblmgr.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Label Manager Test - * - * Copyright (C) 2017 by Bingen Eguzkitza, - * Volta Networks Inc. - * - * This file is part of FreeRangeRouting (FRR) - * - * FRR 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. - * - * FRR 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "lib/stream.h" -#include "lib/zclient.h" - -#define ZSERV_PATH "/tmp/zserv.api" // TODO!! -#define KEEP 0 /* change to 1 to avoid garbage collection */ -#define CHUNK_SIZE 32 - -struct zclient *zclient; -unsigned short instance = 1; - -const char *sequence = "GGRGGGRRG"; - -static int zebra_send_get_label_chunk(void); -static int zebra_send_release_label_chunk(uint32_t start, uint32_t end); - -static void process_next_call(uint32_t start, uint32_t end) -{ - sleep(3); - if (!*sequence) - exit(0); - if (*sequence == 'G') - zebra_send_get_label_chunk(); - else if (*sequence == 'R') - zebra_send_release_label_chunk(start, end); -} - -/* Connect to Label Manager */ - -static int zebra_send_label_manager_connect() -{ - int ret; - - printf("Connect to Label Manager\n"); - - ret = lm_label_manager_connect(zclient, 0); - printf("Label Manager connection result: %u \n", ret); - if (ret != 0) { - fprintf(stderr, "Error %d connecting to Label Manager %s\n", - ret, strerror(errno)); - exit(1); - } - - process_next_call(0, 0); -} - -/* Get Label Chunk */ - -static int zebra_send_get_label_chunk() -{ - uint32_t start; - uint32_t end; - int ret; - - printf("Ask for label chunk \n"); - - ret = lm_get_label_chunk(zclient, KEEP, CHUNK_SIZE, &start, &end); - if (ret != 0) { - fprintf(stderr, "Error %d requesting label chunk %s\n", ret, - strerror(errno)); - exit(1); - } - - sequence++; - - printf("Label Chunk assign: %u - %u \n", start, end); - - process_next_call(start, end); -} - -/* Release Label Chunk */ - -static int zebra_send_release_label_chunk(uint32_t start, uint32_t end) -{ - struct stream *s; - int ret; - - printf("Release label chunk: %u - %u\n", start, end); - - ret = lm_release_label_chunk(zclient, start, end); - if (ret != 0) { - fprintf(stderr, "Error releasing label chunk\n"); - exit(1); - } - - sequence++; - - process_next_call(start - CHUNK_SIZE, end - CHUNK_SIZE); -} - - -void init_zclient(struct thread_master *master, char *lm_zserv_path) -{ - frr_zclient_addr(&zclient_addr, &zclient_addr_len, lm_zserv_path); - - zclient = zclient_new(master, &zclient_options_default); - /* zclient_init(zclient, ZEBRA_LABEL_MANAGER, 0); */ - zclient->sock = -1; - zclient->redist_default = ZEBRA_ROUTE_LDP; - zclient->instance = instance; - if (zclient_socket_connect(zclient) < 0) { - printf("Error connecting synchronous zclient!\n"); - exit(1); - } -} - -int main(int argc, char *argv[]) -{ - struct thread_master *master; - struct thread thread; - int ret; - - printf("Sequence to be tested: %s\n", sequence); - - master = thread_master_create(NULL); - init_zclient(master, ZSERV_PATH); - - zebra_send_label_manager_connect(); - - return 0; -} diff --git a/tests/topotests/lm-proxy-topo1/ce1/bgpd.conf b/tests/topotests/lm-proxy-topo1/ce1/bgpd.conf deleted file mode 100644 index 33cb3d3209..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce1/bgpd.conf +++ /dev/null @@ -1,9 +0,0 @@ -debug bgp vpn label -! -router bgp 9101 - bgp router-id 10.1.1.2 - neighbor 10.1.1.1 remote-as 7777 - address-family ipv4 unicast - redistribute connected - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/ce1/zebra.conf b/tests/topotests/lm-proxy-topo1/ce1/zebra.conf deleted file mode 100644 index 7118cc6b88..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce1/zebra.conf +++ /dev/null @@ -1,11 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 66.1.0.1/32 -! -interface ce1-eth0 - ip address 10.1.1.2/30 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/ce2/bgpd.conf b/tests/topotests/lm-proxy-topo1/ce2/bgpd.conf deleted file mode 100644 index 358be342a4..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce2/bgpd.conf +++ /dev/null @@ -1,9 +0,0 @@ -debug bgp vpn label -! -router bgp 9102 - bgp router-id 10.1.2.2 - neighbor 10.1.2.1 remote-as 7777 - address-family ipv4 unicast - redistribute connected - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/ce2/zebra.conf b/tests/topotests/lm-proxy-topo1/ce2/zebra.conf deleted file mode 100644 index 8b4e3e31d7..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce2/zebra.conf +++ /dev/null @@ -1,11 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 66.1.0.2/32 -! -interface ce2-eth0 - ip address 10.1.2.2/30 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.dot b/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.dot deleted file mode 100644 index 71e6f34ba0..0000000000 --- a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.dot +++ /dev/null @@ -1,102 +0,0 @@ -## Color coding: -######################### -## Main FRR: #f08080 red -## Switches: #d0e0d0 gray -## RIP: #19e3d9 Cyan -## RIPng: #fcb314 dark yellow -## OSPFv2: #32b835 Green -## OSPFv3: #19e3d9 Cyan -## ISIS IPv4 #fcb314 dark yellow -## ISIS IPv6 #9a81ec purple -## BGP IPv4 #eee3d3 beige -## BGP IPv6 #fdff00 yellow -##### Colors (see http://www.color-hex.com/) - -graph template { - label="Test Topology - Label Manager proxy"; - - # Routers - - lm [ shape=doubleoctagon, - label="label manager", - fillcolor="#f08080", - style=filled - ]; - ce1 [ - shape=doubleoctagon, - label="ce1", - fillcolor="#f08080", - style=filled - ]; - ce2 [ - shape=doubleoctagon - label="ce2", - fillcolor="#f08080", - style=filled - ]; - pe1 [ - shape=doubleoctagon, - label="pe1", - fillcolor="#f08080", - style=filled - ]; - pe2 [ - shape=doubleoctagon - label="pe2", - fillcolor="#f08080", - style=filled - ]; - p1 [ - shape=doubleoctagon - label="p1", - fillcolor="#f08080", - style=filled - ]; - - # Switches - - s1 [ - shape=oval, - label="s1\n10.1.1.0/30", - fillcolor="#d0e0d0", - style=filled - ]; - s2 [ - shape=oval, - label="s2\n77.0.1.0/24", - fillcolor="#d0e0d0", - style=filled - ]; - s3 [ - shape=oval, - label="s3\n77.0.2.0/24", - fillcolor="#d0e0d0", - style=filled - ]; - s4 [ - shape=oval, - label="s4\n10.1.2.0/30", - fillcolor="#d0e0d0", - style=filled - ]; - - # Connections - - ce1 -- s1 [label="eth0\n.2"]; - pe1 -- s1 [label="eth0\n.1"]; - - pe1 -- s2 [label="eth1\n.1"]; - p1 -- s2 [label="eth0\n.2"]; - - pe2 -- s3 [label="eth1\n.1"]; - p1 -- s3 [label="eth1\n.2"]; - - ce2 -- s4 [label="eth0\n.2"]; - pe2 -- s4 [label="eth0\n.1"]; - - lm -- ce1; - lm -- pe1; - lm -- p1; - lm -- pe2; - lm -- ce2; -} diff --git a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.pdf b/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.pdf deleted file mode 100644 index 7f7e09ed09..0000000000 Binary files a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.pdf and /dev/null differ diff --git a/tests/topotests/lm-proxy-topo1/lm/zebra.conf b/tests/topotests/lm-proxy-topo1/lm/zebra.conf deleted file mode 100644 index 3c14f3bb3a..0000000000 --- a/tests/topotests/lm-proxy-topo1/lm/zebra.conf +++ /dev/null @@ -1,3 +0,0 @@ -debug zebra events -debug zebra packet -! diff --git a/tests/topotests/lm-proxy-topo1/p1/ldpd.conf b/tests/topotests/lm-proxy-topo1/p1/ldpd.conf deleted file mode 100644 index f86c47e6cc..0000000000 --- a/tests/topotests/lm-proxy-topo1/p1/ldpd.conf +++ /dev/null @@ -1,9 +0,0 @@ -! -mpls ldp - router-id 7.0.0.101 - address-family ipv4 - discovery transport-address 7.0.0.101 - interface p1-eth0 - interface p1-eth1 - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/p1/ospfd.conf b/tests/topotests/lm-proxy-topo1/p1/ospfd.conf deleted file mode 100644 index 721426cb15..0000000000 --- a/tests/topotests/lm-proxy-topo1/p1/ospfd.conf +++ /dev/null @@ -1,7 +0,0 @@ -! -router ospf - ospf router-id 7.0.0.101 - network 7.0.0.101/32 area 0 - network 77.0.1.0/24 area 0 - network 77.0.2.0/24 area 0 -! diff --git a/tests/topotests/lm-proxy-topo1/p1/zebra.conf b/tests/topotests/lm-proxy-topo1/p1/zebra.conf deleted file mode 100644 index bb2fc2f96e..0000000000 --- a/tests/topotests/lm-proxy-topo1/p1/zebra.conf +++ /dev/null @@ -1,12 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 7.0.0.101/32 -! -interface p1-eth0 - ip address 77.0.1.2/24 -! -interface p1-eth1 - ip address 77.0.2.2/24 -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/bgpd.conf b/tests/topotests/lm-proxy-topo1/pe1/bgpd.conf deleted file mode 100644 index b9bab30c30..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/bgpd.conf +++ /dev/null @@ -1,26 +0,0 @@ -debug bgp vpn label -! -router bgp 7777 - bgp router-id 7.0.0.1 - neighbor 7.0.0.2 remote-as 7777 - neighbor 7.0.0.2 update-source 7.0.0.1 - address-family ipv4 unicast - no neighbor 7.0.0.2 activate - exit-address-family - address-family ipv4 vpn - neighbor 7.0.0.2 activate - exit-address-family -! -router bgp 7777 vrf A - bgp router-id 10.1.1.1 - neighbor 10.1.1.2 remote-as 9101 - address-family ipv4 unicast - label vpn export auto - rd vpn export 110:1 - rt vpn both 152:100 - import vpn - export vpn - redistribute connected - redistribute ospf - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/ldpd.conf b/tests/topotests/lm-proxy-topo1/pe1/ldpd.conf deleted file mode 100644 index 1b11f19fd3..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/ldpd.conf +++ /dev/null @@ -1,8 +0,0 @@ -! -mpls ldp - router-id 7.0.0.1 - address-family ipv4 - discovery transport-address 7.0.0.1 - interface pe1-eth1 - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/ospfd.conf b/tests/topotests/lm-proxy-topo1/pe1/ospfd.conf deleted file mode 100644 index 334c2547bf..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/ospfd.conf +++ /dev/null @@ -1,6 +0,0 @@ -! -router ospf - ospf router-id 7.0.0.1 - network 77.0.0.0/16 area 0 - redistribute connected -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/zebra.conf b/tests/topotests/lm-proxy-topo1/pe1/zebra.conf deleted file mode 100644 index ec1413234b..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/zebra.conf +++ /dev/null @@ -1,14 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 7.0.0.1/32 -! -interface pe1-eth0 vrf A - ip address 10.1.1.1/30 -! -interface pe1-eth1 - ip address 77.0.1.1/24 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/bgpd.conf b/tests/topotests/lm-proxy-topo1/pe2/bgpd.conf deleted file mode 100644 index f1ec2a48e8..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/bgpd.conf +++ /dev/null @@ -1,26 +0,0 @@ -debug bgp vpn label -! -router bgp 7777 - bgp router-id 7.0.0.2 - neighbor 7.0.0.1 remote-as 7777 - neighbor 7.0.0.1 update-source 7.0.0.2 - address-family ipv4 unicast - no neighbor 7.0.0.1 activate - exit-address-family - address-family ipv4 vpn - neighbor 7.0.0.1 activate - exit-address-family -! -router bgp 7777 vrf A - bgp router-id 10.1.2.1 - neighbor 10.1.2.2 remote-as 9102 - address-family ipv4 unicast - label vpn export auto - rd vpn export 110:2 - rt vpn both 152:100 - import vpn - export vpn - redistribute connected - redistribute ospf - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/ldpd.conf b/tests/topotests/lm-proxy-topo1/pe2/ldpd.conf deleted file mode 100644 index 727364fe9f..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/ldpd.conf +++ /dev/null @@ -1,8 +0,0 @@ -! -mpls ldp - router-id 7.0.0.2 - address-family ipv4 - discovery transport-address 7.0.0.2 - interface pe2-eth1 - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/ospfd.conf b/tests/topotests/lm-proxy-topo1/pe2/ospfd.conf deleted file mode 100644 index 068de09a98..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/ospfd.conf +++ /dev/null @@ -1,6 +0,0 @@ -! -router ospf - ospf router-id 7.0.0.2 - network 77.0.0.0/16 area 0 - redistribute connected -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/zebra.conf b/tests/topotests/lm-proxy-topo1/pe2/zebra.conf deleted file mode 100644 index 0302fd8018..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/zebra.conf +++ /dev/null @@ -1,14 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 7.0.0.2/32 -! -interface pe2-eth1 - ip address 77.0.2.1/24 -! -interface pe2-eth0 vrf A - ip address 10.1.2.1/30 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/test_lm-proxy-topo1.py b/tests/topotests/lm-proxy-topo1/test_lm-proxy-topo1.py deleted file mode 100644 index 7acbb1ee62..0000000000 --- a/tests/topotests/lm-proxy-topo1/test_lm-proxy-topo1.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python - -# -# test_lm-proxy-topo1.py -# Part of NetDEF Topology Tests -# -# Copyright (c) 2018 by Volta Networks, Inc. -# -# Requirements, so the test is not skipped: -# - Linux kernel with VRF support -# - 'ip' command with VRF support (e.g. iproute2-ss180129 works) -# - FRR BGP daemon supporting label manager using instance id -# -# 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 NETDEF DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. -# - -import os -import sys -import pytest - -from functools import partial - -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib import topotest -from lib.topogen import Topogen, TopoRouter, get_topogen -from lib.topolog import logger - -# Required to instantiate the topology builder class. -from mininet.topo import Topo - -vrf_name = 'A' - -class NetworkTopo(Topo): - "Label Manager proxy topology" - - # Relationship between routers, switches, and hosts - def build(self, *_args, **_opts): - "Build function" - - tgen = get_topogen(self) - - # FRR routers - - for router in ['lm', 'ce1', 'pe1', 'p1', 'pe2', 'ce2']: - tgen.add_router(router); - - # Connections - - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['ce1'], nodeif='ce1-eth0') - switch.add_link(tgen.gears['pe1'], nodeif='pe1-eth0') - - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['pe1'], nodeif='pe1-eth1') - switch.add_link(tgen.gears['p1'], nodeif='p1-eth0') - - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['p1'], nodeif='p1-eth1') - switch.add_link(tgen.gears['pe2'], nodeif='pe2-eth1') - - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['ce2'], nodeif='ce2-eth0') - switch.add_link(tgen.gears['pe2'], nodeif='pe2-eth0') - -# Test environment handling - -def vrf_destroy(router, vrf): - router.run('ip link delete dev ' + vrf) - -def vrf_setup(router, eth_in, vrf, vrf_table): - cmds = ['ip link set dev lo up', - 'echo 10000 > /proc/sys/net/mpls/platform_labels', - 'ip link add dev ' + vrf + ' type vrf table ' + vrf_table, - 'ip link set ' + vrf + ' up', - 'ip link set ' + eth_in + ' vrf ' + vrf, - 'echo 1 > /proc/sys/net/mpls/conf/' + vrf + '/input' - ] - vrf_destroy(router, vrf) - for cmd in cmds: - logger.info('[vrf_setup] cmd: ' + cmd) - out = router.run(cmd) - if out != None and len(out) > 0: - logger.info('[vrf_setup] "{}" error: out="{}"'.format(cmd, out)) - -def setup_module(mod): - "pytest environment setup" - - tgen = Topogen(NetworkTopo, mod.__name__) - tgen.start_topology() - - router_list = tgen.routers() - - # Load router configuration - - ldp_id = 1 - bgp_id = 101 - lm_sock = '../lm/label_mgr.sock' - - for rname, router in router_list.iteritems(): - if rname == 'lm' : - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)), - '-z ' + lm_sock - ) - continue - - rtype = ''.join([i for i in rname if not i.isdigit()]) - - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)), - '-l ' + lm_sock - ) - - if router.check_capability(TopoRouter.RD_ZEBRA, '--vrfwnetns') == False: - return pytest.skip('Skipping test: no VRF support') - - if rtype == 'ce' or rtype == 'pe': - if router.check_capability(TopoRouter.RD_BGP, '--int_num') == False: - return pytest.skip('Skipping test: no BGP LM support') - router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)), - '-I %d' % bgp_id - ) - bgp_id += 1 - - if rtype == 'pe' or rtype == 'p': - router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) - ) - router.load_config( - TopoRouter.RD_LDP, - os.path.join(CWD, '{}/ldpd.conf'.format(rname)), - '-n %d' % ldp_id - ) - ldp_id += 1 - - # Prepare VRF's - - router = tgen.gears['pe1'] - out = router.run('ip -h 2>&1 | grep vrf | wc -l') - if int(out) == 0: - return pytest.skip('Skipping test: ip/iproute2 has no VRF support') - - vrf_setup(tgen.gears['pe1'], 'pe1-eth0', vrf_name, '1') - vrf_setup(tgen.gears['pe2'], 'pe2-eth0', vrf_name, '1') - - # Start routers - - tgen.start_router(tgen.gears['lm']) - for rname, router in router_list.iteritems(): - if rname != 'lm': - tgen.start_router(router) - -def teardown_module(mod): - tgen = get_topogen() - for router in ['pe1', 'pe2']: - vrf_destroy(tgen.gears[router], vrf_name) - tgen.stop_topology() - -def test_lm_proxy(): - logger.info('Test: label manager (LDP and BGP)') - tgen = get_topogen() - - # Skip if previous fatal error condition is raised - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - cmd = 'show mpls ldp binding' - - router = tgen.gears['p1'] - - def check_labels(router, cmd): - output = router.vtysh_cmd(cmd, isjson=False) - logger.info('chk_labels [' + cmd + ']: ' + output) - return output.count('\n') - - test_func = partial(check_labels, router, cmd) - result, diff = topotest.run_and_expect(test_func, 12, count=6, wait=30) - assert result, 'wrong labels' - -if __name__ == '__main__': - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) - diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 8295e461cc..03cfa572db 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -38,297 +38,67 @@ #include "zebra/zebra_router.h" #include "zebra/label_manager.h" #include "zebra/zebra_errors.h" +#include "zebra/zapi_msg.h" #define CONNECTION_DELAY 5 struct label_manager lbl_mgr; -extern struct zebra_privs_t zserv_privs; - DEFINE_MGROUP(LBL_MGR, "Label Manager"); DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk"); -/* In case this zebra daemon is not acting as label manager, - * it will be a proxy to relay messages to external label manager - * This zclient thus is to connect to it +/* define hooks for the basic API, so that it can be specialized or served + * externally */ -static struct stream *obuf; -static struct zclient *zclient; -bool lm_is_external; -static void delete_label_chunk(void *val) +DEFINE_HOOK(lm_client_connect, + (uint8_t proto, uint16_t instance, vrf_id_t vrf_id), + (proto, instance, vrf_id)); +DEFINE_HOOK(lm_client_disconnect, (uint8_t proto, uint16_t instance), + (proto, instance)); +DEFINE_HOOK(lm_get_chunk, + (struct label_manager_chunk * *lmc, uint8_t proto, + uint16_t instance, uint8_t keep, uint32_t size, uint32_t base, + vrf_id_t vrf_id), + (lmc, proto, instance, keep, size, base, vrf_id)); +DEFINE_HOOK(lm_release_chunk, + (uint8_t proto, uint16_t instance, uint32_t start, uint32_t end), + (proto, instance, start, end)); +DEFINE_HOOK(lm_cbs_inited, (), ()); + +/* define wrappers to be called in zapi_msg.c (as hooks must be called in + * source file where they were defined) + */ +void lm_client_connect_call(uint8_t proto, uint16_t instance, vrf_id_t vrf_id) +{ + hook_call(lm_client_connect, proto, instance, vrf_id); +} +void lm_get_chunk_call(struct label_manager_chunk **lmc, uint8_t proto, + uint16_t instance, uint8_t keep, uint32_t size, + uint32_t base, vrf_id_t vrf_id) +{ + hook_call(lm_get_chunk, lmc, proto, instance, keep, size, base, vrf_id); +} +void lm_release_chunk_call(uint8_t proto, uint16_t instance, uint32_t start, + uint32_t end) +{ + hook_call(lm_release_chunk, proto, instance, start, end); +} + +/* forward declarations of the static functions to be used for some hooks */ +static int label_manager_connect(uint8_t proto, uint16_t instance, + vrf_id_t vrf_id); +static int label_manager_disconnect(uint8_t proto, uint16_t instance); +static int label_manager_get_chunk(struct label_manager_chunk **lmc, + uint8_t proto, uint16_t instance, + uint8_t keep, uint32_t size, uint32_t base, + vrf_id_t vrf_id); + +void delete_label_chunk(void *val) { XFREE(MTYPE_LM_CHUNK, val); } -static int relay_response_back(void) -{ - int ret = 0; - struct stream *src, *dst; - uint16_t size = 0; - uint8_t marker; - uint8_t version; - vrf_id_t vrf_id; - uint16_t resp_cmd; - uint8_t proto; - const char *proto_str; - unsigned short instance; - struct zserv *zserv; - - /* sanity */ - if (!zclient || zclient->sock < 0) - return -1; - - /* input buffer with msg from label manager */ - src = zclient->ibuf; - - stream_reset(src); - - /* parse header */ - ret = zclient_read_header(src, zclient->sock, &size, &marker, &version, - &vrf_id, &resp_cmd); - if (ret < 0) { - if (errno != EAGAIN) - flog_err(EC_ZEBRA_LM_RESPONSE, - "Error reading Label Manager response: %s", - strerror(errno)); - return -1; - } - - /* do not relay a msg that has nothing to do with LM */ - switch (resp_cmd) { - case ZEBRA_LABEL_MANAGER_CONNECT: - case ZEBRA_LABEL_MANAGER_CONNECT_ASYNC: /* should not be seen */ - case ZEBRA_GET_LABEL_CHUNK: - case ZEBRA_RELEASE_LABEL_CHUNK: - break; - default: - zlog_debug("Not relaying '%s' response (size %d) from LM", - zserv_command_string(resp_cmd), size); - return -1; - } - - zlog_debug("Received '%s' response (size %d) from LM", - zserv_command_string(resp_cmd), size); - - if (size == 0) - return -1; - - /* Get the 'proto' field of the message */ - proto = stream_getc(src); - - /* Get the 'instance' field of the message */ - instance = stream_getw(src); - - proto_str = zebra_route_string(proto); - - /* lookup the client to relay the msg to */ - zserv = zserv_find_client(proto, instance); - if (!zserv) { - flog_err( - EC_ZEBRA_LM_NO_SUCH_CLIENT, - "Error relaying LM response: can't find client %s, instance %u", - proto_str, instance); - return -1; - } - zlog_debug("Found client to relay LM response to client %s instance %u", - proto_str, instance); - - /* copy msg into output buffer */ - dst = obuf; - stream_copy(dst, src); - - /* send response back */ - ret = writen(zserv->sock, dst->data, stream_get_endp(dst)); - if (ret <= 0) { - flog_err(EC_ZEBRA_LM_RELAY_FAILED, - "Error relaying LM response to %s instance %u: %s", - proto_str, instance, strerror(errno)); - return -1; - } - zlog_debug("Relayed LM response (%d bytes) to %s instance %u", ret, - proto_str, instance); - - return 0; -} - -static int lm_zclient_read(struct thread *t) -{ - int ret; - - zclient->t_read = NULL; - - /* read response and send it back */ - ret = relay_response_back(); - - /* re-arm read */ - thread_add_read(zclient->master, lm_zclient_read, NULL, - zclient->sock, &zclient->t_read); - return ret; -} - -static int reply_error(int cmd, struct zserv *zserv, vrf_id_t vrf_id) -{ - int ret; - struct stream *s; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, vrf_id); - - /* proto */ - stream_putc(s, zserv->proto); - /* instance */ - stream_putw(s, zserv->instance); - /* result */ - stream_putc(s, 1); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - ret = writen(zserv->sock, s->data, stream_get_endp(s)); - - stream_free(s); - return ret; -} -/** - * Receive a request to get or release a label chunk and forward it to external - * label manager. - * - * It's called from zserv in case it's not an actual label manager, but just a - * proxy. - * - * @param cmd Type of request (connect, get or release) - * @param zserv - * @return 0 on success, -1 otherwise - */ -int zread_relay_label_manager_request(int cmd, struct zserv *zserv, - struct stream *msg, vrf_id_t vrf_id) -{ - struct stream *dst; - int ret = 0; - uint8_t proto; - const char *proto_str; - unsigned short instance; - - if (zclient->sock < 0) { - flog_err(EC_ZEBRA_LM_NO_SOCKET, - "Unable to relay LM request: no socket"); - reply_error(cmd, zserv, vrf_id); - return -1; - } - - /* peek msg to get proto and instance id. This zebra, which acts as - * a proxy needs to have such values for each client in order to - * relay responses back to it. - */ - - /* Get the 'proto' field of incoming msg */ - proto = stream_getc(msg); - - /* Get the 'instance' field of incoming msg */ - instance = stream_getw(msg); - - /* stringify proto */ - proto_str = zebra_route_string(proto); - - /* check & set client proto if unset */ - if (zserv->proto && zserv->proto != proto) { - flog_warn(EC_ZEBRAING_LM_PROTO_MISMATCH, - "Client proto(%u) != msg proto(%u)", zserv->proto, - proto); - return -1; - } - - /* check & set client instance if unset */ - if (zserv->instance && zserv->instance != instance) { - flog_err(EC_ZEBRA_LM_BAD_INSTANCE, - "Client instance(%u) != msg instance(%u)", - zserv->instance, instance); - return -1; - } - - /* recall proto and instance */ - zserv->instance = instance; - zserv->proto = proto; - - /* in case there's any incoming message enqueued, read and forward it */ - if (zserv->is_synchronous) - while (ret == 0) - ret = relay_response_back(); - - /* get the msg buffer used toward the 'master' Label Manager */ - dst = zclient->obuf; - - /* copy the message */ - stream_copy(dst, msg); - - /* Send request to external label manager */ - ret = writen(zclient->sock, dst->data, stream_get_endp(dst)); - if (ret <= 0) { - flog_err(EC_ZEBRA_LM_RELAY_FAILED, - "Error relaying LM request from %s instance %u: %s", - proto_str, instance, strerror(errno)); - reply_error(cmd, zserv, vrf_id); - return -1; - } - zlog_debug("Relayed LM request (%d bytes) from %s instance %u", ret, - proto_str, instance); - - - /* Release label chunk has no response */ - if (cmd == ZEBRA_RELEASE_LABEL_CHUNK) - return 0; - - /* make sure we listen to the response */ - if (!zclient->t_read) - thread_add_read(zclient->master, lm_zclient_read, NULL, - zclient->sock, &zclient->t_read); - - return 0; -} - -static int lm_zclient_connect(struct thread *t) -{ - zclient->t_connect = NULL; - - if (zclient->sock >= 0) - return 0; - - if (zclient_socket_connect(zclient) < 0) { - flog_err(EC_ZEBRA_LM_CLIENT_CONNECTION_FAILED, - "Error connecting synchronous zclient!"); - thread_add_timer(zrouter.master, lm_zclient_connect, zclient, - CONNECTION_DELAY, &zclient->t_connect); - return -1; - } - - /* make socket non-blocking */ - (void)set_nonblocking(zclient->sock); - - return 0; -} - -/** - * Function to initialize zclient in case this is not an actual - * label manager, but just a proxy to an external one. - * - * @param lm_zserv_path Path to zserv socket of external label manager - */ -static void lm_zclient_init(char *lm_zserv_path) -{ - if (lm_zserv_path) - frr_zclient_addr(&zclient_addr, &zclient_addr_len, - lm_zserv_path); - - /* Set default values. */ - zclient = zclient_new(zrouter.master, &zclient_options_default); - zclient->privs = &zserv_privs; - zclient->sock = -1; - zclient->t_connect = NULL; - lm_zclient_connect(NULL); -} - /** * Release label chunks from a client. * @@ -339,15 +109,16 @@ static void lm_zclient_init(char *lm_zserv_path) * @param instance Instance, to identify the owner * @return Number of chunks released */ -int release_daemon_label_chunks(struct zserv *client) +int release_daemon_label_chunks(uint8_t proto, unsigned short instance) { - uint8_t proto = client->proto; - uint16_t instance = client->instance; struct listnode *node; struct label_manager_chunk *lmc; int count = 0; int ret; + zlog_debug("%s: Releasing chunks for client proto %s, instance %d", + __func__, zebra_route_string(proto), instance); + for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { if (lmc->proto == proto && lmc->instance == instance && lmc->keep == 0) { @@ -363,31 +134,153 @@ int release_daemon_label_chunks(struct zserv *client) return count; } -/** - * Init label manager (or proxy to an external one) - */ -void label_manager_init(char *lm_zserv_path) +int lm_client_disconnect_cb(struct zserv *client) { - /* this is an actual label manager */ - if (!lm_zserv_path) { - zlog_debug("Initializing internal label manager"); - lm_is_external = false; - lbl_mgr.lc_list = list_new(); - lbl_mgr.lc_list->del = delete_label_chunk; - } else { /* it's acting just as a proxy */ - zlog_debug("Initializing external label manager at %s", - lm_zserv_path); - lm_is_external = true; - lm_zclient_init(lm_zserv_path); - } + uint8_t proto = client->proto; + uint16_t instance = client->instance; - obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + hook_call(lm_client_disconnect, proto, instance); + return 0; +} - hook_register(zserv_client_close, release_daemon_label_chunks); +void lm_hooks_register(void) +{ + hook_register(lm_client_connect, label_manager_connect); + hook_register(lm_client_disconnect, label_manager_disconnect); + hook_register(lm_get_chunk, label_manager_get_chunk); + hook_register(lm_release_chunk, release_label_chunk); +} +void lm_hooks_unregister(void) +{ + hook_unregister(lm_client_connect, label_manager_connect); + hook_unregister(lm_client_disconnect, label_manager_disconnect); + hook_unregister(lm_get_chunk, label_manager_get_chunk); + hook_unregister(lm_release_chunk, release_label_chunk); } /** - * Core function, assigns label cunks + * Init label manager (or proxy to an external one) + */ +void label_manager_init(void) +{ + lbl_mgr.lc_list = list_new(); + lbl_mgr.lc_list->del = delete_label_chunk; + hook_register(zserv_client_close, lm_client_disconnect_cb); + + /* register default hooks for the label manager actions */ + lm_hooks_register(); + + /* notify any external module that we are done */ + hook_call(lm_cbs_inited); +} + +/* alloc and fill a label chunk */ +struct label_manager_chunk *create_label_chunk(uint8_t proto, + unsigned short instance, + uint8_t keep, uint32_t start, + uint32_t end) +{ + /* alloc chunk, fill it and return it */ + struct label_manager_chunk *lmc = + XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk)); + + lmc->start = start; + lmc->end = end; + lmc->proto = proto; + lmc->instance = instance; + lmc->keep = keep; + + return lmc; +} + +/* attempt to get a specific label chunk */ +static struct label_manager_chunk * +assign_specific_label_chunk(uint8_t proto, unsigned short instance, + uint8_t keep, uint32_t size, uint32_t base) +{ + struct label_manager_chunk *lmc; + struct listnode *node, *next = NULL; + struct listnode *first_node = NULL; + struct listnode *last_node = NULL; + struct listnode *insert_node = NULL; + + /* precompute last label from base and size */ + uint32_t end = base + size - 1; + + /* sanities */ + if ((base < MPLS_LABEL_UNRESERVED_MIN) + || (end > MPLS_LABEL_UNRESERVED_MAX)) { + zlog_err("Invalid LM request arguments: base: %u, size: %u", + base, size); + return NULL; + } + + /* Scan the existing chunks to see if the requested range of labels + * falls inside any of such chunks */ + for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { + + /* skip chunks for labels < base */ + if (base > lmc->end) + continue; + + /* requested range is not covered by any existing, free chunk. + * Therefore, need to insert a chunk */ + if ((end < lmc->start) && !first_node) { + insert_node = node; + break; + } + + if (!first_node) + first_node = node; + + /* if chunk is used, cannot honor request */ + if (lmc->proto != NO_PROTO) + return NULL; + + if (end < lmc->end) { + last_node = node; + break; + } + } + + /* insert chunk between existing chunks */ + if (insert_node) { + lmc = create_label_chunk(proto, instance, keep, base, end); + listnode_add_before(lbl_mgr.lc_list, insert_node, lmc); + return lmc; + } + + if (first_node) { + /* get node past the last one, if there */ + if (last_node) + last_node = listnextnode(last_node); + + /* delete node coming after the above chunk whose labels are + * included in the previous one */ + for (node = first_node; node && (node != last_node); + node = next) { + next = listnextnode(node); + list_delete_node(lbl_mgr.lc_list, node); + } + + lmc = create_label_chunk(proto, instance, keep, base, end); + if (last_node) + listnode_add_before(lbl_mgr.lc_list, last_node, lmc); + else + listnode_add(lbl_mgr.lc_list, lmc); + + return lmc; + } else { + /* create a new chunk past all the existing ones and link at + * tail */ + lmc = create_label_chunk(proto, instance, keep, base, end); + listnode_add(lbl_mgr.lc_list, lmc); + return lmc; + } +} + +/** + * Core function, assigns label chunks * * It first searches through the list to check if there's one available * (previously released). Otherwise it creates and assigns a new one @@ -395,15 +288,26 @@ void label_manager_init(char *lm_zserv_path) * @param proto Daemon protocol of client, to identify the owner * @param instance Instance, to identify the owner * @param keep If set, avoid garbage collection - * @para size Size of the label chunk - * @return Pointer to the assigned label chunk + * @param size Size of the label chunk + * @param base Desired starting label of the chunk; if MPLS_LABEL_BASE_ANY it does not apply + * @return Pointer to the assigned label chunk, or NULL if the request could not be satisfied */ struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, - uint8_t keep, uint32_t size) + uint8_t keep, uint32_t size, + uint32_t base) { struct label_manager_chunk *lmc; struct listnode *node; + uint32_t prev_end = 0; + + /* handle chunks request with a specific base label */ + if (base != MPLS_LABEL_BASE_ANY) + return assign_specific_label_chunk(proto, instance, keep, size, + base); + + /* appease scan-build, who gets confused by the use of macros */ + assert(lbl_mgr.lc_list); /* first check if there's one available */ for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { @@ -414,35 +318,44 @@ struct label_manager_chunk *assign_label_chunk(uint8_t proto, lmc->keep = keep; return lmc; } + /* check if we hadve a "hole" behind us that we can squeeze into + */ + if ((lmc->start > prev_end) + && (lmc->start - prev_end >= size)) { + lmc = create_label_chunk(proto, instance, keep, + prev_end + 1, prev_end + size); + listnode_add_before(lbl_mgr.lc_list, node, lmc); + return lmc; + } + prev_end = lmc->end; } /* otherwise create a new one */ - lmc = XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk)); + uint32_t start_free; if (list_isempty(lbl_mgr.lc_list)) - lmc->start = MPLS_LABEL_UNRESERVED_MIN; + start_free = MPLS_LABEL_UNRESERVED_MIN; else - lmc->start = ((struct label_manager_chunk *)listgetdata( + start_free = ((struct label_manager_chunk *)listgetdata( listtail(lbl_mgr.lc_list))) ->end + 1; - if (lmc->start > MPLS_LABEL_UNRESERVED_MAX - size + 1) { + + if (start_free > MPLS_LABEL_UNRESERVED_MAX - size + 1) { flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS, - "Reached max labels. Start: %u, size: %u", lmc->start, + "Reached max labels. Start: %u, size: %u", start_free, size); - XFREE(MTYPE_LM_CHUNK, lmc); return NULL; } - lmc->end = lmc->start + size - 1; - lmc->proto = proto; - lmc->instance = instance; - lmc->keep = keep; - listnode_add(lbl_mgr.lc_list, lmc); + /* create chunk and link at tail */ + lmc = create_label_chunk(proto, instance, keep, start_free, + start_free + size - 1); + listnode_add(lbl_mgr.lc_list, lmc); return lmc; } /** - * Core function, release no longer used label cunks + * Core function, release no longer used label chunks * * @param proto Daemon protocol of client, to identify the owner * @param instance Instance, to identify the owner @@ -483,9 +396,59 @@ int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, return ret; } +/* default functions to be called on hooks */ +static int label_manager_connect(uint8_t proto, uint16_t instance, + vrf_id_t vrf_id) +{ + /* + * Release previous labels of same protocol and instance. + * This is done in case it restarted from an unexpected shutdown. + */ + release_daemon_label_chunks(proto, instance); + return lm_client_connect_response(proto, instance, vrf_id, 0); +} +static int label_manager_disconnect(uint8_t proto, uint16_t instance) +{ + release_daemon_label_chunks(proto, instance); + return 0; +} +static int label_manager_get_chunk(struct label_manager_chunk **lmc, + uint8_t proto, uint16_t instance, + uint8_t keep, uint32_t size, uint32_t base, + vrf_id_t vrf_id) +{ + *lmc = assign_label_chunk(proto, instance, keep, size, base); + return lm_get_chunk_response(*lmc, proto, instance, vrf_id); +} + +/* Respond to a connect request */ +int lm_client_connect_response(uint8_t proto, uint16_t instance, + vrf_id_t vrf_id, uint8_t result) +{ + struct zserv *client = zserv_find_client(proto, instance); + if (!client) { + zlog_err("%s: could not find client for daemon %s instance %u", + __func__, zebra_route_string(proto), instance); + return 1; + } + return zsend_label_manager_connect_response(client, vrf_id, result); +} + +/* Respond to a get_chunk request */ +int lm_get_chunk_response(struct label_manager_chunk *lmc, uint8_t proto, + uint16_t instance, vrf_id_t vrf_id) +{ + struct zserv *client = zserv_find_client(proto, instance); + if (!client) { + zlog_err("%s: could not find client for daemon %s instance %u", + __func__, zebra_route_string(proto), instance); + return 1; + } + return zsend_assign_label_chunk_response(client, vrf_id, proto, + instance, lmc); +} void label_manager_close(void) { list_delete(&lbl_mgr.lc_list); - stream_free(obuf); } diff --git a/zebra/label_manager.h b/zebra/label_manager.h index 3ea89fbfc3..74e283e85e 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -28,6 +28,7 @@ #include "lib/linklist.h" #include "lib/thread.h" +#include "lib/hook.h" #include "zebra/zserv.h" @@ -57,6 +58,54 @@ struct label_manager_chunk { uint32_t end; /* Last label of the chunk */ }; +/* declare hooks for the basic API, so that it can be specialized or served + * externally. Also declare a hook when those functions have been registered, + * so that any external module wanting to replace those can react + */ + +DECLARE_HOOK(lm_client_connect, + (uint8_t proto, uint16_t instance, vrf_id_t vrf_id), + (proto, instance, vrf_id)); +DECLARE_HOOK(lm_client_disconnect, (uint8_t proto, uint16_t instance), + (proto, instance)); +DECLARE_HOOK(lm_get_chunk, + (struct label_manager_chunk * *lmc, uint8_t proto, + uint16_t instance, uint8_t keep, uint32_t size, uint32_t base, + vrf_id_t vrf_id), + (lmc, proto, instance, keep, size, base, vrf_id)); +DECLARE_HOOK(lm_release_chunk, + (uint8_t proto, uint16_t instance, uint32_t start, uint32_t end), + (proto, instance, start, end)); +DECLARE_HOOK(lm_cbs_inited, (), ()); + + +/* declare wrappers to be called in zapi_msg.c (as hooks must be called in + * source file where they were defined) + */ +void lm_client_connect_call(uint8_t proto, uint16_t instance, vrf_id_t vrf_id); +void lm_get_chunk_call(struct label_manager_chunk **lmc, uint8_t proto, + uint16_t instance, uint8_t keep, uint32_t size, + uint32_t base, vrf_id_t vrf_id); +void lm_release_chunk_call(uint8_t proto, uint16_t instance, uint32_t start, + uint32_t end); + +/* API for an external LM to return responses for requests */ +int lm_client_connect_response(uint8_t proto, uint16_t instance, + vrf_id_t vrf_id, uint8_t result); +int lm_get_chunk_response(struct label_manager_chunk *lmc, uint8_t proto, + uint16_t instance, vrf_id_t vrf_id); + +/* convenience function to allocate an lmc to be consumed by the above API */ +struct label_manager_chunk *create_label_chunk(uint8_t proto, + unsigned short instance, + uint8_t keep, uint32_t start, + uint32_t end); +void delete_label_chunk(void *val); + +/* register/unregister callbacks for hooks */ +void lm_hooks_register(void); +void lm_hooks_unregister(void); + /* * Main label manager struct * Holds a linked list of label chunks. @@ -65,17 +114,15 @@ struct label_manager { struct list *lc_list; }; -bool lm_is_external; - -int zread_relay_label_manager_request(int cmd, struct zserv *zserv, - struct stream *msg, vrf_id_t vrf_id); -void label_manager_init(char *lm_zserv_path); +void label_manager_init(void); struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, - uint8_t keep, uint32_t size); + uint8_t keep, uint32_t size, + uint32_t base); int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, uint32_t end); -int release_daemon_label_chunks(struct zserv *client); +int lm_client_disconnect_cb(struct zserv *client); +int release_daemon_label_chunks(uint8_t proto, unsigned short instance); void label_manager_close(void); #ifdef __cplusplus diff --git a/zebra/main.c b/zebra/main.c index 1947106750..84e83bc37e 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -92,7 +92,6 @@ struct option longopts[] = { {"keep_kernel", no_argument, NULL, 'k'}, {"socket", required_argument, NULL, 'z'}, {"ecmp", required_argument, NULL, 'e'}, - {"label_socket", no_argument, NULL, 'l'}, {"retain", no_argument, NULL, 'r'}, {"vrfdefaultname", required_argument, NULL, 'o'}, {"graceful_restart", required_argument, NULL, 'K'}, @@ -252,8 +251,6 @@ int main(int argc, char **argv) // int batch_mode = 0; char *zserv_path = NULL; char *vrf_default_name_configured = NULL; - /* Socket to external label manager */ - char *lblmgr_path = NULL; struct sockaddr_storage dummy; socklen_t dummylen; #if defined(HANDLE_ZAPI_FUZZING) @@ -270,7 +267,7 @@ int main(int argc, char **argv) frr_preinit(&zebra_di, argc, argv); frr_opt_add( - "baz:e:l:o:rK:" + "baz:e:o:rK:" #ifdef HAVE_NETLINK "s:n" #endif @@ -286,7 +283,6 @@ int main(int argc, char **argv) " -a, --allow_delete Allow other processes to delete zebra routes\n" " -z, --socket Set path of zebra socket\n" " -e, --ecmp Specify ECMP to use.\n" - " -l, --label_socket Socket to external label manager\n" " -r, --retain When program terminates, retain added route by zebra.\n" " -o, --vrfdefaultname Set default VRF name.\n" " -K, --graceful_restart Graceful restart at the kernel level, timer in seconds for expiration\n" @@ -341,9 +337,6 @@ int main(int argc, char **argv) exit(1); } break; - case 'l': - lblmgr_path = optarg; - break; case 'r': retain_mode = 1; break; @@ -451,7 +444,7 @@ int main(int argc, char **argv) zserv_start(zserv_path); /* Init label manager */ - label_manager_init(lblmgr_path); + label_manager_init(); /* RNH init */ zebra_rnh_init(); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 9a638f8e7f..007e554929 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -57,7 +57,6 @@ #include "zebra/rtadv.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_mroute.h" -#include "zebra/label_manager.h" #include "zebra/zebra_vxlan.h" #include "zebra/rt.h" #include "zebra/zebra_pbr.h" @@ -928,20 +927,20 @@ int zsend_pw_update(struct zserv *client, struct zebra_pw *pw) } /* Send response to a get label chunk request to client */ -static int zsend_assign_label_chunk_response(struct zserv *client, - vrf_id_t vrf_id, - struct label_manager_chunk *lmc) +int zsend_assign_label_chunk_response(struct zserv *client, vrf_id_t vrf_id, + uint8_t proto, uint16_t instance, + struct label_manager_chunk *lmc) { int ret; struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id); + /* proto */ + stream_putc(s, proto); + /* instance */ + stream_putw(s, instance); if (lmc) { - /* proto */ - stream_putc(s, lmc->proto); - /* instance */ - stream_putw(s, lmc->instance); /* keep */ stream_putc(s, lmc->keep); /* start and end labels */ @@ -958,9 +957,8 @@ static int zsend_assign_label_chunk_response(struct zserv *client, } /* Send response to a label manager connect request to client */ -static int zsend_label_manager_connect_response(struct zserv *client, - vrf_id_t vrf_id, - unsigned short result) +int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id, + unsigned short result) { int ret; struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); @@ -1896,27 +1894,16 @@ static void zread_label_manager_connect(struct zserv *client, flog_err(EC_ZEBRA_TM_WRONG_PROTO, "client %d has wrong protocol %s", client->sock, zebra_route_string(proto)); - if (client->is_synchronous) - zsend_label_manager_connect_response(client, vrf_id, 1); + zsend_label_manager_connect_response(client, vrf_id, 1); return; } - zlog_notice("client %d with vrf %u instance %u connected as %s", - client->sock, vrf_id, instance, zebra_route_string(proto)); + + /* recall proto and instance in this socket */ client->proto = proto; client->instance = instance; - /* - * Release previous labels of same protocol and instance. - * This is done in case it restarted from an unexpected shutdown. - */ - release_daemon_label_chunks(client); - - zlog_debug( - " Label Manager client connected: sock %d, proto %s, vrf %u instance %u", - client->sock, zebra_route_string(proto), vrf_id, instance); - /* send response back */ - if (client->is_synchronous) - zsend_label_manager_connect_response(client, vrf_id, 0); + /* call hook for connection using wrapper */ + lm_client_connect_call(proto, instance, vrf_id); stream_failure: return; @@ -1927,8 +1914,8 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, { struct stream *s; uint8_t keep; - uint32_t size; - struct label_manager_chunk *lmc; + uint32_t size, base; + struct label_manager_chunk *lmc = NULL; uint8_t proto; unsigned short instance; @@ -1940,8 +1927,11 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, STREAM_GETW(s, instance); STREAM_GETC(s, keep); STREAM_GETL(s, size); + STREAM_GETL(s, base); + + /* call hook to get a chunk using wrapper */ + lm_get_chunk_call(&lmc, proto, instance, keep, size, base, vrf_id); - lmc = assign_label_chunk(proto, instance, keep, size); if (!lmc) flog_err( EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK, @@ -1951,8 +1941,6 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, zlog_debug("Assigned Label Chunk %u - %u to %s instance %u", lmc->start, lmc->end, zebra_route_string(proto), instance); - /* send response back */ - zsend_assign_label_chunk_response(client, vrf_id, lmc); stream_failure: return; @@ -1974,33 +1962,23 @@ static void zread_release_label_chunk(struct zserv *client, struct stream *msg) STREAM_GETL(s, start); STREAM_GETL(s, end); - release_label_chunk(proto, instance, start, end); + /* call hook to release a chunk using wrapper */ + lm_release_chunk_call(proto, instance, start, end); stream_failure: return; } + static void zread_label_manager_request(ZAPI_HANDLER_ARGS) { - /* to avoid sending other messages like ZERBA_INTERFACE_UP */ - client->is_synchronous = hdr->command == - ZEBRA_LABEL_MANAGER_CONNECT; - - /* external label manager */ - if (lm_is_external) - zread_relay_label_manager_request(hdr->command, client, msg, - zvrf_id(zvrf)); - /* this is a label manager */ + if (hdr->command == ZEBRA_LABEL_MANAGER_CONNECT + || hdr->command == ZEBRA_LABEL_MANAGER_CONNECT_ASYNC) + zread_label_manager_connect(client, msg, zvrf_id(zvrf)); else { - if (hdr->command == ZEBRA_LABEL_MANAGER_CONNECT || - hdr->command == ZEBRA_LABEL_MANAGER_CONNECT_ASYNC) - zread_label_manager_connect(client, msg, zvrf_id(zvrf)); - else { - if (hdr->command == ZEBRA_GET_LABEL_CHUNK) - zread_get_label_chunk(client, msg, - zvrf_id(zvrf)); - else if (hdr->command == ZEBRA_RELEASE_LABEL_CHUNK) - zread_release_label_chunk(client, msg); - } + if (hdr->command == ZEBRA_GET_LABEL_CHUNK) + zread_get_label_chunk(client, msg, zvrf_id(zvrf)); + else if (hdr->command == ZEBRA_RELEASE_LABEL_CHUNK) + zread_release_label_chunk(client, msg); } } diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index d30fa2d0ef..884edfef04 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -29,6 +29,8 @@ #include "zebra/zserv.h" #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" +#include "zebra/label_manager.h" + #ifdef __cplusplus extern "C" { @@ -90,6 +92,14 @@ extern void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num); extern void zsend_capabilities_all_clients(void); +extern int zsend_assign_label_chunk_response(struct zserv *client, + vrf_id_t vrf_id, uint8_t proto, + uint16_t instance, + struct label_manager_chunk *lmc); +extern int zsend_label_manager_connect_response(struct zserv *client, + vrf_id_t vrf_id, + unsigned short result); + #ifdef __cplusplus } diff --git a/zebra/zserv.c b/zebra/zserv.c index cb9ca6578b..70b4594813 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -535,28 +535,6 @@ static int zserv_process_messages(struct thread *thread) int zserv_send_message(struct zserv *client, struct stream *msg) { - /* - * This is a somewhat poorly named variable added with Zebra's portion - * of the label manager. That component does not use the regular - * zserv/zapi_msg interface for handling its messages, as the client - * itself runs in-process. Instead it uses synchronous writes on the - * zserv client's socket directly in the zread* handlers for its - * message types. Furthermore, it cannot handle the usual messages - * Zebra sends (such as those for interface changes) and so has added - * this flag and check here as a hack to suppress all messages that it - * does not explicitly know about. - * - * In any case this needs to be cleaned up at some point. - * - * See also: - * zread_label_manager_request - * zsend_label_manager_connect_response - * zsend_assign_label_chunk_response - * ... - */ - if (client->is_synchronous) - return 0; - pthread_mutex_lock(&client->obuf_mtx); { stream_fifo_push(client->obuf_fifo, msg); @@ -710,9 +688,6 @@ static struct zserv *zserv_client_create(int sock) } client->ridinfo = vrf_bitmap_init(); - /* by default, it's not a synchronous client */ - client->is_synchronous = 0; - /* Add this client to linked list. */ listnode_add(zrouter.client_list, client); diff --git a/zebra/zserv.h b/zebra/zserv.h index 34965618f2..708ff1e226 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -98,7 +98,6 @@ struct zserv { /* client's protocol */ uint8_t proto; uint16_t instance; - uint8_t is_synchronous; /* Statistics */ uint32_t redist_v4_add_cnt;