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