1bd03f10aSAlexander V. Chernikovimport pytest 2bd03f10aSAlexander V. Chernikovfrom atf_python.sys.net.rtsock import RtConst 3bd03f10aSAlexander V. Chernikovfrom atf_python.sys.net.rtsock import Rtsock 4bd03f10aSAlexander V. Chernikovfrom atf_python.sys.net.rtsock import RtsockRtMessage 5bd03f10aSAlexander V. Chernikovfrom atf_python.sys.net.tools import ToolsHelper 6bd03f10aSAlexander V. Chernikovfrom atf_python.sys.net.vnet import SingleVnetTestTemplate 7bd03f10aSAlexander V. Chernikov 8bd03f10aSAlexander V. Chernikov 9bd03f10aSAlexander V. Chernikovclass TestRtmMultipath(SingleVnetTestTemplate): 10bd03f10aSAlexander V. Chernikov def setup_method(self, method): 11bd03f10aSAlexander V. Chernikov method_name = method.__name__ 12bd03f10aSAlexander V. Chernikov if "multipath4" in method_name: 13bd03f10aSAlexander V. Chernikov self.IPV4_PREFIXES = ["192.0.2.1/24"] 14bd03f10aSAlexander V. Chernikov self.PREFIX = "128.66.0.0/24" 15bd03f10aSAlexander V. Chernikov elif "multipath6" in method_name: 16bd03f10aSAlexander V. Chernikov self.IPV6_PREFIXES = ["2001:db8::1/64"] 17bd03f10aSAlexander V. Chernikov self.PREFIX = "2001:db8:0:ddbb::/64" 18bd03f10aSAlexander V. Chernikov super().setup_method(method) 19bd03f10aSAlexander V. Chernikov self.rtsock = Rtsock() 20bd03f10aSAlexander V. Chernikov 21bd03f10aSAlexander V. Chernikov def get_prefix_routes(self): 22bd03f10aSAlexander V. Chernikov family = "inet6" if ":" in self.PREFIX else "inet" 23bd03f10aSAlexander V. Chernikov routes = ToolsHelper.get_routes(family) 24bd03f10aSAlexander V. Chernikov return [r for r in routes if r["destination"] == self.PREFIX] 25bd03f10aSAlexander V. Chernikov 26bd03f10aSAlexander V. Chernikov @pytest.mark.parametrize( 27bd03f10aSAlexander V. Chernikov "gws", 28bd03f10aSAlexander V. Chernikov [ 29bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=3"], id="transition_multi"), 30bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=3", "-.10=2"], id="transition_single1"), 31bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=3", "-.5=3"], id="transition_single2"), 32bd03f10aSAlexander V. Chernikov pytest.param( 33bd03f10aSAlexander V. Chernikov ["+.10", "+.11", "+.50", "+.13", "+.145", "+.72"], id="correctness1" 34bd03f10aSAlexander V. Chernikov ), 35bd03f10aSAlexander V. Chernikov pytest.param( 36bd03f10aSAlexander V. Chernikov ["+.10", "+.11", "+.50", "-.50", "+.145", "+.72"], id="correctness2" 37bd03f10aSAlexander V. Chernikov ), 38bd03f10aSAlexander V. Chernikov pytest.param(["+.10=1", "+.5=2"], id="weight1"), 39bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=7"], id="weight2"), 40bd03f10aSAlexander V. Chernikov pytest.param(["+.10=13", "+.5=21"], id="weight3_max"), 41bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=3", "~.5=4"], id="change_new_weight1"), 42bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=3", "~.10=3"], id="change_new_weight2"), 43bd03f10aSAlexander V. Chernikov pytest.param( 44bd03f10aSAlexander V. Chernikov ["+.10=2", "+.5=3", "+.7=4", "~.10=3"], id="change_new_weight3" 45bd03f10aSAlexander V. Chernikov ), 46bd03f10aSAlexander V. Chernikov pytest.param(["+.10=2", "+.5=3", "~.5=3"], id="change_same_weight1"), 47bd03f10aSAlexander V. Chernikov pytest.param( 48bd03f10aSAlexander V. Chernikov ["+.10=2", "+.5=3", "+.7=4", "~.5=3"], id="change_same_weight2" 49bd03f10aSAlexander V. Chernikov ), 50bd03f10aSAlexander V. Chernikov ], 51bd03f10aSAlexander V. Chernikov ) 52bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 53bd03f10aSAlexander V. Chernikov def test_rtm_multipath4(self, gws): 54bd03f10aSAlexander V. Chernikov """Tests RTM_ADD with IPv4 dest transitioning to multipath""" 55bd03f10aSAlexander V. Chernikov self._test_rtm_multipath(gws, "192.0.2") 56bd03f10aSAlexander V. Chernikov 57bd03f10aSAlexander V. Chernikov @pytest.mark.parametrize( 58bd03f10aSAlexander V. Chernikov "gws", 59bd03f10aSAlexander V. Chernikov [ 60bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=3"], id="transition_multi"), 61bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=3", "-:10=2"], id="transition_single1"), 62bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=3", "-:5=3"], id="transition_single2"), 63bd03f10aSAlexander V. Chernikov pytest.param( 64bd03f10aSAlexander V. Chernikov ["+:10", "+:11", "+:50", "+:13", "+:145", "+:72"], id="correctness1" 65bd03f10aSAlexander V. Chernikov ), 66bd03f10aSAlexander V. Chernikov pytest.param( 67bd03f10aSAlexander V. Chernikov ["+:10", "+:11", "+:50", "-:50", "+:145", "+:72"], id="correctness2" 68bd03f10aSAlexander V. Chernikov ), 69bd03f10aSAlexander V. Chernikov pytest.param(["+:10=1", "+:5=2"], id="weight1"), 70bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=7"], id="weight2"), 71bd03f10aSAlexander V. Chernikov pytest.param(["+:10=13", "+:5=21"], id="weight3_max"), 72bd03f10aSAlexander V. Chernikov pytest.param(["+:10=13", "+:5=21"], id="weight3_max"), 73bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=3", "~:5=4"], id="change_new_weight1"), 74bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=3", "~:10=3"], id="change_new_weight2"), 75bd03f10aSAlexander V. Chernikov pytest.param( 76bd03f10aSAlexander V. Chernikov ["+:10=2", "+:5=3", "+:7=4", "~:10=3"], id="change_new_weight3" 77bd03f10aSAlexander V. Chernikov ), 78bd03f10aSAlexander V. Chernikov pytest.param(["+:10=2", "+:5=3", "~:5=3"], id="change_same_weight1"), 79bd03f10aSAlexander V. Chernikov pytest.param( 80bd03f10aSAlexander V. Chernikov ["+:10=2", "+:5=3", "+:7=4", "~:5=3"], id="change_same_weight2" 81bd03f10aSAlexander V. Chernikov ), 82bd03f10aSAlexander V. Chernikov ], 83bd03f10aSAlexander V. Chernikov ) 84bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 85bd03f10aSAlexander V. Chernikov def test_rtm_multipath6(self, gws): 86bd03f10aSAlexander V. Chernikov """Tests RTM_ADD with IPv6 dest transitioning to multipath""" 87bd03f10aSAlexander V. Chernikov self._test_rtm_multipath(gws, "2001:db8:") 88bd03f10aSAlexander V. Chernikov 89bd03f10aSAlexander V. Chernikov def _test_rtm_multipath(self, gws, gw_prefix: str): 90bd03f10aSAlexander V. Chernikov desired_map = {} 91bd03f10aSAlexander V. Chernikov for gw_act in gws: 92bd03f10aSAlexander V. Chernikov # GW format: <+-~>GW[=weight] 93bd03f10aSAlexander V. Chernikov if "=" in gw_act: 94bd03f10aSAlexander V. Chernikov arr = gw_act[1:].split("=") 95bd03f10aSAlexander V. Chernikov weight = int(arr[1]) 96bd03f10aSAlexander V. Chernikov gw = gw_prefix + arr[0] 97bd03f10aSAlexander V. Chernikov else: 98bd03f10aSAlexander V. Chernikov weight = None 99bd03f10aSAlexander V. Chernikov gw = gw_prefix + gw_act[1:] 100bd03f10aSAlexander V. Chernikov if gw_act[0] == "+": 101bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_add(self.PREFIX, gw) 102bd03f10aSAlexander V. Chernikov desired_map[gw] = self.rtsock.get_weight(weight) 103bd03f10aSAlexander V. Chernikov elif gw_act[0] == "-": 104bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_del(self.PREFIX, gw) 105bd03f10aSAlexander V. Chernikov del desired_map[gw] 106bd03f10aSAlexander V. Chernikov else: 107bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_change(self.PREFIX, gw) 108bd03f10aSAlexander V. Chernikov desired_map[gw] = self.rtsock.get_weight(weight) 109bd03f10aSAlexander V. Chernikov 110bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 111bd03f10aSAlexander V. Chernikov if weight: 112bd03f10aSAlexander V. Chernikov msg.rtm_inits |= RtConst.RTV_WEIGHT 113bd03f10aSAlexander V. Chernikov msg.rtm_rmx.rmx_weight = weight 114bd03f10aSAlexander V. Chernikov # Prepare SAs to check for 115bd03f10aSAlexander V. Chernikov desired_sa = { 116bd03f10aSAlexander V. Chernikov RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST), 117bd03f10aSAlexander V. Chernikov RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK), 118bd03f10aSAlexander V. Chernikov RtConst.RTA_GATEWAY: msg.get_sa(RtConst.RTA_GATEWAY), 119bd03f10aSAlexander V. Chernikov } 120bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 121bd03f10aSAlexander V. Chernikov 122bd03f10aSAlexander V. Chernikov data = self.rtsock.read_data(msg.rtm_seq) 123bd03f10aSAlexander V. Chernikov msg_in = RtsockRtMessage.from_bytes(data) 124bd03f10aSAlexander V. Chernikov msg_in.print_in_message() 125bd03f10aSAlexander V. Chernikov msg_in.verify(msg.rtm_type, desired_sa) 126bd03f10aSAlexander V. Chernikov assert msg_in.rtm_rmx.rmx_weight == self.rtsock.get_weight(weight) 127bd03f10aSAlexander V. Chernikov 128bd03f10aSAlexander V. Chernikov routes = self.get_prefix_routes() 129bd03f10aSAlexander V. Chernikov derived_map = {r["gateway"]: r["weight"] for r in routes} 130bd03f10aSAlexander V. Chernikov assert derived_map == desired_map 131bd03f10aSAlexander V. Chernikov 132bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 133bd03f10aSAlexander V. Chernikov def test_rtm_multipath4_add_same_eexist(self): 134bd03f10aSAlexander V. Chernikov """Tests adding same IPv4 gw to the multipath group (EEXIST)""" 135bd03f10aSAlexander V. Chernikov gws = ["192.0.2.10", "192.0.2.11", "192.0.2.11"] 136bd03f10aSAlexander V. Chernikov self._test_rtm_multipath_add_same_eexist(gws) 137bd03f10aSAlexander V. Chernikov 138bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 139bd03f10aSAlexander V. Chernikov def test_rtm_multipath6_add_same_eexist(self): 140bd03f10aSAlexander V. Chernikov """Tests adding same IPv4 gw to the multipath group (EEXIST)""" 141bd03f10aSAlexander V. Chernikov gws = ["2001:db8::10", "2001:db8::11", "2001:db8::11"] 142bd03f10aSAlexander V. Chernikov self._test_rtm_multipath_add_same_eexist(gws) 143bd03f10aSAlexander V. Chernikov 144bd03f10aSAlexander V. Chernikov def _test_rtm_multipath_add_same_eexist(self, gws): 145bd03f10aSAlexander V. Chernikov for idx, gw in enumerate(gws): 146bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_add(self.PREFIX, gw) 147bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 148bd03f10aSAlexander V. Chernikov try: 149bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 150bd03f10aSAlexander V. Chernikov except FileExistsError as e: 151bd03f10aSAlexander V. Chernikov if idx != 2: 152bd03f10aSAlexander V. Chernikov raise 153bd03f10aSAlexander V. Chernikov print("Succcessfully raised {}".format(e)) 154bd03f10aSAlexander V. Chernikov 155bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 156bd03f10aSAlexander V. Chernikov def test_rtm_multipath4_del_unknown_esrch(self): 157bd03f10aSAlexander V. Chernikov """Tests deleting non-existing dest from the multipath group (ESRCH)""" 158bd03f10aSAlexander V. Chernikov gws = ["192.0.2.10", "192.0.2.11"] 159bd03f10aSAlexander V. Chernikov self._test_rtm_multipath_del_unknown_esrch(gws, "192.0.2.7") 160bd03f10aSAlexander V. Chernikov 161bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 162bd03f10aSAlexander V. Chernikov def test_rtm_multipath6_del_unknown_esrch(self): 163bd03f10aSAlexander V. Chernikov """Tests deleting non-existing dest from the multipath group (ESRCH)""" 164bd03f10aSAlexander V. Chernikov gws = ["2001:db8::10", "2001:db8::11"] 165bd03f10aSAlexander V. Chernikov self._test_rtm_multipath_del_unknown_esrch(gws, "2001:db8::7") 166bd03f10aSAlexander V. Chernikov 167bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 168bd03f10aSAlexander V. Chernikov def _test_rtm_multipath_del_unknown_esrch(self, gws, target_gw): 169bd03f10aSAlexander V. Chernikov for gw in gws: 170bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_add(self.PREFIX, gw) 171bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 172bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 173bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_del(self.PREFIX, target_gw) 174bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 175bd03f10aSAlexander V. Chernikov try: 176bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 177bd03f10aSAlexander V. Chernikov except ProcessLookupError as e: 178bd03f10aSAlexander V. Chernikov print("Succcessfully raised {}".format(e)) 179bd03f10aSAlexander V. Chernikov 180bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 181bd03f10aSAlexander V. Chernikov def test_rtm_multipath4_change_unknown_esrch(self): 182bd03f10aSAlexander V. Chernikov """Tests changing non-existing dest in the multipath group (ESRCH)""" 183bd03f10aSAlexander V. Chernikov gws = ["192.0.2.10", "192.0.2.11"] 184bd03f10aSAlexander V. Chernikov self._test_rtm_multipath_change_unknown_esrch(gws, "192.0.2.7") 185bd03f10aSAlexander V. Chernikov 186bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 187bd03f10aSAlexander V. Chernikov def test_rtm_multipath6_change_unknown_esrch(self): 188bd03f10aSAlexander V. Chernikov """Tests changing non-existing dest in the multipath group (ESRCH)""" 189bd03f10aSAlexander V. Chernikov gws = ["2001:db8::10", "2001:db8::11"] 190bd03f10aSAlexander V. Chernikov self._test_rtm_multipath_change_unknown_esrch(gws, "2001:db8::7") 191bd03f10aSAlexander V. Chernikov 192bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 193bd03f10aSAlexander V. Chernikov def _test_rtm_multipath_change_unknown_esrch(self, gws, target_gw): 194bd03f10aSAlexander V. Chernikov for gw in gws: 195bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_add(self.PREFIX, gw) 196bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 197bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 198bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_change(self.PREFIX, target_gw) 199bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 200bd03f10aSAlexander V. Chernikov try: 201bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 202bd03f10aSAlexander V. Chernikov except ProcessLookupError as e: 203bd03f10aSAlexander V. Chernikov print("Succcessfully raised {}".format(e)) 204bd03f10aSAlexander V. Chernikov 205bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 206bd03f10aSAlexander V. Chernikov def test_rtm_multipath4_add_zero_weight(self): 207bd03f10aSAlexander V. Chernikov """Tests RTM_ADD with dest transitioning to multipath""" 208bd03f10aSAlexander V. Chernikov 209bd03f10aSAlexander V. Chernikov desired_map = {} 210bd03f10aSAlexander V. Chernikov for gw in ["192.0.2.10", "192.0.2.11", "192.0.2.13"]: 211bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_add(self.PREFIX, gw) 212bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 213bd03f10aSAlexander V. Chernikov msg.rtm_rmx.rmx_weight = 0 214bd03f10aSAlexander V. Chernikov msg.rtm_inits |= RtConst.RTV_WEIGHT 215bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 216bd03f10aSAlexander V. Chernikov desired_map[gw] = self.rtsock.get_weight(0) 217bd03f10aSAlexander V. Chernikov 218bd03f10aSAlexander V. Chernikov routes = self.get_prefix_routes() 219bd03f10aSAlexander V. Chernikov derived_map = {r["gateway"]: r["weight"] for r in routes} 220bd03f10aSAlexander V. Chernikov assert derived_map == desired_map 221bd03f10aSAlexander V. Chernikov 222bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 223bd03f10aSAlexander V. Chernikov def test_rtm_multipath4_getroute(self): 224bd03f10aSAlexander V. Chernikov """Tests RTM_GET with exact prefix lookup on the multipath group""" 225bd03f10aSAlexander V. Chernikov gws = ["192.0.2.10", "192.0.2.11", "192.0.2.13"] 226bd03f10aSAlexander V. Chernikov return self._test_rtm_multipath_getroute(gws) 227bd03f10aSAlexander V. Chernikov 228bd03f10aSAlexander V. Chernikov @pytest.mark.require_user("root") 229bd03f10aSAlexander V. Chernikov def test_rtm_multipath6_getroute(self): 230bd03f10aSAlexander V. Chernikov """Tests RTM_GET with exact prefix lookup on the multipath group""" 231bd03f10aSAlexander V. Chernikov gws = ["2001:db8::10", "2001:db8::11", "2001:db8::13"] 232bd03f10aSAlexander V. Chernikov return self._test_rtm_multipath_getroute(gws) 233bd03f10aSAlexander V. Chernikov 234bd03f10aSAlexander V. Chernikov def _test_rtm_multipath_getroute(self, gws): 235bd03f10aSAlexander V. Chernikov valid_gws = [] 236bd03f10aSAlexander V. Chernikov for gw in gws: 237bd03f10aSAlexander V. Chernikov msg = self.rtsock.new_rtm_add(self.PREFIX, gw) 238bd03f10aSAlexander V. Chernikov msg.rtm_flags = RtConst.RTF_GATEWAY 239bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg) 240bd03f10aSAlexander V. Chernikov 241bd03f10aSAlexander V. Chernikov desired_sa = { 242bd03f10aSAlexander V. Chernikov RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST), 243bd03f10aSAlexander V. Chernikov RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK), 244bd03f10aSAlexander V. Chernikov } 245bd03f10aSAlexander V. Chernikov valid_gws.append(msg.get_sa(RtConst.RTA_GATEWAY)) 246bd03f10aSAlexander V. Chernikov 247bd03f10aSAlexander V. Chernikov msg_get = RtsockRtMessage( 248bd03f10aSAlexander V. Chernikov RtConst.RTM_GET, 249bd03f10aSAlexander V. Chernikov self.rtsock.get_seq(), 250bd03f10aSAlexander V. Chernikov msg.get_sa(RtConst.RTA_DST), 251bd03f10aSAlexander V. Chernikov msg.get_sa(RtConst.RTA_NETMASK), 252bd03f10aSAlexander V. Chernikov ) 253bd03f10aSAlexander V. Chernikov self.rtsock.write_message(msg_get) 254bd03f10aSAlexander V. Chernikov 255bd03f10aSAlexander V. Chernikov data = self.rtsock.read_data(msg_get.rtm_seq) 256bd03f10aSAlexander V. Chernikov msg_in = RtsockRtMessage.from_bytes(data) 257bd03f10aSAlexander V. Chernikov msg_in.print_in_message() 258bd03f10aSAlexander V. Chernikov msg_in.verify(RtConst.RTM_GET, desired_sa) 259bd03f10aSAlexander V. Chernikov 260bd03f10aSAlexander V. Chernikov # Additionally, check that the gateway is among the valid 261bd03f10aSAlexander V. Chernikov # gateways 262bd03f10aSAlexander V. Chernikov gw_found = False 263bd03f10aSAlexander V. Chernikov gw_in = msg_in.get_sa(RtConst.RTA_GATEWAY) 264bd03f10aSAlexander V. Chernikov for valid_gw in valid_gws: 265bd03f10aSAlexander V. Chernikov try: 266bd03f10aSAlexander V. Chernikov assert valid_gw == gw_in 267bd03f10aSAlexander V. Chernikov gw_found = True 268bd03f10aSAlexander V. Chernikov break 269bd03f10aSAlexander V. Chernikov except AssertionError: 270bd03f10aSAlexander V. Chernikov pass 271bd03f10aSAlexander V. Chernikov assert gw_found is True 272