From 4ab467017e922b6b32565952523051862e636e4e Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Thu, 8 Apr 2021 10:28:35 +0000 Subject: [PATCH] bgpd: Support tcp-mss for bgp neighbors Problem Statement: ================= In scale setup BGP sessions start flapping. RCA: ==== In virtualized environment there are multiple places where MTU need to be set. If there are some places were MTU is not set properly then there is chances that BGP packets get fragmented, in scale setup this will lead to BGP session flap. Fix: ==== A new tcp option is provided as part of this implementation, which can be configured per neighbor and helps to set the TCP max segment size. User need to derive the path MTU between the BGP neighbors and set that value as part of tcp-mss setting. 1. CLI Configuration: [no] neighbor tcp-mss (1-65535) 2. Running config frr# show running-config router bgp 100 neighbor 198.51.100.2 tcp-mss 150 => new entry neighbor 2001:DB8::2 tcp-mss 400 => new entry 3. Show command frr# show bgp neighbors 198.51.100.2 BGP neighbor is 198.51.100.2, remote AS 100, local AS 100, internal link Hostname: frr Configured tcp-mss is 150, synced tcp-mss is 138 => new display 4. Show command json output frr# show bgp neighbors 2001:DB8::2 json { "2001:DB8::2":{ "remoteAs":100, "bgpTimerKeepAliveIntervalMsecs":60000, "bgpTcpMssConfigured":400, => new entry "bgpTcpMssSynced":388, => new entry Risk: ===== Low - This is a config driven feature and it sets the max segment size for the TCP session between BGP peers. Tests Executed: =============== Have done manual testing with three router topology. 1. Executed basic config and un config scenarios 2. Verified if the config is updated in running config during config and no config operation 3. Verified the show command output in both CLI format and JSON format. 4. Verified if TCP SYN messages carry the max segment size in their initial packets. 5. Verified the behaviour during clear bgp session. 6. done packet capture to see if the new segment size takes effect. Signed-off-by: Abhinay Ramesh --- bgpd/bgp_network.c | 9 + bgpd/bgp_vty.c | 75 ++++++++ bgpd/bgpd.c | 23 +++ bgpd/bgpd.h | 9 +- doc/user/bgp.rst | 141 ++++++++++++++ lib/sockopt.c | 36 ++++ lib/sockopt.h | 20 ++ tests/topotests/bgp_tcp_mss/__init__.py | 0 tests/topotests/bgp_tcp_mss/r1/bgpd.conf | 6 + tests/topotests/bgp_tcp_mss/r1/zebra.conf | 6 + tests/topotests/bgp_tcp_mss/r2/bgpd.conf | 6 + tests/topotests/bgp_tcp_mss/r2/zebra.conf | 6 + .../topotests/bgp_tcp_mss/test_bgp_tcp_mss.py | 178 ++++++++++++++++++ 13 files changed, 513 insertions(+), 2 deletions(-) create mode 100644 tests/topotests/bgp_tcp_mss/__init__.py create mode 100644 tests/topotests/bgp_tcp_mss/r1/bgpd.conf create mode 100644 tests/topotests/bgp_tcp_mss/r1/zebra.conf create mode 100644 tests/topotests/bgp_tcp_mss/r2/bgpd.conf create mode 100644 tests/topotests/bgp_tcp_mss/r2/zebra.conf create mode 100644 tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 8d9024e07c..d3f30b4848 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -443,6 +443,11 @@ static int bgp_accept(struct thread *thread) if (peer1) { /* Dynamic neighbor has been created, let it proceed */ peer1->fd = bgp_sock; + + /* Set the user configured MSS to TCP socket */ + if (CHECK_FLAG(peer1->flags, PEER_FLAG_TCP_MSS)) + sockopt_tcp_mss_set(bgp_sock, peer1->tcp_mss); + bgp_fsm_change_status(peer1, Active); BGP_TIMER_OFF( peer1->t_start); /* created in peer_create() */ @@ -714,6 +719,10 @@ int bgp_connect(struct peer *peer) set_nonblocking(peer->fd); + /* Set the user configured MSS to TCP socket */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_TCP_MSS)) + sockopt_tcp_mss_set(peer->fd, peer->tcp_mss); + bgp_socket_set_buffer_size(peer->fd); if (bgp_set_socket_ttl(peer, peer->fd) < 0) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 1e465d2620..f05b2ff1f8 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -22,6 +22,7 @@ #include "command.h" #include "lib/json.h" +#include "lib/sockopt.h" #include "lib_errors.h" #include "lib/zclient.h" #include "lib/printfrr.h" @@ -12597,6 +12598,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, uint8_t *msg; json_object *json_neigh = NULL; time_t epoch_tbuf; + uint32_t sync_tcp_mss; bgp = p->bgp; @@ -12858,6 +12860,15 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, p->v_delayopen * 1000); } + /* Configured and Synced tcp-mss value for peer */ + if (CHECK_FLAG(p->flags, PEER_FLAG_TCP_MSS)) { + sync_tcp_mss = sockopt_tcp_mss_get(p->fd); + json_object_int_add(json_neigh, "bgpTcpMssConfigured", + p->tcp_mss); + json_object_int_add(json_neigh, "bgpTcpMssSynced", + sync_tcp_mss); + } + if (CHECK_FLAG(p->flags, PEER_FLAG_TIMER)) { json_object_int_add(json_neigh, "bgpTimerConfiguredHoldTimeMsecs", @@ -12941,6 +12952,13 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, " Configured DelayOpenTime is %d seconds\n", p->delayopen); + + /* Configured and synced tcp-mss value for peer */ + if (CHECK_FLAG(p->flags, PEER_FLAG_TCP_MSS)) { + sync_tcp_mss = sockopt_tcp_mss_get(p->fd); + vty_out(vty, " Configured tcp-mss is %d", p->tcp_mss); + vty_out(vty, ", synced tcp-mss is %d\n", sync_tcp_mss); + } } /* Capability. */ if (p->status == Established) { @@ -16360,6 +16378,55 @@ void cli_show_bgp_global_afi_safi_ip_unicast_redistribution_list( vty_out(vty, "\n"); } +/* Neighbor update tcp-mss. */ +static int peer_tcp_mss_vty(struct vty *vty, const char *peer_str, + const char *tcp_mss_str) +{ + struct peer *peer; + uint32_t tcp_mss_val = 0; + + peer = peer_and_group_lookup_vty(vty, peer_str); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (tcp_mss_str) { + tcp_mss_val = strtoul(tcp_mss_str, NULL, 10); + peer_tcp_mss_set(peer, tcp_mss_val); + } else { + peer_tcp_mss_unset(peer); + } + + return CMD_SUCCESS; +} + +DEFUN(neighbor_tcp_mss, neighbor_tcp_mss_cmd, + "neighbor tcp-mss (1-65535)", + NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "TCP max segment size\n" + "TCP MSS value\n") +{ + int peer_index = 1; + int mss_index = 3; + + vty_out(vty, + " Warning: Reset BGP session for tcp-mss value to take effect\n"); + return peer_tcp_mss_vty(vty, argv[peer_index]->arg, + argv[mss_index]->arg); +} + +DEFUN(no_neighbor_tcp_mss, no_neighbor_tcp_mss_cmd, + "no neighbor tcp-mss [(1-65535)]", + NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "TCP max segment size\n" + "TCP MSS value\n") +{ + int peer_index = 2; + + vty_out(vty, + " Warning: Reset BGP session for tcp-mss value to take effect\n"); + return peer_tcp_mss_vty(vty, argv[peer_index]->arg, NULL); +} + static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { @@ -16809,6 +16876,10 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); } + /* TCP max segment size */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_TCP_MSS)) + vty_out(vty, " neighbor %s tcp-mss %d\n", addr, peer->tcp_mss); + /* passive */ if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) vty_out(vty, " neighbor %s passive\n", addr); @@ -19229,6 +19300,10 @@ void bgp_vty_init(void) install_element(BGP_IPV6_NODE, &af_no_route_map_vpn_imexport_cmd); install_element(BGP_IPV4_NODE, &af_no_import_vrf_route_map_cmd); install_element(BGP_IPV6_NODE, &af_no_import_vrf_route_map_cmd); + + /* tcp-mss command */ + install_element(BGP_NODE, &neighbor_tcp_mss_cmd); + install_element(BGP_NODE, &no_neighbor_tcp_mss_cmd); } #include "memory.h" diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 20bb5e5320..2c9aad5d21 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -43,6 +43,7 @@ #include "jhash.h" #include "table.h" #include "lib/json.h" +#include "lib/sockopt.h" #include "frr_pthread.h" #include "bitfield.h" @@ -1439,6 +1440,8 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->local_as = peer_src->local_as; peer_dst->port = peer_src->port; + /* copy tcp_mss value */ + peer_dst->tcp_mss = peer_src->tcp_mss; (void)peer_sort(peer_dst); peer_dst->rmap_type = peer_src->rmap_type; @@ -5116,6 +5119,26 @@ void peer_port_unset(struct peer *peer) peer->port = BGP_PORT_DEFAULT; } +/* Set the TCP-MSS value in the peer structure, + * This gets applied only after connection reset + * So this value will be used in bgp_connect. + */ +void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss) +{ + peer->tcp_mss = tcp_mss; + SET_FLAG(peer->flags, PEER_FLAG_TCP_MSS); +} + +/* Reset the TCP-MSS value in the peer structure, + * This gets applied only after connection reset + * So this value will be used in bgp_connect. + */ +void peer_tcp_mss_unset(struct peer *peer) +{ + UNSET_FLAG(peer->flags, PEER_FLAG_TCP_MSS); + peer->tcp_mss = 0; +} + /* * Helper function that is called after the name of the policy * being used by a peer has changed (AF specific). Automatically diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 88588952ba..3343b332f4 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1241,6 +1241,8 @@ struct peer { #define PEER_FLAG_GRACEFUL_RESTART (1U << 24) /* Graceful Restart */ #define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1U << 25) /* Global-Inherit */ #define PEER_FLAG_RTT_SHUTDOWN (1U << 26) /* shutdown rtt */ +#define PEER_FLAG_TIMER_DELAYOPEN (1U << 27) /* delayopen timer */ +#define PEER_FLAG_TCP_MSS (1U << 28) /* tcp-mss */ /* *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART @@ -1248,8 +1250,6 @@ struct peer { *and PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT */ -#define PEER_FLAG_TIMER_DELAYOPEN (1 << 27) /* delayopen timer */ - struct bgp_peer_gr PEER_GR_FSM[BGP_PEER_GR_MODE][BGP_PEER_GR_EVENT_CMD]; enum peer_mode peer_gr_present_state; /* Non stop forwarding afi-safi count for BGP gr feature*/ @@ -1606,6 +1606,9 @@ struct peer { bool advmap_config_change[AFI_MAX][SAFI_MAX]; bool advmap_table_change; + /* set TCP max segment size */ + uint32_t tcp_mss; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(peer); @@ -2409,4 +2412,6 @@ DECLARE_HOOK(bgp_rpki_prefix_status, void peer_nsf_stop(struct peer *peer); +void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss); +void peer_tcp_mss_unset(struct peer *peer); #endif /* _QUAGGA_BGPD_H */ diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 87710e98c6..314873640d 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -3908,6 +3908,147 @@ Example of how to set up a 6-Bone connection. log file bgpd.log ! +.. _bgp-tcp-mss: + +BGP tcp-mss support +=================== +TCP provides a mechanism for the user to specify the max segment size. +setsockopt API is used to set the max segment size for TCP session. We +can configure this as part of BGP neighbor configuration. + +This document explains how to avoid ICMP vulnerability issues by limiting +TCP max segment size when you are using MTU discovery. Using MTU discovery +on TCP paths is one method of avoiding BGP packet fragmentation. + +TCP negotiates a maximum segment size (MSS) value during session connection +establishment between two peers. The MSS value negotiated is primarily based +on the maximum transmission unit (MTU) of the interfaces to which the +communicating peers are directly connected. However, due to variations in +link MTU on the path taken by the TCP packets, some packets in the network +that are well within the MSS value might be fragmented when the packet size +exceeds the link's MTU. + +This feature is supported with TCP over IPv4 and TCP over IPv6. + +CLI Configuration: +------------------ +Below configuration can be done in router bgp mode and allows the user to +configure the tcp-mss value per neighbor. The configuration gets applied +only after hard reset is performed on that neighbor. If we configure tcp-mss +on both the neighbors then both neighbors need to be reset. + +The configuration takes effect based on below rules, so there is a configured +tcp-mss and a synced tcp-mss value per TCP session. + +By default if the configuration is not done then the TCP max segment size is +set to the Maximum Transmission unit (MTU) – (IP/IP6 header size + TCP header +size + ethernet header). For IPv4 its MTU – (20 bytes IP header + 20 bytes TCP +header + 12 bytes ethernet header) and for IPv6 its MTU – (40 bytes IPv6 header ++ 20 bytes TCP header + 12 bytes ethernet header). + +If the config is done then it reduces 12-14 bytes for the ether header and +uses it after synchronizing in TCP handshake. + +.. clicmd:: neighbor tcp-mss (1-65535) + +When tcp-mss is configured kernel reduces 12-14 bytes for ethernet header. +E.g. if tcp-mss is configured as 150 the synced value will be 138. + +Note: configured and synced value is different since TCP module will reduce +12 bytes for ethernet header. + +Running config: +--------------- + +.. code-block:: frr + + frr# show running-config + Building configuration... + + Current configuration: + ! + router bgp 100 + bgp router-id 192.0.2.1 + neighbor 198.51.100.2 remote-as 100 + neighbor 198.51.100.2 tcp-mss 150 => new entry + neighbor 2001:DB8::2 remote-as 100 + neighbor 2001:DB8::2 tcp-mss 400 => new entry + +Show command: +------------- + +.. code-block:: frr + + frr# show bgp neighbors 198.51.100.2 + BGP neighbor is 198.51.100.2, remote AS 100, local AS 100, internal link + Hostname: frr + BGP version 4, remote router ID 192.0.2.2, local router ID 192.0.2.1 + BGP state = Established, up for 02:15:28 + Last read 00:00:28, Last write 00:00:28 + Hold time is 180, keepalive interval is 60 seconds + Configured tcp-mss is 150, synced tcp-mss is 138 => new display + +.. code-block:: frr + + frr# show bgp neighbors 2001:DB8::2 + BGP neighbor is 2001:DB8::2, remote AS 100, local AS 100, internal link + Hostname: frr + BGP version 4, remote router ID 192.0.2.2, local router ID 192.0.2.1 + BGP state = Established, up for 02:16:34 + Last read 00:00:34, Last write 00:00:34 + Hold time is 180, keepalive interval is 60 seconds + Configured tcp-mss is 400, synced tcp-mss is 388 => new display + +Show command json output: +------------------------- + +.. code-block:: frr + + frr# show bgp neighbors 2001:DB8::2 json + { + "2001:DB8::2":{ + "remoteAs":100, + "localAs":100, + "nbrInternalLink":true, + "hostname":"frr", + "bgpVersion":4, + "remoteRouterId":"192.0.2.2", + "localRouterId":"192.0.2.1", + "bgpState":"Established", + "bgpTimerUpMsec":8349000, + "bgpTimerUpString":"02:19:09", + "bgpTimerUpEstablishedEpoch":1613054251, + "bgpTimerLastRead":9000, + "bgpTimerLastWrite":9000, + "bgpInUpdateElapsedTimeMsecs":8347000, + "bgpTimerHoldTimeMsecs":180000, + "bgpTimerKeepAliveIntervalMsecs":60000, + "bgpTcpMssConfigured":400, => new entry + "bgpTcpMssSynced":388, => new entry + +.. code-block:: frr + + frr# show bgp neighbors 198.51.100.2 json + { + "198.51.100.2":{ + "remoteAs":100, + "localAs":100, + "nbrInternalLink":true, + "hostname":"frr", + "bgpVersion":4, + "remoteRouterId":"192.0.2.2", + "localRouterId":"192.0.2.1", + "bgpState":"Established", + "bgpTimerUpMsec":8370000, + "bgpTimerUpString":"02:19:30", + "bgpTimerUpEstablishedEpoch":1613054251, + "bgpTimerLastRead":30000, + "bgpTimerLastWrite":30000, + "bgpInUpdateElapsedTimeMsecs":8368000, + "bgpTimerHoldTimeMsecs":180000, + "bgpTimerKeepAliveIntervalMsecs":60000, + "bgpTcpMssConfigured":150, => new entry + "bgpTcpMssSynced":138, => new entry .. include:: routeserver.rst diff --git a/lib/sockopt.c b/lib/sockopt.c index b624fe2230..98bfda5079 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -667,3 +667,39 @@ int sockopt_tcp_signature(int sock, union sockunion *su, const char *password) { return sockopt_tcp_signature_ext(sock, su, 0, password); } + +/* set TCP mss value to socket */ +int sockopt_tcp_mss_set(int sock, int tcp_maxseg) +{ + int ret = 0; + socklen_t tcp_maxseg_len = sizeof(tcp_maxseg); + + ret = setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, + tcp_maxseg_len); + if (ret != 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "%s failed: setsockopt(%d): %s", __func__, sock, + safe_strerror(errno)); + } + + return ret; +} + +/* get TCP mss value synced by socket */ +int sockopt_tcp_mss_get(int sock) +{ + int ret = 0; + int tcp_maxseg = 0; + socklen_t tcp_maxseg_len = sizeof(tcp_maxseg); + + ret = getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, + &tcp_maxseg_len); + if (ret != 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "%s failed: getsockopt(%d): %s", __func__, sock, + safe_strerror(errno)); + return 0; + } + + return tcp_maxseg; +} diff --git a/lib/sockopt.h b/lib/sockopt.h index 545abe631f..6c80841e3c 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -133,6 +133,26 @@ extern int sockopt_tcp_signature(int sock, union sockunion *su, extern int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, const char *password); +/* + * set TCP max segment size. This option allows user to configure + * max segment size for TCP session + * + * sock + * Socket to enable option on. + * + * tcp_maxseg + * value used for TCP segment size negotiation during SYN + */ +extern int sockopt_tcp_mss_set(int sock, int tcp_maxseg); + +/* + * get TCP max segment size. This option allows user to get + * the segment size for TCP session + * + * sock + * Socket to get max segement size. + */ +extern int sockopt_tcp_mss_get(int sock); #ifdef __cplusplus } #endif diff --git a/tests/topotests/bgp_tcp_mss/__init__.py b/tests/topotests/bgp_tcp_mss/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_tcp_mss/r1/bgpd.conf b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf new file mode 100644 index 0000000000..07cfe2e2f1 --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_tcp_mss/r1/zebra.conf b/tests/topotests/bgp_tcp_mss/r1/zebra.conf new file mode 100644 index 0000000000..6e9b0b4a7e --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_tcp_mss/r2/bgpd.conf b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf new file mode 100644 index 0000000000..b2d945583c --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/bgp_tcp_mss/r2/zebra.conf b/tests/topotests/bgp_tcp_mss/r2/zebra.conf new file mode 100644 index 0000000000..6c14de583b --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py new file mode 100644 index 0000000000..7500c3b3ad --- /dev/null +++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +# +# bgp_tcp_mss.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Abhinay Ramesh +# +# 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. +# + +""" +bgp_tcp_mss.py: + +Test if works the following commands: +router bgp 65000 + neighbor 192.168.255.2 tcp-mss 500 + +Need to verify if the tcp-mss value is reflected in the TCP session. +""" + +import os +import sys +import json +import time +import pytest +import functools + +# add after imports, before defining classes or functions: +pytestmark = [pytest.mark.bgpd] + +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.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + 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(TemplateTopo, 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_tcp_mss(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 0}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_conf_tcp_mss(router, as_num, neigh): + router.vtysh_cmd( + """configure terminal + router bgp {0} + neighbor {1} tcp-mss 500""".format( + as_num, neigh + ) + ) + + def _bgp_clear_session(router): + router.vtysh_cmd("clear bgp *") + + def _bgp_check_neighbor_tcp_mss(router, neigh): + output = json.loads(router.vtysh_cmd("show bgp neighbor {} json".format(neigh))) + expected = { + "{}".format(neigh): {"bgpTcpMssConfigured": 500, "bgpTcpMssSynced": 488} + } + return topotest.json_cmp(output, expected) + + logger.info("Check if neighbor sessions are up in {}".format(router1.name)) + test_func = functools.partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name) + + logger.info("BGP neighbor session is up in {}".format(router1.name)) + + logger.info( + "Configure tcp-mss 500 on {} and reset the session".format(router1.name) + ) + _bgp_conf_tcp_mss(router1, "65000", "192.168.255.2") + _bgp_clear_session(router1) + + logger.info( + "Configure tcp-mss 500 on {} and reset the session".format(router2.name) + ) + _bgp_conf_tcp_mss(router2, "65001", "192.168.255.1") + _bgp_clear_session(router2) + + logger.info( + "Check if neighbor session is up after reset in {}".format(router1.name) + ) + test_func = functools.partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence after reset in "{}"'.format( + router1.name + ) + + logger.info( + "Verify if TCP MSS value is synced with neighbor in {}".format(router1.name) + ) + test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router1, "192.168.255.2") + success, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5) + assert ( + result is None + ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router1.name) + logger.info("TCP MSS value is synced with neighbor in {}".format(router1.name)) + + logger.info( + "Verify if TCP MSS value is synced with neighbor in {}".format(router2.name) + ) + test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router2, "192.168.255.1") + success, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5) + assert ( + result is None + ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router2.name) + logger.info("TCP MSS value is synced with neighbor in {}".format(router2.name)) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) -- 2.39.5