Home | History | Annotate | Download | only in test
      1 #!/usr/bin/python
      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 
     17 import os
     18 from socket import *  # pylint: disable=wildcard-import
     19 import threading
     20 import time
     21 import unittest
     22 
     23 import cstruct
     24 import multinetwork_base
     25 import net_test
     26 
     27 IPV6_JOIN_ANYCAST = 27
     28 IPV6_LEAVE_ANYCAST = 28
     29 
     30 # pylint: disable=invalid-name
     31 IPv6Mreq = cstruct.Struct("IPv6Mreq", "=16si", "multiaddr ifindex")
     32 
     33 
     34 _CLOSE_HUNG = False
     35 
     36 
     37 def CauseOops():
     38   open("/proc/sysrq-trigger", "w").write("c")
     39 
     40 
     41 class CloseFileDescriptorThread(threading.Thread):
     42 
     43   def __init__(self, fd):
     44     super(CloseFileDescriptorThread, self).__init__()
     45     self.daemon = True
     46     self._fd = fd
     47     self.finished = False
     48 
     49   def run(self):
     50     global _CLOSE_HUNG
     51     _CLOSE_HUNG = True
     52     self._fd.close()
     53     _CLOSE_HUNG = False
     54     self.finished = True
     55 
     56 
     57 class AnycastTest(multinetwork_base.MultiNetworkBaseTest):
     58   """Tests for IPv6 anycast addresses.
     59 
     60   Relevant kernel commits:
     61     upstream net-next:
     62       381f4dc ipv6: clean up anycast when an interface is destroyed
     63 
     64     android-3.10:
     65       86a47ad ipv6: clean up anycast when an interface is destroyed
     66   """
     67   _TEST_NETID = 123
     68 
     69   def AnycastSetsockopt(self, s, is_add, netid, addr):
     70     ifindex = self.ifindices[netid]
     71     self.assertTrue(ifindex)
     72     ipv6mreq = IPv6Mreq((addr, ifindex))
     73     option = IPV6_JOIN_ANYCAST if is_add else IPV6_LEAVE_ANYCAST
     74     s.setsockopt(IPPROTO_IPV6, option, ipv6mreq.Pack())
     75 
     76   def testAnycastNetdeviceUnregister(self):
     77     netid = self._TEST_NETID
     78     self.assertNotIn(netid, self.tuns)
     79     self.tuns[netid] = self.CreateTunInterface(netid)
     80     self.SendRA(netid)
     81     iface = self.GetInterfaceName(netid)
     82     self.ifindices[netid] = net_test.GetInterfaceIndex(iface)
     83 
     84     s = socket(AF_INET6, SOCK_DGRAM, 0)
     85     addr = self.MyAddress(6, netid)
     86     self.assertIsNotNone(addr)
     87 
     88     addr = inet_pton(AF_INET6, addr)
     89     addr = addr[:8] + os.urandom(8)
     90     self.AnycastSetsockopt(s, True, netid, addr)
     91 
     92     # Close the tun fd in the background.
     93     # This will hang if the kernel has the bug.
     94     thread = CloseFileDescriptorThread(self.tuns[netid])
     95     thread.start()
     96     time.sleep(0.1)
     97 
     98     # Make teardown work.
     99     del self.tuns[netid]
    100     # Check that the interface is gone.
    101     try:
    102       self.assertIsNone(self.MyAddress(6, netid))
    103     finally:
    104       # This doesn't seem to help, but still.
    105       self.AnycastSetsockopt(s, False, netid, addr)
    106     self.assertTrue(thread.finished)
    107 
    108 
    109 if __name__ == "__main__":
    110   unittest.main(exit=False)
    111   if _CLOSE_HUNG:
    112     time.sleep(3)
    113     CauseOops()
    114