• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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  open("/proc/sysrq-trigger", "w").write("c")
39
40
41class 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
57class 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    # Wait up to 3 seconds for the thread to finish, but
97    # continue and fail the test if the thread hangs.
98
99    # For kernels with MPTCP ported, closing tun interface need more
100    # than 0.5 sec. DAD procedure within MPTCP fullmesh module takes
101    # more time, because duplicate address-timer takes a refcount
102    # on the IPv6-address, preventing it from getting closed.
103    thread.join(3)
104
105    # Make teardown work.
106    del self.tuns[netid]
107    # Check that the interface is gone.
108    try:
109      self.assertIsNone(self.MyAddress(6, netid))
110    finally:
111      # This doesn't seem to help, but still.
112      self.AnycastSetsockopt(s, False, netid, addr)
113    self.assertTrue(thread.finished)
114
115
116if __name__ == "__main__":
117  unittest.main(exit=False)
118  if _CLOSE_HUNG:
119    time.sleep(3)
120    CauseOops()
121