1*2f2c4c7aSAndroid Build Coastguard Worker#!/usr/bin/python3 2*2f2c4c7aSAndroid Build Coastguard Worker# 3*2f2c4c7aSAndroid Build Coastguard Worker# Copyright 2017 The Android Open Source Project 4*2f2c4c7aSAndroid Build Coastguard Worker# 5*2f2c4c7aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*2f2c4c7aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*2f2c4c7aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*2f2c4c7aSAndroid Build Coastguard Worker# 9*2f2c4c7aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*2f2c4c7aSAndroid Build Coastguard Worker# 11*2f2c4c7aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*2f2c4c7aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*2f2c4c7aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*2f2c4c7aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*2f2c4c7aSAndroid Build Coastguard Worker# limitations under the License. 16*2f2c4c7aSAndroid Build Coastguard Worker 17*2f2c4c7aSAndroid Build Coastguard Workerimport posix 18*2f2c4c7aSAndroid Build Coastguard Workerimport select 19*2f2c4c7aSAndroid Build Coastguard Workerfrom socket import * # pylint: disable=wildcard-import 20*2f2c4c7aSAndroid Build Coastguard Workerimport time 21*2f2c4c7aSAndroid Build Coastguard Workerimport unittest 22*2f2c4c7aSAndroid Build Coastguard Workerfrom math import pow 23*2f2c4c7aSAndroid Build Coastguard Worker 24*2f2c4c7aSAndroid Build Coastguard Workerimport multinetwork_base 25*2f2c4c7aSAndroid Build Coastguard Worker 26*2f2c4c7aSAndroid Build Coastguard Workerdef accumulate(lis): 27*2f2c4c7aSAndroid Build Coastguard Worker total = 0 28*2f2c4c7aSAndroid Build Coastguard Worker for x in lis: 29*2f2c4c7aSAndroid Build Coastguard Worker total += x 30*2f2c4c7aSAndroid Build Coastguard Worker yield total 31*2f2c4c7aSAndroid Build Coastguard Worker 32*2f2c4c7aSAndroid Build Coastguard Worker# This test attempts to validate time related behavior of the kernel 33*2f2c4c7aSAndroid Build Coastguard Worker# under test and is therefore inherently prone to races. To avoid 34*2f2c4c7aSAndroid Build Coastguard Worker# flakes, this test is biased to declare RFC 7559 RS backoff is 35*2f2c4c7aSAndroid Build Coastguard Worker# present on the assumption that repeated runs will detect 36*2f2c4c7aSAndroid Build Coastguard Worker# non-compliant kernels with high probability. 37*2f2c4c7aSAndroid Build Coastguard Worker# 38*2f2c4c7aSAndroid Build Coastguard Worker# If higher confidence is required, REQUIRED_SAMPLES and 39*2f2c4c7aSAndroid Build Coastguard Worker# SAMPLE_INTERVAL can be increased at the cost of increased runtime. 40*2f2c4c7aSAndroid Build Coastguard Workerclass ResilientRouterSolicitationTest(multinetwork_base.MultiNetworkBaseTest): 41*2f2c4c7aSAndroid Build Coastguard Worker """Tests for IPv6 'resilient rs' RFC 7559 backoff behaviour. 42*2f2c4c7aSAndroid Build Coastguard Worker 43*2f2c4c7aSAndroid Build Coastguard Worker Relevant kernel commits: 44*2f2c4c7aSAndroid Build Coastguard Worker upstream: 45*2f2c4c7aSAndroid Build Coastguard Worker bd11f0741fa5 ipv6 addrconf: implement RFC7559 router solicitation backoff 46*2f2c4c7aSAndroid Build Coastguard Worker android-4.4: 47*2f2c4c7aSAndroid Build Coastguard Worker e246a2f11fcc UPSTREAM: ipv6 addrconf: implement RFC7559 router solicitation backoff 48*2f2c4c7aSAndroid Build Coastguard Worker 49*2f2c4c7aSAndroid Build Coastguard Worker android-4.1: 50*2f2c4c7aSAndroid Build Coastguard Worker c6e9a50816a0 UPSTREAM: ipv6 addrconf: implement RFC7559 router solicitation backoff 51*2f2c4c7aSAndroid Build Coastguard Worker 52*2f2c4c7aSAndroid Build Coastguard Worker android-3.18: 53*2f2c4c7aSAndroid Build Coastguard Worker 2a7561c61417 UPSTREAM: ipv6 addrconf: implement RFC7559 router solicitation backoff 54*2f2c4c7aSAndroid Build Coastguard Worker 55*2f2c4c7aSAndroid Build Coastguard Worker android-3.10: 56*2f2c4c7aSAndroid Build Coastguard Worker ce2d59ac01f3 BACKPORT: ipv6 addrconf: implement RFC7559 router solicitation backoff 57*2f2c4c7aSAndroid Build Coastguard Worker 58*2f2c4c7aSAndroid Build Coastguard Worker """ 59*2f2c4c7aSAndroid Build Coastguard Worker ROUTER_SOLICIT = 133 60*2f2c4c7aSAndroid Build Coastguard Worker 61*2f2c4c7aSAndroid Build Coastguard Worker _TEST_NETID = 123 62*2f2c4c7aSAndroid Build Coastguard Worker _PROC_NET_TUNABLE = "/proc/sys/net/ipv6/conf/%s/%s" 63*2f2c4c7aSAndroid Build Coastguard Worker 64*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 65*2f2c4c7aSAndroid Build Coastguard Worker def setUpClass(cls): 66*2f2c4c7aSAndroid Build Coastguard Worker return 67*2f2c4c7aSAndroid Build Coastguard Worker 68*2f2c4c7aSAndroid Build Coastguard Worker def setUp(self): 69*2f2c4c7aSAndroid Build Coastguard Worker return 70*2f2c4c7aSAndroid Build Coastguard Worker 71*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 72*2f2c4c7aSAndroid Build Coastguard Worker def tearDownClass(cls): 73*2f2c4c7aSAndroid Build Coastguard Worker return 74*2f2c4c7aSAndroid Build Coastguard Worker 75*2f2c4c7aSAndroid Build Coastguard Worker def tearDown(self): 76*2f2c4c7aSAndroid Build Coastguard Worker return 77*2f2c4c7aSAndroid Build Coastguard Worker 78*2f2c4c7aSAndroid Build Coastguard Worker @classmethod 79*2f2c4c7aSAndroid Build Coastguard Worker def isIPv6RouterSolicitation(cls, packet): 80*2f2c4c7aSAndroid Build Coastguard Worker def ToByte(c): 81*2f2c4c7aSAndroid Build Coastguard Worker return c if isinstance(c, int) else ord(c) 82*2f2c4c7aSAndroid Build Coastguard Worker 83*2f2c4c7aSAndroid Build Coastguard Worker return ((len(packet) >= 14 + 40 + 1) and 84*2f2c4c7aSAndroid Build Coastguard Worker # Use net_test.ETH_P_IPV6 here 85*2f2c4c7aSAndroid Build Coastguard Worker (ToByte(packet[12]) == 0x86) and 86*2f2c4c7aSAndroid Build Coastguard Worker (ToByte(packet[13]) == 0xdd) and 87*2f2c4c7aSAndroid Build Coastguard Worker (ToByte(packet[14]) >> 4 == 6) and 88*2f2c4c7aSAndroid Build Coastguard Worker (ToByte(packet[14 + 40]) == cls.ROUTER_SOLICIT)) 89*2f2c4c7aSAndroid Build Coastguard Worker 90*2f2c4c7aSAndroid Build Coastguard Worker def makeTunInterface(self, netid): 91*2f2c4c7aSAndroid Build Coastguard Worker defaultDisableIPv6Path = self._PROC_NET_TUNABLE % ("default", "disable_ipv6") 92*2f2c4c7aSAndroid Build Coastguard Worker savedDefaultDisableIPv6 = self.GetSysctl(defaultDisableIPv6Path) 93*2f2c4c7aSAndroid Build Coastguard Worker self.SetSysctl(defaultDisableIPv6Path, 1) 94*2f2c4c7aSAndroid Build Coastguard Worker tun = self.CreateTunInterface(netid) 95*2f2c4c7aSAndroid Build Coastguard Worker self.SetSysctl(defaultDisableIPv6Path, savedDefaultDisableIPv6) 96*2f2c4c7aSAndroid Build Coastguard Worker return tun 97*2f2c4c7aSAndroid Build Coastguard Worker 98*2f2c4c7aSAndroid Build Coastguard Worker def testFeatureExists(self): 99*2f2c4c7aSAndroid Build Coastguard Worker return 100*2f2c4c7aSAndroid Build Coastguard Worker 101*2f2c4c7aSAndroid Build Coastguard Worker def testRouterSolicitationBackoff(self): 102*2f2c4c7aSAndroid Build Coastguard Worker # Test error tolerance 103*2f2c4c7aSAndroid Build Coastguard Worker EPSILON = 0.15 104*2f2c4c7aSAndroid Build Coastguard Worker # Minimum RFC3315 S14 backoff 105*2f2c4c7aSAndroid Build Coastguard Worker MIN_EXP = 1.9 - EPSILON 106*2f2c4c7aSAndroid Build Coastguard Worker # Maximum RFC3315 S14 backoff 107*2f2c4c7aSAndroid Build Coastguard Worker MAX_EXP = 2.1 + EPSILON 108*2f2c4c7aSAndroid Build Coastguard Worker SOLICITATION_INTERVAL = 1 109*2f2c4c7aSAndroid Build Coastguard Worker # Linear backoff for 4 samples yields 3.5 < T < 4.5 110*2f2c4c7aSAndroid Build Coastguard Worker # Exponential backoff for 4 samples yields 4.36 < T < 10.39 111*2f2c4c7aSAndroid Build Coastguard Worker REQUIRED_SAMPLES = 4 112*2f2c4c7aSAndroid Build Coastguard Worker # Give up after 10 seconds. Tuned for REQUIRED_SAMPLES = 4 113*2f2c4c7aSAndroid Build Coastguard Worker SAMPLE_INTERVAL = 10 114*2f2c4c7aSAndroid Build Coastguard Worker # Practically unlimited backoff 115*2f2c4c7aSAndroid Build Coastguard Worker SOLICITATION_MAX_INTERVAL = 1000 116*2f2c4c7aSAndroid Build Coastguard Worker MIN_LIN = SOLICITATION_INTERVAL * (0.9 - EPSILON) 117*2f2c4c7aSAndroid Build Coastguard Worker MAX_LIN = SOLICITATION_INTERVAL * (1.1 + EPSILON) 118*2f2c4c7aSAndroid Build Coastguard Worker netid = self._TEST_NETID 119*2f2c4c7aSAndroid Build Coastguard Worker tun = self.makeTunInterface(netid) 120*2f2c4c7aSAndroid Build Coastguard Worker poll = select.poll() 121*2f2c4c7aSAndroid Build Coastguard Worker poll.register(tun, select.POLLIN | select.POLLPRI) 122*2f2c4c7aSAndroid Build Coastguard Worker 123*2f2c4c7aSAndroid Build Coastguard Worker PROC_SETTINGS = [ 124*2f2c4c7aSAndroid Build Coastguard Worker ("router_solicitation_delay", 1), 125*2f2c4c7aSAndroid Build Coastguard Worker ("router_solicitation_interval", SOLICITATION_INTERVAL), 126*2f2c4c7aSAndroid Build Coastguard Worker ("router_solicitation_max_interval", SOLICITATION_MAX_INTERVAL), 127*2f2c4c7aSAndroid Build Coastguard Worker ("router_solicitations", -1), 128*2f2c4c7aSAndroid Build Coastguard Worker ("disable_ipv6", 0) # MUST be last 129*2f2c4c7aSAndroid Build Coastguard Worker ] 130*2f2c4c7aSAndroid Build Coastguard Worker 131*2f2c4c7aSAndroid Build Coastguard Worker iface = self.GetInterfaceName(netid) 132*2f2c4c7aSAndroid Build Coastguard Worker for tunable, value in PROC_SETTINGS: 133*2f2c4c7aSAndroid Build Coastguard Worker self.SetSysctl(self._PROC_NET_TUNABLE % (iface, tunable), value) 134*2f2c4c7aSAndroid Build Coastguard Worker 135*2f2c4c7aSAndroid Build Coastguard Worker start = time.time() 136*2f2c4c7aSAndroid Build Coastguard Worker deadline = start + SAMPLE_INTERVAL 137*2f2c4c7aSAndroid Build Coastguard Worker 138*2f2c4c7aSAndroid Build Coastguard Worker rsSendTimes = [] 139*2f2c4c7aSAndroid Build Coastguard Worker while True: 140*2f2c4c7aSAndroid Build Coastguard Worker now = time.time(); 141*2f2c4c7aSAndroid Build Coastguard Worker poll.poll((deadline - now) * 1000) 142*2f2c4c7aSAndroid Build Coastguard Worker try: 143*2f2c4c7aSAndroid Build Coastguard Worker packet = posix.read(tun.fileno(), 4096) 144*2f2c4c7aSAndroid Build Coastguard Worker except OSError: 145*2f2c4c7aSAndroid Build Coastguard Worker break 146*2f2c4c7aSAndroid Build Coastguard Worker 147*2f2c4c7aSAndroid Build Coastguard Worker txTime = time.time() 148*2f2c4c7aSAndroid Build Coastguard Worker if txTime > deadline: 149*2f2c4c7aSAndroid Build Coastguard Worker break; 150*2f2c4c7aSAndroid Build Coastguard Worker if not self.isIPv6RouterSolicitation(packet): 151*2f2c4c7aSAndroid Build Coastguard Worker continue 152*2f2c4c7aSAndroid Build Coastguard Worker 153*2f2c4c7aSAndroid Build Coastguard Worker # Record time relative to first router solicitation 154*2f2c4c7aSAndroid Build Coastguard Worker rsSendTimes.append(txTime - start) 155*2f2c4c7aSAndroid Build Coastguard Worker 156*2f2c4c7aSAndroid Build Coastguard Worker # Exit early if we have at least REQUIRED_SAMPLES 157*2f2c4c7aSAndroid Build Coastguard Worker if len(rsSendTimes) >= REQUIRED_SAMPLES: 158*2f2c4c7aSAndroid Build Coastguard Worker continue 159*2f2c4c7aSAndroid Build Coastguard Worker 160*2f2c4c7aSAndroid Build Coastguard Worker # Expect at least REQUIRED_SAMPLES router solicitations 161*2f2c4c7aSAndroid Build Coastguard Worker self.assertLessEqual(REQUIRED_SAMPLES, len(rsSendTimes)) 162*2f2c4c7aSAndroid Build Coastguard Worker 163*2f2c4c7aSAndroid Build Coastguard Worker # Compute minimum and maximum bounds for RFC3315 S14 exponential backoff. 164*2f2c4c7aSAndroid Build Coastguard Worker # First retransmit is linear backoff, subsequent retransmits are exponential 165*2f2c4c7aSAndroid Build Coastguard Worker min_exp_bound = accumulate([MIN_LIN * pow(MIN_EXP, i) for i in range(0, len(rsSendTimes))]) 166*2f2c4c7aSAndroid Build Coastguard Worker max_exp_bound = accumulate([MAX_LIN * pow(MAX_EXP, i) for i in range(0, len(rsSendTimes))]) 167*2f2c4c7aSAndroid Build Coastguard Worker 168*2f2c4c7aSAndroid Build Coastguard Worker # Assert that each sample falls within the worst case interval. If all samples fit we accept 169*2f2c4c7aSAndroid Build Coastguard Worker # the exponential backoff hypothesis 170*2f2c4c7aSAndroid Build Coastguard Worker for (t, min_exp, max_exp) in zip(rsSendTimes[1:], min_exp_bound, max_exp_bound): 171*2f2c4c7aSAndroid Build Coastguard Worker self.assertLess(min_exp, t) 172*2f2c4c7aSAndroid Build Coastguard Worker self.assertGreater(max_exp, t) 173*2f2c4c7aSAndroid Build Coastguard Worker 174*2f2c4c7aSAndroid Build Coastguard Worker tun.close() 175*2f2c4c7aSAndroid Build Coastguard Worker 176*2f2c4c7aSAndroid Build Coastguard Workerif __name__ == "__main__": 177*2f2c4c7aSAndroid Build Coastguard Worker unittest.main() 178