Merge pull request #11519 from opensourcerouting/feature/turn_on_reserved_ip_ranges

Allow using IPv4 (Class E) IP ranges for daemons
This commit is contained in:
Pushpasis Sarkar 2022-07-05 20:57:37 +05:30 committed by GitHub
commit 96929d759f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 257 additions and 27 deletions

View File

@ -1606,13 +1606,9 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer, enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
struct attr *attr) struct attr *attr)
{ {
in_addr_t nexthop_h;
struct bgp *bgp = peer->bgp; struct bgp *bgp = peer->bgp;
nexthop_h = ntohl(attr->nexthop.s_addr); if (ipv4_martian(&attr->nexthop) && !bgp->allow_martian) {
if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) ||
!ipv4_unicast_valid(&attr->nexthop)) &&
!bgp->allow_martian) {
uint8_t data[7]; /* type(2) + length(1) + nhop(4) */ uint8_t data[7]; /* type(2) + length(1) + nhop(4) */
char buf[INET_ADDRSTRLEN]; char buf[INET_ADDRSTRLEN];

View File

@ -338,6 +338,12 @@ Basic Config Commands
Restrict vty connections with an access list. Restrict vty connections with an access list.
.. clicmd:: allow-reserved-ranges
Allow using IPv4 reserved (Class E) IP ranges for daemons. E.g.: setting
IPv4 addresses for interfaces or allowing reserved ranges in BGP next-hops.
Default: off.
.. _sample-config-file: .. _sample-config-file:

View File

@ -121,6 +121,11 @@ const char *cmd_version_get(void)
return host.version; return host.version;
} }
bool cmd_allow_reserved_ranges_get(void)
{
return host.allow_reserved_ranges;
}
static int root_on_exit(struct vty *vty); static int root_on_exit(struct vty *vty);
/* Standard command node structures. */ /* Standard command node structures. */
@ -454,6 +459,9 @@ static int config_write_host(struct vty *vty)
if (name && name[0] != '\0') if (name && name[0] != '\0')
vty_out(vty, "domainname %s\n", name); vty_out(vty, "domainname %s\n", name);
if (cmd_allow_reserved_ranges_get())
vty_out(vty, "allow-reserved-ranges\n");
/* The following are all configuration commands that are not sent to /* The following are all configuration commands that are not sent to
* watchfrr. For instance watchfrr is hardcoded to log to syslog so * watchfrr. For instance watchfrr is hardcoded to log to syslog so
* we would always display 'log syslog informational' in the config * we would always display 'log syslog informational' in the config
@ -2294,6 +2302,21 @@ DEFUN (no_banner_motd,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(allow_reserved_ranges, allow_reserved_ranges_cmd, "allow-reserved-ranges",
"Allow using IPv4 (Class E) reserved IP space\n")
{
host.allow_reserved_ranges = true;
return CMD_SUCCESS;
}
DEFUN(no_allow_reserved_ranges, no_allow_reserved_ranges_cmd,
"no allow-reserved-ranges",
NO_STR "Allow using IPv4 (Class E) reserved IP space\n")
{
host.allow_reserved_ranges = false;
return CMD_SUCCESS;
}
int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc) int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc)
{ {
const struct cmd_node *node; const struct cmd_node *node;
@ -2483,6 +2506,7 @@ void cmd_init(int terminal)
host.lines = -1; host.lines = -1;
cmd_banner_motd_line(FRR_DEFAULT_MOTD); cmd_banner_motd_line(FRR_DEFAULT_MOTD);
host.motdfile = NULL; host.motdfile = NULL;
host.allow_reserved_ranges = false;
/* Install top nodes. */ /* Install top nodes. */
install_node(&view_node); install_node(&view_node);
@ -2552,6 +2576,8 @@ void cmd_init(int terminal)
install_element(CONFIG_NODE, &no_banner_motd_cmd); install_element(CONFIG_NODE, &no_banner_motd_cmd);
install_element(CONFIG_NODE, &service_terminal_length_cmd); install_element(CONFIG_NODE, &service_terminal_length_cmd);
install_element(CONFIG_NODE, &no_service_terminal_length_cmd); install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
install_element(CONFIG_NODE, &allow_reserved_ranges_cmd);
install_element(CONFIG_NODE, &no_allow_reserved_ranges_cmd);
log_cmd_init(); log_cmd_init();
vrf_install_commands(); vrf_install_commands();

View File

@ -84,6 +84,9 @@ struct host {
/* Banner configuration. */ /* Banner configuration. */
char *motd; char *motd;
char *motdfile; char *motdfile;
/* Allow using IPv4 (Class E) reserved IP space */
bool allow_reserved_ranges;
}; };
/* List of CLI nodes. Please remember to update the name array in command.c. */ /* List of CLI nodes. Please remember to update the name array in command.c. */
@ -614,6 +617,7 @@ extern const char *cmd_domainname_get(void);
extern const char *cmd_system_get(void); extern const char *cmd_system_get(void);
extern const char *cmd_release_get(void); extern const char *cmd_release_get(void);
extern const char *cmd_version_get(void); extern const char *cmd_version_get(void);
extern bool cmd_allow_reserved_ranges_get(void);
/* NOT safe for general use; call this only if DEV_BUILD! */ /* NOT safe for general use; call this only if DEV_BUILD! */
extern void grammar_sandbox_init(void); extern void grammar_sandbox_init(void);

View File

@ -21,6 +21,7 @@
#include <zebra.h> #include <zebra.h>
#include "command.h"
#include "prefix.h" #include "prefix.h"
#include "ipaddr.h" #include "ipaddr.h"
#include "vty.h" #include "vty.h"
@ -1386,6 +1387,23 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len)
return buf; return buf;
} }
bool ipv4_unicast_valid(const struct in_addr *addr)
{
in_addr_t ip = ntohl(addr->s_addr);
if (IPV4_CLASS_D(ip))
return false;
if (IPV4_CLASS_E(ip)) {
if (cmd_allow_reserved_ranges_get())
return true;
else
return false;
}
return true;
}
printfrr_ext_autoreg_p("EA", printfrr_ea); printfrr_ext_autoreg_p("EA", printfrr_ea);
static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr) const void *ptr)

View File

@ -382,6 +382,8 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
#define IPV4_NET0(a) ((((uint32_t)(a)) & 0xff000000) == 0x00000000) #define IPV4_NET0(a) ((((uint32_t)(a)) & 0xff000000) == 0x00000000)
#define IPV4_NET127(a) ((((uint32_t)(a)) & 0xff000000) == 0x7f000000) #define IPV4_NET127(a) ((((uint32_t)(a)) & 0xff000000) == 0x7f000000)
#define IPV4_LINKLOCAL(a) ((((uint32_t)(a)) & 0xffff0000) == 0xa9fe0000) #define IPV4_LINKLOCAL(a) ((((uint32_t)(a)) & 0xffff0000) == 0xa9fe0000)
#define IPV4_CLASS_D(a) ((((uint32_t)(a)) & 0xf0000000) == 0xe0000000)
#define IPV4_CLASS_E(a) ((((uint32_t)(a)) & 0xf0000000) == 0xf0000000)
#define IPV4_CLASS_DE(a) ((((uint32_t)(a)) & 0xe0000000) == 0xe0000000) #define IPV4_CLASS_DE(a) ((((uint32_t)(a)) & 0xe0000000) == 0xe0000000)
#define IPV4_MC_LINKLOCAL(a) ((((uint32_t)(a)) & 0xffffff00) == 0xe0000000) #define IPV4_MC_LINKLOCAL(a) ((((uint32_t)(a)) & 0xffffff00) == 0xe0000000)
@ -507,17 +509,7 @@ extern int str_to_esi(const char *str, esi_t *esi);
extern char *esi_to_str(const esi_t *esi, char *buf, int size); extern char *esi_to_str(const esi_t *esi, char *buf, int size);
extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len); extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len);
extern void prefix_evpn_hexdump(const struct prefix_evpn *p); extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
extern bool ipv4_unicast_valid(const struct in_addr *addr);
static inline bool ipv4_unicast_valid(const struct in_addr *addr)
{
in_addr_t ip = ntohl(addr->s_addr);
if (IPV4_CLASS_DE(ip))
return false;
return true;
}
static inline int ipv6_martian(const struct in6_addr *addr) static inline int ipv6_martian(const struct in6_addr *addr)
{ {
@ -534,14 +526,14 @@ static inline int ipv6_martian(const struct in6_addr *addr)
extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p); extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p);
/* NOTE: This routine expects the address argument in network byte order. */ /* NOTE: This routine expects the address argument in network byte order. */
static inline int ipv4_martian(const struct in_addr *addr) static inline bool ipv4_martian(const struct in_addr *addr)
{ {
in_addr_t ip = ntohl(addr->s_addr); in_addr_t ip = ntohl(addr->s_addr);
if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) { if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) {
return 1; return true;
} }
return 0; return false;
} }
static inline bool is_default_prefix4(const struct prefix_ipv4 *p) static inline bool is_default_prefix4(const struct prefix_ipv4 *p)

View File

@ -0,0 +1,12 @@
!
allow-reserved-ranges
!
router bgp 65001
no bgp ebgp-requires-policy
neighbor 240.0.0.2 remote-as external
neighbor 240.0.0.2 timers 1 3
neighbor 240.0.0.2 timers connect 1
address-family ipv4
redistribute connected
exit-address-family
!

View File

@ -0,0 +1,11 @@
!
allow-reserved-ranges
!
interface lo
ip address 172.16.255.1/32
!
interface r1-eth0
ip address 240.0.0.1/24
!
ip forwarding
!

View File

@ -0,0 +1,9 @@
!
allow-reserved-ranges
!
router bgp 65002
no bgp ebgp-requires-policy
neighbor 240.0.0.1 remote-as external
neighbor 240.0.0.1 timers 1 3
neighbor 240.0.0.1 timers connect 1
!

View File

@ -0,0 +1,8 @@
!
allow-reserved-ranges
!
interface r2-eth0
ip address 240.0.0.2/24
!
ip forwarding
!

View File

@ -0,0 +1,127 @@
#!/usr/bin/env python
#
# bgp_ipv4_class_e_peer.py
#
# Copyright (c) 2022 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
# 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.
#
"""
Check if the peering works by using IPv4 Class E IP ranges, and if
we don't treat next-hop as martian in such a case.
"""
import os
import sys
import json
import pytest
import functools
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.common_config import step
pytestmark = [pytest.mark.bgpd]
def build_topo(tgen):
for routern in range(1, 3):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for i, (rname, router) in enumerate(router_list.items(), 1):
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
router.load_config(
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_ipv4_class_e_peer():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
router = tgen.gears["r2"]
def _bgp_converge():
output = json.loads(router.vtysh_cmd("show ip bgp neighbor 240.0.0.1 json"))
expected = {
"240.0.0.1": {
"bgpState": "Established",
"addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
}
}
return topotest.json_cmp(output, expected)
def _bgp_next_hop_ipv4_class_e():
output = json.loads(
router.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")
)
expected = {
"paths": [
{
"valid": True,
"nexthops": [
{
"ip": "240.0.0.1",
"accessible": True,
}
],
}
]
}
return topotest.json_cmp(output, expected)
step("Initial BGP converge")
test_func = functools.partial(_bgp_converge)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed to see BGP convergence on R2"
step("Check if IPv4 BGP peering works with Class E IP ranges")
test_func = functools.partial(_bgp_next_hop_ipv4_class_e)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed to see 172.16.255.1/32 via 240.0.0.1 on R2"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View File

@ -3140,6 +3140,20 @@ DEFUN(vtysh_debug_uid_backtrace,
return err; return err;
} }
DEFUNSH(VTYSH_ALL, vtysh_allow_reserved_ranges, vtysh_allow_reserved_ranges_cmd,
"allow-reserved-ranges",
"Allow using IPv4 (Class E) reserved IP space\n")
{
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_ALL, no_vtysh_allow_reserved_ranges,
no_vtysh_allow_reserved_ranges_cmd, "no allow-reserved-ranges",
NO_STR "Allow using IPv4 (Class E) reserved IP space\n")
{
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_ALL, vtysh_service_password_encrypt, DEFUNSH(VTYSH_ALL, vtysh_service_password_encrypt,
vtysh_service_password_encrypt_cmd, "service password-encryption", vtysh_service_password_encrypt_cmd, "service password-encryption",
"Set up miscellaneous service\n" "Set up miscellaneous service\n"
@ -4902,6 +4916,9 @@ void vtysh_init_vty(void)
install_element(CONFIG_NODE, &vtysh_service_password_encrypt_cmd); install_element(CONFIG_NODE, &vtysh_service_password_encrypt_cmd);
install_element(CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd); install_element(CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd);
install_element(CONFIG_NODE, &vtysh_allow_reserved_ranges_cmd);
install_element(CONFIG_NODE, &no_vtysh_allow_reserved_ranges_cmd);
install_element(CONFIG_NODE, &vtysh_password_cmd); install_element(CONFIG_NODE, &vtysh_password_cmd);
install_element(CONFIG_NODE, &no_vtysh_password_cmd); install_element(CONFIG_NODE, &no_vtysh_password_cmd);
install_element(CONFIG_NODE, &vtysh_enable_password_cmd); install_element(CONFIG_NODE, &vtysh_enable_password_cmd);

View File

@ -478,14 +478,18 @@ void vtysh_config_parse_line(void *arg, const char *line)
else if (strncmp(line, "rpki", strlen("rpki")) == 0) else if (strncmp(line, "rpki", strlen("rpki")) == 0)
config = config_get(RPKI_NODE, line); config = config_get(RPKI_NODE, line);
else { else {
if (strncmp(line, "log", strlen("log")) == 0 if (strncmp(line, "log", strlen("log")) == 0 ||
|| strncmp(line, "hostname", strlen("hostname")) == 0 strncmp(line, "hostname", strlen("hostname")) ==
|| strncmp(line, "domainname", strlen("domainname")) == 0 0 ||
|| strncmp(line, "frr", strlen("frr")) == 0 strncmp(line, "domainname", strlen("domainname")) ==
|| strncmp(line, "agentx", strlen("agentx")) == 0 0 ||
|| strncmp(line, "no log", strlen("no log")) == 0 strncmp(line, "frr", strlen("frr")) == 0 ||
|| strncmp(line, "no ip prefix-list", strlen("no ip prefix-list")) == 0 strncmp(line, "agentx", strlen("agentx")) == 0 ||
|| strncmp(line, "no ipv6 prefix-list", strlen("no ipv6 prefix-list")) == 0) strncmp(line, "no log", strlen("no log")) == 0 ||
strncmp(line, "no ip prefix-list",
strlen("no ip prefix-list")) == 0 ||
strncmp(line, "no ipv6 prefix-list",
strlen("no ipv6 prefix-list")) == 0)
config_add_line_uniq(config_top, line); config_add_line_uniq(config_top, line);
else else
config_add_line(config_top, line); config_add_line(config_top, line);