1#!/usr/bin/python3 2# 3# Copyright 2014 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import os 18from socket import * # pylint: disable=wildcard-import 19import threading 20import time 21import unittest 22 23import cstruct 24import multinetwork_base 25import net_test 26 27IPV6_JOIN_ANYCAST = 27 28IPV6_LEAVE_ANYCAST = 28 29 30# pylint: disable=invalid-name 31IPv6Mreq = cstruct.Struct("IPv6Mreq", "=16si", "multiaddr ifindex") 32 33 34_CLOSE_HUNG = False 35 36 37def CauseOops(): 38 with open("/proc/sysrq-trigger", "w") as trigger: 39 trigger.write("c") 40 41 42class CloseFileDescriptorThread(threading.Thread): 43 44 def __init__(self, fd): 45 super(CloseFileDescriptorThread, self).__init__() 46 self.daemon = True 47 self._fd = fd 48 self.finished = False 49 50 def run(self): 51 global _CLOSE_HUNG 52 _CLOSE_HUNG = True 53 self._fd.close() 54 _CLOSE_HUNG = False 55 self.finished = True 56 57 58class AnycastTest(multinetwork_base.MultiNetworkBaseTest): 59 """Tests for IPv6 anycast addresses. 60 61 Relevant kernel commits: 62 upstream net-next: 63 381f4dc ipv6: clean up anycast when an interface is destroyed 64 65 android-3.10: 66 86a47ad ipv6: clean up anycast when an interface is destroyed 67 """ 68 _TEST_NETID = 123 69 70 def AnycastSetsockopt(self, s, is_add, netid, addr): 71 ifindex = self.ifindices[netid] 72 self.assertTrue(ifindex) 73 ipv6mreq = IPv6Mreq((addr, ifindex)) 74 option = IPV6_JOIN_ANYCAST if is_add else IPV6_LEAVE_ANYCAST 75 s.setsockopt(IPPROTO_IPV6, option, ipv6mreq.Pack()) 76 77 def testAnycastNetdeviceUnregister(self): 78 netid = self._TEST_NETID 79 self.assertNotIn(netid, self.tuns) 80 self.tuns[netid] = self.CreateTunInterface(netid) 81 self.SendRA(netid) 82 iface = self.GetInterfaceName(netid) 83 self.ifindices[netid] = net_test.GetInterfaceIndex(iface) 84 85 s = socket(AF_INET6, SOCK_DGRAM, 0) 86 addr = self.MyAddress(6, netid) 87 self.assertIsNotNone(addr) 88 89 addr = inet_pton(AF_INET6, addr) 90 addr = addr[:8] + os.urandom(8) 91 self.AnycastSetsockopt(s, True, netid, addr) 92 93 # Close the tun fd in the background. 94 # This will hang if the kernel has the bug. 95 thread = CloseFileDescriptorThread(self.tuns[netid]) 96 thread.start() 97 # Wait up to 3 seconds for the thread to finish, but 98 # continue and fail the test if the thread hangs. 99 100 # For kernels with MPTCP ported, closing tun interface need more 101 # than 0.5 sec. DAD procedure within MPTCP fullmesh module takes 102 # more time, because duplicate address-timer takes a refcount 103 # on the IPv6-address, preventing it from getting closed. 104 thread.join(3) 105 106 # Make teardown work. 107 del self.tuns[netid] 108 # Check that the interface is gone. 109 try: 110 self.assertIsNone(self.MyAddress(6, netid)) 111 finally: 112 # This doesn't seem to help, but still. 113 self.AnycastSetsockopt(s, False, netid, addr) 114 self.assertTrue(thread.finished) 115 s.close() 116 117 118if __name__ == "__main__": 119 unittest.main(exit=False) 120 if _CLOSE_HUNG: 121 time.sleep(3) 122 CauseOops() 123