1#!/usr/bin/python
2#
3# Copyright 2014 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import errno
18import os
19import random
20from socket import *  # pylint: disable=wildcard-import
21import struct
22import time           # pylint: disable=unused-import
23import unittest
24
25from scapy import all as scapy
26
27import csocket
28import iproute
29import multinetwork_base
30import net_test
31import packets
32
33# For brevity.
34UDP_PAYLOAD = net_test.UDP_PAYLOAD
35
36IPV6_FLOWINFO = 11
37
38IPV4_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv4/fwmark_reflect"
39IPV6_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv6/fwmark_reflect"
40SYNCOOKIES_SYSCTL = "/proc/sys/net/ipv4/tcp_syncookies"
41TCP_MARK_ACCEPT_SYSCTL = "/proc/sys/net/ipv4/tcp_fwmark_accept"
42
43# The IP[V6]UNICAST_IF socket option was added between 3.1 and 3.4.
44HAVE_UNICAST_IF = net_test.LINUX_VERSION >= (3, 4, 0)
45
46MAX_PLEN_SYSCTL = "/proc/sys/net/ipv6/conf/default/accept_ra_rt_info_max_plen"
47HAVE_MAX_PLEN = os.path.isfile(MAX_PLEN_SYSCTL)
48
49class ConfigurationError(AssertionError):
50  pass
51
52
53class InboundMarkingTest(multinetwork_base.MultiNetworkBaseTest):
54
55  @classmethod
56  def _SetInboundMarking(cls, netid, is_add):
57    for version in [4, 6]:
58      # Run iptables to set up incoming packet marking.
59      iface = cls.GetInterfaceName(netid)
60      add_del = "-A" if is_add else "-D"
61      iptables = {4: "iptables", 6: "ip6tables"}[version]
62      args = "%s %s INPUT -t mangle -i %s -j MARK --set-mark %d" % (
63          iptables, add_del, iface, netid)
64      iptables = "/sbin/" + iptables
65      ret = os.spawnvp(os.P_WAIT, iptables, args.split(" "))
66      if ret:
67        raise ConfigurationError("Setup command failed: %s" % args)
68
69  @classmethod
70  def setUpClass(cls):
71    super(InboundMarkingTest, cls).setUpClass()
72    for netid in cls.tuns:
73      cls._SetInboundMarking(netid, True)
74
75  @classmethod
76  def tearDownClass(cls):
77    for netid in cls.tuns:
78      cls._SetInboundMarking(netid, False)
79    super(InboundMarkingTest, cls).tearDownClass()
80
81  @classmethod
82  def SetMarkReflectSysctls(cls, value):
83    cls.SetSysctl(IPV4_MARK_REFLECT_SYSCTL, value)
84    try:
85      cls.SetSysctl(IPV6_MARK_REFLECT_SYSCTL, value)
86    except IOError:
87      # This does not exist if we use the version of the patch that uses a
88      # common sysctl for IPv4 and IPv6.
89      pass
90
91
92class OutgoingTest(multinetwork_base.MultiNetworkBaseTest):
93
94  # How many times to run outgoing packet tests.
95  ITERATIONS = 5
96
97  def CheckPingPacket(self, version, netid, routing_mode, dstaddr, packet):
98    s = self.BuildSocket(version, net_test.PingSocket, netid, routing_mode)
99
100    myaddr = self.MyAddress(version, netid)
101    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
102    s.bind((myaddr, packets.PING_IDENT))
103    net_test.SetSocketTos(s, packets.PING_TOS)
104
105    desc, expected = packets.ICMPEcho(version, myaddr, dstaddr)
106    msg = "IPv%d ping: expected %s on %s" % (
107        version, desc, self.GetInterfaceName(netid))
108
109    s.sendto(packet + packets.PING_PAYLOAD, (dstaddr, 19321))
110
111    self.ExpectPacketOn(netid, msg, expected)
112
113  def CheckTCPSYNPacket(self, version, netid, routing_mode, dstaddr):
114    s = self.BuildSocket(version, net_test.TCPSocket, netid, routing_mode)
115
116    if version == 6 and dstaddr.startswith("::ffff"):
117      version = 4
118    myaddr = self.MyAddress(version, netid)
119    desc, expected = packets.SYN(53, version, myaddr, dstaddr,
120                                 sport=None, seq=None)
121
122    # Non-blocking TCP connects always return EINPROGRESS.
123    self.assertRaisesErrno(errno.EINPROGRESS, s.connect, (dstaddr, 53))
124    msg = "IPv%s TCP connect: expected %s on %s" % (
125        version, desc, self.GetInterfaceName(netid))
126    self.ExpectPacketOn(netid, msg, expected)
127    s.close()
128
129  def CheckUDPPacket(self, version, netid, routing_mode, dstaddr):
130    s = self.BuildSocket(version, net_test.UDPSocket, netid, routing_mode)
131
132    if version == 6 and dstaddr.startswith("::ffff"):
133      version = 4
134    myaddr = self.MyAddress(version, netid)
135    desc, expected = packets.UDP(version, myaddr, dstaddr, sport=None)
136    msg = "IPv%s UDP %%s: expected %s on %s" % (
137        version, desc, self.GetInterfaceName(netid))
138
139    s.sendto(UDP_PAYLOAD, (dstaddr, 53))
140    self.ExpectPacketOn(netid, msg % "sendto", expected)
141
142    # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
143    if routing_mode != "ucast_oif":
144      s.connect((dstaddr, 53))
145      s.send(UDP_PAYLOAD)
146      self.ExpectPacketOn(netid, msg % "connect/send", expected)
147      s.close()
148
149  def CheckRawGrePacket(self, version, netid, routing_mode, dstaddr):
150    s = self.BuildSocket(version, net_test.RawGRESocket, netid, routing_mode)
151
152    inner_version = {4: 6, 6: 4}[version]
153    inner_src = self.MyAddress(inner_version, netid)
154    inner_dst = self.GetRemoteAddress(inner_version)
155    inner = str(packets.UDP(inner_version, inner_src, inner_dst, sport=None)[1])
156
157    ethertype = {4: net_test.ETH_P_IP, 6: net_test.ETH_P_IPV6}[inner_version]
158    # A GRE header can be as simple as two zero bytes and the ethertype.
159    packet = struct.pack("!i", ethertype) + inner
160    myaddr = self.MyAddress(version, netid)
161
162    s.sendto(packet, (dstaddr, IPPROTO_GRE))
163    desc, expected = packets.GRE(version, myaddr, dstaddr, ethertype, inner)
164    msg = "Raw IPv%d GRE with inner IPv%d UDP: expected %s on %s" % (
165        version, inner_version, desc, self.GetInterfaceName(netid))
166    self.ExpectPacketOn(netid, msg, expected)
167
168  def CheckOutgoingPackets(self, routing_mode):
169    v4addr = self.IPV4_ADDR
170    v6addr = self.IPV6_ADDR
171    v4mapped = "::ffff:" + v4addr
172
173    for _ in xrange(self.ITERATIONS):
174      for netid in self.tuns:
175
176        self.CheckPingPacket(4, netid, routing_mode, v4addr, self.IPV4_PING)
177        # Kernel bug.
178        if routing_mode != "oif":
179          self.CheckPingPacket(6, netid, routing_mode, v6addr, self.IPV6_PING)
180
181        # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
182        if routing_mode != "ucast_oif":
183          self.CheckTCPSYNPacket(4, netid, routing_mode, v4addr)
184          self.CheckTCPSYNPacket(6, netid, routing_mode, v6addr)
185          self.CheckTCPSYNPacket(6, netid, routing_mode, v4mapped)
186
187        self.CheckUDPPacket(4, netid, routing_mode, v4addr)
188        self.CheckUDPPacket(6, netid, routing_mode, v6addr)
189        self.CheckUDPPacket(6, netid, routing_mode, v4mapped)
190
191        # Creating raw sockets on non-root UIDs requires properly setting
192        # capabilities, which is hard to do from Python.
193        # IP_UNICAST_IF is not supported on raw sockets.
194        if routing_mode not in ["uid", "ucast_oif"]:
195          self.CheckRawGrePacket(4, netid, routing_mode, v4addr)
196          self.CheckRawGrePacket(6, netid, routing_mode, v6addr)
197
198  def testMarkRouting(self):
199    """Checks that socket marking selects the right outgoing interface."""
200    self.CheckOutgoingPackets("mark")
201
202  def testUidRouting(self):
203    """Checks that UID routing selects the right outgoing interface."""
204    self.CheckOutgoingPackets("uid")
205
206  def testOifRouting(self):
207    """Checks that oif routing selects the right outgoing interface."""
208    self.CheckOutgoingPackets("oif")
209
210  @unittest.skipUnless(HAVE_UNICAST_IF, "no support for UNICAST_IF")
211  def testUcastOifRouting(self):
212    """Checks that ucast oif routing selects the right outgoing interface."""
213    self.CheckOutgoingPackets("ucast_oif")
214
215  def CheckRemarking(self, version, use_connect):
216    modes = ["mark", "oif", "uid"]
217    # Setting UNICAST_IF on connected sockets does not work.
218    if not use_connect and HAVE_UNICAST_IF:
219      modes += ["ucast_oif"]
220
221    for mode in modes:
222      s = net_test.UDPSocket(self.GetProtocolFamily(version))
223
224      # Figure out what packets to expect.
225      sport = net_test.BindRandomPort(version, s)
226      dstaddr = {4: self.IPV4_ADDR, 6: self.IPV6_ADDR}[version]
227      unspec = {4: "0.0.0.0", 6: "::"}[version]  # Placeholder.
228      desc, expected = packets.UDP(version, unspec, dstaddr, sport)
229
230      # If we're testing connected sockets, connect the socket on the first
231      # netid now.
232      if use_connect:
233        netid = self.tuns.keys()[0]
234        self.SelectInterface(s, netid, mode)
235        s.connect((dstaddr, 53))
236        expected.src = self.MyAddress(version, netid)
237
238      # For each netid, select that network without closing the socket, and
239      # check that the packets sent on that socket go out on the right network.
240      #
241      # For connected sockets, routing is cached in the socket's destination
242      # cache entry. In this case, we check that just re-selecting the netid
243      # (except via SO_BINDTODEVICE) does not change routing, but that
244      # subsequently invalidating the destination cache entry does. Arguably
245      # this is a bug in the kernel because re-selecting the netid should cause
246      # routing to change. But it is a convenient way to check that
247      # InvalidateDstCache actually works.
248      prevnetid = None
249      for netid in self.tuns:
250        self.SelectInterface(s, netid, mode)
251        if not use_connect:
252          expected.src = self.MyAddress(version, netid)
253
254        def ExpectSendUsesNetid(netid):
255          connected_str = "Connected" if use_connect else "Unconnected"
256          msg = "%s UDPv%d socket remarked using %s: expecting %s on %s" % (
257              connected_str, version, mode, desc, self.GetInterfaceName(netid))
258          if use_connect:
259            s.send(UDP_PAYLOAD)
260          else:
261            s.sendto(UDP_PAYLOAD, (dstaddr, 53))
262          self.ExpectPacketOn(netid, msg, expected)
263
264        if use_connect and mode in ["mark", "uid", "ucast_oif"]:
265          # If we have a destination cache entry, packets are not rerouted...
266          if prevnetid:
267            ExpectSendUsesNetid(prevnetid)
268            # ... until we invalidate it.
269            self.InvalidateDstCache(version, dstaddr, prevnetid)
270          ExpectSendUsesNetid(netid)
271        else:
272          ExpectSendUsesNetid(netid)
273
274        self.SelectInterface(s, None, mode)
275        prevnetid = netid
276
277  def testIPv4Remarking(self):
278    """Checks that updating the mark on an IPv4 socket changes routing."""
279    self.CheckRemarking(4, False)
280    self.CheckRemarking(4, True)
281
282  def testIPv6Remarking(self):
283    """Checks that updating the mark on an IPv6 socket changes routing."""
284    self.CheckRemarking(6, False)
285    self.CheckRemarking(6, True)
286
287  def testIPv6StickyPktinfo(self):
288    for _ in xrange(self.ITERATIONS):
289      for netid in self.tuns:
290        s = net_test.UDPSocket(AF_INET6)
291
292        # Set a flowlabel.
293        net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead)
294        s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1)
295
296        # Set some destination options.
297        nonce = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
298        dstopts = "".join([
299            "\x11\x02",              # Next header=UDP, 24 bytes of options.
300            "\x01\x06", "\x00" * 6,  # PadN, 6 bytes of padding.
301            "\x8b\x0c",              # ILNP nonce, 12 bytes.
302            nonce
303        ])
304        s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, dstopts)
305        s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 255)
306
307        pktinfo = multinetwork_base.MakePktInfo(6, None, self.ifindices[netid])
308
309        # Set the sticky pktinfo option.
310        s.setsockopt(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo)
311
312        # Specify the flowlabel in the destination address.
313        s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 53, 0xdead, 0))
314
315        sport = s.getsockname()[1]
316        srcaddr = self.MyAddress(6, netid)
317        expected = (scapy.IPv6(src=srcaddr, dst=net_test.IPV6_ADDR,
318                               fl=0xdead, hlim=255) /
319                    scapy.IPv6ExtHdrDestOpt(
320                        options=[scapy.PadN(optdata="\x00\x00\x00\x00\x00\x00"),
321                                 scapy.HBHOptUnknown(otype=0x8b,
322                                                     optdata=nonce)]) /
323                    scapy.UDP(sport=sport, dport=53) /
324                    UDP_PAYLOAD)
325        msg = "IPv6 UDP using sticky pktinfo: expected UDP packet on %s" % (
326            self.GetInterfaceName(netid))
327        self.ExpectPacketOn(netid, msg, expected)
328
329  def CheckPktinfoRouting(self, version):
330    for _ in xrange(self.ITERATIONS):
331      for netid in self.tuns:
332        family = self.GetProtocolFamily(version)
333        s = net_test.UDPSocket(family)
334
335        if version == 6:
336          # Create a flowlabel so we can use it.
337          net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xbeef)
338
339          # Specify some arbitrary options.
340          cmsgs = [
341              (net_test.SOL_IPV6, IPV6_HOPLIMIT, 39),
342              (net_test.SOL_IPV6, IPV6_TCLASS, 0x83),
343              (net_test.SOL_IPV6, IPV6_FLOWINFO, int(htonl(0xbeef))),
344          ]
345        else:
346          # Support for setting IPv4 TOS and TTL via cmsg only appeared in 3.13.
347          cmsgs = []
348          s.setsockopt(net_test.SOL_IP, IP_TTL, 39)
349          s.setsockopt(net_test.SOL_IP, IP_TOS, 0x83)
350
351        dstaddr = self.GetRemoteAddress(version)
352        self.SendOnNetid(version, s, dstaddr, 53, netid, UDP_PAYLOAD, cmsgs)
353
354        sport = s.getsockname()[1]
355        srcaddr = self.MyAddress(version, netid)
356
357        desc, expected = packets.UDPWithOptions(version, srcaddr, dstaddr,
358                                                sport=sport)
359
360        msg = "IPv%d UDP using pktinfo routing: expected %s on %s" % (
361            version, desc, self.GetInterfaceName(netid))
362        self.ExpectPacketOn(netid, msg, expected)
363
364  def testIPv4PktinfoRouting(self):
365    self.CheckPktinfoRouting(4)
366
367  def testIPv6PktinfoRouting(self):
368    self.CheckPktinfoRouting(6)
369
370
371class MarkTest(InboundMarkingTest):
372
373  def CheckReflection(self, version, gen_packet, gen_reply):
374    """Checks that replies go out on the same interface as the original.
375
376    For each combination:
377     - Calls gen_packet to generate a packet to that IP address.
378     - Writes the packet generated by gen_packet on the given tun
379       interface, causing the kernel to receive it.
380     - Checks that the kernel's reply matches the packet generated by
381       gen_reply.
382
383    Args:
384      version: An integer, 4 or 6.
385      gen_packet: A function taking an IP version (an integer), a source
386        address and a destination address (strings), and returning a scapy
387        packet.
388      gen_reply: A function taking the same arguments as gen_packet,
389        plus a scapy packet, and returning a scapy packet.
390    """
391    for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
392      # Generate a test packet.
393      desc, packet = gen_packet(version, remoteaddr, myaddr)
394
395      # Test with mark reflection enabled and disabled.
396      for reflect in [0, 1]:
397        self.SetMarkReflectSysctls(reflect)
398        # HACK: IPv6 ping replies always do a routing lookup with the
399        # interface the ping came in on. So even if mark reflection is not
400        # working, IPv6 ping replies will be properly reflected. Don't
401        # fail when that happens.
402        if reflect or desc == "ICMPv6 echo":
403          reply_desc, reply = gen_reply(version, myaddr, remoteaddr, packet)
404        else:
405          reply_desc, reply = None, None
406
407        msg = self._FormatMessage(iif, ip_if, "reflect=%d" % reflect,
408                                  desc, reply_desc)
409        self._ReceiveAndExpectResponse(netid, packet, reply, msg)
410
411  def SYNToClosedPort(self, *args):
412    return packets.SYN(999, *args)
413
414  def testIPv4ICMPErrorsReflectMark(self):
415    self.CheckReflection(4, packets.UDP, packets.ICMPPortUnreachable)
416
417  def testIPv6ICMPErrorsReflectMark(self):
418    self.CheckReflection(6, packets.UDP, packets.ICMPPortUnreachable)
419
420  def testIPv4PingRepliesReflectMarkAndTos(self):
421    self.CheckReflection(4, packets.ICMPEcho, packets.ICMPReply)
422
423  def testIPv6PingRepliesReflectMarkAndTos(self):
424    self.CheckReflection(6, packets.ICMPEcho, packets.ICMPReply)
425
426  def testIPv4RSTsReflectMark(self):
427    self.CheckReflection(4, self.SYNToClosedPort, packets.RST)
428
429  def testIPv6RSTsReflectMark(self):
430    self.CheckReflection(6, self.SYNToClosedPort, packets.RST)
431
432
433class TCPAcceptTest(InboundMarkingTest):
434
435  MODE_BINDTODEVICE = "SO_BINDTODEVICE"
436  MODE_INCOMING_MARK = "incoming mark"
437  MODE_EXPLICIT_MARK = "explicit mark"
438  MODE_UID = "uid"
439
440  @classmethod
441  def setUpClass(cls):
442    super(TCPAcceptTest, cls).setUpClass()
443
444    # Open a port so we can observe SYN+ACKs. Since it's a dual-stack socket it
445    # will accept both IPv4 and IPv6 connections. We do this here instead of in
446    # each test so we can use the same socket every time. That way, if a kernel
447    # bug causes incoming packets to mark the listening socket instead of the
448    # accepted socket, the test will fail as soon as the next address/interface
449    # combination is tried.
450    cls.listensocket = net_test.IPv6TCPSocket()
451    cls.listenport = net_test.BindRandomPort(6, cls.listensocket)
452
453  def _SetTCPMarkAcceptSysctl(self, value):
454    self.SetSysctl(TCP_MARK_ACCEPT_SYSCTL, value)
455
456  def CheckTCPConnection(self, mode, listensocket, netid, version,
457                         myaddr, remoteaddr, packet, reply, msg):
458    establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1]
459
460    # Attempt to confuse the kernel.
461    self.InvalidateDstCache(version, remoteaddr, netid)
462
463    self.ReceivePacketOn(netid, establishing_ack)
464
465    # If we're using UID routing, the accept() call has to be run as a UID that
466    # is routed to the specified netid, because the UID of the socket returned
467    # by accept() is the effective UID of the process that calls it. It doesn't
468    # need to be the same UID; any UID that selects the same interface will do.
469    with net_test.RunAsUid(self.UidForNetid(netid)):
470      s, _ = listensocket.accept()
471
472    try:
473      # Check that data sent on the connection goes out on the right interface.
474      desc, data = packets.ACK(version, myaddr, remoteaddr, establishing_ack,
475                               payload=UDP_PAYLOAD)
476      s.send(UDP_PAYLOAD)
477      self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data)
478      self.InvalidateDstCache(version, remoteaddr, netid)
479
480      # Keep up our end of the conversation.
481      ack = packets.ACK(version, remoteaddr, myaddr, data)[1]
482      self.InvalidateDstCache(version, remoteaddr, netid)
483      self.ReceivePacketOn(netid, ack)
484
485      mark = self.GetSocketMark(s)
486    finally:
487      self.InvalidateDstCache(version, remoteaddr, netid)
488      s.close()
489      self.InvalidateDstCache(version, remoteaddr, netid)
490
491    if mode == self.MODE_INCOMING_MARK:
492      self.assertEquals(netid, mark,
493                        msg + ": Accepted socket: Expected mark %d, got %d" % (
494                            netid, mark))
495    elif mode != self.MODE_EXPLICIT_MARK:
496      self.assertEquals(0, self.GetSocketMark(listensocket))
497
498    # Check the FIN was sent on the right interface, and ack it. We don't expect
499    # this to fail because by the time the connection is established things are
500    # likely working, but a) extra tests are always good and b) extra packets
501    # like the FIN (and retransmitted FINs) could cause later tests that expect
502    # no packets to fail.
503    desc, fin = packets.FIN(version, myaddr, remoteaddr, ack)
504    self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin)
505
506    desc, finack = packets.FIN(version, remoteaddr, myaddr, fin)
507    self.ReceivePacketOn(netid, finack)
508
509    # Since we called close() earlier, the userspace socket object is gone, so
510    # the socket has no UID. If we're doing UID routing, the ack might be routed
511    # incorrectly. Not much we can do here.
512    desc, finackack = packets.ACK(version, myaddr, remoteaddr, finack)
513    self.ExpectPacketOn(netid, msg + ": expecting final ack", finackack)
514
515  def CheckTCP(self, version, modes):
516    """Checks that incoming TCP connections work.
517
518    Args:
519      version: An integer, 4 or 6.
520      modes: A list of modes to excercise.
521    """
522    for syncookies in [0, 2]:
523      for mode in modes:
524        for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
525          listensocket = self.listensocket
526          listenport = listensocket.getsockname()[1]
527
528          accept_sysctl = 1 if mode == self.MODE_INCOMING_MARK else 0
529          self._SetTCPMarkAcceptSysctl(accept_sysctl)
530          self.SetMarkReflectSysctls(accept_sysctl)
531
532          bound_dev = iif if mode == self.MODE_BINDTODEVICE else None
533          self.BindToDevice(listensocket, bound_dev)
534
535          mark = netid if mode == self.MODE_EXPLICIT_MARK else 0
536          self.SetSocketMark(listensocket, mark)
537
538          uid = self.UidForNetid(netid) if mode == self.MODE_UID else 0
539          os.fchown(listensocket.fileno(), uid, -1)
540
541          # Generate the packet here instead of in the outer loop, so
542          # subsequent TCP connections use different source ports and
543          # retransmissions from old connections don't confuse subsequent
544          # tests.
545          desc, packet = packets.SYN(listenport, version, remoteaddr, myaddr)
546
547          if mode:
548            reply_desc, reply = packets.SYNACK(version, myaddr, remoteaddr,
549                                               packet)
550          else:
551            reply_desc, reply = None, None
552
553          extra = "mode=%s, syncookies=%d" % (mode, syncookies)
554          msg = self._FormatMessage(iif, ip_if, extra, desc, reply_desc)
555          reply = self._ReceiveAndExpectResponse(netid, packet, reply, msg)
556          if reply:
557            self.CheckTCPConnection(mode, listensocket, netid, version, myaddr,
558                                    remoteaddr, packet, reply, msg)
559
560  def testBasicTCP(self):
561    self.CheckTCP(4, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
562    self.CheckTCP(6, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
563
564  def testIPv4MarkAccept(self):
565    self.CheckTCP(4, [self.MODE_INCOMING_MARK])
566
567  def testIPv6MarkAccept(self):
568    self.CheckTCP(6, [self.MODE_INCOMING_MARK])
569
570  def testIPv4UidAccept(self):
571    self.CheckTCP(4, [self.MODE_UID])
572
573  def testIPv6UidAccept(self):
574    self.CheckTCP(6, [self.MODE_UID])
575
576  def testIPv6ExplicitMark(self):
577    self.CheckTCP(6, [self.MODE_EXPLICIT_MARK])
578
579class RIOTest(multinetwork_base.MultiNetworkBaseTest):
580
581  def setUp(self):
582    self.NETID = random.choice(self.NETIDS)
583    self.IFACE = self.GetInterfaceName(self.NETID)
584
585  def GetRoutingTable(self):
586    return self._TableForNetid(self.NETID)
587
588  def SetAcceptRaRtInfoMaxPlen(self, plen):
589    self.SetSysctl(
590        "/proc/sys/net/ipv6/conf/%s/accept_ra_rt_info_max_plen"
591        % self.IFACE, str(plen))
592
593  def GetAcceptRaRtInfoMaxPlen(self):
594    return int(self.GetSysctl(
595        "/proc/sys/net/ipv6/conf/%s/accept_ra_rt_info_max_plen" % self.IFACE))
596
597  def SendRIO(self, rtlifetime, plen, prefix, prf):
598    options = scapy.ICMPv6NDOptRouteInfo(rtlifetime=rtlifetime, plen=plen,
599                                         prefix=prefix, prf=prf)
600    self.SendRA(self.NETID, options=(options,))
601
602  def FindRoutesWithDestination(self, destination):
603    canonical = net_test.CanonicalizeIPv6Address(destination)
604    return [r for _, r in self.iproute.DumpRoutes(6, self.GetRoutingTable())
605            if ('RTA_DST' in r and r['RTA_DST'] == canonical)]
606
607  def FindRoutesWithGateway(self):
608    return [r for _, r in self.iproute.DumpRoutes(6, self.GetRoutingTable())
609            if 'RTA_GATEWAY' in r]
610
611  def CountRoutes(self):
612    return len(self.iproute.DumpRoutes(6, self.GetRoutingTable()))
613
614  def GetRouteExpiration(self, route):
615    return float(route['RTA_CACHEINFO'].expires) / 100.0
616
617  @unittest.skipUnless(HAVE_MAX_PLEN and multinetwork_base.HAVE_AUTOCONF_TABLE,
618                       "need support for RIO and per-table autoconf")
619  def testSetAcceptRaRtInfoMaxPlen(self):
620    for plen in xrange(-1, 130):
621      self.SetAcceptRaRtInfoMaxPlen(plen)
622      self.assertEquals(plen, self.GetAcceptRaRtInfoMaxPlen())
623
624  @unittest.skipUnless(HAVE_MAX_PLEN and multinetwork_base.HAVE_AUTOCONF_TABLE,
625                       "need support for RIO and per-table autoconf")
626  def testZeroRtLifetime(self):
627    PREFIX = "2001:db8:8901:2300::"
628    RTLIFETIME = 7372
629    PLEN = 56
630    PRF = 0
631    self.SetAcceptRaRtInfoMaxPlen(PLEN)
632    self.SendRIO(RTLIFETIME, PLEN, PREFIX, PRF)
633    # Give the kernel time to notice our RA
634    time.sleep(0.01)
635    self.assertTrue(self.FindRoutesWithDestination(PREFIX))
636    # RIO with rtlifetime = 0 should remove from routing table
637    self.SendRIO(0, PLEN, PREFIX, PRF)
638    # Give the kernel time to notice our RA
639    time.sleep(0.01)
640    self.assertFalse(self.FindRoutesWithDestination(PREFIX))
641
642  @unittest.skipUnless(HAVE_MAX_PLEN and multinetwork_base.HAVE_AUTOCONF_TABLE,
643                       "need support for RIO and per-table autoconf")
644  def testMaxPrefixLenRejection(self):
645    PREFIX = "2001:db8:8901:2345::"
646    RTLIFETIME = 7372
647    PRF = 0
648    for plen in xrange(0, 64):
649      self.SetAcceptRaRtInfoMaxPlen(plen)
650      # RIO with plen > max_plen should be ignored
651      self.SendRIO(RTLIFETIME, plen + 1, PREFIX, PRF)
652      # Give the kernel time to notice our RA
653      time.sleep(0.01)
654      routes = self.FindRoutesWithDestination(PREFIX)
655      self.assertFalse(routes)
656
657  @unittest.skipUnless(HAVE_MAX_PLEN and multinetwork_base.HAVE_AUTOCONF_TABLE,
658                       "need support for RIO and per-table autoconf")
659  def testZeroLengthPrefix(self):
660    PREFIX = "::"
661    RTLIFETIME = self.RA_VALIDITY * 2
662    PLEN = 0
663    PRF = 0
664    # Max plen = 0 still allows default RIOs!
665    self.SetAcceptRaRtInfoMaxPlen(PLEN)
666    default = self.FindRoutesWithGateway()
667    self.assertTrue(default)
668    self.assertLess(self.GetRouteExpiration(default[0]), self.RA_VALIDITY)
669    # RIO with prefix length = 0, should overwrite default route lifetime
670    # note that the RIO lifetime overwrites the RA lifetime.
671    self.SendRIO(RTLIFETIME, PLEN, PREFIX, PRF)
672    # Give the kernel time to notice our RA
673    time.sleep(0.01)
674    default = self.FindRoutesWithGateway()
675    self.assertTrue(default)
676    if net_test.LINUX_VERSION > (3, 12, 0):
677      # Vanilla linux earlier than 3.13 handles RIOs with zero length prefixes
678      # incorrectly. There's nothing useful to assert other than the existence
679      # of a default route.
680      # TODO: remove this condition after pulling bullhead/angler backports to
681      # other 3.10 flavors.
682      self.assertGreater(self.GetRouteExpiration(default[0]), self.RA_VALIDITY)
683
684  @unittest.skipUnless(HAVE_MAX_PLEN and multinetwork_base.HAVE_AUTOCONF_TABLE,
685                       "need support for RIO and per-table autoconf")
686  def testManyRIOs(self):
687    RTLIFETIME = 6809
688    PLEN = 56
689    PRF = 0
690    COUNT = 1000
691    baseline = self.CountRoutes()
692    self.SetAcceptRaRtInfoMaxPlen(56)
693    # Send many RIOs compared to the expected number on a healthy system.
694    for i in xrange(0, COUNT):
695      prefix = "2001:db8:%x:1100::" % i
696      self.SendRIO(RTLIFETIME, PLEN, prefix, PRF)
697    self.assertEquals(COUNT + baseline, self.CountRoutes())
698    # Use lifetime = 0 to cleanup all previously announced RIOs.
699    for i in xrange(0, COUNT):
700      prefix = "2001:db8:%x:1100::" % i
701      self.SendRIO(0, PLEN, prefix, PRF)
702    # Expect that we can return to baseline config without lingering routes.
703    self.assertEquals(baseline, self.CountRoutes())
704
705class RATest(multinetwork_base.MultiNetworkBaseTest):
706
707  def testDoesNotHaveObsoleteSysctl(self):
708    self.assertFalse(os.path.isfile(
709        "/proc/sys/net/ipv6/route/autoconf_table_offset"))
710
711  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
712                       "no support for per-table autoconf")
713  def testPurgeDefaultRouters(self):
714
715    def CheckIPv6Connectivity(expect_connectivity):
716      for netid in self.NETIDS:
717        s = net_test.UDPSocket(AF_INET6)
718        self.SetSocketMark(s, netid)
719        if expect_connectivity:
720          self.assertTrue(s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 1234)))
721        else:
722          self.assertRaisesErrno(errno.ENETUNREACH, s.sendto, UDP_PAYLOAD,
723                                 (net_test.IPV6_ADDR, 1234))
724
725    try:
726      CheckIPv6Connectivity(True)
727      self.SetIPv6SysctlOnAllIfaces("accept_ra", 1)
728      self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1)
729      CheckIPv6Connectivity(False)
730    finally:
731      self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0)
732      for netid in self.NETIDS:
733        self.SendRA(netid)
734      CheckIPv6Connectivity(True)
735
736  def testOnlinkCommunication(self):
737    """Checks that on-link communication goes direct and not through routers."""
738    for netid in self.tuns:
739      # Send a UDP packet to a random on-link destination.
740      s = net_test.UDPSocket(AF_INET6)
741      iface = self.GetInterfaceName(netid)
742      self.BindToDevice(s, iface)
743      # dstaddr can never be our address because GetRandomDestination only fills
744      # in the lower 32 bits, but our address has 0xff in the byte before that
745      # (since it's constructed from the EUI-64 and so has ff:fe in the middle).
746      dstaddr = self.GetRandomDestination(self.OnlinkPrefix(6, netid))
747      s.sendto(UDP_PAYLOAD, (dstaddr, 53))
748
749      # Expect an NS for that destination on the interface.
750      myaddr = self.MyAddress(6, netid)
751      mymac = self.MyMacAddress(netid)
752      desc, expected = packets.NS(myaddr, dstaddr, mymac)
753      msg = "Sending UDP packet to on-link destination: expecting %s" % desc
754      time.sleep(0.0001)  # Required to make the test work on kernel 3.1(!)
755      self.ExpectPacketOn(netid, msg, expected)
756
757      # Send an NA.
758      tgtmac = "02:00:00:00:%02x:99" % netid
759      _, reply = packets.NA(dstaddr, myaddr, tgtmac)
760      # Don't use ReceivePacketOn, since that uses the router's MAC address as
761      # the source. Instead, construct our own Ethernet header with source
762      # MAC of tgtmac.
763      reply = scapy.Ether(src=tgtmac, dst=mymac) / reply
764      self.ReceiveEtherPacketOn(netid, reply)
765
766      # Expect the kernel to send the original UDP packet now that the ND cache
767      # entry has been populated.
768      sport = s.getsockname()[1]
769      desc, expected = packets.UDP(6, myaddr, dstaddr, sport=sport)
770      msg = "After NA response, expecting %s" % desc
771      self.ExpectPacketOn(netid, msg, expected)
772
773  # This test documents a known issue: routing tables are never deleted.
774  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
775                       "no support for per-table autoconf")
776  def testLeftoverRoutes(self):
777    def GetNumRoutes():
778      return len(open("/proc/net/ipv6_route").readlines())
779
780    num_routes = GetNumRoutes()
781    for i in xrange(10, 20):
782      try:
783        self.tuns[i] = self.CreateTunInterface(i)
784        self.SendRA(i)
785        self.tuns[i].close()
786      finally:
787        del self.tuns[i]
788    self.assertLess(num_routes, GetNumRoutes())
789
790
791class PMTUTest(InboundMarkingTest):
792
793  PAYLOAD_SIZE = 1400
794  dstaddrs = set()
795
796  def GetSocketMTU(self, version, s):
797    if version == 6:
798      ip6_mtuinfo = s.getsockopt(net_test.SOL_IPV6, csocket.IPV6_PATHMTU, 32)
799      unused_sockaddr, mtu = struct.unpack("=28sI", ip6_mtuinfo)
800      return mtu
801    else:
802      return s.getsockopt(net_test.SOL_IP, csocket.IP_MTU)
803
804  def DisableFragmentationAndReportErrors(self, version, s):
805    if version == 4:
806      s.setsockopt(net_test.SOL_IP, csocket.IP_MTU_DISCOVER,
807                   csocket.IP_PMTUDISC_DO)
808      s.setsockopt(net_test.SOL_IP, net_test.IP_RECVERR, 1)
809    else:
810      s.setsockopt(net_test.SOL_IPV6, csocket.IPV6_DONTFRAG, 1)
811      s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1)
812
813  def CheckPMTU(self, version, use_connect, modes):
814
815    def SendBigPacket(version, s, dstaddr, netid, payload):
816      if use_connect:
817        s.send(payload)
818      else:
819        self.SendOnNetid(version, s, dstaddr, 1234, netid, payload, [])
820
821    for netid in self.tuns:
822      for mode in modes:
823        s = self.BuildSocket(version, net_test.UDPSocket, netid, mode)
824        self.DisableFragmentationAndReportErrors(version, s)
825
826        srcaddr = self.MyAddress(version, netid)
827        dst_prefix, intermediate = {
828            4: ("172.19.", "172.16.9.12"),
829            6: ("2001:db8::", "2001:db8::1")
830        }[version]
831
832        # Run this test often enough (e.g., in presubmits), and eventually
833        # we'll be unlucky enough to pick the same address twice, in which
834        # case the test will fail because the kernel will already have seen
835        # the lower MTU. Don't do this.
836        dstaddr = self.GetRandomDestination(dst_prefix)
837        while dstaddr in self.dstaddrs:
838          dstaddr = self.GetRandomDestination(dst_prefix)
839        self.dstaddrs.add(dstaddr)
840
841        if use_connect:
842          s.connect((dstaddr, 1234))
843
844        payload = self.PAYLOAD_SIZE * "a"
845
846        # Send a packet and receive a packet too big.
847        SendBigPacket(version, s, dstaddr, netid, payload)
848        received = self.ReadAllPacketsOn(netid)
849        self.assertEquals(1, len(received),
850                          "unexpected packets: %s" % received[1:])
851        _, toobig = packets.ICMPPacketTooBig(version, intermediate, srcaddr,
852                                             received[0])
853        self.ReceivePacketOn(netid, toobig)
854
855        # Check that another send on the same socket returns EMSGSIZE.
856        self.assertRaisesErrno(
857            errno.EMSGSIZE,
858            SendBigPacket, version, s, dstaddr, netid, payload)
859
860        # If this is a connected socket, make sure the socket MTU was set.
861        # Note that in IPv4 this only started working in Linux 3.6!
862        if use_connect and (version == 6 or net_test.LINUX_VERSION >= (3, 6)):
863          self.assertEquals(1280, self.GetSocketMTU(version, s))
864
865        s.close()
866
867        # Check that other sockets pick up the PMTU we have been told about by
868        # connecting another socket to the same destination and getting its MTU.
869        # This new socket can use any method to select its outgoing interface;
870        # here we use a mark for simplicity.
871        s2 = self.BuildSocket(version, net_test.UDPSocket, netid, "mark")
872        s2.connect((dstaddr, 1234))
873        self.assertEquals(1280, self.GetSocketMTU(version, s2))
874
875        # Also check the MTU reported by ip route get, this time using the oif.
876        routes = self.iproute.GetRoutes(dstaddr, self.ifindices[netid], 0, None)
877        self.assertTrue(routes)
878        route = routes[0]
879        rtmsg, attributes = route
880        self.assertEquals(iproute.RTN_UNICAST, rtmsg.type)
881        metrics = attributes["RTA_METRICS"]
882        self.assertEquals(metrics["RTAX_MTU"], 1280)
883
884  def testIPv4BasicPMTU(self):
885    """Tests IPv4 path MTU discovery.
886
887    Relevant kernel commits:
888      upstream net-next:
889        6a66271 ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif
890
891      android-3.10:
892        4bc64dd ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif
893    """
894
895    self.CheckPMTU(4, True, ["mark", "oif"])
896    self.CheckPMTU(4, False, ["mark", "oif"])
897
898  def testIPv6BasicPMTU(self):
899    self.CheckPMTU(6, True, ["mark", "oif"])
900    self.CheckPMTU(6, False, ["mark", "oif"])
901
902  def testIPv4UIDPMTU(self):
903    self.CheckPMTU(4, True, ["uid"])
904    self.CheckPMTU(4, False, ["uid"])
905
906  def testIPv6UIDPMTU(self):
907    self.CheckPMTU(6, True, ["uid"])
908    self.CheckPMTU(6, False, ["uid"])
909
910  # Making Path MTU Discovery work on unmarked  sockets requires that mark
911  # reflection be enabled. Otherwise the kernel has no way to know what routing
912  # table the original packet used, and thus it won't be able to clone the
913  # correct route.
914
915  def testIPv4UnmarkedSocketPMTU(self):
916    self.SetMarkReflectSysctls(1)
917    try:
918      self.CheckPMTU(4, False, [None])
919    finally:
920      self.SetMarkReflectSysctls(0)
921
922  def testIPv6UnmarkedSocketPMTU(self):
923    self.SetMarkReflectSysctls(1)
924    try:
925      self.CheckPMTU(6, False, [None])
926    finally:
927      self.SetMarkReflectSysctls(0)
928
929
930class UidRoutingTest(multinetwork_base.MultiNetworkBaseTest):
931  """Tests that per-UID routing works properly.
932
933  Relevant kernel commits:
934    upstream net-next:
935      7d99569460 net: ipv4: Don't crash if passing a null sk to ip_do_redirect.
936      d109e61bfe net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu.
937      35b80733b3 net: core: add missing check for uid_range in rule_exists.
938      e2d118a1cb net: inet: Support UID-based routing in IP protocols.
939      622ec2c9d5 net: core: add UID to flows, rules, and routes
940      86741ec254 net: core: Add a UID field to struct sock.
941
942    android-3.18:
943      b004e79504 net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu.
944      04c0eace81 net: inet: Support UID-based routing in IP protocols.
945      18c36d7b71 net: core: add UID to flows, rules, and routes
946      80e3440721 net: core: Add a UID field to struct sock.
947      fa8cc2c30c Revert "net: core: Support UID-based routing."
948      b585141890 Revert "Handle 'sk' being NULL in UID-based routing."
949      5115ab7514 Revert "net: core: fix UID-based routing build"
950      f9f4281f79 Revert "ANDROID: net: fib: remove duplicate assignment"
951
952    android-4.4:
953      341965cf10 net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu.
954      344afd627c net: inet: Support UID-based routing in IP protocols.
955      03441d56d8 net: core: add UID to flows, rules, and routes
956      eb964bdba7 net: core: Add a UID field to struct sock.
957      9789b697c6 Revert "net: core: Support UID-based routing."
958  """
959
960  def GetRulesAtPriority(self, version, priority):
961    rules = self.iproute.DumpRules(version)
962    out = [(rule, attributes) for rule, attributes in rules
963           if attributes.get("FRA_PRIORITY", 0) == priority]
964    return out
965
966  def CheckInitialTablesHaveNoUIDs(self, version):
967    rules = []
968    for priority in [0, 32766, 32767]:
969      rules.extend(self.GetRulesAtPriority(version, priority))
970    for _, attributes in rules:
971      self.assertNotIn("FRA_UID_RANGE", attributes)
972
973  def testIPv4InitialTablesHaveNoUIDs(self):
974    self.CheckInitialTablesHaveNoUIDs(4)
975
976  def testIPv6InitialTablesHaveNoUIDs(self):
977    self.CheckInitialTablesHaveNoUIDs(6)
978
979  @staticmethod
980  def _Random():
981    return random.randint(1000000, 2000000)
982
983  def CheckGetAndSetRules(self, version):
984    start, end = tuple(sorted([self._Random(), self._Random()]))
985    table = self._Random()
986    priority = self._Random()
987
988    # Can't create a UID range to UID -1 because -1 is INVALID_UID...
989    self.assertRaisesErrno(
990        errno.EINVAL,
991        self.iproute.UidRangeRule, version, True, 100, 0xffffffff, table,
992        priority)
993
994    # ... but -2 is valid.
995    self.iproute.UidRangeRule(version, True, 100, 0xfffffffe, table, priority)
996    self.iproute.UidRangeRule(version, False, 100, 0xfffffffe, table, priority)
997
998    try:
999      # Create a UID range rule.
1000      self.iproute.UidRangeRule(version, True, start, end, table, priority)
1001
1002      # Check that deleting the wrong UID range doesn't work.
1003      self.assertRaisesErrno(
1004          errno.ENOENT,
1005          self.iproute.UidRangeRule, version, False, start, end + 1, table,
1006          priority)
1007      self.assertRaisesErrno(errno.ENOENT,
1008        self.iproute.UidRangeRule, version, False, start + 1, end, table,
1009        priority)
1010
1011      # Check that the UID range appears in dumps.
1012      rules = self.GetRulesAtPriority(version, priority)
1013      self.assertTrue(rules)
1014      _, attributes = rules[-1]
1015      self.assertEquals(priority, attributes["FRA_PRIORITY"])
1016      uidrange = attributes["FRA_UID_RANGE"]
1017      self.assertEquals(start, uidrange.start)
1018      self.assertEquals(end, uidrange.end)
1019      self.assertEquals(table, attributes["FRA_TABLE"])
1020    finally:
1021      self.iproute.UidRangeRule(version, False, start, end, table, priority)
1022      self.assertRaisesErrno(
1023          errno.ENOENT,
1024          self.iproute.UidRangeRule, version, False, start, end, table,
1025          priority)
1026
1027    try:
1028      # Create a rule without a UID range.
1029      self.iproute.FwmarkRule(version, True, 300, 301, priority + 1)
1030
1031      # Check it doesn't have a UID range.
1032      rules = self.GetRulesAtPriority(version, priority + 1)
1033      self.assertTrue(rules)
1034      for _, attributes in rules:
1035        self.assertIn("FRA_TABLE", attributes)
1036        self.assertNotIn("FRA_UID_RANGE", attributes)
1037    finally:
1038      self.iproute.FwmarkRule(version, False, 300, 301, priority + 1)
1039
1040    # Test that EEXIST worksfor UID range rules too. This behaviour was only
1041    # added in 4.8.
1042    if net_test.LINUX_VERSION >= (4, 8, 0):
1043      ranges = [(100, 101), (100, 102), (99, 101), (1234, 5678)]
1044      dup = ranges[0]
1045      try:
1046        # Check that otherwise identical rules with different UID ranges can be
1047        # created without EEXIST.
1048        for start, end in ranges:
1049          self.iproute.UidRangeRule(version, True, start, end, table, priority)
1050        # ... but EEXIST is returned if the UID range is identical.
1051        self.assertRaisesErrno(
1052          errno.EEXIST,
1053          self.iproute.UidRangeRule, version, True, dup[0], dup[1], table,
1054          priority)
1055      finally:
1056        # Clean up.
1057        for start, end in ranges + [dup]:
1058          try:
1059            self.iproute.UidRangeRule(version, False, start, end, table,
1060                                      priority)
1061          except IOError:
1062            pass
1063
1064  def testIPv4GetAndSetRules(self):
1065    self.CheckGetAndSetRules(4)
1066
1067  def testIPv6GetAndSetRules(self):
1068    self.CheckGetAndSetRules(6)
1069
1070  def ExpectNoRoute(self, addr, oif, mark, uid):
1071    # The lack of a route may be either an error, or an unreachable route.
1072    try:
1073      routes = self.iproute.GetRoutes(addr, oif, mark, uid)
1074      rtmsg, _ = routes[0]
1075      self.assertEquals(iproute.RTN_UNREACHABLE, rtmsg.type)
1076    except IOError, e:
1077      if int(e.errno) != -int(errno.ENETUNREACH):
1078        raise e
1079
1080  def ExpectRoute(self, addr, oif, mark, uid):
1081    routes = self.iproute.GetRoutes(addr, oif, mark, uid)
1082    rtmsg, _ = routes[0]
1083    self.assertEquals(iproute.RTN_UNICAST, rtmsg.type)
1084
1085  def CheckGetRoute(self, version, addr):
1086    self.ExpectNoRoute(addr, 0, 0, 0)
1087    for netid in self.NETIDS:
1088      uid = self.UidForNetid(netid)
1089      self.ExpectRoute(addr, 0, 0, uid)
1090    self.ExpectNoRoute(addr, 0, 0, 0)
1091
1092  def testIPv4RouteGet(self):
1093    self.CheckGetRoute(4, net_test.IPV4_ADDR)
1094
1095  def testIPv6RouteGet(self):
1096    self.CheckGetRoute(6, net_test.IPV6_ADDR)
1097
1098  def testChangeFdAttributes(self):
1099    netid = random.choice(self.NETIDS)
1100    uid = self._Random()
1101    table = self._TableForNetid(netid)
1102    remoteaddr = self.GetRemoteAddress(6)
1103    s = socket(AF_INET6, SOCK_DGRAM, 0)
1104
1105    def CheckSendFails():
1106      self.assertRaisesErrno(errno.ENETUNREACH,
1107                             s.sendto, "foo", (remoteaddr, 53))
1108    def CheckSendSucceeds():
1109      self.assertEquals(len("foo"), s.sendto("foo", (remoteaddr, 53)))
1110
1111    CheckSendFails()
1112    self.iproute.UidRangeRule(6, True, uid, uid, table, self.PRIORITY_UID)
1113    try:
1114      CheckSendFails()
1115      os.fchown(s.fileno(), uid, -1)
1116      CheckSendSucceeds()
1117      os.fchown(s.fileno(), -1, -1)
1118      CheckSendSucceeds()
1119      os.fchown(s.fileno(), -1, 12345)
1120      CheckSendSucceeds()
1121      os.fchmod(s.fileno(), 0777)
1122      CheckSendSucceeds()
1123      os.fchown(s.fileno(), 0, -1)
1124      CheckSendFails()
1125    finally:
1126      self.iproute.UidRangeRule(6, False, uid, uid, table, self.PRIORITY_UID)
1127
1128
1129class RulesTest(net_test.NetworkTest):
1130
1131  RULE_PRIORITY = 99999
1132
1133  def setUp(self):
1134    self.iproute = iproute.IPRoute()
1135    for version in [4, 6]:
1136      self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
1137
1138  def tearDown(self):
1139    for version in [4, 6]:
1140      self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
1141
1142  def testRuleDeletionMatchesTable(self):
1143    for version in [4, 6]:
1144      # Add rules with mark 300 pointing at tables 301 and 302.
1145      # This checks for a kernel bug where deletion request for tables > 256
1146      # ignored the table.
1147      self.iproute.FwmarkRule(version, True, 300, 301,
1148                              priority=self.RULE_PRIORITY)
1149      self.iproute.FwmarkRule(version, True, 300, 302,
1150                              priority=self.RULE_PRIORITY)
1151      # Delete rule with mark 300 pointing at table 302.
1152      self.iproute.FwmarkRule(version, False, 300, 302,
1153                              priority=self.RULE_PRIORITY)
1154      # Check that the rule pointing at table 301 is still around.
1155      attributes = [a for _, a in self.iproute.DumpRules(version)
1156                    if a.get("FRA_PRIORITY", 0) == self.RULE_PRIORITY]
1157      self.assertEquals(1, len(attributes))
1158      self.assertEquals(301, attributes[0]["FRA_TABLE"])
1159
1160
1161if __name__ == "__main__":
1162  unittest.main()
1163