Merge pull request #7748 from adrianomarto/bgp-listen-on-multiple-addresses

BGP daemon listen for connections on multiple addresses
This commit is contained in:
Donatas Abraitis 2021-01-13 08:57:38 +02:00 committed by GitHub
commit 6f860c4618
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 379 additions and 25 deletions

View File

@ -253,6 +253,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status)
bf_free(bm->rd_idspace); bf_free(bm->rd_idspace);
list_delete(&bm->bgp); list_delete(&bm->bgp);
list_delete(&bm->addresses);
bgp_lp_finish(); bgp_lp_finish();
@ -404,12 +405,16 @@ int main(int argc, char **argv)
int tmp_port; int tmp_port;
int bgp_port = BGP_PORT_DEFAULT; int bgp_port = BGP_PORT_DEFAULT;
char *bgp_address = NULL; struct list *addresses = list_new();
int no_fib_flag = 0; int no_fib_flag = 0;
int no_zebra_flag = 0; int no_zebra_flag = 0;
int skip_runas = 0; int skip_runas = 0;
int instance = 0; int instance = 0;
int buffer_size = BGP_SOCKET_SNDBUF_SIZE; int buffer_size = BGP_SOCKET_SNDBUF_SIZE;
char *address;
struct listnode *node;
addresses->cmp = (int (*)(void *, void *))strcmp;
frr_preinit(&bgpd_di, argc, argv); frr_preinit(&bgpd_di, argc, argv);
frr_opt_add( frr_opt_add(
@ -463,7 +468,7 @@ int main(int argc, char **argv)
break; break;
} }
case 'l': case 'l':
bgp_address = optarg; listnode_add_sort_nodup(addresses, optarg);
/* listenon implies -n */ /* listenon implies -n */
/* fallthru */ /* fallthru */
case 'n': case 'n':
@ -493,11 +498,10 @@ int main(int argc, char **argv)
memset(&bgpd_privs, 0, sizeof(bgpd_privs)); memset(&bgpd_privs, 0, sizeof(bgpd_privs));
/* BGP master init. */ /* BGP master init. */
bgp_master_init(frr_init(), buffer_size); bgp_master_init(frr_init(), buffer_size, addresses);
bm->port = bgp_port; bm->port = bgp_port;
if (bgp_port == 0) if (bgp_port == 0)
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);
bm->address = bgp_address;
if (no_fib_flag || no_zebra_flag) if (no_fib_flag || no_zebra_flag)
bgp_option_set(BGP_OPT_NO_FIB); bgp_option_set(BGP_OPT_NO_FIB);
if (no_zebra_flag) if (no_zebra_flag)
@ -513,8 +517,16 @@ int main(int argc, char **argv)
/* BGP related initialization. */ /* BGP related initialization. */
bgp_init((unsigned short)instance); bgp_init((unsigned short)instance);
snprintf(bgpd_di.startinfo, sizeof(bgpd_di.startinfo), ", bgp@%s:%d", if (list_isempty(bm->addresses)) {
(bm->address ? bm->address : "<all>"), bm->port); snprintf(bgpd_di.startinfo, sizeof(bgpd_di.startinfo),
", bgp@<all>:%d", bm->port);
} else {
for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
snprintf(bgpd_di.startinfo + strlen(bgpd_di.startinfo),
sizeof(bgpd_di.startinfo)
- strlen(bgpd_di.startinfo),
", bgp@%s:%d", address, bm->port);
}
frr_config_fork(); frr_config_fork();
/* must be called after fork() */ /* must be called after fork() */

View File

@ -121,12 +121,20 @@ extern struct zclient *zclient;
static int bgp_check_main_socket(bool create, struct bgp *bgp) static int bgp_check_main_socket(bool create, struct bgp *bgp)
{ {
static int bgp_server_main_created; static int bgp_server_main_created;
struct listnode *node;
char *address;
if (create) { if (create) {
if (bgp_server_main_created) if (bgp_server_main_created)
return 0; return 0;
if (bgp_socket(bgp, bm->port, bm->address) < 0) if (list_isempty(bm->addresses)) {
if (bgp_socket(bgp, bm->port, NULL) < 0)
return BGP_ERR_INVALID_VALUE; return BGP_ERR_INVALID_VALUE;
} else {
for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
if (bgp_socket(bgp, bm->port, address) < 0)
return BGP_ERR_INVALID_VALUE;
}
bgp_server_main_created = 1; bgp_server_main_created = 1;
return 0; return 0;
} }
@ -3288,7 +3296,8 @@ struct bgp *bgp_get_evpn(void)
int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id, int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
bool create) bool create)
{ {
int ret = 0; struct listnode *node;
char *address;
/* Create BGP server socket, if listen mode not disabled */ /* Create BGP server socket, if listen mode not disabled */
if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN)) if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
@ -3317,9 +3326,14 @@ int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
*/ */
if (vrf->vrf_id == VRF_UNKNOWN) if (vrf->vrf_id == VRF_UNKNOWN)
return 0; return 0;
ret = bgp_socket(bgp, bm->port, bm->address); if (list_isempty(bm->addresses)) {
if (ret < 0) if (bgp_socket(bgp, bm->port, NULL) < 0)
return BGP_ERR_INVALID_VALUE; return BGP_ERR_INVALID_VALUE;
} else {
for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
if (bgp_socket(bgp, bm->port, address) < 0)
return BGP_ERR_INVALID_VALUE;
}
return 0; return 0;
} else } else
return bgp_check_main_socket(create, bgp); return bgp_check_main_socket(create, bgp);
@ -7445,7 +7459,8 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json,
return buf; return buf;
} }
void bgp_master_init(struct thread_master *master, const int buffer_size) void bgp_master_init(struct thread_master *master, const int buffer_size,
struct list *addresses)
{ {
qobj_init(); qobj_init();
@ -7455,6 +7470,7 @@ void bgp_master_init(struct thread_master *master, const int buffer_size)
bm->bgp = list_new(); bm->bgp = list_new();
bm->listen_sockets = list_new(); bm->listen_sockets = list_new();
bm->port = BGP_PORT_DEFAULT; bm->port = BGP_PORT_DEFAULT;
bm->addresses = addresses;
bm->master = master; bm->master = master;
bm->start_time = bgp_clock(); bm->start_time = bgp_clock();
bm->t_rmap_update = NULL; bm->t_rmap_update = NULL;

View File

@ -125,8 +125,8 @@ struct bgp_master {
/* BGP port number. */ /* BGP port number. */
uint16_t port; uint16_t port;
/* Listener address */ /* Listener addresses */
char *address; struct list *addresses;
/* The Mac table */ /* The Mac table */
struct hash *self_mac_hash; struct hash *self_mac_hash;
@ -1872,8 +1872,8 @@ extern char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json,
extern int bgp_config_write(struct vty *); extern int bgp_config_write(struct vty *);
extern void bgp_master_init(struct thread_master *master, extern void bgp_master_init(struct thread_master *master, const int buffer_size,
const int buffer_size); struct list *addresses);
extern void bgp_init(unsigned short instance); extern void bgp_init(unsigned short instance);
extern void bgp_pthreads_run(void); extern void bgp_pthreads_run(void);

View File

@ -31,12 +31,23 @@ be specified (:ref:`common-invocation-options`).
.. option:: -l, --listenon .. option:: -l, --listenon
Specify a specific IP address for bgpd to listen on, rather than its default Specify specific IP addresses for bgpd to listen on, rather than its default
of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal
address, or to run multiple bgpd processes on one host. address, or to run multiple bgpd processes on one host. Multiple addresses
can be specified.
In the following example, bgpd is started listening for connections on the
addresses 100.0.1.2 and fd00::2:2. The options -d (runs in daemon mode) and
-f (uses specific configuration file) are also used in this example as we
are likely to run multiple bgpd instances, each one with different
configurations, when using -l option.
Note that this option implies the --no_kernel option, and no learned routes will be installed into the linux kernel. Note that this option implies the --no_kernel option, and no learned routes will be installed into the linux kernel.
.. code-block:: shell
# /usr/lib/frr/bgpd -d -f /some-folder/bgpd.conf -l 100.0.1.2 -l fd00::2:2
.. option:: -n, --no_kernel .. option:: -n, --no_kernel
Do not install learned routes into the linux kernel. This option is useful Do not install learned routes into the linux kernel. This option is useful

View File

@ -1265,7 +1265,8 @@ int main(void)
{ {
int i = 0; int i = 0;
qobj_init(); qobj_init();
bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE); bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE,
list_new());
master = bm->master; master = bm->master;
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);
bgp_attr_init(); bgp_attr_init();

View File

@ -912,7 +912,7 @@ int main(void)
qobj_init(); qobj_init();
master = thread_master_create(NULL); master = thread_master_create(NULL);
bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL); vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);

View File

@ -1086,7 +1086,7 @@ int main(void)
cmd_init(0); cmd_init(0);
bgp_vty_init(); bgp_vty_init();
master = thread_master_create("test mp attr"); master = thread_master_create("test mp attr");
bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL); vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);
bgp_attr_init(); bgp_attr_init();

View File

@ -393,7 +393,7 @@ static int global_test_init(void)
qobj_init(); qobj_init();
master = thread_master_create(NULL); master = thread_master_create(NULL);
zclient = zclient_new(master, &zclient_options_default); zclient = zclient_new(master, &zclient_options_default);
bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL); vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);

View File

@ -59,7 +59,7 @@ int main(int argc, char *argv[])
qobj_init(); qobj_init();
bgp_attr_init(); bgp_attr_init();
master = thread_master_create(NULL); master = thread_master_create(NULL);
bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL); vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);

View File

@ -1399,7 +1399,7 @@ static void bgp_startup(void)
master = thread_master_create(NULL); master = thread_master_create(NULL);
yang_init(true); yang_init(true);
nb_init(master, bgpd_yang_modules, array_size(bgpd_yang_modules), false); nb_init(master, bgpd_yang_modules, array_size(bgpd_yang_modules), false);
bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
bgp_option_set(BGP_OPT_NO_LISTEN); bgp_option_set(BGP_OPT_NO_LISTEN);
vrf_init(NULL, NULL, NULL, NULL, NULL); vrf_init(NULL, NULL, NULL, NULL, NULL);
frr_pthread_init(); frr_pthread_init();

View File

@ -0,0 +1,154 @@
{
"ipv4base": "10.0.0.0",
"ipv4mask": 24,
"ipv6base": "fd00::",
"ipv6mask": 64,
"link_ip_start": {
"ipv4": "10.0.0.0",
"v4mask": 24,
"ipv6": "fd00::",
"v6mask": 64
},
"lo_prefix": {
"ipv4": "1.0.",
"v4mask": 32,
"ipv6": "2001:DB8:F::",
"v6mask": 128
},
"routers": {
"r1": {
"links": {
"lo": {
"ipv4": "auto",
"ipv6": "auto",
"type": "loopback"
},
"r2": {
"ipv4": "auto",
"ipv6": "auto"
}
},
"bgp": {
"local_as": "1000",
"address_family": {
"ipv4": {
"unicast": {
"neighbor": {
"r2": {
"dest_link": {
"r1": {}
}
}
}
}
}
}
}
},
"r2": {
"links": {
"lo": {
"ipv4": "auto",
"ipv6": "auto",
"type": "loopback"
},
"r1": {
"ipv4": "auto",
"ipv6": "auto"
},
"r3": {
"ipv4": "auto",
"ipv6": "auto"
}
},
"bgp": {
"local_as": "2000",
"address_family": {
"ipv4": {
"unicast": {
"neighbor": {
"r1": {
"dest_link": {
"r2": {}
}
},
"r3": {
"dest_link": {
"r2": {}
}
}
}
}
}
}
}
},
"r3": {
"links": {
"lo": {
"ipv4": "auto",
"ipv6": "auto",
"type": "loopback"
},
"r2": {
"ipv4": "auto",
"ipv6": "auto"
},
"r4": {
"ipv4": "auto",
"ipv6": "auto"
}
},
"bgp": {
"local_as": "2000",
"address_family": {
"ipv4": {
"unicast": {
"neighbor": {
"r2": {
"dest_link": {
"r3": {}
}
},
"r4": {
"dest_link": {
"r3": {}
}
}
}
}
}
}
}
},
"r4": {
"links": {
"lo": {
"ipv4": "auto",
"ipv6": "auto",
"type": "loopback"
},
"r3": {
"ipv4": "auto",
"ipv6": "auto"
}
},
"bgp": {
"local_as": "3000",
"address_family": {
"ipv4": {
"unicast": {
"neighbor": {
"r3": {
"dest_link": {
"r4": {}
}
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,160 @@
#!/usr/bin/env python
#
# test_bgp_listen_on_multiple_addresses.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2021 by Boeing Defence Australia
# Adriano Marto Reis
#
# 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.
#
"""
test_bgp_listen_on_multiple_addresses.py: Test BGP daemon listening for
connections on multiple addresses.
+------+ +------+ +------+ +------+
| | | | | | | |
| r1 |--------| r2 |--------| r3 |--------| r4 |
| | | | | | | |
+------+ +------+ +------+ +------+
| | | |
| AS 1000 | AS 2000 | AS 3000 |
| | | |
+------------+--------------------------------+-------------+
"""
import os
import sys
import json
import pytest
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
from lib.topogen import Topogen, get_topogen
from lib.topojson import build_topo_from_json, build_config_from_json
from lib.common_config import start_topology
from lib.topotest import router_json_cmp, run_and_expect
from mininet.topo import Topo
from functools import partial
LISTEN_ADDRESSES = {
"r1": ["10.0.0.1"],
"r2": ["10.0.0.2", "10.0.1.1"],
"r3": ["10.0.1.2", "10.0.2.1"],
"r4": ["10.0.2.2"],
}
# Reads data from JSON File for topology and configuration creation.
jsonFile = "{}/bgp_listen_on_multiple_addresses.json".format(CWD)
try:
with open(jsonFile, "r") as topoJson:
topo = json.load(topoJson)
except IOError:
assert False, "Could not read file {}".format(jsonFile)
class TemplateTopo(Topo):
"Topology builder."
def build(self, *_args, **_opts):
"Defines the allocation and relationship between routers and switches."
tgen = get_topogen(self)
build_topo_from_json(tgen, topo)
def setup_module(mod):
"Sets up the test environment."
tgen = Topogen(TemplateTopo, mod.__name__)
# Adds extra parameters to bgpd so they listen for connections on specific
# multiple addresses.
for router_name in tgen.routers().keys():
tgen.net[router_name].daemons_options["bgpd"] = "-l " + " -l ".join(
LISTEN_ADDRESSES[router_name]
)
start_topology(tgen)
build_config_from_json(tgen, topo)
def teardown_module(_mod):
"Tears-down the test environment."
tgen = get_topogen()
tgen.stop_topology()
def test_peering():
"Checks if the routers peer-up."
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
_bgp_converge_initial("r1", "10.0.0.2")
_bgp_converge_initial("r2", "10.0.0.1")
_bgp_converge_initial("r2", "10.0.1.2")
_bgp_converge_initial("r3", "10.0.1.1")
_bgp_converge_initial("r3", "10.0.2.2")
_bgp_converge_initial("r4", "10.0.2.1")
def test_listening_address():
"""
Checks if bgpd is only listening on the specified IP addresses.
"""
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for router in tgen.routers().values():
# bgpd must not be listening on the default address.
output = router.run("netstat -nlt4 | grep 0.0.0.0:179")
assert output == "", "{}: bpgd is listening on 0.0.0.0:179".format(router.name)
# bgpd must be listening on the specified addresses.
for address in LISTEN_ADDRESSES[router.name]:
output = router.run("netstat -nlt4 | grep {}:179".format(address))
assert output != "", "{}: bpgd is not listening on {}:179".format(
router.name, address
)
def _bgp_converge_initial(router_name, peer_address, timeout=180):
"""
Waits for the BGP connection between a given router and a given peer
(specified by its IP address) to be established. If the connection is
not established within a given timeout, then an exception is raised.
"""
tgen = get_topogen()
router = tgen.routers()[router_name]
expected = {"ipv4Unicast": {"peers": {peer_address: {"state": "Established"}}}}
test_func = partial(router_json_cmp, router, "show ip bgp summary json", expected)
_, result = run_and_expect(test_func, None, count=timeout, wait=1)
assert result is None, "{}: Failed to establish connection with {}".format(
router_name, peer_address
)
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))