You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.4 KiB
121 lines
3.4 KiB
#!/usr/bin/python
|
|
#
|
|
# Copyright 2014 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import os
|
|
from socket import * # pylint: disable=wildcard-import
|
|
import threading
|
|
import time
|
|
import unittest
|
|
|
|
import cstruct
|
|
import multinetwork_base
|
|
import net_test
|
|
|
|
IPV6_JOIN_ANYCAST = 27
|
|
IPV6_LEAVE_ANYCAST = 28
|
|
|
|
# pylint: disable=invalid-name
|
|
IPv6Mreq = cstruct.Struct("IPv6Mreq", "=16si", "multiaddr ifindex")
|
|
|
|
|
|
_CLOSE_HUNG = False
|
|
|
|
|
|
def CauseOops():
|
|
open("/proc/sysrq-trigger", "w").write("c")
|
|
|
|
|
|
class CloseFileDescriptorThread(threading.Thread):
|
|
|
|
def __init__(self, fd):
|
|
super(CloseFileDescriptorThread, self).__init__()
|
|
self.daemon = True
|
|
self._fd = fd
|
|
self.finished = False
|
|
|
|
def run(self):
|
|
global _CLOSE_HUNG
|
|
_CLOSE_HUNG = True
|
|
self._fd.close()
|
|
_CLOSE_HUNG = False
|
|
self.finished = True
|
|
|
|
|
|
class AnycastTest(multinetwork_base.MultiNetworkBaseTest):
|
|
"""Tests for IPv6 anycast addresses.
|
|
|
|
Relevant kernel commits:
|
|
upstream net-next:
|
|
381f4dc ipv6: clean up anycast when an interface is destroyed
|
|
|
|
android-3.10:
|
|
86a47ad ipv6: clean up anycast when an interface is destroyed
|
|
"""
|
|
_TEST_NETID = 123
|
|
|
|
def AnycastSetsockopt(self, s, is_add, netid, addr):
|
|
ifindex = self.ifindices[netid]
|
|
self.assertTrue(ifindex)
|
|
ipv6mreq = IPv6Mreq((addr, ifindex))
|
|
option = IPV6_JOIN_ANYCAST if is_add else IPV6_LEAVE_ANYCAST
|
|
s.setsockopt(IPPROTO_IPV6, option, ipv6mreq.Pack())
|
|
|
|
def testAnycastNetdeviceUnregister(self):
|
|
netid = self._TEST_NETID
|
|
self.assertNotIn(netid, self.tuns)
|
|
self.tuns[netid] = self.CreateTunInterface(netid)
|
|
self.SendRA(netid)
|
|
iface = self.GetInterfaceName(netid)
|
|
self.ifindices[netid] = net_test.GetInterfaceIndex(iface)
|
|
|
|
s = socket(AF_INET6, SOCK_DGRAM, 0)
|
|
addr = self.MyAddress(6, netid)
|
|
self.assertIsNotNone(addr)
|
|
|
|
addr = inet_pton(AF_INET6, addr)
|
|
addr = addr[:8] + os.urandom(8)
|
|
self.AnycastSetsockopt(s, True, netid, addr)
|
|
|
|
# Close the tun fd in the background.
|
|
# This will hang if the kernel has the bug.
|
|
thread = CloseFileDescriptorThread(self.tuns[netid])
|
|
thread.start()
|
|
# Wait up to 3 seconds for the thread to finish, but
|
|
# continue and fail the test if the thread hangs.
|
|
|
|
# For kernels with MPTCP ported, closing tun interface need more
|
|
# than 0.5 sec. DAD procedure within MPTCP fullmesh module takes
|
|
# more time, because duplicate address-timer takes a refcount
|
|
# on the IPv6-address, preventing it from getting closed.
|
|
thread.join(3)
|
|
|
|
# Make teardown work.
|
|
del self.tuns[netid]
|
|
# Check that the interface is gone.
|
|
try:
|
|
self.assertIsNone(self.MyAddress(6, netid))
|
|
finally:
|
|
# This doesn't seem to help, but still.
|
|
self.AnycastSetsockopt(s, False, netid, addr)
|
|
self.assertTrue(thread.finished)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(exit=False)
|
|
if _CLOSE_HUNG:
|
|
time.sleep(3)
|
|
CauseOops()
|