xref: /aosp_15_r20/kernel/tests/net/test/multinetwork_test.py (revision 2f2c4c7ab4226c71756b9c31670392fdd6887c4f)
1#!/usr/bin/python3
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 cstruct
18import ctypes
19import errno
20import os
21import random
22from socket import *  # pylint: disable=wildcard-import
23import struct
24import time           # pylint: disable=unused-import
25import unittest
26
27from scapy import all as scapy
28
29import csocket
30import iproute
31import multinetwork_base
32import net_test
33import netlink
34import packets
35
36# For brevity.
37UDP_PAYLOAD = net_test.UDP_PAYLOAD
38
39IPV6_FLOWINFO = 11
40
41SYNCOOKIES_SYSCTL = "/proc/sys/net/ipv4/tcp_syncookies"
42TCP_MARK_ACCEPT_SYSCTL = "/proc/sys/net/ipv4/tcp_fwmark_accept"
43
44
45class OutgoingTest(multinetwork_base.MultiNetworkBaseTest):
46
47  # How many times to run outgoing packet tests.
48  ITERATIONS = 5
49
50  def CheckPingPacket(self, version, netid, routing_mode, packet):
51    s = self.BuildSocket(version, net_test.PingSocket, netid, routing_mode)
52
53    myaddr = self.MyAddress(version, netid)
54    mysockaddr = self.MySocketAddress(version, netid)
55    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
56    s.bind((mysockaddr, packets.PING_IDENT))
57    net_test.SetSocketTos(s, packets.PING_TOS)
58
59    dstaddr = self.GetRemoteAddress(version)
60    dstsockaddr = self.GetRemoteSocketAddress(version)
61    desc, expected = packets.ICMPEcho(version, myaddr, dstaddr)
62    msg = "IPv%d ping: expected %s on %s" % (
63        version, desc, self.GetInterfaceName(netid))
64
65    s.sendto(packet + packets.PING_PAYLOAD, (dstsockaddr, 19321))
66
67    self.ExpectPacketOn(netid, msg, expected)
68    s.close()
69
70  def CheckTCPSYNPacket(self, version, netid, routing_mode):
71    s = self.BuildSocket(version, net_test.TCPSocket, netid, routing_mode)
72
73    myaddr = self.MyAddress(version, netid)
74    dstaddr = self.GetRemoteAddress(version)
75    dstsockaddr = self.GetRemoteSocketAddress(version)
76    desc, expected = packets.SYN(53, version, myaddr, dstaddr,
77                                 sport=None, seq=None)
78
79
80    # Non-blocking TCP connects always return EINPROGRESS.
81    self.assertRaisesErrno(errno.EINPROGRESS, s.connect, (dstsockaddr, 53))
82    msg = "IPv%s TCP connect: expected %s on %s" % (
83        version, desc, self.GetInterfaceName(netid))
84    self.ExpectPacketOn(netid, msg, expected)
85    s.close()
86
87  def CheckUDPPacket(self, version, netid, routing_mode):
88    s = self.BuildSocket(version, net_test.UDPSocket, netid, routing_mode)
89
90    myaddr = self.MyAddress(version, netid)
91    dstaddr = self.GetRemoteAddress(version)
92    dstsockaddr = self.GetRemoteSocketAddress(version)
93
94    desc, expected = packets.UDP(version, myaddr, dstaddr, sport=None)
95    msg = "IPv%s UDP %%s: expected %s on %s" % (
96        version, desc, self.GetInterfaceName(netid))
97
98    s.sendto(UDP_PAYLOAD, (dstsockaddr, 53))
99    self.ExpectPacketOn(netid, msg % "sendto", expected)
100
101    # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
102    if routing_mode != "ucast_oif":
103      s.connect((dstsockaddr, 53))
104      s.send(UDP_PAYLOAD)
105      self.ExpectPacketOn(netid, msg % "connect/send", expected)
106
107    s.close()
108
109  def CheckRawGrePacket(self, version, netid, routing_mode):
110    s = self.BuildSocket(version, net_test.RawGRESocket, netid, routing_mode)
111
112    inner_version = {4: 6, 6: 4}[version]
113    inner_src = self.MyAddress(inner_version, netid)
114    inner_dst = self.GetRemoteAddress(inner_version)
115    inner = bytes(packets.UDP(inner_version, inner_src, inner_dst, sport=None)[1])
116
117    ethertype = {4: net_test.ETH_P_IP, 6: net_test.ETH_P_IPV6}[inner_version]
118    # A GRE header can be as simple as two zero bytes and the ethertype.
119    packet = struct.pack("!i", ethertype) + inner
120    myaddr = self.MyAddress(version, netid)
121    dstaddr = self.GetRemoteAddress(version)
122
123    s.sendto(packet, (dstaddr, IPPROTO_GRE))
124    desc, expected = packets.GRE(version, myaddr, dstaddr, ethertype, inner)
125    msg = "Raw IPv%d GRE with inner IPv%d UDP: expected %s on %s" % (
126        version, inner_version, desc, self.GetInterfaceName(netid))
127    self.ExpectPacketOn(netid, msg, expected)
128    s.close()
129
130  def CheckOutgoingPackets(self, routing_mode):
131    for _ in range(self.ITERATIONS):
132      for netid in self.tuns:
133
134        self.CheckPingPacket(4, netid, routing_mode, self.IPV4_PING)
135        # Kernel bug.
136        if routing_mode != "oif":
137          self.CheckPingPacket(6, netid, routing_mode, self.IPV6_PING)
138
139        # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
140        if routing_mode != "ucast_oif":
141          self.CheckTCPSYNPacket(4, netid, routing_mode)
142          self.CheckTCPSYNPacket(6, netid, routing_mode)
143          self.CheckTCPSYNPacket(5, netid, routing_mode)
144
145        self.CheckUDPPacket(4, netid, routing_mode)
146        self.CheckUDPPacket(6, netid, routing_mode)
147        self.CheckUDPPacket(5, netid, routing_mode)
148
149        # Creating raw sockets on non-root UIDs requires properly setting
150        # capabilities, which is hard to do from Python.
151        # IP_UNICAST_IF is not supported on raw sockets.
152        if routing_mode not in ["uid", "ucast_oif"]:
153          self.CheckRawGrePacket(4, netid, routing_mode)
154          self.CheckRawGrePacket(6, netid, routing_mode)
155
156  def testMarkRouting(self):
157    """Checks that socket marking selects the right outgoing interface."""
158    self.CheckOutgoingPackets("mark")
159
160  def testUidRouting(self):
161    """Checks that UID routing selects the right outgoing interface."""
162    self.CheckOutgoingPackets("uid")
163
164  def testOifRouting(self):
165    """Checks that oif routing selects the right outgoing interface."""
166    self.CheckOutgoingPackets("oif")
167
168  def testUcastOifRouting(self):
169    """Checks that ucast oif routing selects the right outgoing interface."""
170    self.CheckOutgoingPackets("ucast_oif")
171
172  def CheckRemarking(self, version, use_connect):
173    modes = ["mark", "oif", "uid"]
174    # Setting UNICAST_IF on connected sockets does not work.
175    if not use_connect:
176      modes += ["ucast_oif"]
177
178    for mode in modes:
179      s = net_test.UDPSocket(self.GetProtocolFamily(version))
180
181      # Figure out what packets to expect.
182      sport = net_test.BindRandomPort(version, s)
183      dstaddr = {4: self.IPV4_ADDR, 6: self.IPV6_ADDR}[version]
184      unspec = {4: "0.0.0.0", 6: "::"}[version]  # Placeholder.
185      desc, expected = packets.UDP(version, unspec, dstaddr, sport)
186
187      # If we're testing connected sockets, connect the socket on the first
188      # netid now.
189      if use_connect:
190        netid = list(self.tuns.keys())[0]
191        self.SelectInterface(s, netid, mode)
192        s.connect((dstaddr, 53))
193        expected.src = self.MyAddress(version, netid)
194
195      # For each netid, select that network without closing the socket, and
196      # check that the packets sent on that socket go out on the right network.
197      #
198      # For connected sockets, routing is cached in the socket's destination
199      # cache entry. In this case, we check that selecting the network a second
200      # time on the same socket (except via SO_BINDTODEVICE, or SO_MARK on 5.0+
201      # kernels) does not change routing, but that subsequently invalidating the
202      # destination cache entry does. This is a bug in the kernel because
203      # re-selecting the netid should cause routing to change, and future
204      # kernels may fix this bug for per-UID routing and ucast_oif routing like
205      # they already have for mark-based routing. But until they do, this
206      # behaviour provides a convenient way to check that InvalidateDstCache
207      # actually works.
208      prevnetid = None
209      for netid in self.tuns:
210        self.SelectInterface(s, netid, mode)
211        if not use_connect:
212          expected.src = self.MyAddress(version, netid)
213
214        def ExpectSendUsesNetid(netid):
215          connected_str = "Connected" if use_connect else "Unconnected"
216          msg = "%s UDPv%d socket remarked using %s: expecting %s on %s" % (
217              connected_str, version, mode, desc, self.GetInterfaceName(netid))
218          if use_connect:
219            s.send(UDP_PAYLOAD)
220          else:
221            s.sendto(UDP_PAYLOAD, (dstaddr, 53))
222          self.ExpectPacketOn(netid, msg, expected)
223
224        # Does this socket have a stale dst cache entry that we need to clear?
225        def SocketHasStaleDstCacheEntry():
226          if not prevnetid:
227            # This is the first time we're marking the socket.
228            return False
229          if not use_connect:
230            # Non-connected sockets never have dst cache entries.
231            return False
232          if mode in ["uid", "ucast_oif"]:
233            # No kernel invalidates the dst cache entry if the UID or the
234            # UCAST_OIF socket option changes.
235            return True
236          if mode == "oif":
237            # Changing SO_BINDTODEVICE always invalidates the dst cache entry.
238            return False
239          if mode == "mark":
240            # Changing the mark invalidates the dst cache entry in 5.0+.
241            return net_test.LINUX_VERSION < (5, 0, 0)
242          raise AssertionError("%s must be one of %s" % (mode, modes))
243
244        if SocketHasStaleDstCacheEntry():
245            ExpectSendUsesNetid(prevnetid)
246            # ... until we invalidate it.
247            self.InvalidateDstCache(version, prevnetid)
248
249        # In any case, future sends must be correct.
250        ExpectSendUsesNetid(netid)
251
252        self.SelectInterface(s, None, mode)
253        prevnetid = netid
254
255      s.close()
256
257  def testIPv4Remarking(self):
258    """Checks that updating the mark on an IPv4 socket changes routing."""
259    self.CheckRemarking(4, False)
260    self.CheckRemarking(4, True)
261
262  def testIPv6Remarking(self):
263    """Checks that updating the mark on an IPv6 socket changes routing."""
264    self.CheckRemarking(6, False)
265    self.CheckRemarking(6, True)
266
267  def testIPv6StickyPktinfo(self):
268    for _ in range(self.ITERATIONS):
269      for netid in self.tuns:
270        s = net_test.UDPSocket(AF_INET6)
271
272        # Set a flowlabel.
273        net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead)
274        s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1)
275
276        # Set some destination options.
277        nonce = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
278        dstopts = b"".join([
279            b"\x11\x02",               # Next header=UDP, 24 bytes of options.
280            b"\x01\x06", b"\x00" * 6,  # PadN, 6 bytes of padding.
281            b"\x8b\x0c",               # ILNP nonce, 12 bytes.
282            nonce
283        ])
284        s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, dstopts)
285        s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 255)
286
287        pktinfo = multinetwork_base.MakePktInfo(6, None, self.ifindices[netid])
288
289        # Set the sticky pktinfo option.
290        s.setsockopt(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo)
291
292        # Specify the flowlabel in the destination address.
293        s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 53, 0xdead, 0))
294
295        sport = s.getsockname()[1]
296        srcaddr = self.MyAddress(6, netid)
297        expected = (scapy.IPv6(src=srcaddr, dst=net_test.IPV6_ADDR,
298                               fl=0xdead, hlim=255) /
299                    scapy.IPv6ExtHdrDestOpt(
300                        options=[scapy.PadN(optdata="\x00\x00\x00\x00\x00\x00"),
301                                 scapy.HBHOptUnknown(otype=0x8b,
302                                                     optdata=nonce)]) /
303                    scapy.UDP(sport=sport, dport=53) /
304                    UDP_PAYLOAD)
305        msg = "IPv6 UDP using sticky pktinfo: expected UDP packet on %s" % (
306            self.GetInterfaceName(netid))
307        self.ExpectPacketOn(netid, msg, expected)
308        s.close()
309
310  def CheckPktinfoRouting(self, version):
311    for _ in range(self.ITERATIONS):
312      for netid in self.tuns:
313        family = self.GetProtocolFamily(version)
314        s = net_test.UDPSocket(family)
315
316        if version == 6:
317          # Create a flowlabel so we can use it.
318          net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xbeef)
319
320          # Specify some arbitrary options.
321          # We declare the flowlabel as ctypes.c_uint32 because on a 32-bit
322          # Python interpreter an integer greater than 0x7fffffff (such as our
323          # chosen flowlabel after being passed through htonl) is converted to
324          # long, and _MakeMsgControl doesn't know what to do with longs.
325          cmsgs = [
326              (net_test.SOL_IPV6, IPV6_HOPLIMIT, 39),
327              (net_test.SOL_IPV6, IPV6_TCLASS, 0x83),
328              (net_test.SOL_IPV6, IPV6_FLOWINFO, ctypes.c_uint(htonl(0xbeef))),
329          ]
330        else:
331          # Support for setting IPv4 TOS and TTL via cmsg only appeared in 3.13.
332          cmsgs = []
333          s.setsockopt(net_test.SOL_IP, IP_TTL, 39)
334          s.setsockopt(net_test.SOL_IP, IP_TOS, 0x83)
335
336        dstaddr = self.GetRemoteAddress(version)
337        self.SendOnNetid(version, s, dstaddr, 53, netid, UDP_PAYLOAD, cmsgs)
338
339        sport = s.getsockname()[1]
340        srcaddr = self.MyAddress(version, netid)
341
342        desc, expected = packets.UDPWithOptions(version, srcaddr, dstaddr,
343                                                sport=sport)
344
345        msg = "IPv%d UDP using pktinfo routing: expected %s on %s" % (
346            version, desc, self.GetInterfaceName(netid))
347        self.ExpectPacketOn(netid, msg, expected)
348
349        s.close()
350
351  def testIPv4PktinfoRouting(self):
352    self.CheckPktinfoRouting(4)
353
354  def testIPv6PktinfoRouting(self):
355    self.CheckPktinfoRouting(6)
356
357
358class MarkTest(multinetwork_base.InboundMarkingTest):
359
360  def CheckReflection(self, version, gen_packet, gen_reply):
361    """Checks that replies go out on the same interface as the original.
362
363    For each combination:
364     - Calls gen_packet to generate a packet to that IP address.
365     - Writes the packet generated by gen_packet on the given tun
366       interface, causing the kernel to receive it.
367     - Checks that the kernel's reply matches the packet generated by
368       gen_reply.
369
370    Args:
371      version: An integer, 4 or 6.
372      gen_packet: A function taking an IP version (an integer), a source
373        address and a destination address (strings), and returning a scapy
374        packet.
375      gen_reply: A function taking the same arguments as gen_packet,
376        plus a scapy packet, and returning a scapy packet.
377    """
378    for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
379      # Generate a test packet.
380      desc, packet = gen_packet(version, remoteaddr, myaddr)
381
382      # Test with mark reflection enabled and disabled.
383      for reflect in [0, 1]:
384        self.SetMarkReflectSysctls(reflect)
385        # HACK: IPv6 ping replies always do a routing lookup with the
386        # interface the ping came in on. So even if mark reflection is not
387        # working, IPv6 ping replies will be properly reflected. Don't
388        # fail when that happens.
389        if reflect or desc == "ICMPv6 echo":
390          reply_desc, reply = gen_reply(version, myaddr, remoteaddr, packet)
391        else:
392          reply_desc, reply = None, None
393
394        msg = self._FormatMessage(iif, ip_if, "reflect=%d" % reflect,
395                                  desc, reply_desc)
396        self._ReceiveAndExpectResponse(netid, packet, reply, msg)
397
398  def SYNToClosedPort(self, *args):
399    return packets.SYN(999, *args)
400
401  def testIPv4ICMPErrorsReflectMark(self):
402    self.CheckReflection(4, packets.UDP, packets.ICMPPortUnreachable)
403
404  def testIPv6ICMPErrorsReflectMark(self):
405    self.CheckReflection(6, packets.UDP, packets.ICMPPortUnreachable)
406
407  def testIPv4PingRepliesReflectMarkAndTos(self):
408    self.CheckReflection(4, packets.ICMPEcho, packets.ICMPReply)
409
410  def testIPv6PingRepliesReflectMarkAndTos(self):
411    self.CheckReflection(6, packets.ICMPEcho, packets.ICMPReply)
412
413  def testIPv4RSTsReflectMark(self):
414    self.CheckReflection(4, self.SYNToClosedPort, packets.RST)
415
416  def testIPv6RSTsReflectMark(self):
417    self.CheckReflection(6, self.SYNToClosedPort, packets.RST)
418
419
420class TCPAcceptTest(multinetwork_base.InboundMarkingTest):
421
422  MODE_BINDTODEVICE = "SO_BINDTODEVICE"
423  MODE_INCOMING_MARK = "incoming mark"
424  MODE_EXPLICIT_MARK = "explicit mark"
425  MODE_UID = "uid"
426
427  @classmethod
428  def setUpClass(cls):
429    super(TCPAcceptTest, cls).setUpClass()
430
431    # Open a port so we can observe SYN+ACKs. Since it's a dual-stack socket it
432    # will accept both IPv4 and IPv6 connections. We do this here instead of in
433    # each test so we can use the same socket every time. That way, if a kernel
434    # bug causes incoming packets to mark the listening socket instead of the
435    # accepted socket, the test will fail as soon as the next address/interface
436    # combination is tried.
437    cls.listensocket = net_test.IPv6TCPSocket()
438    cls.listenport = net_test.BindRandomPort(6, cls.listensocket)
439
440  def _SetTCPMarkAcceptSysctl(self, value):
441    self.SetSysctl(TCP_MARK_ACCEPT_SYSCTL, value)
442
443  def CheckTCPConnection(self, mode, listensocket, netid, version,
444                         myaddr, remoteaddr, packet, reply, msg):
445    establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1]
446
447    # Attempt to confuse the kernel.
448    self.InvalidateDstCache(version, netid)
449
450    self.ReceivePacketOn(netid, establishing_ack)
451
452    # If we're using UID routing, the accept() call has to be run as a UID that
453    # is routed to the specified netid, because the UID of the socket returned
454    # by accept() is the effective UID of the process that calls it. It doesn't
455    # need to be the same UID; any UID that selects the same interface will do.
456    with net_test.RunAsUid(self.UidForNetid(netid)):
457      s, _ = listensocket.accept()
458
459    try:
460      # Check that data sent on the connection goes out on the right interface.
461      desc, data = packets.ACK(version, myaddr, remoteaddr, establishing_ack,
462                               payload=UDP_PAYLOAD)
463      s.send(UDP_PAYLOAD)
464      self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data)
465      self.InvalidateDstCache(version, netid)
466
467      # Keep up our end of the conversation.
468      ack = packets.ACK(version, remoteaddr, myaddr, data)[1]
469      self.InvalidateDstCache(version, netid)
470      self.ReceivePacketOn(netid, ack)
471
472      mark = self.GetSocketMark(s)
473    finally:
474      self.InvalidateDstCache(version, netid)
475      s.close()
476      self.InvalidateDstCache(version, netid)
477
478    if mode == self.MODE_INCOMING_MARK:
479      self.assertEqual(netid, mark & self.NETID_FWMASK,
480                        msg + ": Accepted socket: Expected mark %d, got %d" % (
481                            netid, mark))
482    elif mode != self.MODE_EXPLICIT_MARK:
483      self.assertEqual(0, self.GetSocketMark(listensocket))
484
485    # Check the FIN was sent on the right interface, and ack it. We don't expect
486    # this to fail because by the time the connection is established things are
487    # likely working, but a) extra tests are always good and b) extra packets
488    # like the FIN (and retransmitted FINs) could cause later tests that expect
489    # no packets to fail.
490    desc, fin = packets.FIN(version, myaddr, remoteaddr, ack)
491    self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin)
492
493    desc, finack = packets.FIN(version, remoteaddr, myaddr, fin)
494    self.ReceivePacketOn(netid, finack)
495
496    # Since we called close() earlier, the userspace socket object is gone, so
497    # the socket has no UID. If we're doing UID routing, the ack might be routed
498    # incorrectly. Not much we can do here.
499    desc, finackack = packets.ACK(version, myaddr, remoteaddr, finack)
500    self.ExpectPacketOn(netid, msg + ": expecting final ack", finackack)
501
502  def CheckTCP(self, version, modes):
503    """Checks that incoming TCP connections work.
504
505    Args:
506      version: An integer, 4 or 6.
507      modes: A list of modes to excercise.
508    """
509    for syncookies in [0, 2]:
510      for mode in modes:
511        for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
512          listensocket = self.listensocket
513          listenport = listensocket.getsockname()[1]
514
515          accept_sysctl = 1 if mode == self.MODE_INCOMING_MARK else 0
516          self._SetTCPMarkAcceptSysctl(accept_sysctl)
517          self.SetMarkReflectSysctls(accept_sysctl)
518
519          bound_dev = iif if mode == self.MODE_BINDTODEVICE else None
520          self.BindToDevice(listensocket, bound_dev)
521
522          mark = netid if mode == self.MODE_EXPLICIT_MARK else 0
523          self.SetSocketMark(listensocket, mark)
524
525          uid = self.UidForNetid(netid) if mode == self.MODE_UID else 0
526          os.fchown(listensocket.fileno(), uid, -1)
527
528          # Generate the packet here instead of in the outer loop, so
529          # subsequent TCP connections use different source ports and
530          # retransmissions from old connections don't confuse subsequent
531          # tests.
532          desc, packet = packets.SYN(listenport, version, remoteaddr, myaddr)
533
534          if mode:
535            reply_desc, reply = packets.SYNACK(version, myaddr, remoteaddr,
536                                               packet)
537          else:
538            reply_desc, reply = None, None
539
540          extra = "mode=%s, syncookies=%d" % (mode, syncookies)
541          msg = self._FormatMessage(iif, ip_if, extra, desc, reply_desc)
542          reply = self._ReceiveAndExpectResponse(netid, packet, reply, msg)
543          if reply:
544            self.CheckTCPConnection(mode, listensocket, netid, version, myaddr,
545                                    remoteaddr, packet, reply, msg)
546
547  def testBasicTCP(self):
548    self.CheckTCP(4, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
549    self.CheckTCP(6, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
550
551  def testIPv4MarkAccept(self):
552    self.CheckTCP(4, [self.MODE_INCOMING_MARK])
553
554  def testIPv6MarkAccept(self):
555    self.CheckTCP(6, [self.MODE_INCOMING_MARK])
556
557  def testIPv4UidAccept(self):
558    self.CheckTCP(4, [self.MODE_UID])
559
560  def testIPv6UidAccept(self):
561    self.CheckTCP(6, [self.MODE_UID])
562
563  def testIPv6ExplicitMark(self):
564    self.CheckTCP(6, [self.MODE_EXPLICIT_MARK])
565
566class RIOTest(multinetwork_base.MultiNetworkBaseTest):
567  """Test for IPv6 RFC 4191 route information option
568
569  Relevant kernel commits:
570    upstream:
571      f104a567e673 ipv6: use rt6_get_dflt_router to get default router in rt6_route_rcv
572      bbea124bc99d net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs.
573
574    android-4.9:
575      d860b2e8a7f1 FROMLIST: net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs
576
577    android-4.4:
578      e953f89b8563 net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs.
579
580    android-4.1:
581      84f2f47716cd net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs.
582
583    android-3.18:
584      65f8936934fa net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs.
585
586    android-3.10:
587      161e88ebebc7 net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs.
588
589  """
590
591  def setUp(self):
592    super(RIOTest, self).setUp()
593    self.NETID = random.choice(self.NETIDS)
594    self.IFACE = self.GetInterfaceName(self.NETID)
595    # return sysctls to default values before each test case
596    self.SetAcceptRaRtInfoMinPlen(0)
597    self.SetAcceptRaRtInfoMaxPlen(0)
598    if multinetwork_base.HAVE_ACCEPT_RA_MIN_LFT:
599      self.SetAcceptRaMinLft(0)
600    if multinetwork_base.HAVE_RA_HONOR_PIO_LIFE:
601      self.SetRaHonorPioLife(0)
602    if multinetwork_base.HAVE_RA_HONOR_PIO_PFLAG:
603      self.SetRaHonorPioPflag(0)
604
605  def GetRoutingTable(self):
606    if multinetwork_base.HAVE_AUTOCONF_TABLE:
607      return self._TableForNetid(self.NETID)
608    else:
609      # main table
610      return 254
611
612  def SetAcceptRaRtInfoMinPlen(self, plen):
613    self.SetSysctl(
614        "/proc/sys/net/ipv6/conf/%s/accept_ra_rt_info_min_plen"
615        % self.IFACE, plen)
616
617  def GetAcceptRaRtInfoMinPlen(self):
618    return int(self.GetSysctl(
619        "/proc/sys/net/ipv6/conf/%s/accept_ra_rt_info_min_plen" % self.IFACE))
620
621  def SetAcceptRaRtInfoMaxPlen(self, plen):
622    self.SetSysctl(
623        "/proc/sys/net/ipv6/conf/%s/accept_ra_rt_info_max_plen"
624        % self.IFACE, plen)
625
626  def GetAcceptRaRtInfoMaxPlen(self):
627    return int(self.GetSysctl(
628        "/proc/sys/net/ipv6/conf/%s/accept_ra_rt_info_max_plen" % self.IFACE))
629
630  def SetAcceptRaMinLft(self, min_lft):
631    self.SetSysctl(
632        "/proc/sys/net/ipv6/conf/%s/accept_ra_min_lft" % self.IFACE, min_lft)
633
634  def SetRaHonorPioPflag(self, val):
635    self.SetSysctl(
636        "/proc/sys/net/ipv6/conf/%s/ra_honor_pio_pflag" % self.IFACE, val)
637
638  def GetAcceptRaMinLft(self):
639    return int(self.GetSysctl(
640        "/proc/sys/net/ipv6/conf/%s/accept_ra_min_lft" % self.IFACE))
641
642  def SetRaHonorPioLife(self, enabled):
643    self.SetSysctl(
644        "/proc/sys/net/ipv6/conf/%s/ra_honor_pio_life" % self.IFACE, enabled)
645
646  def GetRaHonorPioLife(self):
647    return int(self.GetSysctl(
648        "/proc/sys/net/ipv6/conf/%s/ra_honor_pio_life" % self.IFACE))
649
650  def SendRIO(self, rtlifetime, plen, prefix, prf):
651    options = scapy.ICMPv6NDOptRouteInfo(rtlifetime=rtlifetime, plen=plen,
652                                         prefix=prefix, prf=prf)
653    self.SendRA(self.NETID, options=(options,))
654
655  def FindRoutesWithDestination(self, destination):
656    canonical = net_test.CanonicalizeIPv6Address(destination)
657    return [r for _, r in self.iproute.DumpRoutes(6, self.GetRoutingTable())
658            if ('RTA_DST' in r and r['RTA_DST'] == canonical)]
659
660  def FindRoutesWithGateway(self):
661    return [r for _, r in self.iproute.DumpRoutes(6, self.GetRoutingTable())
662            if 'RTA_GATEWAY' in r]
663
664  def CountRoutes(self):
665    return len(self.iproute.DumpRoutes(6, self.GetRoutingTable()))
666
667  def GetRouteExpiration(self, route):
668    return float(route['RTA_CACHEINFO'].expires) / 100.0
669
670  def AssertExpirationInRange(self, routes, lifetime, epsilon):
671    self.assertTrue(routes)
672    found = False
673    # Assert that at least one route in routes has the expected lifetime
674    for route in routes:
675      expiration = self.GetRouteExpiration(route)
676      if expiration < lifetime - epsilon:
677        continue
678      if expiration > lifetime + epsilon:
679        continue
680      found = True
681    self.assertTrue(found)
682
683  def DelRA6(self, prefix, plen):
684    version = 6
685    netid = self.NETID
686    table = self._TableForNetid(netid)
687    router = self._RouterAddress(netid, version)
688    ifindex = self.ifindices[netid]
689    self.iproute._Route(version, iproute.RTPROT_RA, iproute.RTM_DELROUTE,
690                        table, prefix, plen, router, ifindex, None, None)
691
692  def testSetAcceptRaRtInfoMinPlen(self):
693    for plen in range(-1, 130):
694      self.SetAcceptRaRtInfoMinPlen(plen)
695      self.assertEqual(plen, self.GetAcceptRaRtInfoMinPlen())
696
697  def testSetAcceptRaRtInfoMaxPlen(self):
698    for plen in range(-1, 130):
699      self.SetAcceptRaRtInfoMaxPlen(plen)
700      self.assertEqual(plen, self.GetAcceptRaRtInfoMaxPlen())
701
702  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
703                       "need support for per-table autoconf")
704  def testZeroRtLifetime(self):
705    PREFIX = "2001:db8:8901:2300::"
706    RTLIFETIME = 73500
707    PLEN = 56
708    PRF = 0
709    self.SetAcceptRaRtInfoMaxPlen(PLEN)
710    self.SendRIO(RTLIFETIME, PLEN, PREFIX, PRF)
711    # Give the kernel time to notice our RA
712    time.sleep(0.01)
713    self.assertTrue(self.FindRoutesWithDestination(PREFIX))
714    # RIO with rtlifetime = 0 should remove from routing table
715    self.SendRIO(0, PLEN, PREFIX, PRF)
716    # Give the kernel time to notice our RA
717    time.sleep(0.01)
718    self.assertFalse(self.FindRoutesWithDestination(PREFIX))
719
720  def testMinPrefixLenRejection(self):
721    PREFIX = "2001:db8:8902:2345::"
722    RTLIFETIME = 70372
723    PRF = 0
724    # sweep from high to low to avoid spurious failures from late arrivals.
725    for plen in range(130, 1, -1):
726      self.SetAcceptRaRtInfoMinPlen(plen)
727      # RIO with plen < min_plen should be ignored
728      self.SendRIO(RTLIFETIME, plen - 1, PREFIX, PRF)
729    # Give the kernel time to notice our RAs
730    time.sleep(0.1)
731    # Expect no routes
732    routes = self.FindRoutesWithDestination(PREFIX)
733    self.assertFalse(routes)
734
735  def testMaxPrefixLenRejection(self):
736    PREFIX = "2001:db8:8903:2345::"
737    RTLIFETIME = 73078
738    PRF = 0
739    # sweep from low to high to avoid spurious failures from late arrivals.
740    for plen in range(-1, 128, 1):
741      self.SetAcceptRaRtInfoMaxPlen(plen)
742      # RIO with plen > max_plen should be ignored
743      self.SendRIO(RTLIFETIME, plen + 1, PREFIX, PRF)
744    # Give the kernel time to notice our RAs
745    time.sleep(0.1)
746    # Expect no routes
747    routes = self.FindRoutesWithDestination(PREFIX)
748    self.assertFalse(routes)
749
750  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
751                       "need support for per-table autoconf")
752  def testSimpleAccept(self):
753    PREFIX = "2001:db8:8904:2345::"
754    RTLIFETIME = 9993
755    PRF = 0
756    PLEN = 56
757    self.SetAcceptRaRtInfoMinPlen(48)
758    self.SetAcceptRaRtInfoMaxPlen(64)
759    self.SendRIO(RTLIFETIME, PLEN, PREFIX, PRF)
760    # Give the kernel time to notice our RA
761    time.sleep(0.01)
762    routes = self.FindRoutesWithGateway()
763    self.AssertExpirationInRange(routes, RTLIFETIME, 1)
764    self.DelRA6(PREFIX, PLEN)
765
766  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
767                       "need support for per-table autoconf")
768  def testEqualMinMaxAccept(self):
769    PREFIX = "2001:db8:8905:2345::"
770    RTLIFETIME = 6326
771    PLEN = 21
772    PRF = 0
773    self.SetAcceptRaRtInfoMinPlen(PLEN)
774    self.SetAcceptRaRtInfoMaxPlen(PLEN)
775    self.SendRIO(RTLIFETIME, PLEN, PREFIX, PRF)
776    # Give the kernel time to notice our RA
777    time.sleep(0.01)
778    routes = self.FindRoutesWithGateway()
779    self.AssertExpirationInRange(routes, RTLIFETIME, 1)
780    self.DelRA6(PREFIX, PLEN)
781
782  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
783                       "need support for per-table autoconf")
784  def testZeroLengthPrefix(self):
785    PREFIX = "2001:db8:8906:2345::"
786    RTLIFETIME = self.RA_VALIDITY * 2
787    PLEN = 0
788    PRF = 0
789    # Max plen = 0 still allows default RIOs!
790    self.SetAcceptRaRtInfoMaxPlen(PLEN)
791    self.SendRA(self.NETID)
792    # Give the kernel time to notice our RA
793    time.sleep(0.01)
794    default = self.FindRoutesWithGateway()
795    self.AssertExpirationInRange(default, self.RA_VALIDITY, 1)
796    # RIO with prefix length = 0, should overwrite default route lifetime
797    # note that the RIO lifetime overwrites the RA lifetime.
798    self.SendRIO(RTLIFETIME, PLEN, PREFIX, PRF)
799    # Give the kernel time to notice our RA
800    time.sleep(0.01)
801    default = self.FindRoutesWithGateway()
802    self.AssertExpirationInRange(default, RTLIFETIME, 1)
803    self.DelRA6(PREFIX, PLEN)
804
805  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
806                       "need support for per-table autoconf")
807  def testManyRIOs(self):
808    RTLIFETIME = 68012
809    PLEN = 56
810    PRF = 0
811    COUNT = 1000
812    baseline = self.CountRoutes()
813    self.SetAcceptRaRtInfoMaxPlen(56)
814    # Send many RIOs compared to the expected number on a healthy system.
815    for i in range(0, COUNT):
816      prefix = "2001:db8:%x:1100::" % i
817      self.SendRIO(RTLIFETIME, PLEN, prefix, PRF)
818    time.sleep(0.1)
819    self.assertEqual(COUNT + baseline, self.CountRoutes())
820    for i in range(0, COUNT):
821      prefix = "2001:db8:%x:1100::" % i
822      self.DelRA6(prefix, PLEN)
823    # Expect that we can return to baseline config without lingering routes.
824    self.assertEqual(baseline, self.CountRoutes())
825
826  # Contextually, testAcceptRa tests do not belong in RIOTest, but as it
827  # turns out, RIOTest has all the useful helpers defined for these tests.
828  # TODO: Rename test class or merge RIOTest with RATest.
829  @unittest.skipUnless(multinetwork_base.HAVE_ACCEPT_RA_MIN_LFT,
830                       "need support for accept_ra_min_lft")
831  def testAcceptRaMinLftReadWrite(self):
832    self.SetAcceptRaMinLft(500)
833    self.assertEqual(500, self.GetAcceptRaMinLft())
834
835  @unittest.skipUnless(multinetwork_base.HAVE_RA_HONOR_PIO_LIFE,
836                       "need support for ra_honor_pio_life")
837  def testRaHonorPioLifeReadWrite(self):
838    self.assertEqual(0, self.GetRaHonorPioLife())
839    self.SetRaHonorPioLife(1)
840    self.assertEqual(1, self.GetRaHonorPioLife())
841
842  @unittest.skipUnless(multinetwork_base.HAVE_RA_HONOR_PIO_LIFE,
843                       "need support for ra_honor_pio_life")
844  def testRaHonorPioLife(self):
845    self.SetRaHonorPioLife(1)
846
847    # Test setup has sent an initial RA -- expire it.
848    self.SendRA(self.NETID, routerlft=0, piolft=0)
849    time.sleep(0.1) # Give the kernel time to notice our RA
850
851    # Assert that the address was deleted.
852    self.assertIsNone(self.MyAddress(6, self.NETID))
853
854  @unittest.skipUnless(multinetwork_base.HAVE_ACCEPT_RA_MIN_LFT,
855                       "need support for accept_ra_min_lft")
856  def testAcceptRaMinLftRouterLifetime(self):
857    self.SetAcceptRaMinLft(500)
858
859    # Test setup has sent an initial RA. Expire it and test that the RA with
860    # lifetime 0 deletes the default route.
861    self.SendRA(self.NETID, routerlft=0, piolft=0)
862    time.sleep(0.1) # Give the kernel time to notice our RA
863    self.assertEqual([], self.FindRoutesWithGateway())
864
865    # RA with lifetime 400 is ignored
866    self.SendRA(self.NETID, routerlft=400)
867    time.sleep(0.1) # Give the kernel time to notice our RA
868    self.assertEqual([], self.FindRoutesWithGateway())
869
870    # RA with lifetime 600 is processed
871    self.SendRA(self.NETID, routerlft=600)
872    time.sleep(0.1) # Give the kernel time to notice our RA
873    # SendRA sets routerlft to 0 if HAVE_AUTOCONF_TABLE is false...
874    # TODO: Fix this correctly.
875    if multinetwork_base.HAVE_AUTOCONF_TABLE:
876      self.assertEqual(1, len(self.FindRoutesWithGateway()))
877
878  @unittest.skipUnless(multinetwork_base.HAVE_ACCEPT_RA_MIN_LFT,
879                       "need support for accept_ra_min_lft")
880  def testAcceptRaMinLftPIOLifetime(self):
881    self.SetAcceptRaMinLft(500)
882
883    # Test setup has sent an initial RA -- expire it.
884    self.SendRA(self.NETID, routerlft=0, piolft=0)
885    time.sleep(0.1) # Give the kernel time to notice our RA
886    # Check that the prefix route was deleted.
887    prefixroutes = self.FindRoutesWithDestination(self.OnlinkPrefix(6, self.NETID))
888    self.assertEqual([], prefixroutes)
889
890    # Sending a 0-lifetime PIO does not cause the address to be deleted, see
891    # rfc2462#section-5.5.3.
892    address = self.MyAddress(6, self.NETID)
893    self.iproute.DelAddress(address, 64, self.ifindices[self.NETID])
894
895    # PIO with lifetime 400 is ignored
896    self.SendRA(self.NETID, piolft=400)
897    time.sleep(0.1) # Give the kernel time to notice our RA
898    self.assertIsNone(self.MyAddress(6, self.NETID))
899
900    # PIO with lifetime 600 is processed
901    self.SendRA(self.NETID, piolft=600)
902    time.sleep(0.1) # Give the kernel time to notice our RA
903    self.assertIsNotNone(self.MyAddress(6, self.NETID))
904
905  @unittest.skipUnless(multinetwork_base.HAVE_ACCEPT_RA_MIN_LFT,
906                       "need support for accept_ra_min_lft")
907  def testAcceptRaMinLftRIOLifetime(self):
908    PREFIX = "2001:db8:8901:2300::"
909    PLEN = 64
910    PRF = 0
911
912    self.SetAcceptRaRtInfoMaxPlen(PLEN)
913    self.SetAcceptRaMinLft(500)
914
915    # RIO with lifetime 400 is ignored
916    self.SendRIO(400, PLEN, PREFIX, PRF)
917    time.sleep(0.1) # Give the kernel time to notice our RA
918    self.assertFalse(self.FindRoutesWithDestination(PREFIX))
919
920    # RIO with lifetime 600 is processed
921    self.SendRIO(600, PLEN, PREFIX, PRF)
922    time.sleep(0.1) # Give the kernel time to notice our RA
923    self.assertTrue(self.FindRoutesWithDestination(PREFIX))
924
925    # RIO with lifetime 0 deletes the route
926    self.SendRIO(0, PLEN, PREFIX, PRF)
927    time.sleep(0.1) # Give the kernel time to notice our RA
928    self.assertFalse(self.FindRoutesWithDestination(PREFIX))
929
930  @unittest.skipUnless(multinetwork_base.HAVE_RA_HONOR_PIO_PFLAG,
931                       "needs support for ra_honor_pio_pflag")
932  def testPioPflag(self):
933    self.SetRaHonorPioPflag(1);
934
935    # Test setup has sent an initial RA -- expire it.
936    self.SendRA(self.NETID, routerlft=0, piolft=0)
937    time.sleep(0.1) # Give the kernel time to notice our RA
938    # Check that the prefix route was deleted.
939    prefixroutes = self.FindRoutesWithDestination(self.OnlinkPrefix(6, self.NETID))
940    self.assertEqual([], prefixroutes)
941
942    # Sending a 0-lifetime PIO does not cause the address to be deleted, see
943    # rfc2462#section-5.5.3.
944    address = self.MyAddress(6, self.NETID)
945    self.iproute.DelAddress(address, 64, self.ifindices[self.NETID])
946
947    # PIO with p-flag is ignored
948    self.SendRA(self.NETID, piopflag=1)
949    time.sleep(0.1) # Give the kernel time to notice our RA
950    self.assertIsNone(self.MyAddress(6, self.NETID))
951
952    self.SetRaHonorPioPflag(0);
953    # PIO with p-flag is processed
954    self.SendRA(self.NETID, piopflag=1)
955    time.sleep(0.1) # Give the kernel time to notice our RA
956    self.assertIsNotNone(self.MyAddress(6, self.NETID))
957
958
959class RATest(multinetwork_base.MultiNetworkBaseTest):
960
961  ND_ROUTER_ADVERT = 134
962  ND_OPT_PIO = 3
963  ND_OPT_PREF64 = 38
964  NDOptHeader = cstruct.Struct("ndopt_header", "!BB", "type length")
965  Pref64Option = cstruct.Struct("pref64_option", "!BBH12s",
966                                "type length lft_plc prefix")
967
968  # Android Common Kernels are always based off of an LTS release,
969  # skipping this (always failing due to lack of an ACK specific patch) test
970  # on Linus's kernels (and various other upstream dev branches) allows
971  # for easier testing of Linux rc's and various developer trees.
972  @unittest.skipUnless(net_test.IS_STABLE, "not STABLE/LTS kernel")
973  def testHasAutoconfTable(self):
974    self.assertTrue(multinetwork_base.HAVE_AUTOCONF_TABLE)
975
976  def testDoesNotHaveObsoleteSysctl(self):
977    self.assertFalse(os.path.isfile(
978        "/proc/sys/net/ipv6/route/autoconf_table_offset"))
979
980  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
981                       "no support for per-table autoconf")
982  def testPurgeDefaultRouters(self):
983
984    def CheckIPv6Connectivity(expect_connectivity):
985      for netid in self.NETIDS:
986        s = net_test.UDPSocket(AF_INET6)
987        self.SetSocketMark(s, netid)
988        if expect_connectivity:
989          self.assertTrue(s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 1234)))
990        else:
991          self.assertRaisesErrno(errno.ENETUNREACH, s.sendto, UDP_PAYLOAD,
992                                 (net_test.IPV6_ADDR, 1234))
993        s.close()
994
995    try:
996      CheckIPv6Connectivity(True)
997      self.SetIPv6SysctlOnAllIfaces("accept_ra", 1)
998      self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1)
999      CheckIPv6Connectivity(False)
1000    finally:
1001      self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0)
1002      for netid in self.NETIDS:
1003        self.SendRA(netid)
1004      CheckIPv6Connectivity(True)
1005
1006  def testOnlinkCommunication(self):
1007    """Checks that on-link communication goes direct and not through routers."""
1008    for netid in self.tuns:
1009      # Send a UDP packet to a random on-link destination.
1010      s = net_test.UDPSocket(AF_INET6)
1011      iface = self.GetInterfaceName(netid)
1012      self.BindToDevice(s, iface)
1013      # dstaddr can never be our address because GetRandomDestination only fills
1014      # in the lower 32 bits, but our address has 0xff in the byte before that
1015      # (since it's constructed from the EUI-64 and so has ff:fe in the middle).
1016      dstaddr = self.GetRandomDestination(self.OnlinkPrefix(6, netid))
1017      s.sendto(UDP_PAYLOAD, (dstaddr, 53))
1018
1019      # Expect an NS for that destination on the interface.
1020      myaddr = self.MyAddress(6, netid)
1021      mymac = self.MyMacAddress(netid)
1022      desc, expected = packets.NS(myaddr, dstaddr, mymac)
1023      msg = "Sending UDP packet to on-link destination: expecting %s" % desc
1024      time.sleep(0.0001)  # Required to make the test work on kernel 3.1(!)
1025      self.ExpectPacketOn(netid, msg, expected)
1026
1027      # Send an NA.
1028      tgtmac = "02:00:00:00:%02x:99" % netid
1029      _, reply = packets.NA(dstaddr, myaddr, tgtmac)
1030      # Don't use ReceivePacketOn, since that uses the router's MAC address as
1031      # the source. Instead, construct our own Ethernet header with source
1032      # MAC of tgtmac.
1033      reply = scapy.Ether(src=tgtmac, dst=mymac) / reply
1034      self.ReceiveEtherPacketOn(netid, reply)
1035
1036      # Expect the kernel to send the original UDP packet now that the ND cache
1037      # entry has been populated.
1038      sport = s.getsockname()[1]
1039      desc, expected = packets.UDP(6, myaddr, dstaddr, sport=sport)
1040      msg = "After NA response, expecting %s" % desc
1041      self.ExpectPacketOn(netid, msg, expected)
1042
1043      s.close()
1044
1045  # This test documents a known issue: routing tables are never deleted.
1046  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
1047                       "no support for per-table autoconf")
1048  def testLeftoverRoutes(self):
1049    def GetNumRoutes():
1050      with open("/proc/net/ipv6_route") as ipv6_route:
1051        return len(ipv6_route.readlines())
1052
1053    num_routes = GetNumRoutes()
1054    for i in range(10, 20):
1055      try:
1056        self.tuns[i] = self.CreateTunInterface(i)
1057        self.SendRA(i)
1058        self.tuns[i].close()
1059      finally:
1060        del self.tuns[i]
1061    self.assertLess(num_routes, GetNumRoutes())
1062
1063  def SendNdUseropt(self, option):
1064    options = scapy.ICMPv6NDOptRouteInfo(rtlifetime=rtlifetime, plen=plen,
1065                                         prefix=prefix, prf=prf)
1066    self.SendRA(self.NETID, options=(options,))
1067
1068  def MakePref64Option(self, prefix, lifetime):
1069    prefix = inet_pton(AF_INET6, prefix)[:12]
1070    lft_plc = (lifetime & 0xfff8) | 0  # 96-bit prefix length
1071    return self.Pref64Option((self.ND_OPT_PREF64, 2, lft_plc, prefix))
1072
1073  def testPref64UserOption(self):
1074    # Open a netlink socket to receive RTM_NEWNDUSEROPT messages.
1075    s = netlink.NetlinkSocket(netlink.NETLINK_ROUTE, iproute.RTMGRP_ND_USEROPT)
1076
1077    # Send an RA with the PREF64 option.
1078    netid = random.choice(self.NETIDS)
1079    opt = self.MakePref64Option("64:ff9b::", 300)
1080    self.SendRA(netid, options=(opt.Pack(),))
1081
1082    # Check that we get an an RTM_NEWNDUSEROPT message on the socket with the
1083    # expected option.
1084    csocket.SetSocketTimeout(s.sock, 100)
1085
1086    needPIO = multinetwork_base.HAVE_USEROPT_PIO_FIX
1087    needPref64 = True
1088
1089    while needPIO or needPref64:
1090      try:
1091        data = s._Recv()
1092      except IOError as e:
1093        self.fail("Should have received an RTM_NEWNDUSEROPT message. "
1094                  "Please ensure the kernel supports receiving the "
1095                  "PREF64 RA option. Error: %s" % e)
1096      # Check that the message is received correctly.
1097      nlmsghdr, data = cstruct.Read(data, netlink.NLMsgHdr)
1098      self.assertEqual(iproute.RTM_NEWNDUSEROPT, nlmsghdr.type)
1099
1100      # print("data=[%s]\n" % data)
1101
1102      # Check the option contents.
1103      ndopthdr, data = cstruct.Read(data, iproute.NdUseroptMsg)
1104      self.assertEqual(AF_INET6, ndopthdr.family)
1105      self.assertEqual(self.ND_ROUTER_ADVERT, ndopthdr.icmp_type)
1106      self.assertEqual(0, ndopthdr.icmp_code)
1107
1108      self.assertLessEqual(ndopthdr.opts_len, len(data))
1109      data, leftover = data[:ndopthdr.opts_len], data[ndopthdr.opts_len:]
1110
1111      # print("ndopthdr=[%s] data=[%s] leftover=[%s]" % (ndopthdr, data, leftover))
1112
1113      while data:
1114        # print("data2=[%s]\n" % data)
1115
1116        header_opt = self.NDOptHeader(data)
1117        self.assertNotEqual(header_opt.length, 0)
1118        self.assertLessEqual(header_opt.length * 8, len(data))
1119        payload, data = data[:header_opt.length * 8], data[header_opt.length * 8:]
1120
1121        # print("type=%d len=%d payload[%s]\n" % (header_opt.type, header_opt.length * 8, payload))
1122
1123        if header_opt.type == self.ND_OPT_PIO:
1124          needPIO = False
1125        elif header_opt.type == self.ND_OPT_PREF64:
1126          needPref64 = False
1127          self.assertEqual(len(opt), len(payload))
1128          self.assertEqual(opt, self.Pref64Option(payload))
1129        else:
1130          # cannot happen: no other options we generate are currently considered user options
1131          assert False
1132
1133    # we only ever reach here if we find all options we need
1134    s.close()
1135
1136  def testRaFlags(self):
1137    def GetInterfaceIpv6Flags(iface):
1138      attrs = self.iproute.GetIflaAfSpecificData(iface, AF_INET6)
1139      return int(attrs["IFLA_INET6_FLAGS"])
1140
1141    netid = random.choice(self.NETIDS)
1142    iface = self.GetInterfaceName(netid)
1143    expected = iproute.IF_RS_SENT | iproute.IF_RA_RCVD | iproute.IF_READY
1144    self.assertEqual(expected, GetInterfaceIpv6Flags(iface))
1145
1146    self.SendRA(netid, m=1, o=0)
1147    expected |= iproute.IF_RA_MANAGED
1148    self.assertEqual(expected, GetInterfaceIpv6Flags(iface))
1149
1150    self.SendRA(netid, m=1, o=1)
1151    expected |= iproute.IF_RA_OTHERCONF
1152    self.assertEqual(expected, GetInterfaceIpv6Flags(iface))
1153
1154    self.SendRA(netid, m=0, o=1)
1155    expected &= ~iproute.IF_RA_MANAGED
1156    self.assertEqual(expected, GetInterfaceIpv6Flags(iface))
1157
1158
1159class PMTUTest(multinetwork_base.InboundMarkingTest):
1160
1161  PAYLOAD_SIZE = 1400
1162  dstaddrs = set()
1163
1164  def GetSocketMTU(self, version, s):
1165    if version == 6:
1166      ip6_mtuinfo = s.getsockopt(net_test.SOL_IPV6, csocket.IPV6_PATHMTU, 32)
1167      unused_sockaddr, mtu = struct.unpack("=28sI", ip6_mtuinfo)
1168      return mtu
1169    else:
1170      return s.getsockopt(net_test.SOL_IP, csocket.IP_MTU)
1171
1172  def DisableFragmentationAndReportErrors(self, version, s):
1173    if version == 4:
1174      s.setsockopt(net_test.SOL_IP, csocket.IP_MTU_DISCOVER,
1175                   csocket.IP_PMTUDISC_DO)
1176      s.setsockopt(net_test.SOL_IP, net_test.IP_RECVERR, 1)
1177    else:
1178      s.setsockopt(net_test.SOL_IPV6, csocket.IPV6_DONTFRAG, 1)
1179      s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1)
1180
1181  def CheckPMTU(self, version, use_connect, modes):
1182
1183    def SendBigPacket(version, s, dstaddr, netid, payload):
1184      if use_connect:
1185        s.send(payload)
1186      else:
1187        self.SendOnNetid(version, s, dstaddr, 1234, netid, payload, [])
1188
1189    for netid in self.tuns:
1190      for mode in modes:
1191        s = self.BuildSocket(version, net_test.UDPSocket, netid, mode)
1192        self.DisableFragmentationAndReportErrors(version, s)
1193
1194        srcaddr = self.MyAddress(version, netid)
1195        dst_prefix, intermediate = {
1196            4: ("172.19.", "172.16.9.12"),
1197            6: ("2001:db8::", "2001:db8::1")
1198        }[version]
1199
1200        # Run this test often enough (e.g., in presubmits), and eventually
1201        # we'll be unlucky enough to pick the same address twice, in which
1202        # case the test will fail because the kernel will already have seen
1203        # the lower MTU. Don't do this.
1204        dstaddr = self.GetRandomDestination(dst_prefix)
1205        while dstaddr in self.dstaddrs:
1206          dstaddr = self.GetRandomDestination(dst_prefix)
1207        self.dstaddrs.add(dstaddr)
1208
1209        if use_connect:
1210          s.connect((dstaddr, 1234))
1211
1212        payload = self.PAYLOAD_SIZE * b"a"
1213
1214        # Send a packet and receive a packet too big.
1215        SendBigPacket(version, s, dstaddr, netid, payload)
1216        received = self.ReadAllPacketsOn(netid)
1217        self.assertEqual(1, len(received),
1218                          "unexpected packets: %s" % received[1:])
1219        _, toobig = packets.ICMPPacketTooBig(version, intermediate, srcaddr,
1220                                             received[0])
1221        self.ReceivePacketOn(netid, toobig)
1222
1223        # Check that another send on the same socket returns EMSGSIZE.
1224        self.assertRaisesErrno(
1225            errno.EMSGSIZE,
1226            SendBigPacket, version, s, dstaddr, netid, payload)
1227
1228        # If this is a connected socket, make sure the socket MTU was set.
1229        # Note that in IPv4 this only started working in Linux 3.6!
1230        if use_connect:
1231          self.assertEqual(packets.PTB_MTU, self.GetSocketMTU(version, s))
1232
1233        s.close()
1234
1235        # Check that other sockets pick up the PMTU we have been told about by
1236        # connecting another socket to the same destination and getting its MTU.
1237        # This new socket can use any method to select its outgoing interface;
1238        # here we use a mark for simplicity.
1239        s2 = self.BuildSocket(version, net_test.UDPSocket, netid, "mark")
1240        s2.connect((dstaddr, 1234))
1241        self.assertEqual(packets.PTB_MTU, self.GetSocketMTU(version, s2))
1242
1243        # Also check the MTU reported by ip route get, this time using the oif.
1244        routes = self.iproute.GetRoutes(dstaddr, self.ifindices[netid], 0, None)
1245        self.assertTrue(routes)
1246        route = routes[0]
1247        rtmsg, attributes = route
1248        self.assertEqual(iproute.RTN_UNICAST, rtmsg.type)
1249        metrics = attributes["RTA_METRICS"]
1250        self.assertEqual(packets.PTB_MTU, metrics["RTAX_MTU"])
1251
1252        s2.close()
1253
1254  def testIPv4BasicPMTU(self):
1255    """Tests IPv4 path MTU discovery.
1256
1257    Relevant kernel commits:
1258      upstream net-next:
1259        6a66271 ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif
1260
1261      android-3.10:
1262        4bc64dd ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif
1263    """
1264
1265    self.CheckPMTU(4, True, ["mark", "oif"])
1266    self.CheckPMTU(4, False, ["mark", "oif"])
1267
1268  def testIPv6BasicPMTU(self):
1269    self.CheckPMTU(6, True, ["mark", "oif"])
1270    self.CheckPMTU(6, False, ["mark", "oif"])
1271
1272  def testIPv4UIDPMTU(self):
1273    self.CheckPMTU(4, True, ["uid"])
1274    self.CheckPMTU(4, False, ["uid"])
1275
1276  def testIPv6UIDPMTU(self):
1277    self.CheckPMTU(6, True, ["uid"])
1278    self.CheckPMTU(6, False, ["uid"])
1279
1280  # Making Path MTU Discovery work on unmarked  sockets requires that mark
1281  # reflection be enabled. Otherwise the kernel has no way to know what routing
1282  # table the original packet used, and thus it won't be able to clone the
1283  # correct route.
1284
1285  def testIPv4UnmarkedSocketPMTU(self):
1286    self.SetMarkReflectSysctls(1)
1287    try:
1288      self.CheckPMTU(4, False, [None])
1289    finally:
1290      self.SetMarkReflectSysctls(0)
1291
1292  def testIPv6UnmarkedSocketPMTU(self):
1293    self.SetMarkReflectSysctls(1)
1294    try:
1295      self.CheckPMTU(6, False, [None])
1296    finally:
1297      self.SetMarkReflectSysctls(0)
1298
1299
1300class UidRoutingTest(multinetwork_base.MultiNetworkBaseTest):
1301  """Tests that per-UID routing works properly.
1302
1303  Relevant kernel commits:
1304    upstream net-next:
1305      7d99569460 net: ipv4: Don't crash if passing a null sk to ip_do_redirect.
1306      d109e61bfe net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu.
1307      35b80733b3 net: core: add missing check for uid_range in rule_exists.
1308      e2d118a1cb net: inet: Support UID-based routing in IP protocols.
1309      622ec2c9d5 net: core: add UID to flows, rules, and routes
1310      86741ec254 net: core: Add a UID field to struct sock.
1311
1312    android-3.18:
1313      b004e79504 net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu.
1314      04c0eace81 net: inet: Support UID-based routing in IP protocols.
1315      18c36d7b71 net: core: add UID to flows, rules, and routes
1316      80e3440721 net: core: Add a UID field to struct sock.
1317      fa8cc2c30c Revert "net: core: Support UID-based routing."
1318      b585141890 Revert "Handle 'sk' being NULL in UID-based routing."
1319      5115ab7514 Revert "net: core: fix UID-based routing build"
1320      f9f4281f79 Revert "ANDROID: net: fib: remove duplicate assignment"
1321
1322    android-4.4:
1323      341965cf10 net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu.
1324      344afd627c net: inet: Support UID-based routing in IP protocols.
1325      03441d56d8 net: core: add UID to flows, rules, and routes
1326      eb964bdba7 net: core: Add a UID field to struct sock.
1327      9789b697c6 Revert "net: core: Support UID-based routing."
1328  """
1329
1330  def GetRulesAtPriority(self, version, priority):
1331    rules = self.iproute.DumpRules(version)
1332    out = [(rule, attributes) for rule, attributes in rules
1333           if attributes.get("FRA_PRIORITY", 0) == priority]
1334    return out
1335
1336  def CheckInitialTablesHaveNoUIDs(self, version):
1337    rules = []
1338    for priority in [0, 32766, 32767]:
1339      rules.extend(self.GetRulesAtPriority(version, priority))
1340    for _, attributes in rules:
1341      self.assertNotIn("FRA_UID_RANGE", attributes)
1342
1343  def testIPv4InitialTablesHaveNoUIDs(self):
1344    self.CheckInitialTablesHaveNoUIDs(4)
1345
1346  def testIPv6InitialTablesHaveNoUIDs(self):
1347    self.CheckInitialTablesHaveNoUIDs(6)
1348
1349  @staticmethod
1350  def _Random():
1351    return random.randint(1000000, 2000000)
1352
1353  @staticmethod
1354  def _RandomUid(cls):
1355    return random.randint(cls.UID_RANGE_START, cls.UID_RANGE_END)
1356
1357  def CheckGetAndSetRules(self, version):
1358    start, end = tuple(sorted([self._Random(), self._Random()]))
1359    table = self._Random()
1360    priority = self._Random()
1361
1362    # Can't create a UID range to UID -1 because -1 is INVALID_UID...
1363    self.assertRaisesErrno(
1364        errno.EINVAL,
1365        self.iproute.UidRangeRule, version, True, 100, 0xffffffff, table,
1366        priority)
1367
1368    # ... but -2 is valid.
1369    self.iproute.UidRangeRule(version, True, 100, 0xfffffffe, table, priority)
1370    self.iproute.UidRangeRule(version, False, 100, 0xfffffffe, table, priority)
1371
1372    try:
1373      # Create a UID range rule.
1374      self.iproute.UidRangeRule(version, True, start, end, table, priority)
1375
1376      # Check that deleting the wrong UID range doesn't work.
1377      self.assertRaisesErrno(
1378          errno.ENOENT,
1379          self.iproute.UidRangeRule, version, False, start, end + 1, table,
1380          priority)
1381      self.assertRaisesErrno(errno.ENOENT,
1382        self.iproute.UidRangeRule, version, False, start + 1, end, table,
1383        priority)
1384
1385      # Check that the UID range appears in dumps.
1386      rules = self.GetRulesAtPriority(version, priority)
1387      self.assertTrue(rules)
1388      _, attributes = rules[-1]
1389      self.assertEqual(priority, attributes["FRA_PRIORITY"])
1390      uidrange = attributes["FRA_UID_RANGE"]
1391      self.assertEqual(start, uidrange.start)
1392      self.assertEqual(end, uidrange.end)
1393      self.assertEqual(table, attributes["FRA_TABLE"])
1394    finally:
1395      self.iproute.UidRangeRule(version, False, start, end, table, priority)
1396      self.assertRaisesErrno(
1397          errno.ENOENT,
1398          self.iproute.UidRangeRule, version, False, start, end, table,
1399          priority)
1400
1401    fwmask = 0xfefefefe
1402    try:
1403      # Create a rule without a UID range.
1404      self.iproute.FwmarkRule(version, True, 300, fwmask, 301, priority + 1)
1405
1406      # Check it doesn't have a UID range.
1407      rules = self.GetRulesAtPriority(version, priority + 1)
1408      self.assertTrue(rules)
1409      for _, attributes in rules:
1410        self.assertIn("FRA_TABLE", attributes)
1411        self.assertNotIn("FRA_UID_RANGE", attributes)
1412    finally:
1413      self.iproute.FwmarkRule(version, False, 300, fwmask, 301, priority + 1)
1414
1415    # Test that EEXIST worksfor UID range rules too.
1416    ranges = [(100, 101), (100, 102), (99, 101), (1234, 5678)]
1417    dup = ranges[0]
1418    try:
1419      # Check that otherwise identical rules with different UID ranges can be
1420      # created without EEXIST.
1421      for start, end in ranges:
1422        self.iproute.UidRangeRule(version, True, start, end, table, priority)
1423      # ... but EEXIST is returned if the UID range is identical.
1424      self.assertRaisesErrno(
1425        errno.EEXIST,
1426        self.iproute.UidRangeRule, version, True, dup[0], dup[1], table,
1427        priority)
1428    finally:
1429      # Clean up.
1430      for start, end in ranges + [dup]:
1431        try:
1432          self.iproute.UidRangeRule(version, False, start, end, table,
1433                                    priority)
1434        except IOError:
1435          pass
1436
1437  def testIPv4GetAndSetRules(self):
1438    self.CheckGetAndSetRules(4)
1439
1440  def testIPv6GetAndSetRules(self):
1441    self.CheckGetAndSetRules(6)
1442
1443  def testDeleteErrno(self):
1444    for version in [4, 6]:
1445      table = self._Random()
1446      priority = self._Random()
1447      self.assertRaisesErrno(
1448          errno.EINVAL,
1449          self.iproute.UidRangeRule, version, False, 100, 0xffffffff, table,
1450          priority)
1451
1452  def ExpectNoRoute(self, addr, oif, mark, uid):
1453    # The lack of a route may be either an error, or an unreachable route.
1454    try:
1455      routes = self.iproute.GetRoutes(addr, oif, mark, uid)
1456      rtmsg, _ = routes[0]
1457      self.assertEqual(iproute.RTN_UNREACHABLE, rtmsg.type)
1458    except IOError as e:
1459      if int(e.errno) != int(errno.ENETUNREACH):
1460        raise e
1461
1462  def ExpectRoute(self, addr, oif, mark, uid):
1463    routes = self.iproute.GetRoutes(addr, oif, mark, uid)
1464    rtmsg, _ = routes[0]
1465    self.assertEqual(iproute.RTN_UNICAST, rtmsg.type)
1466
1467  def CheckGetRoute(self, version, addr):
1468    self.ExpectNoRoute(addr, 0, 0, 0)
1469    for netid in self.NETIDS:
1470      uid = self.UidForNetid(netid)
1471      self.ExpectRoute(addr, 0, 0, uid)
1472    self.ExpectNoRoute(addr, 0, 0, 0)
1473
1474  def testIPv4RouteGet(self):
1475    self.CheckGetRoute(4, net_test.IPV4_ADDR)
1476
1477  def testIPv6RouteGet(self):
1478    self.CheckGetRoute(6, net_test.IPV6_ADDR)
1479
1480  def testChangeFdAttributes(self):
1481    netid = random.choice(self.NETIDS)
1482    uid = self._RandomUid(self)
1483    table = self._TableForNetid(netid)
1484    remoteaddr = self.GetRemoteAddress(6)
1485    s = socket(AF_INET6, SOCK_DGRAM, 0)
1486
1487    def CheckSendFails():
1488      self.assertRaisesErrno(errno.ENETUNREACH,
1489                             s.sendto, b"foo", (remoteaddr, 53))
1490    def CheckSendSucceeds():
1491      self.assertEqual(len(b"foo"), s.sendto(b"foo", (remoteaddr, 53)))
1492
1493    CheckSendFails()
1494    self.iproute.UidRangeRule(6, True, uid, uid, table, self.PRIORITY_UID)
1495    try:
1496      CheckSendFails()
1497      os.fchown(s.fileno(), uid, -1)
1498      CheckSendSucceeds()
1499      os.fchown(s.fileno(), -1, -1)
1500      CheckSendSucceeds()
1501      os.fchown(s.fileno(), -1, 12345)
1502      CheckSendSucceeds()
1503      os.fchmod(s.fileno(), 0o777)
1504      CheckSendSucceeds()
1505      os.fchown(s.fileno(), 0, -1)
1506      CheckSendFails()
1507    finally:
1508      self.iproute.UidRangeRule(6, False, uid, uid, table, self.PRIORITY_UID)
1509      s.close()
1510
1511
1512class RulesTest(net_test.NetworkTest):
1513
1514  RULE_PRIORITY = 99999
1515  FWMASK = 0xffffffff
1516
1517  def setUp(self):
1518    self.iproute = iproute.IPRoute()
1519    for version in [4, 6]:
1520      self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
1521
1522  def tearDown(self):
1523    for version in [4, 6]:
1524      self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
1525
1526  def testRuleDeletionMatchesTable(self):
1527    for version in [4, 6]:
1528      # Add rules with mark 300 pointing at tables 301 and 302.
1529      # This checks for a kernel bug where deletion request for tables > 256
1530      # ignored the table.
1531      self.iproute.FwmarkRule(version, True, 300, self.FWMASK, 301,
1532                              priority=self.RULE_PRIORITY)
1533      self.iproute.FwmarkRule(version, True, 300, self.FWMASK, 302,
1534                              priority=self.RULE_PRIORITY)
1535      # Delete rule with mark 300 pointing at table 302.
1536      self.iproute.FwmarkRule(version, False, 300, self.FWMASK, 302,
1537                              priority=self.RULE_PRIORITY)
1538      # Check that the rule pointing at table 301 is still around.
1539      attributes = [a for _, a in self.iproute.DumpRules(version)
1540                    if a.get("FRA_PRIORITY", 0) == self.RULE_PRIORITY]
1541      self.assertEqual(1, len(attributes))
1542      self.assertEqual(301, attributes[0]["FRA_TABLE"])
1543
1544
1545if __name__ == "__main__":
1546  unittest.main()
1547