Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # |
| 3 | # Copyright 2015 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 errno |
| 18 | import random |
| 19 | from socket import * # pylint: disable=wildcard-import |
| 20 | import time |
| 21 | import unittest |
| 22 | |
| 23 | from scapy import all as scapy |
| 24 | |
| 25 | import multinetwork_base |
| 26 | import net_test |
| 27 | |
| 28 | |
| 29 | RTMGRP_NEIGH = 4 |
| 30 | |
| 31 | NUD_INCOMPLETE = 0x01 |
| 32 | NUD_REACHABLE = 0x02 |
| 33 | NUD_STALE = 0x04 |
| 34 | NUD_DELAY = 0x08 |
| 35 | NUD_PROBE = 0x10 |
| 36 | NUD_FAILED = 0x20 |
| 37 | NUD_PERMANENT = 0x80 |
| 38 | |
| 39 | |
| 40 | # TODO: Support IPv4. |
| 41 | class NeighbourTest(multinetwork_base.MultiNetworkBaseTest): |
| 42 | |
| 43 | # Set a 100-ms retrans timer so we can test for ND retransmits without |
| 44 | # waiting too long. Apparently this cannot go below 500ms. |
| 45 | RETRANS_TIME_MS = 500 |
| 46 | |
| 47 | # This can only be in seconds, so 1000 is the minimum. |
| 48 | DELAY_TIME_MS = 1000 |
| 49 | |
| 50 | # Unfortunately, this must be above the delay timer or the kernel ND code will |
| 51 | # not behave correctly (e.g., go straight from REACHABLE into DELAY). This is |
| 52 | # is fuzzed by the kernel from 0.5x to 1.5x of its value, so we need a value |
| 53 | # that's 2x the delay timer. |
| 54 | REACHABLE_TIME_MS = 2 * DELAY_TIME_MS |
| 55 | |
| 56 | @classmethod |
| 57 | def setUpClass(cls): |
| 58 | super(NeighbourTest, cls).setUpClass() |
| 59 | for netid in cls.tuns: |
| 60 | iface = cls.GetInterfaceName(netid) |
| 61 | # This can't be set in an RA. |
| 62 | cls.SetSysctl( |
| 63 | "/proc/sys/net/ipv6/neigh/%s/delay_first_probe_time" % iface, |
| 64 | cls.DELAY_TIME_MS / 1000) |
| 65 | |
| 66 | def setUp(self): |
| 67 | super(NeighbourTest, self).setUp() |
| 68 | |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 69 | for netid in self.tuns: |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 70 | # Clear the ND cache entries for all routers, so each test starts with |
| 71 | # the IPv6 default router in state STALE. |
| 72 | addr = self._RouterAddress(netid, 6) |
| 73 | ifindex = self.ifindices[netid] |
| 74 | self.iproute.UpdateNeighbour(6, addr, None, ifindex, NUD_FAILED) |
| 75 | |
| 76 | # Configure IPv6 by sending an RA. |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 77 | self.SendRA(netid, |
| 78 | retranstimer=self.RETRANS_TIME_MS, |
| 79 | reachabletime=self.REACHABLE_TIME_MS) |
| 80 | |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 81 | self.sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) |
| 82 | self.sock.bind((0, RTMGRP_NEIGH)) |
| 83 | net_test.SetNonBlocking(self.sock) |
| 84 | |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 85 | self.netid = random.choice(self.tuns.keys()) |
Lorenzo Colitti | ecde78b | 2015-07-01 13:45:35 +0900 | [diff] [blame] | 86 | self.ifindex = self.ifindices[self.netid] |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 87 | |
| 88 | def GetNeighbour(self, addr): |
| 89 | version = 6 if ":" in addr else 4 |
| 90 | for msg, args in self.iproute.DumpNeighbours(version): |
| 91 | if args["NDA_DST"] == addr: |
| 92 | return msg, args |
| 93 | |
| 94 | def GetNdEntry(self, addr): |
| 95 | return self.GetNeighbour(addr) |
| 96 | |
| 97 | def CheckNoNdEvents(self): |
| 98 | self.assertRaisesErrno(errno.EAGAIN, self.sock.recvfrom, 4096, MSG_PEEK) |
| 99 | |
| 100 | def assertNeighbourState(self, state, addr): |
| 101 | self.assertEquals(state, self.GetNdEntry(addr)[0].state) |
| 102 | |
| 103 | def assertNeighbourAttr(self, addr, name, value): |
| 104 | self.assertEquals(value, self.GetNdEntry(addr)[1][name]) |
| 105 | |
| 106 | def ExpectNeighbourNotification(self, addr, state, attrs=None): |
| 107 | msg = self.sock.recv(4096) |
| 108 | msg, actual_attrs = self.iproute.ParseNeighbourMessage(msg) |
| 109 | self.assertEquals(addr, actual_attrs["NDA_DST"]) |
| 110 | self.assertEquals(state, msg.state) |
| 111 | if attrs: |
| 112 | for name in attrs: |
| 113 | self.assertEquals(attrs[name], actual_attrs[name]) |
| 114 | |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 115 | def ExpectProbe(self, is_unicast, addr): |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 116 | version = 6 if ":" in addr else 4 |
| 117 | if version == 6: |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 118 | llsrc = self.MyMacAddress(self.netid) |
| 119 | if is_unicast: |
| 120 | src = self.MyLinkLocalAddress(self.netid) |
| 121 | dst = addr |
| 122 | else: |
| 123 | solicited = inet_pton(AF_INET6, addr) |
| 124 | last3bytes = tuple([ord(b) for b in solicited[-3:]]) |
| 125 | dst = "ff02::1:ff%02x:%02x%02x" % last3bytes |
| 126 | src = self.MyAddress(6, self.netid) |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 127 | expected = ( |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 128 | scapy.IPv6(src=src, dst=dst) / |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 129 | scapy.ICMPv6ND_NS(tgt=addr) / |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 130 | scapy.ICMPv6NDOptSrcLLAddr(lladdr=llsrc) |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 131 | ) |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 132 | msg = "%s probe" % ("Unicast" if is_unicast else "Multicast") |
| 133 | self.ExpectPacketOn(self.netid, msg, expected) |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 134 | else: |
| 135 | raise NotImplementedError |
| 136 | |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 137 | def ExpectUnicastProbe(self, addr): |
| 138 | self.ExpectProbe(True, addr) |
| 139 | |
| 140 | def ExpectMulticastNS(self, addr): |
| 141 | self.ExpectProbe(False, addr) |
| 142 | |
| 143 | def ReceiveUnicastAdvertisement(self, addr, mac, srcaddr=None, dstaddr=None, |
| 144 | S=1, O=0, R=1): |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 145 | version = 6 if ":" in addr else 4 |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 146 | if srcaddr is None: |
| 147 | srcaddr = addr |
| 148 | if dstaddr is None: |
| 149 | dstaddr = self.MyLinkLocalAddress(self.netid) |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 150 | if version == 6: |
| 151 | packet = ( |
| 152 | scapy.Ether(src=mac, dst=self.MyMacAddress(self.netid)) / |
Lorenzo Colitti | 87a2822 | 2015-12-14 15:41:30 +0900 | [diff] [blame] | 153 | scapy.IPv6(src=srcaddr, dst=dstaddr) / |
| 154 | scapy.ICMPv6ND_NA(tgt=addr, S=S, O=O, R=R) / |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 155 | scapy.ICMPv6NDOptDstLLAddr(lladdr=mac) |
| 156 | ) |
| 157 | self.ReceiveEtherPacketOn(self.netid, packet) |
| 158 | else: |
| 159 | raise NotImplementedError |
| 160 | |
Lorenzo Colitti | e0d102c | 2015-09-01 13:42:31 +0900 | [diff] [blame] | 161 | def MonitorSleepMs(self, interval, addr): |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 162 | slept = 0 |
Lorenzo Colitti | e0d102c | 2015-09-01 13:42:31 +0900 | [diff] [blame] | 163 | while slept < interval: |
| 164 | sleep_ms = min(100, interval - slept) |
| 165 | time.sleep(sleep_ms / 1000.0) |
| 166 | slept += sleep_ms |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 167 | print self.GetNdEntry(addr) |
| 168 | |
Lorenzo Colitti | e0d102c | 2015-09-01 13:42:31 +0900 | [diff] [blame] | 169 | def MonitorSleep(self, intervalseconds, addr): |
Lorenzo Colitti | 332b408 | 2015-09-01 13:42:56 +0900 | [diff] [blame] | 170 | self.MonitorSleepMs(intervalseconds * 1000, addr) |
Lorenzo Colitti | e0d102c | 2015-09-01 13:42:31 +0900 | [diff] [blame] | 171 | |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 172 | def SleepMs(self, ms): |
| 173 | time.sleep(ms / 1000.0) |
| 174 | |
| 175 | def testNotifications(self): |
Filipe Brandenburger | a4f746e | 2015-09-03 15:11:13 -0700 | [diff] [blame] | 176 | """Tests neighbour notifications. |
| 177 | |
| 178 | Relevant kernel commits: |
| 179 | upstream net-next: |
Lorenzo Colitti | c8c33b8 | 2015-09-18 11:00:19 +0900 | [diff] [blame] | 180 | 765c9c6 neigh: Better handling of transition to NUD_PROBE state |
Filipe Brandenburger | a4f746e | 2015-09-03 15:11:13 -0700 | [diff] [blame] | 181 | 53385d2 neigh: Netlink notification for administrative NUD state change |
| 182 | (only checked on kernel v3.13+, not on v3.10) |
Lorenzo Colitti | c8c33b8 | 2015-09-18 11:00:19 +0900 | [diff] [blame] | 183 | |
| 184 | android-3.10: |
| 185 | e4a6d6b neigh: Better handling of transition to NUD_PROBE state |
Lorenzo Colitti | 10ffbcd | 2016-01-26 15:59:47 +0900 | [diff] [blame^] | 186 | |
| 187 | android-3.18: |
| 188 | 2011e72 neigh: Better handling of transition to NUD_PROBE state |
Filipe Brandenburger | a4f746e | 2015-09-03 15:11:13 -0700 | [diff] [blame] | 189 | """ |
| 190 | |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 191 | router4 = self._RouterAddress(self.netid, 4) |
| 192 | router6 = self._RouterAddress(self.netid, 6) |
| 193 | self.assertNeighbourState(NUD_PERMANENT, router4) |
| 194 | self.assertNeighbourState(NUD_STALE, router6) |
| 195 | |
| 196 | # Send a packet and check that we go into DELAY. |
| 197 | routing_mode = random.choice(["mark", "oif", "uid"]) |
| 198 | s = self.BuildSocket(6, net_test.UDPSocket, self.netid, routing_mode) |
| 199 | s.connect((net_test.IPV6_ADDR, 53)) |
| 200 | s.send(net_test.UDP_PAYLOAD) |
| 201 | self.assertNeighbourState(NUD_DELAY, router6) |
| 202 | |
| 203 | # Wait for the probe interval, then check that we're in PROBE, and that the |
| 204 | # kernel has notified us. |
| 205 | self.SleepMs(self.DELAY_TIME_MS) |
| 206 | self.ExpectNeighbourNotification(router6, NUD_PROBE) |
| 207 | self.assertNeighbourState(NUD_PROBE, router6) |
| 208 | self.ExpectUnicastProbe(router6) |
| 209 | |
| 210 | # Respond to the NS and verify we're in REACHABLE again. |
| 211 | self.ReceiveUnicastAdvertisement(router6, self.RouterMacAddress(self.netid)) |
| 212 | self.assertNeighbourState(NUD_REACHABLE, router6) |
Filipe Brandenburger | a4f746e | 2015-09-03 15:11:13 -0700 | [diff] [blame] | 213 | if net_test.LINUX_VERSION >= (3, 13, 0): |
| 214 | # commit 53385d2 (v3.13) "neigh: Netlink notification for administrative |
| 215 | # NUD state change" produces notifications for NUD_REACHABLE, but these |
| 216 | # are not generated on earlier kernels. |
| 217 | self.ExpectNeighbourNotification(router6, NUD_REACHABLE) |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 218 | |
| 219 | # Wait until the reachable time has passed, and verify we're in STALE. |
| 220 | self.SleepMs(self.REACHABLE_TIME_MS * 1.5) |
| 221 | self.assertNeighbourState(NUD_STALE, router6) |
| 222 | self.ExpectNeighbourNotification(router6, NUD_STALE) |
| 223 | |
| 224 | # Send a packet, and verify we go into DELAY and then to PROBE. |
| 225 | s.send(net_test.UDP_PAYLOAD) |
| 226 | self.assertNeighbourState(NUD_DELAY, router6) |
| 227 | self.SleepMs(self.DELAY_TIME_MS) |
| 228 | self.assertNeighbourState(NUD_PROBE, router6) |
| 229 | self.ExpectNeighbourNotification(router6, NUD_PROBE) |
| 230 | |
| 231 | # Wait for the probes to time out, and expect a FAILED notification. |
| 232 | self.assertNeighbourAttr(router6, "NDA_PROBES", 1) |
| 233 | self.ExpectUnicastProbe(router6) |
| 234 | |
| 235 | self.SleepMs(self.RETRANS_TIME_MS) |
| 236 | self.ExpectUnicastProbe(router6) |
| 237 | self.assertNeighbourAttr(router6, "NDA_PROBES", 2) |
| 238 | |
| 239 | self.SleepMs(self.RETRANS_TIME_MS) |
| 240 | self.ExpectUnicastProbe(router6) |
| 241 | self.assertNeighbourAttr(router6, "NDA_PROBES", 3) |
| 242 | |
| 243 | self.SleepMs(self.RETRANS_TIME_MS) |
| 244 | self.assertNeighbourState(NUD_FAILED, router6) |
| 245 | self.ExpectNeighbourNotification(router6, NUD_FAILED, {"NDA_PROBES": 3}) |
| 246 | |
Lorenzo Colitti | ecde78b | 2015-07-01 13:45:35 +0900 | [diff] [blame] | 247 | def testRepeatedProbes(self): |
| 248 | router4 = self._RouterAddress(self.netid, 4) |
| 249 | router6 = self._RouterAddress(self.netid, 6) |
| 250 | routermac = self.RouterMacAddress(self.netid) |
| 251 | self.assertNeighbourState(NUD_PERMANENT, router4) |
| 252 | self.assertNeighbourState(NUD_STALE, router6) |
| 253 | |
| 254 | def ForceProbe(addr, mac): |
| 255 | self.iproute.UpdateNeighbour(6, addr, None, self.ifindex, NUD_PROBE) |
| 256 | self.assertNeighbourState(NUD_PROBE, addr) |
| 257 | self.SleepMs(1) # TODO: Why is this necessary? |
| 258 | self.assertNeighbourState(NUD_PROBE, addr) |
| 259 | self.ExpectUnicastProbe(addr) |
| 260 | self.ReceiveUnicastAdvertisement(addr, mac) |
| 261 | self.assertNeighbourState(NUD_REACHABLE, addr) |
| 262 | |
Lorenzo Colitti | e8ad4ef | 2015-07-01 14:52:33 +0900 | [diff] [blame] | 263 | for _ in xrange(5): |
Lorenzo Colitti | ecde78b | 2015-07-01 13:45:35 +0900 | [diff] [blame] | 264 | ForceProbe(router6, routermac) |
| 265 | |
Lorenzo Colitti | 891d4d7 | 2015-12-14 16:44:20 +0900 | [diff] [blame] | 266 | def testIsRouterFlag(self): |
| 267 | router6 = self._RouterAddress(self.netid, 6) |
| 268 | self.assertNeighbourState(NUD_STALE, router6) |
| 269 | |
| 270 | # Get into FAILED. |
| 271 | ifindex = self.ifindices[self.netid] |
| 272 | self.iproute.UpdateNeighbour(6, router6, None, ifindex, NUD_FAILED) |
| 273 | self.ExpectNeighbourNotification(router6, NUD_FAILED) |
| 274 | self.assertNeighbourState(NUD_FAILED, router6) |
| 275 | |
| 276 | time.sleep(1) |
| 277 | |
| 278 | # Send another packet and expect a multicast NS. |
| 279 | routing_mode = random.choice(["mark", "oif", "uid"]) |
| 280 | s = self.BuildSocket(6, net_test.UDPSocket, self.netid, routing_mode) |
| 281 | s.connect((net_test.IPV6_ADDR, 53)) |
| 282 | s.send(net_test.UDP_PAYLOAD) |
| 283 | self.ExpectMulticastNS(router6) |
| 284 | |
| 285 | # Receive a unicast NA with the R flag set to 0. |
| 286 | self.ReceiveUnicastAdvertisement(router6, self.RouterMacAddress(self.netid), |
| 287 | srcaddr=self._RouterAddress(self.netid, 6), |
| 288 | dstaddr=self.MyAddress(6, self.netid), |
| 289 | S=1, O=0, R=0) |
| 290 | |
| 291 | # Expect that this takes us to REACHABLE. |
| 292 | self.ExpectNeighbourNotification(router6, NUD_REACHABLE) |
| 293 | self.assertNeighbourState(NUD_REACHABLE, router6) |
| 294 | |
Lorenzo Colitti | 43e86f5 | 2015-06-30 17:21:00 +0900 | [diff] [blame] | 295 | |
| 296 | if __name__ == "__main__": |
| 297 | unittest.main() |