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