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"""Base module for multinetwork tests.""" 18 19import errno 20import fcntl 21import os 22import posix 23import random 24import re 25from socket import * # pylint: disable=wildcard-import 26import struct 27import time 28 29from scapy import all as scapy 30 31import csocket 32import cstruct 33import iproute 34import net_test 35 36 37IFF_TUN = 1 38IFF_TAP = 2 39IFF_NO_PI = 0x1000 40TUNSETIFF = 0x400454ca 41 42SO_BINDTODEVICE = 25 43 44# Setsockopt values. 45IP_UNICAST_IF = 50 46IPV6_MULTICAST_IF = 17 47IPV6_UNICAST_IF = 76 48 49# Cmsg values. 50IP_TTL = 2 51IPV6_2292PKTOPTIONS = 6 52IPV6_FLOWINFO = 11 53IPV6_HOPLIMIT = 52 # Different from IPV6_UNICAST_HOPS, this is cmsg only. 54 55 56AUTOCONF_TABLE_SYSCTL = "/proc/sys/net/ipv6/conf/default/accept_ra_rt_table" 57 58HAVE_AUTOCONF_TABLE = os.path.isfile(AUTOCONF_TABLE_SYSCTL) 59 60 61class UnexpectedPacketError(AssertionError): 62 pass 63 64 65def MakePktInfo(version, addr, ifindex): 66 family = {4: AF_INET, 6: AF_INET6}[version] 67 if not addr: 68 addr = {4: "0.0.0.0", 6: "::"}[version] 69 if addr: 70 addr = inet_pton(family, addr) 71 if version == 6: 72 return csocket.In6Pktinfo((addr, ifindex)).Pack() 73 else: 74 return csocket.InPktinfo((ifindex, addr, "\x00" * 4)).Pack() 75 76 77class MultiNetworkBaseTest(net_test.NetworkTest): 78 """Base class for all multinetwork tests. 79 80 This class does not contain any test code, but contains code to set up and 81 tear a multi-network environment using multiple tun interfaces. The 82 environment is designed to be similar to a real Android device in terms of 83 rules and routes, and supports IPv4 and IPv6. 84 85 Tests wishing to use this environment should inherit from this class and 86 ensure that any setupClass, tearDownClass, setUp, and tearDown methods they 87 implement also call the superclass versions. 88 """ 89 90 # Must be between 1 and 256, since we put them in MAC addresses and IIDs. 91 NETIDS = [100, 150, 200, 250] 92 93 # Stores sysctl values to write back when the test completes. 94 saved_sysctls = {} 95 96 # Wether to output setup commands. 97 DEBUG = False 98 99 # The size of our UID ranges. 100 UID_RANGE_SIZE = 1000 101 102 # Rule priorities. 103 PRIORITY_UID = 100 104 PRIORITY_OIF = 200 105 PRIORITY_FWMARK = 300 106 PRIORITY_IIF = 400 107 PRIORITY_DEFAULT = 999 108 PRIORITY_UNREACHABLE = 1000 109 110 # For convenience. 111 IPV4_ADDR = net_test.IPV4_ADDR 112 IPV6_ADDR = net_test.IPV6_ADDR 113 IPV4_PING = net_test.IPV4_PING 114 IPV6_PING = net_test.IPV6_PING 115 116 RA_VALIDITY = 300 # seconds 117 118 @classmethod 119 def UidRangeForNetid(cls, netid): 120 return ( 121 cls.UID_RANGE_SIZE * netid, 122 cls.UID_RANGE_SIZE * (netid + 1) - 1 123 ) 124 125 @classmethod 126 def UidForNetid(cls, netid): 127 if not netid: 128 return 0 129 return random.randint(*cls.UidRangeForNetid(netid)) 130 131 @classmethod 132 def _TableForNetid(cls, netid): 133 if cls.AUTOCONF_TABLE_OFFSET and netid in cls.ifindices: 134 return cls.ifindices[netid] + (-cls.AUTOCONF_TABLE_OFFSET) 135 else: 136 return netid 137 138 @staticmethod 139 def GetInterfaceName(netid): 140 return "nettest%d" % netid 141 142 @staticmethod 143 def RouterMacAddress(netid): 144 return "02:00:00:00:%02x:00" % netid 145 146 @staticmethod 147 def MyMacAddress(netid): 148 return "02:00:00:00:%02x:01" % netid 149 150 @staticmethod 151 def _RouterAddress(netid, version): 152 if version == 6: 153 return "fe80::%02x00" % netid 154 elif version == 4: 155 return "10.0.%d.1" % netid 156 else: 157 raise ValueError("Don't support IPv%s" % version) 158 159 @classmethod 160 def _MyIPv4Address(cls, netid): 161 return "10.0.%d.2" % netid 162 163 @classmethod 164 def _MyIPv6Address(cls, netid): 165 return net_test.GetLinkAddress(cls.GetInterfaceName(netid), False) 166 167 @classmethod 168 def MyAddress(cls, version, netid): 169 return {4: cls._MyIPv4Address(netid), 170 5: "::ffff:" + cls._MyIPv4Address(netid), 171 6: cls._MyIPv6Address(netid)}[version] 172 173 @classmethod 174 def MyLinkLocalAddress(cls, netid): 175 return net_test.GetLinkAddress(cls.GetInterfaceName(netid), True) 176 177 @staticmethod 178 def OnlinkPrefixLen(version): 179 return {4: 24, 6: 64}[version] 180 181 @staticmethod 182 def OnlinkPrefix(version, netid): 183 return {4: "10.0.%d.0" % netid, 184 6: "2001:db8:%02x::" % netid}[version] 185 186 @staticmethod 187 def GetRandomDestination(prefix): 188 if "." in prefix: 189 return prefix + "%d.%d" % (random.randint(0, 255), random.randint(0, 255)) 190 else: 191 return prefix + "%x:%x" % (random.randint(0, 65535), 192 random.randint(0, 65535)) 193 194 def GetProtocolFamily(self, version): 195 return {4: AF_INET, 6: AF_INET6}[version] 196 197 @classmethod 198 def CreateTunInterface(cls, netid): 199 iface = cls.GetInterfaceName(netid) 200 f = open("/dev/net/tun", "r+b") 201 ifr = struct.pack("16sH", iface, IFF_TAP | IFF_NO_PI) 202 ifr += "\x00" * (40 - len(ifr)) 203 fcntl.ioctl(f, TUNSETIFF, ifr) 204 # Give ourselves a predictable MAC address. 205 net_test.SetInterfaceHWAddr(iface, cls.MyMacAddress(netid)) 206 # Disable DAD so we don't have to wait for it. 207 cls.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_dad" % iface, 0) 208 # Set accept_ra to 2, because that's what we use. 209 cls.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_ra" % iface, 2) 210 net_test.SetInterfaceUp(iface) 211 net_test.SetNonBlocking(f) 212 return f 213 214 @classmethod 215 def SendRA(cls, netid, retranstimer=None, reachabletime=0, options=()): 216 validity = cls.RA_VALIDITY # seconds 217 macaddr = cls.RouterMacAddress(netid) 218 lladdr = cls._RouterAddress(netid, 6) 219 220 if retranstimer is None: 221 # If no retrans timer was specified, pick one that's as long as the 222 # router lifetime. This ensures that no spurious ND retransmits 223 # will interfere with test expectations. 224 retranstimer = validity * 1000 # Lifetime is in s, retrans timer in ms. 225 226 # We don't want any routes in the main table. If the kernel doesn't support 227 # putting RA routes into per-interface tables, configure routing manually. 228 routerlifetime = validity if HAVE_AUTOCONF_TABLE else 0 229 230 ra = (scapy.Ether(src=macaddr, dst="33:33:00:00:00:01") / 231 scapy.IPv6(src=lladdr, hlim=255) / 232 scapy.ICMPv6ND_RA(reachabletime=reachabletime, 233 retranstimer=retranstimer, 234 routerlifetime=routerlifetime) / 235 scapy.ICMPv6NDOptSrcLLAddr(lladdr=macaddr) / 236 scapy.ICMPv6NDOptPrefixInfo(prefix=cls.OnlinkPrefix(6, netid), 237 prefixlen=cls.OnlinkPrefixLen(6), 238 L=1, A=1, 239 validlifetime=validity, 240 preferredlifetime=validity)) 241 for option in options: 242 ra /= option 243 posix.write(cls.tuns[netid].fileno(), str(ra)) 244 245 @classmethod 246 def _RunSetupCommands(cls, netid, is_add): 247 for version in [4, 6]: 248 # Find out how to configure things. 249 iface = cls.GetInterfaceName(netid) 250 ifindex = cls.ifindices[netid] 251 macaddr = cls.RouterMacAddress(netid) 252 router = cls._RouterAddress(netid, version) 253 table = cls._TableForNetid(netid) 254 255 # Set up routing rules. 256 start, end = cls.UidRangeForNetid(netid) 257 cls.iproute.UidRangeRule(version, is_add, start, end, table, 258 cls.PRIORITY_UID) 259 cls.iproute.OifRule(version, is_add, iface, table, cls.PRIORITY_OIF) 260 cls.iproute.FwmarkRule(version, is_add, netid, table, 261 cls.PRIORITY_FWMARK) 262 263 # Configure routing and addressing. 264 # 265 # IPv6 uses autoconf for everything, except if per-device autoconf routing 266 # tables are not supported, in which case the default route (only) is 267 # configured manually. For IPv4 we have to manually configure addresses, 268 # routes, and neighbour cache entries (since we don't reply to ARP or ND). 269 # 270 # Since deleting addresses also causes routes to be deleted, we need to 271 # be careful with ordering or the delete commands will fail with ENOENT. 272 # 273 # A real Android system will have both IPv4 and IPv6 routes for 274 # directly-connected subnets in the per-interface routing tables. Ensure 275 # we create those as well. 276 do_routing = (version == 4 or cls.AUTOCONF_TABLE_OFFSET is None) 277 if is_add: 278 if version == 4: 279 cls.iproute.AddAddress(cls._MyIPv4Address(netid), 280 cls.OnlinkPrefixLen(4), ifindex) 281 cls.iproute.AddNeighbour(version, router, macaddr, ifindex) 282 if do_routing: 283 cls.iproute.AddRoute(version, table, 284 cls.OnlinkPrefix(version, netid), 285 cls.OnlinkPrefixLen(version), None, ifindex) 286 cls.iproute.AddRoute(version, table, "default", 0, router, ifindex) 287 else: 288 if do_routing: 289 cls.iproute.DelRoute(version, table, "default", 0, router, ifindex) 290 cls.iproute.DelRoute(version, table, 291 cls.OnlinkPrefix(version, netid), 292 cls.OnlinkPrefixLen(version), None, ifindex) 293 if version == 4: 294 cls.iproute.DelNeighbour(version, router, macaddr, ifindex) 295 cls.iproute.DelAddress(cls._MyIPv4Address(netid), 296 cls.OnlinkPrefixLen(4), ifindex) 297 298 @classmethod 299 def SetDefaultNetwork(cls, netid): 300 table = cls._TableForNetid(netid) if netid else None 301 for version in [4, 6]: 302 is_add = table is not None 303 cls.iproute.DefaultRule(version, is_add, table, cls.PRIORITY_DEFAULT) 304 305 @classmethod 306 def ClearDefaultNetwork(cls): 307 cls.SetDefaultNetwork(None) 308 309 @classmethod 310 def GetSysctl(cls, sysctl): 311 return open(sysctl, "r").read() 312 313 @classmethod 314 def SetSysctl(cls, sysctl, value): 315 # Only save each sysctl value the first time we set it. This is so we can 316 # set it to arbitrary values multiple times and still write it back 317 # correctly at the end. 318 if sysctl not in cls.saved_sysctls: 319 cls.saved_sysctls[sysctl] = cls.GetSysctl(sysctl) 320 open(sysctl, "w").write(str(value) + "\n") 321 322 @classmethod 323 def SetIPv6SysctlOnAllIfaces(cls, sysctl, value): 324 for netid in cls.tuns: 325 iface = cls.GetInterfaceName(netid) 326 name = "/proc/sys/net/ipv6/conf/%s/%s" % (iface, sysctl) 327 cls.SetSysctl(name, value) 328 329 @classmethod 330 def _RestoreSysctls(cls): 331 for sysctl, value in cls.saved_sysctls.iteritems(): 332 try: 333 open(sysctl, "w").write(value) 334 except IOError: 335 pass 336 337 @classmethod 338 def _ICMPRatelimitFilename(cls, version): 339 return "/proc/sys/net/" + {4: "ipv4/icmp_ratelimit", 340 6: "ipv6/icmp/ratelimit"}[version] 341 342 @classmethod 343 def _SetICMPRatelimit(cls, version, limit): 344 cls.SetSysctl(cls._ICMPRatelimitFilename(version), limit) 345 346 @classmethod 347 def setUpClass(cls): 348 # This is per-class setup instead of per-testcase setup because shelling out 349 # to ip and iptables is slow, and because routing configuration doesn't 350 # change during the test. 351 cls.iproute = iproute.IPRoute() 352 cls.tuns = {} 353 cls.ifindices = {} 354 if HAVE_AUTOCONF_TABLE: 355 cls.SetSysctl(AUTOCONF_TABLE_SYSCTL, -1000) 356 cls.AUTOCONF_TABLE_OFFSET = -1000 357 else: 358 cls.AUTOCONF_TABLE_OFFSET = None 359 360 # Disable ICMP rate limits. These will be restored by _RestoreSysctls. 361 for version in [4, 6]: 362 cls._SetICMPRatelimit(version, 0) 363 364 for version in [4, 6]: 365 cls.iproute.UnreachableRule(version, True, cls.PRIORITY_UNREACHABLE) 366 367 for netid in cls.NETIDS: 368 cls.tuns[netid] = cls.CreateTunInterface(netid) 369 iface = cls.GetInterfaceName(netid) 370 cls.ifindices[netid] = net_test.GetInterfaceIndex(iface) 371 372 cls.SendRA(netid) 373 cls._RunSetupCommands(netid, True) 374 375 # Don't print lots of "device foo entered promiscuous mode" warnings. 376 cls.loglevel = cls.GetConsoleLogLevel() 377 cls.SetConsoleLogLevel(net_test.KERN_INFO) 378 379 # Uncomment to look around at interface and rule configuration while 380 # running in the background. (Once the test finishes running, all the 381 # interfaces and rules are gone.) 382 # time.sleep(30) 383 384 @classmethod 385 def tearDownClass(cls): 386 for version in [4, 6]: 387 try: 388 cls.iproute.UnreachableRule(version, False, cls.PRIORITY_UNREACHABLE) 389 except IOError: 390 pass 391 392 for netid in cls.tuns: 393 cls._RunSetupCommands(netid, False) 394 cls.tuns[netid].close() 395 396 cls._RestoreSysctls() 397 cls.SetConsoleLogLevel(cls.loglevel) 398 399 def setUp(self): 400 self.ClearTunQueues() 401 402 def SetSocketMark(self, s, netid): 403 if netid is None: 404 netid = 0 405 s.setsockopt(SOL_SOCKET, net_test.SO_MARK, netid) 406 407 def GetSocketMark(self, s): 408 return s.getsockopt(SOL_SOCKET, net_test.SO_MARK) 409 410 def ClearSocketMark(self, s): 411 self.SetSocketMark(s, 0) 412 413 def BindToDevice(self, s, iface): 414 if not iface: 415 iface = "" 416 s.setsockopt(SOL_SOCKET, SO_BINDTODEVICE, iface) 417 418 def SetUnicastInterface(self, s, ifindex): 419 # Otherwise, Python thinks it's a 1-byte option. 420 ifindex = struct.pack("!I", ifindex) 421 422 # Always set the IPv4 interface, because it will be used even on IPv6 423 # sockets if the destination address is a mapped address. 424 s.setsockopt(net_test.SOL_IP, IP_UNICAST_IF, ifindex) 425 if s.family == AF_INET6: 426 s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_IF, ifindex) 427 428 def GetRemoteAddress(self, version): 429 return {4: self.IPV4_ADDR, 430 5: "::ffff:" + self.IPV4_ADDR, 431 6: self.IPV6_ADDR}[version] 432 433 def SelectInterface(self, s, netid, mode): 434 if mode == "uid": 435 os.fchown(s.fileno(), self.UidForNetid(netid), -1) 436 elif mode == "mark": 437 self.SetSocketMark(s, netid) 438 elif mode == "oif": 439 iface = self.GetInterfaceName(netid) if netid else "" 440 self.BindToDevice(s, iface) 441 elif mode == "ucast_oif": 442 self.SetUnicastInterface(s, self.ifindices.get(netid, 0)) 443 else: 444 raise ValueError("Unknown interface selection mode %s" % mode) 445 446 def BuildSocket(self, version, constructor, netid, routing_mode): 447 if version == 5: version = 6 448 s = constructor(self.GetProtocolFamily(version)) 449 450 if routing_mode not in [None, "uid"]: 451 self.SelectInterface(s, netid, routing_mode) 452 elif routing_mode == "uid": 453 os.fchown(s.fileno(), self.UidForNetid(netid), -1) 454 455 return s 456 457 def SendOnNetid(self, version, s, dstaddr, dstport, netid, payload, cmsgs): 458 if netid is not None: 459 pktinfo = MakePktInfo(version, None, self.ifindices[netid]) 460 cmsg_level, cmsg_name = { 461 4: (net_test.SOL_IP, csocket.IP_PKTINFO), 462 6: (net_test.SOL_IPV6, csocket.IPV6_PKTINFO)}[version] 463 cmsgs.append((cmsg_level, cmsg_name, pktinfo)) 464 csocket.Sendmsg(s, (dstaddr, dstport), payload, cmsgs, csocket.MSG_CONFIRM) 465 466 def ReceiveEtherPacketOn(self, netid, packet): 467 posix.write(self.tuns[netid].fileno(), str(packet)) 468 469 def ReceivePacketOn(self, netid, ip_packet): 470 routermac = self.RouterMacAddress(netid) 471 mymac = self.MyMacAddress(netid) 472 packet = scapy.Ether(src=routermac, dst=mymac) / ip_packet 473 self.ReceiveEtherPacketOn(netid, packet) 474 475 def ReadAllPacketsOn(self, netid, include_multicast=False): 476 packets = [] 477 retries = 0 478 max_retries = 1 479 while True: 480 try: 481 packet = posix.read(self.tuns[netid].fileno(), 4096) 482 if not packet: 483 break 484 ether = scapy.Ether(packet) 485 # Multicast frames are frames where the first byte of the destination 486 # MAC address has 1 in the least-significant bit. 487 if include_multicast or not int(ether.dst.split(":")[0], 16) & 0x1: 488 packets.append(ether.payload) 489 except OSError, e: 490 # EAGAIN means there are no more packets waiting. 491 if re.match(e.message, os.strerror(errno.EAGAIN)): 492 # If we didn't see any packets, try again for good luck. 493 if not packets and retries < max_retries: 494 time.sleep(0.01) 495 retries += 1 496 continue 497 else: 498 break 499 # Anything else is unexpected. 500 else: 501 raise e 502 return packets 503 504 def InvalidateDstCache(self, version, remoteaddr, netid): 505 """Invalidates destination cache entries of sockets to remoteaddr. 506 507 Creates and then deletes a route pointing to remoteaddr, which invalidates 508 the destination cache entries of any sockets connected to remoteaddr. 509 The fact that this method actually invalidates destination cache entries is 510 tested by OutgoingTest#testIPv[46]Remarking, which checks that the kernel 511 does not re-route sockets when they are remarked, but does re-route them if 512 this method is called. 513 514 Args: 515 version: The IP version, 4 or 6. 516 remoteaddr: The IP address to temporarily reroute. 517 netid: The netid to add/remove the route to. 518 """ 519 iface = self.GetInterfaceName(netid) 520 ifindex = self.ifindices[netid] 521 table = self._TableForNetid(netid) 522 nexthop = self._RouterAddress(netid, version) 523 plen = {4: 32, 6: 128}[version] 524 self.iproute.AddRoute(version, table, remoteaddr, plen, nexthop, ifindex) 525 self.iproute.DelRoute(version, table, remoteaddr, plen, nexthop, ifindex) 526 527 def ClearTunQueues(self): 528 # Keep reading packets on all netids until we get no packets on any of them. 529 waiting = None 530 while waiting != 0: 531 waiting = sum(len(self.ReadAllPacketsOn(netid)) for netid in self.NETIDS) 532 533 def assertPacketMatches(self, expected, actual): 534 # The expected packet is just a rough sketch of the packet we expect to 535 # receive. For example, it doesn't contain fields we can't predict, such as 536 # initial TCP sequence numbers, or that depend on the host implementation 537 # and settings, such as TCP options. To check whether the packet matches 538 # what we expect, instead of just checking all the known fields one by one, 539 # we blank out fields in the actual packet and then compare the whole 540 # packets to each other as strings. Because we modify the actual packet, 541 # make a copy here. 542 actual = actual.copy() 543 544 # Blank out IPv4 fields that we can't predict, like ID and the DF bit. 545 actualip = actual.getlayer("IP") 546 expectedip = expected.getlayer("IP") 547 if actualip and expectedip: 548 actualip.id = expectedip.id 549 actualip.flags &= 5 550 actualip.chksum = None # Change the header, recalculate the checksum. 551 552 # Blank out the flow label, since new kernels randomize it by default. 553 actualipv6 = actual.getlayer("IPv6") 554 expectedipv6 = expected.getlayer("IPv6") 555 if actualipv6 and expectedipv6: 556 actualipv6.fl = expectedipv6.fl 557 558 # Blank out UDP fields that we can't predict (e.g., the source port for 559 # kernel-originated packets). 560 actualudp = actual.getlayer("UDP") 561 expectedudp = expected.getlayer("UDP") 562 if actualudp and expectedudp: 563 if expectedudp.sport is None: 564 actualudp.sport = None 565 actualudp.chksum = None 566 elif actualudp.chksum == 0xffff: 567 # Scapy does not appear to change 0 to 0xffff as required by RFC 768. 568 actualudp.chksum = 0 569 570 # Since the TCP code below messes with options, recalculate the length. 571 if actualip: 572 actualip.len = None 573 if actualipv6: 574 actualipv6.plen = None 575 576 # Blank out TCP fields that we can't predict. 577 actualtcp = actual.getlayer("TCP") 578 expectedtcp = expected.getlayer("TCP") 579 if actualtcp and expectedtcp: 580 actualtcp.dataofs = expectedtcp.dataofs 581 actualtcp.options = expectedtcp.options 582 actualtcp.window = expectedtcp.window 583 if expectedtcp.sport is None: 584 actualtcp.sport = None 585 if expectedtcp.seq is None: 586 actualtcp.seq = None 587 if expectedtcp.ack is None: 588 actualtcp.ack = None 589 actualtcp.chksum = None 590 591 # Serialize the packet so that expected packet fields that are only set when 592 # a packet is serialized e.g., the checksum) are filled in. 593 expected_real = expected.__class__(str(expected)) 594 actual_real = actual.__class__(str(actual)) 595 # repr() can be expensive. Call it only if the test is going to fail and we 596 # want to see the error. 597 if expected_real != actual_real: 598 self.assertEquals(repr(expected_real), repr(actual_real)) 599 600 def PacketMatches(self, expected, actual): 601 try: 602 self.assertPacketMatches(expected, actual) 603 return True 604 except AssertionError: 605 return False 606 607 def ExpectNoPacketsOn(self, netid, msg): 608 packets = self.ReadAllPacketsOn(netid) 609 if packets: 610 firstpacket = repr(packets[0]) 611 else: 612 firstpacket = "" 613 self.assertFalse(packets, msg + ": unexpected packet: " + firstpacket) 614 615 def ExpectPacketOn(self, netid, msg, expected): 616 # To avoid confusion due to lots of ICMPv6 ND going on all the time, drop 617 # multicast packets unless the packet we expect to see is a multicast 618 # packet. For now the only tests that use this are IPv6. 619 ipv6 = expected.getlayer("IPv6") 620 if ipv6 and ipv6.dst.startswith("ff"): 621 include_multicast = True 622 else: 623 include_multicast = False 624 625 packets = self.ReadAllPacketsOn(netid, include_multicast=include_multicast) 626 self.assertTrue(packets, msg + ": received no packets") 627 628 # If we receive a packet that matches what we expected, return it. 629 for packet in packets: 630 if self.PacketMatches(expected, packet): 631 return packet 632 633 # None of the packets matched. Call assertPacketMatches to output a diff 634 # between the expected packet and the last packet we received. In theory, 635 # we'd output a diff to the packet that's the best match for what we 636 # expected, but this is good enough for now. 637 try: 638 self.assertPacketMatches(expected, packets[-1]) 639 except Exception, e: 640 raise UnexpectedPacketError( 641 "%s: diff with last packet:\n%s" % (msg, e.message)) 642 643 def Combinations(self, version): 644 """Produces a list of combinations to test.""" 645 combinations = [] 646 647 # Check packets addressed to the IP addresses of all our interfaces... 648 for dest_ip_netid in self.tuns: 649 ip_if = self.GetInterfaceName(dest_ip_netid) 650 myaddr = self.MyAddress(version, dest_ip_netid) 651 prefix = {4: "172.22.", 6: "2001:db8:aaaa::"}[version] 652 remoteaddr = self.GetRandomDestination(prefix) 653 654 # ... coming in on all our interfaces. 655 for netid in self.tuns: 656 iif = self.GetInterfaceName(netid) 657 combinations.append((netid, iif, ip_if, myaddr, remoteaddr)) 658 659 return combinations 660 661 def _FormatMessage(self, iif, ip_if, extra, desc, reply_desc): 662 msg = "Receiving %s on %s to %s IP, %s" % (desc, iif, ip_if, extra) 663 if reply_desc: 664 msg += ": Expecting %s on %s" % (reply_desc, iif) 665 else: 666 msg += ": Expecting no packets on %s" % iif 667 return msg 668 669 def _ReceiveAndExpectResponse(self, netid, packet, reply, msg): 670 self.ReceivePacketOn(netid, packet) 671 if reply: 672 return self.ExpectPacketOn(netid, msg, reply) 673 else: 674 self.ExpectNoPacketsOn(netid, msg) 675 return None 676