6 from socket import * # pylint: disable=wildcard-import
8 import time # pylint: disable=unused-import
11 from scapy import all as scapy
14 import multinetwork_base
18 PING_PAYLOAD = "foobarbaz"
25 UDP_PAYLOAD = str(scapy.DNS(rd=1,
26 id=random.randint(0, 65535),
27 qd=scapy.DNSQR(qname="wWW.GoOGle.CoM",
31 IPV4_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv4/fwmark_reflect"
32 IPV6_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv6/fwmark_reflect"
33 SYNCOOKIES_SYSCTL = "/proc/sys/net/ipv4/tcp_syncookies"
34 TCP_MARK_ACCEPT_SYSCTL = "/proc/sys/net/ipv4/tcp_fwmark_accept"
36 HAVE_MARK_REFLECT = os.path.isfile(IPV4_MARK_REFLECT_SYSCTL)
37 HAVE_TCP_MARK_ACCEPT = os.path.isfile(TCP_MARK_ACCEPT_SYSCTL)
39 # The IP[V6]UNICAST_IF socket option was added between 3.1 and 3.4.
40 HAVE_UNICAST_IF = net_test.LINUX_VERSION >= (3, 4, 0)
43 class ConfigurationError(AssertionError):
47 class Packets(object):
60 return random.randint(1025, 65535)
63 def _GetIpLayer(version):
64 return {4: scapy.IP, 6: scapy.IPv6}[version]
67 def _SetPacketTos(packet, tos):
68 if isinstance(packet, scapy.IPv6):
70 elif isinstance(packet, scapy.IP):
73 raise ValueError("Can't find ToS Field")
76 def UDP(cls, version, srcaddr, dstaddr, sport=0):
77 ip = cls._GetIpLayer(version)
78 # Can't just use "if sport" because None has meaning (it means unspecified).
80 sport = cls.RandomPort()
81 return ("UDPv%d packet" % version,
82 ip(src=srcaddr, dst=dstaddr) /
83 scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD)
86 def UDPWithOptions(cls, version, srcaddr, dstaddr, sport=0):
88 packet = (scapy.IP(src=srcaddr, dst=dstaddr, ttl=39, tos=0x83) /
89 scapy.UDP(sport=sport, dport=53) /
92 packet = (scapy.IPv6(src=srcaddr, dst=dstaddr,
93 fl=0xbeef, hlim=39, tc=0x83) /
94 scapy.UDP(sport=sport, dport=53) /
96 return ("UDPv%d packet with options" % version, packet)
99 def SYN(cls, dport, version, srcaddr, dstaddr, sport=0, seq=TCP_SEQ):
100 ip = cls._GetIpLayer(version)
102 sport = cls.RandomPort()
104 ip(src=srcaddr, dst=dstaddr) /
105 scapy.TCP(sport=sport, dport=dport,
107 flags=cls.TCP_SYN, window=cls.TCP_WINDOW))
110 def RST(cls, version, srcaddr, dstaddr, packet):
111 ip = cls._GetIpLayer(version)
112 original = packet.getlayer("TCP")
114 ip(src=srcaddr, dst=dstaddr) /
115 scapy.TCP(sport=original.dport, dport=original.sport,
116 ack=original.seq + 1, seq=None,
117 flags=cls.TCP_RST | cls.TCP_ACK, window=cls.TCP_WINDOW))
120 def SYNACK(cls, version, srcaddr, dstaddr, packet):
121 ip = cls._GetIpLayer(version)
122 original = packet.getlayer("TCP")
123 return ("TCP SYN+ACK",
124 ip(src=srcaddr, dst=dstaddr) /
125 scapy.TCP(sport=original.dport, dport=original.sport,
126 ack=original.seq + 1, seq=None,
127 flags=cls.TCP_SYN | cls.TCP_ACK, window=None))
130 def ACK(cls, version, srcaddr, dstaddr, packet, payload=""):
131 ip = cls._GetIpLayer(version)
132 original = packet.getlayer("TCP")
133 was_syn_or_fin = (original.flags & (cls.TCP_SYN | cls.TCP_FIN)) != 0
134 ack_delta = was_syn_or_fin + len(original.payload)
135 desc = "TCP data" if payload else "TCP ACK"
136 flags = cls.TCP_ACK | cls.TCP_PSH if payload else cls.TCP_ACK
138 ip(src=srcaddr, dst=dstaddr) /
139 scapy.TCP(sport=original.dport, dport=original.sport,
140 ack=original.seq + ack_delta, seq=original.ack,
141 flags=flags, window=cls.TCP_WINDOW) /
145 def FIN(cls, version, srcaddr, dstaddr, packet):
146 ip = cls._GetIpLayer(version)
147 original = packet.getlayer("TCP")
148 was_fin = (original.flags & cls.TCP_FIN) != 0
150 ip(src=srcaddr, dst=dstaddr) /
151 scapy.TCP(sport=original.dport, dport=original.sport,
152 ack=original.seq + was_fin, seq=original.ack,
153 flags=cls.TCP_ACK | cls.TCP_FIN, window=cls.TCP_WINDOW))
156 def GRE(cls, version, srcaddr, dstaddr, proto, packet):
158 ip = scapy.IP(src=srcaddr, dst=dstaddr, proto=net_test.IPPROTO_GRE)
160 ip = scapy.IPv6(src=srcaddr, dst=dstaddr, nh=net_test.IPPROTO_GRE)
161 packet = ip / scapy.GRE(proto=proto) / packet
162 return ("GRE packet", packet)
165 def ICMPPortUnreachable(cls, version, srcaddr, dstaddr, packet):
167 # Linux hardcodes the ToS on ICMP errors to 0xc0 or greater because of
168 # RFC 1812 4.3.2.5 (!).
169 return ("ICMPv4 port unreachable",
170 scapy.IP(src=srcaddr, dst=dstaddr, proto=1, tos=0xc0) /
171 scapy.ICMPerror(type=3, code=3) / packet)
173 return ("ICMPv6 port unreachable",
174 scapy.IPv6(src=srcaddr, dst=dstaddr) /
175 scapy.ICMPv6DestUnreach(code=4) / packet)
178 def ICMPPacketTooBig(cls, version, srcaddr, dstaddr, packet):
180 return ("ICMPv4 fragmentation needed",
181 scapy.IP(src=srcaddr, dst=dstaddr, proto=1) /
182 scapy.ICMPerror(type=3, code=4, unused=1280) / str(packet)[:64])
184 udp = packet.getlayer("UDP")
185 udp.payload = str(udp.payload)[:1280-40-8]
186 return ("ICMPv6 Packet Too Big",
187 scapy.IPv6(src=srcaddr, dst=dstaddr) /
188 scapy.ICMPv6PacketTooBig() / str(packet)[:1232])
191 def ICMPEcho(cls, version, srcaddr, dstaddr):
192 ip = cls._GetIpLayer(version)
193 icmp = {4: scapy.ICMP, 6: scapy.ICMPv6EchoRequest}[version]
194 packet = (ip(src=srcaddr, dst=dstaddr) /
195 icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD)
196 cls._SetPacketTos(packet, PING_TOS)
197 return ("ICMPv%d echo" % version, packet)
200 def ICMPReply(cls, version, srcaddr, dstaddr, packet):
201 ip = cls._GetIpLayer(version)
202 # Scapy doesn't provide an ICMP echo reply constructor.
203 icmpv4_reply = lambda **kwargs: scapy.ICMP(type=0, **kwargs)
204 icmp = {4: icmpv4_reply, 6: scapy.ICMPv6EchoReply}[version]
205 packet = (ip(src=srcaddr, dst=dstaddr) /
206 icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD)
207 # IPv6 only started copying the tclass to echo replies in 3.14.
208 if version == 4 or net_test.LINUX_VERSION >= (3, 14):
209 cls._SetPacketTos(packet, PING_TOS)
210 return ("ICMPv%d echo reply" % version, packet)
213 def NS(cls, srcaddr, tgtaddr, srcmac):
214 solicited = inet_pton(AF_INET6, tgtaddr)
215 last3bytes = tuple([ord(b) for b in solicited[-3:]])
216 solicited = "ff02::1:ff%02x:%02x%02x" % last3bytes
217 packet = (scapy.IPv6(src=srcaddr, dst=solicited) /
218 scapy.ICMPv6ND_NS(tgt=tgtaddr) /
219 scapy.ICMPv6NDOptSrcLLAddr(lladdr=srcmac))
220 return ("ICMPv6 NS", packet)
223 def NA(cls, srcaddr, dstaddr, srcmac):
224 packet = (scapy.IPv6(src=srcaddr, dst=dstaddr) /
225 scapy.ICMPv6ND_NA(tgt=srcaddr, R=0, S=1, O=1) /
226 scapy.ICMPv6NDOptDstLLAddr(lladdr=srcmac))
227 return ("ICMPv6 NA", packet)
230 class InboundMarkingTest(multinetwork_base.MultiNetworkBaseTest):
233 def _SetInboundMarking(cls, netid, is_add):
234 for version in [4, 6]:
235 # Run iptables to set up incoming packet marking.
236 iface = cls.GetInterfaceName(netid)
237 add_del = "-A" if is_add else "-D"
238 iptables = {4: "iptables", 6: "ip6tables"}[version]
239 args = "%s %s INPUT -t mangle -i %s -j MARK --set-mark %d" % (
240 iptables, add_del, iface, netid)
241 iptables = "/sbin/" + iptables
242 ret = os.spawnvp(os.P_WAIT, iptables, args.split(" "))
244 raise ConfigurationError("Setup command failed: %s" % args)
248 super(InboundMarkingTest, cls).setUpClass()
249 for netid in cls.tuns:
250 cls._SetInboundMarking(netid, True)
253 def tearDownClass(cls):
254 for netid in cls.tuns:
255 cls._SetInboundMarking(netid, False)
256 super(InboundMarkingTest, cls).tearDownClass()
259 def SetMarkReflectSysctls(cls, value):
260 cls.SetSysctl(IPV4_MARK_REFLECT_SYSCTL, value)
262 cls.SetSysctl(IPV6_MARK_REFLECT_SYSCTL, value)
264 # This does not exist if we use the version of the patch that uses a
265 # common sysctl for IPv4 and IPv6.
269 class OutgoingTest(multinetwork_base.MultiNetworkBaseTest):
271 # How many times to run outgoing packet tests.
274 def CheckPingPacket(self, version, netid, routing_mode, dstaddr, packet):
275 s = self.BuildSocket(version, net_test.PingSocket, netid, routing_mode)
277 myaddr = self.MyAddress(version, netid)
278 s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
279 s.bind((myaddr, PING_IDENT))
280 net_test.SetSocketTos(s, PING_TOS)
282 desc, expected = Packets.ICMPEcho(version, myaddr, dstaddr)
283 msg = "IPv%d ping: expected %s on %s" % (
284 version, desc, self.GetInterfaceName(netid))
286 s.sendto(packet + PING_PAYLOAD, (dstaddr, 19321))
288 self.ExpectPacketOn(netid, msg, expected)
290 def CheckTCPSYNPacket(self, version, netid, routing_mode, dstaddr):
291 s = self.BuildSocket(version, net_test.TCPSocket, netid, routing_mode)
293 if version == 6 and dstaddr.startswith("::ffff"):
295 myaddr = self.MyAddress(version, netid)
296 desc, expected = Packets.SYN(53, version, myaddr, dstaddr,
297 sport=None, seq=None)
299 # Non-blocking TCP connects always return EINPROGRESS.
300 self.assertRaisesErrno(errno.EINPROGRESS, s.connect, (dstaddr, 53))
301 msg = "IPv%s TCP connect: expected %s on %s" % (
302 version, desc, self.GetInterfaceName(netid))
303 self.ExpectPacketOn(netid, msg, expected)
306 def CheckUDPPacket(self, version, netid, routing_mode, dstaddr):
307 s = self.BuildSocket(version, net_test.UDPSocket, netid, routing_mode)
309 if version == 6 and dstaddr.startswith("::ffff"):
311 myaddr = self.MyAddress(version, netid)
312 desc, expected = Packets.UDP(version, myaddr, dstaddr, sport=None)
313 msg = "IPv%s UDP %%s: expected %s on %s" % (
314 version, desc, self.GetInterfaceName(netid))
316 s.sendto(UDP_PAYLOAD, (dstaddr, 53))
317 self.ExpectPacketOn(netid, msg % "sendto", expected)
319 # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
320 if routing_mode != "ucast_oif":
321 s.connect((dstaddr, 53))
323 self.ExpectPacketOn(netid, msg % "connect/send", expected)
326 def CheckRawGrePacket(self, version, netid, routing_mode, dstaddr):
327 s = self.BuildSocket(version, net_test.RawGRESocket, netid, routing_mode)
329 inner_version = {4: 6, 6: 4}[version]
330 inner_src = self.MyAddress(inner_version, netid)
331 inner_dst = self.GetRemoteAddress(inner_version)
332 inner = str(Packets.UDP(inner_version, inner_src, inner_dst, sport=None)[1])
334 ethertype = {4: net_test.ETH_P_IP, 6: net_test.ETH_P_IPV6}[inner_version]
335 # A GRE header can be as simple as two zero bytes and the ethertype.
336 packet = struct.pack("!i", ethertype) + inner
337 myaddr = self.MyAddress(version, netid)
339 s.sendto(packet, (dstaddr, IPPROTO_GRE))
340 desc, expected = Packets.GRE(version, myaddr, dstaddr, ethertype, inner)
341 msg = "Raw IPv%d GRE with inner IPv%d UDP: expected %s on %s" % (
342 version, inner_version, desc, self.GetInterfaceName(netid))
343 self.ExpectPacketOn(netid, msg, expected)
345 def CheckOutgoingPackets(self, routing_mode):
346 v4addr = self.IPV4_ADDR
347 v6addr = self.IPV6_ADDR
348 v4mapped = "::ffff:" + v4addr
350 for _ in xrange(self.ITERATIONS):
351 for netid in self.tuns:
353 self.CheckPingPacket(4, netid, routing_mode, v4addr, self.IPV4_PING)
355 if routing_mode != "oif":
356 self.CheckPingPacket(6, netid, routing_mode, v6addr, self.IPV6_PING)
358 # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
359 if routing_mode != "ucast_oif":
360 self.CheckTCPSYNPacket(4, netid, routing_mode, v4addr)
361 self.CheckTCPSYNPacket(6, netid, routing_mode, v6addr)
362 self.CheckTCPSYNPacket(6, netid, routing_mode, v4mapped)
364 self.CheckUDPPacket(4, netid, routing_mode, v4addr)
365 self.CheckUDPPacket(6, netid, routing_mode, v6addr)
366 self.CheckUDPPacket(6, netid, routing_mode, v4mapped)
368 # Creating raw sockets on non-root UIDs requires properly setting
369 # capabilities, which is hard to do from Python.
370 # IP_UNICAST_IF is not supported on raw sockets.
371 if routing_mode not in ["uid", "ucast_oif"]:
372 self.CheckRawGrePacket(4, netid, routing_mode, v4addr)
373 self.CheckRawGrePacket(6, netid, routing_mode, v6addr)
375 def testMarkRouting(self):
376 """Checks that socket marking selects the right outgoing interface."""
377 self.CheckOutgoingPackets("mark")
379 @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
380 def testUidRouting(self):
381 """Checks that UID routing selects the right outgoing interface."""
382 self.CheckOutgoingPackets("uid")
384 def testOifRouting(self):
385 """Checks that oif routing selects the right outgoing interface."""
386 self.CheckOutgoingPackets("oif")
388 @unittest.skipUnless(HAVE_UNICAST_IF, "no support for UNICAST_IF")
389 def testUcastOifRouting(self):
390 """Checks that ucast oif routing selects the right outgoing interface."""
391 self.CheckOutgoingPackets("ucast_oif")
393 def CheckRemarking(self, version, use_connect):
394 # Remarking or resetting UNICAST_IF on connected sockets does not work.
398 modes = ["mark", "oif"]
400 modes += ["ucast_oif"]
403 s = net_test.UDPSocket(self.GetProtocolFamily(version))
405 # Figure out what packets to expect.
406 unspec = {4: "0.0.0.0", 6: "::"}[version]
407 sport = Packets.RandomPort()
408 s.bind((unspec, sport))
409 dstaddr = {4: self.IPV4_ADDR, 6: self.IPV6_ADDR}[version]
410 desc, expected = Packets.UDP(version, unspec, dstaddr, sport)
412 # If we're testing connected sockets, connect the socket on the first
415 netid = self.tuns.keys()[0]
416 self.SelectInterface(s, netid, mode)
417 s.connect((dstaddr, 53))
418 expected.src = self.MyAddress(version, netid)
420 # For each netid, select that network without closing the socket, and
421 # check that the packets sent on that socket go out on the right network.
422 for netid in self.tuns:
423 self.SelectInterface(s, netid, mode)
425 expected.src = self.MyAddress(version, netid)
426 s.sendto(UDP_PAYLOAD, (dstaddr, 53))
427 connected_str = "Connected" if use_connect else "Unconnected"
428 msg = "%s UDPv%d socket remarked using %s: expecting %s on %s" % (
429 connected_str, version, mode, desc, self.GetInterfaceName(netid))
430 self.ExpectPacketOn(netid, msg, expected)
431 self.SelectInterface(s, None, mode)
433 def testIPv4Remarking(self):
434 """Checks that updating the mark on an IPv4 socket changes routing."""
435 self.CheckRemarking(4, False)
436 self.CheckRemarking(4, True)
438 def testIPv6Remarking(self):
439 """Checks that updating the mark on an IPv6 socket changes routing."""
440 self.CheckRemarking(6, False)
441 self.CheckRemarking(6, True)
443 def testIPv6StickyPktinfo(self):
444 for _ in xrange(self.ITERATIONS):
445 for netid in self.tuns:
446 s = net_test.UDPSocket(AF_INET6)
449 net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead)
450 s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1)
452 # Set some destination options.
453 nonce = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
455 "\x11\x02", # Next header=UDP, 24 bytes of options.
456 "\x01\x06", "\x00" * 6, # PadN, 6 bytes of padding.
457 "\x8b\x0c", # ILNP nonce, 12 bytes.
460 s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, dstopts)
461 s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 255)
463 pktinfo = multinetwork_base.MakePktInfo(6, None, self.ifindices[netid])
465 # Set the sticky pktinfo option.
466 s.setsockopt(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo)
468 # Specify the flowlabel in the destination address.
469 s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 53, 0xdead, 0))
471 sport = s.getsockname()[1]
472 srcaddr = self.MyAddress(6, netid)
473 expected = (scapy.IPv6(src=srcaddr, dst=net_test.IPV6_ADDR,
474 fl=0xdead, hlim=255) /
475 scapy.IPv6ExtHdrDestOpt(
476 options=[scapy.PadN(optdata="\x00\x00\x00\x00\x00\x00"),
477 scapy.HBHOptUnknown(otype=0x8b,
479 scapy.UDP(sport=sport, dport=53) /
481 msg = "IPv6 UDP using sticky pktinfo: expected UDP packet on %s" % (
482 self.GetInterfaceName(netid))
483 self.ExpectPacketOn(netid, msg, expected)
485 def CheckPktinfoRouting(self, version):
486 for _ in xrange(self.ITERATIONS):
487 for netid in self.tuns:
488 family = self.GetProtocolFamily(version)
489 s = net_test.UDPSocket(family)
492 # Create a flowlabel so we can use it.
493 net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xbeef)
495 # Specify some arbitrary options.
497 (net_test.SOL_IPV6, IPV6_HOPLIMIT, 39),
498 (net_test.SOL_IPV6, IPV6_TCLASS, 0x83),
499 (net_test.SOL_IPV6, IPV6_FLOWINFO, int(htonl(0xbeef))),
502 # Support for setting IPv4 TOS and TTL via cmsg only appeared in 3.13.
504 s.setsockopt(net_test.SOL_IP, IP_TTL, 39)
505 s.setsockopt(net_test.SOL_IP, IP_TOS, 0x83)
507 dstaddr = self.GetRemoteAddress(version)
508 self.SendOnNetid(version, s, dstaddr, 53, netid, UDP_PAYLOAD, cmsgs)
510 sport = s.getsockname()[1]
511 srcaddr = self.MyAddress(version, netid)
513 desc, expected = Packets.UDPWithOptions(version, srcaddr, dstaddr,
516 msg = "IPv%d UDP using pktinfo routing: expected %s on %s" % (
517 version, desc, self.GetInterfaceName(netid))
518 self.ExpectPacketOn(netid, msg, expected)
520 def testIPv4PktinfoRouting(self):
521 self.CheckPktinfoRouting(4)
523 def testIPv6PktinfoRouting(self):
524 self.CheckPktinfoRouting(6)
527 class MarkTest(InboundMarkingTest):
529 def CheckReflection(self, version, gen_packet, gen_reply):
530 """Checks that replies go out on the same interface as the original.
532 For each combination:
533 - Calls gen_packet to generate a packet to that IP address.
534 - Writes the packet generated by gen_packet on the given tun
535 interface, causing the kernel to receive it.
536 - Checks that the kernel's reply matches the packet generated by
540 version: An integer, 4 or 6.
541 gen_packet: A function taking an IP version (an integer), a source
542 address and a destination address (strings), and returning a scapy
544 gen_reply: A function taking the same arguments as gen_packet,
545 plus a scapy packet, and returning a scapy packet.
547 for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
548 # Generate a test packet.
549 desc, packet = gen_packet(version, remoteaddr, myaddr)
551 # Test with mark reflection enabled and disabled.
552 for reflect in [0, 1]:
553 self.SetMarkReflectSysctls(reflect)
554 # HACK: IPv6 ping replies always do a routing lookup with the
555 # interface the ping came in on. So even if mark reflection is not
556 # working, IPv6 ping replies will be properly reflected. Don't
557 # fail when that happens.
558 if reflect or desc == "ICMPv6 echo":
559 reply_desc, reply = gen_reply(version, myaddr, remoteaddr, packet)
561 reply_desc, reply = None, None
563 msg = self._FormatMessage(iif, ip_if, "reflect=%d" % reflect,
565 self._ReceiveAndExpectResponse(netid, packet, reply, msg)
567 def SYNToClosedPort(self, *args):
568 return Packets.SYN(999, *args)
570 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
571 def testIPv4ICMPErrorsReflectMark(self):
572 self.CheckReflection(4, Packets.UDP, Packets.ICMPPortUnreachable)
574 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
575 def testIPv6ICMPErrorsReflectMark(self):
576 self.CheckReflection(6, Packets.UDP, Packets.ICMPPortUnreachable)
578 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
579 def testIPv4PingRepliesReflectMarkAndTos(self):
580 self.CheckReflection(4, Packets.ICMPEcho, Packets.ICMPReply)
582 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
583 def testIPv6PingRepliesReflectMarkAndTos(self):
584 self.CheckReflection(6, Packets.ICMPEcho, Packets.ICMPReply)
586 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
587 def testIPv4RSTsReflectMark(self):
588 self.CheckReflection(4, self.SYNToClosedPort, Packets.RST)
590 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
591 def testIPv6RSTsReflectMark(self):
592 self.CheckReflection(6, self.SYNToClosedPort, Packets.RST)
595 class TCPAcceptTest(InboundMarkingTest):
597 MODE_BINDTODEVICE = "SO_BINDTODEVICE"
598 MODE_INCOMING_MARK = "incoming mark"
599 MODE_EXPLICIT_MARK = "explicit mark"
604 super(TCPAcceptTest, cls).setUpClass()
606 # Open a port so we can observe SYN+ACKs. Since it's a dual-stack socket it
607 # will accept both IPv4 and IPv6 connections. We do this here instead of in
608 # each test so we can use the same socket every time. That way, if a kernel
609 # bug causes incoming packets to mark the listening socket instead of the
610 # accepted socket, the test will fail as soon as the next address/interface
611 # combination is tried.
612 cls.listenport = 1234
613 cls.listensocket = net_test.IPv6TCPSocket()
614 cls.listensocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
615 cls.listensocket.bind(("::", cls.listenport))
616 cls.listensocket.listen(100)
618 def BounceSocket(self, s):
619 """Attempts to invalidate a socket's destination cache entry."""
620 if s.family == AF_INET:
621 tos = s.getsockopt(SOL_IP, IP_TOS)
622 s.setsockopt(net_test.SOL_IP, IP_TOS, 53)
623 s.setsockopt(net_test.SOL_IP, IP_TOS, tos)
625 # UDP, 8 bytes dstopts; PAD1, 4 bytes padding; 4 bytes zeros.
626 pad8 = "".join(["\x11\x00", "\x01\x04", "\x00" * 4])
627 s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, pad8)
628 s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, "")
630 def _SetTCPMarkAcceptSysctl(self, value):
631 self.SetSysctl(TCP_MARK_ACCEPT_SYSCTL, value)
633 def CheckTCPConnection(self, mode, listensocket, netid, version,
634 myaddr, remoteaddr, packet, reply, msg):
635 establishing_ack = Packets.ACK(version, remoteaddr, myaddr, reply)[1]
637 # Attempt to confuse the kernel.
638 self.BounceSocket(listensocket)
640 self.ReceivePacketOn(netid, establishing_ack)
642 # If we're using UID routing, the accept() call has to be run as a UID that
643 # is routed to the specified netid, because the UID of the socket returned
644 # by accept() is the effective UID of the process that calls it. It doesn't
645 # need to be the same UID; any UID that selects the same interface will do.
646 with net_test.RunAsUid(self.UidForNetid(netid)):
647 s, _ = listensocket.accept()
650 # Check that data sent on the connection goes out on the right interface.
651 desc, data = Packets.ACK(version, myaddr, remoteaddr, establishing_ack,
654 self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data)
657 # Keep up our end of the conversation.
658 ack = Packets.ACK(version, remoteaddr, myaddr, data)[1]
659 self.BounceSocket(listensocket)
660 self.ReceivePacketOn(netid, ack)
662 mark = self.GetSocketMark(s)
667 if mode == self.MODE_INCOMING_MARK:
668 self.assertEquals(netid, mark,
669 msg + ": Accepted socket: Expected mark %d, got %d" % (
671 elif mode != self.MODE_EXPLICIT_MARK:
672 self.assertEquals(0, self.GetSocketMark(listensocket))
674 # Check the FIN was sent on the right interface, and ack it. We don't expect
675 # this to fail because by the time the connection is established things are
676 # likely working, but a) extra tests are always good and b) extra packets
677 # like the FIN (and retransmitted FINs) could cause later tests that expect
678 # no packets to fail.
679 desc, fin = Packets.FIN(version, myaddr, remoteaddr, ack)
680 self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin)
682 desc, finack = Packets.FIN(version, remoteaddr, myaddr, fin)
683 self.ReceivePacketOn(netid, finack)
685 # Since we called close() earlier, the userspace socket object is gone, so
686 # the socket has no UID. If we're doing UID routing, the ack might be routed
687 # incorrectly. Not much we can do here.
688 desc, finackack = Packets.ACK(version, myaddr, remoteaddr, finack)
689 if mode != self.MODE_UID:
690 self.ExpectPacketOn(netid, msg + ": expecting final ack", finackack)
692 self.ClearTunQueues()
694 def CheckTCP(self, version, modes):
695 """Checks that incoming TCP connections work.
698 version: An integer, 4 or 6.
699 modes: A list of modes to excercise.
701 for syncookies in [0, 2]:
703 for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
704 if mode == self.MODE_UID:
705 listensocket = self.BuildSocket(6, net_test.TCPSocket, netid, mode)
706 listensocket.listen(100)
708 listensocket = self.listensocket
710 listenport = listensocket.getsockname()[1]
712 if HAVE_TCP_MARK_ACCEPT:
713 accept_sysctl = 1 if mode == self.MODE_INCOMING_MARK else 0
714 self._SetTCPMarkAcceptSysctl(accept_sysctl)
716 bound_dev = iif if mode == self.MODE_BINDTODEVICE else None
717 self.BindToDevice(listensocket, bound_dev)
719 mark = netid if mode == self.MODE_EXPLICIT_MARK else 0
720 self.SetSocketMark(listensocket, mark)
722 # Generate the packet here instead of in the outer loop, so
723 # subsequent TCP connections use different source ports and
724 # retransmissions from old connections don't confuse subsequent
726 desc, packet = Packets.SYN(listenport, version, remoteaddr, myaddr)
729 reply_desc, reply = Packets.SYNACK(version, myaddr, remoteaddr,
732 reply_desc, reply = None, None
734 extra = "mode=%s, syncookies=%d" % (mode, syncookies)
735 msg = self._FormatMessage(iif, ip_if, extra, desc, reply_desc)
736 reply = self._ReceiveAndExpectResponse(netid, packet, reply, msg)
738 self.CheckTCPConnection(mode, listensocket, netid, version, myaddr,
739 remoteaddr, packet, reply, msg)
741 def testBasicTCP(self):
742 self.CheckTCP(4, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
743 self.CheckTCP(6, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
745 @unittest.skipUnless(HAVE_TCP_MARK_ACCEPT, "fwmark writeback not supported")
746 def testIPv4MarkAccept(self):
747 self.CheckTCP(4, [self.MODE_INCOMING_MARK])
749 @unittest.skipUnless(HAVE_TCP_MARK_ACCEPT, "fwmark writeback not supported")
750 def testIPv6MarkAccept(self):
751 self.CheckTCP(6, [self.MODE_INCOMING_MARK])
753 @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
754 def testIPv4UidAccept(self):
755 self.CheckTCP(4, [self.MODE_UID])
757 @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
758 def testIPv6UidAccept(self):
759 self.CheckTCP(6, [self.MODE_UID])
761 def testIPv6ExplicitMark(self):
762 self.CheckTCP(6, [self.MODE_EXPLICIT_MARK])
765 class RATest(multinetwork_base.MultiNetworkBaseTest):
767 def testDoesNotHaveObsoleteSysctl(self):
768 self.assertFalse(os.path.isfile(
769 "/proc/sys/net/ipv6/route/autoconf_table_offset"))
771 @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
772 "no support for per-table autoconf")
773 def testPurgeDefaultRouters(self):
775 def CheckIPv6Connectivity(expect_connectivity):
776 for netid in self.NETIDS:
777 s = net_test.UDPSocket(AF_INET6)
778 self.SetSocketMark(s, netid)
779 if expect_connectivity:
780 self.assertTrue(s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 1234)))
782 self.assertRaisesErrno(errno.ENETUNREACH, s.sendto, UDP_PAYLOAD,
783 (net_test.IPV6_ADDR, 1234))
786 CheckIPv6Connectivity(True)
787 self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1)
788 CheckIPv6Connectivity(False)
790 self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0)
791 for netid in self.NETIDS:
793 CheckIPv6Connectivity(True)
795 def testOnlinkCommunication(self):
796 """Checks that on-link communication goes direct and not through routers."""
797 for netid in self.tuns:
798 # Send a UDP packet to a random on-link destination.
799 s = net_test.UDPSocket(AF_INET6)
800 iface = self.GetInterfaceName(netid)
801 self.BindToDevice(s, iface)
802 # dstaddr can never be our address because GetRandomDestination only fills
803 # in the lower 32 bits, but our address has 0xff in the byte before that
804 # (since it's constructed from the EUI-64 and so has ff:fe in the middle).
805 dstaddr = self.GetRandomDestination(self.IPv6Prefix(netid))
806 s.sendto(UDP_PAYLOAD, (dstaddr, 53))
808 # Expect an NS for that destination on the interface.
809 myaddr = self.MyAddress(6, netid)
810 mymac = self.MyMacAddress(netid)
811 desc, expected = Packets.NS(myaddr, dstaddr, mymac)
812 msg = "Sending UDP packet to on-link destination: expecting %s" % desc
813 time.sleep(0.0001) # Required to make the test work on kernel 3.1(!)
814 self.ExpectPacketOn(netid, msg, expected)
817 tgtmac = "02:00:00:00:%02x:99" % netid
818 _, reply = Packets.NA(dstaddr, myaddr, tgtmac)
819 # Don't use ReceivePacketOn, since that uses the router's MAC address as
820 # the source. Instead, construct our own Ethernet header with source
822 reply = scapy.Ether(src=tgtmac, dst=mymac) / reply
823 self.ReceiveEtherPacketOn(netid, reply)
825 # Expect the kernel to send the original UDP packet now that the ND cache
826 # entry has been populated.
827 sport = s.getsockname()[1]
828 desc, expected = Packets.UDP(6, myaddr, dstaddr, sport=sport)
829 msg = "After NA response, expecting %s" % desc
830 self.ExpectPacketOn(netid, msg, expected)
832 # This test documents a known issue: routing tables are never deleted.
833 @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
834 "no support for per-table autoconf")
835 def testLeftoverRoutes(self):
837 return len(open("/proc/net/ipv6_route").readlines())
839 num_routes = GetNumRoutes()
840 for i in xrange(10, 20):
842 self.tuns[i] = self.CreateTunInterface(i)
847 self.assertLess(num_routes, GetNumRoutes())
850 class PMTUTest(InboundMarkingTest):
854 # Socket options to change PMTU behaviour.
859 # Socket options to get the MTU.
863 def GetSocketMTU(self, version, s):
865 ip6_mtuinfo = s.getsockopt(net_test.SOL_IPV6, self.IPV6_PATHMTU, 32)
866 unused_sockaddr, mtu = struct.unpack("=28sI", ip6_mtuinfo)
869 return s.getsockopt(net_test.SOL_IP, self.IP_MTU)
871 def DisableFragmentationAndReportErrors(self, version, s):
873 s.setsockopt(net_test.SOL_IP, self.IP_MTU_DISCOVER, self.IP_PMTUDISC_DO)
874 s.setsockopt(net_test.SOL_IP, net_test.IP_RECVERR, 1)
876 s.setsockopt(net_test.SOL_IPV6, self.IPV6_DONTFRAG, 1)
877 s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1)
879 def CheckPMTU(self, version, use_connect, modes):
881 def SendBigPacket(version, s, dstaddr, netid, payload):
885 self.SendOnNetid(version, s, dstaddr, 1234, netid, payload, [])
887 for netid in self.tuns:
889 s = self.BuildSocket(version, net_test.UDPSocket, netid, mode)
890 self.DisableFragmentationAndReportErrors(version, s)
892 srcaddr = self.MyAddress(version, netid)
893 dst_prefix, intermediate = {
894 4: ("172.19.", "172.16.9.12"),
895 6: ("2001:db8::", "2001:db8::1")
897 dstaddr = self.GetRandomDestination(dst_prefix)
900 s.connect((dstaddr, 1234))
902 payload = self.PAYLOAD_SIZE * "a"
904 # Send a packet and receive a packet too big.
905 SendBigPacket(version, s, dstaddr, netid, payload)
906 packets = self.ReadAllPacketsOn(netid)
907 self.assertEquals(1, len(packets))
908 _, toobig = Packets.ICMPPacketTooBig(version, intermediate, srcaddr,
910 self.ReceivePacketOn(netid, toobig)
912 # Check that another send on the same socket returns EMSGSIZE.
913 self.assertRaisesErrno(
915 SendBigPacket, version, s, dstaddr, netid, payload)
917 # If this is a connected socket, make sure the socket MTU was set.
918 # Note that in IPv4 this only started working in Linux 3.6!
919 if use_connect and (version == 6 or net_test.LINUX_VERSION >= (3, 6)):
920 self.assertEquals(1280, self.GetSocketMTU(version, s))
924 # Check that other sockets pick up the PMTU we have been told about by
925 # connecting another socket to the same destination and getting its MTU.
926 # This new socket can use any method to select its outgoing interface;
927 # here we use a mark for simplicity.
928 s2 = self.BuildSocket(version, net_test.UDPSocket, netid, "mark")
929 s2.connect((dstaddr, 1234))
930 self.assertEquals(1280, self.GetSocketMTU(version, s2))
932 # Also check the MTU reported by ip route get, this time using the oif.
933 routes = self.iproute.GetRoutes(dstaddr, self.ifindices[netid], 0, None)
934 self.assertTrue(routes)
936 rtmsg, attributes = route
937 self.assertEquals(iproute.RTN_UNICAST, rtmsg.type)
938 metrics = attributes["RTA_METRICS"]
939 self.assertEquals(metrics["RTAX_MTU"], 1280)
941 def testIPv4BasicPMTU(self):
942 self.CheckPMTU(4, True, ["mark", "oif"])
943 self.CheckPMTU(4, False, ["mark", "oif"])
945 def testIPv6BasicPMTU(self):
946 self.CheckPMTU(6, True, ["mark", "oif"])
947 self.CheckPMTU(6, False, ["mark", "oif"])
949 @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
950 def testIPv4UIDPMTU(self):
951 self.CheckPMTU(4, True, ["uid"])
952 self.CheckPMTU(4, False, ["uid"])
954 @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
955 def testIPv6UIDPMTU(self):
956 self.CheckPMTU(6, True, ["uid"])
957 self.CheckPMTU(6, False, ["uid"])
959 # Making Path MTU Discovery work on unmarked sockets requires that mark
960 # reflection be enabled. Otherwise the kernel has no way to know what routing
961 # table the original packet used, and thus it won't be able to clone the
964 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
965 def testIPv4UnmarkedSocketPMTU(self):
966 self.SetMarkReflectSysctls(1)
968 self.CheckPMTU(4, False, [None])
970 self.SetMarkReflectSysctls(0)
972 @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
973 def testIPv6UnmarkedSocketPMTU(self):
974 self.SetMarkReflectSysctls(1)
976 self.CheckPMTU(6, False, [None])
978 self.SetMarkReflectSysctls(0)
981 @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
982 class UidRoutingTest(multinetwork_base.MultiNetworkBaseTest):
984 def GetRulesAtPriority(self, version, priority):
985 rules = self.iproute.DumpRules(version)
986 out = [(rule, attributes) for rule, attributes in rules
987 if attributes.get("FRA_PRIORITY", 0) == priority]
990 def CheckInitialTablesHaveNoUIDs(self, version):
992 for priority in [0, 32766, 32767]:
993 rules.extend(self.GetRulesAtPriority(version, priority))
994 for _, attributes in rules:
995 self.assertNotIn("FRA_UID_START", attributes)
996 self.assertNotIn("FRA_UID_END", attributes)
998 def testIPv4InitialTablesHaveNoUIDs(self):
999 self.CheckInitialTablesHaveNoUIDs(4)
1001 def testIPv6InitialTablesHaveNoUIDs(self):
1002 self.CheckInitialTablesHaveNoUIDs(6)
1004 def CheckGetAndSetRules(self, version):
1006 return random.randint(1000000, 2000000)
1008 start, end = tuple(sorted([Random(), Random()]))
1013 self.iproute.UidRangeRule(version, True, start, end, table,
1016 rules = self.GetRulesAtPriority(version, priority)
1017 self.assertTrue(rules)
1018 _, attributes = rules[-1]
1019 self.assertEquals(priority, attributes["FRA_PRIORITY"])
1020 self.assertEquals(start, attributes["FRA_UID_START"])
1021 self.assertEquals(end, attributes["FRA_UID_END"])
1022 self.assertEquals(table, attributes["FRA_TABLE"])
1024 self.iproute.UidRangeRule(version, False, start, end, table,
1027 def testIPv4GetAndSetRules(self):
1028 self.CheckGetAndSetRules(4)
1030 def testIPv6GetAndSetRules(self):
1031 self.CheckGetAndSetRules(6)
1033 def ExpectNoRoute(self, addr, oif, mark, uid):
1034 # The lack of a route may be either an error, or an unreachable route.
1036 routes = self.iproute.GetRoutes(addr, oif, mark, uid)
1037 rtmsg, _ = routes[0]
1038 self.assertEquals(iproute.RTN_UNREACHABLE, rtmsg.type)
1040 if int(e.errno) != -int(errno.ENETUNREACH):
1043 def ExpectRoute(self, addr, oif, mark, uid):
1044 routes = self.iproute.GetRoutes(addr, oif, mark, uid)
1045 rtmsg, _ = routes[0]
1046 self.assertEquals(iproute.RTN_UNICAST, rtmsg.type)
1048 def CheckGetRoute(self, version, addr):
1049 self.ExpectNoRoute(addr, 0, 0, 0)
1050 for netid in self.NETIDS:
1051 uid = self.UidForNetid(netid)
1052 self.ExpectRoute(addr, 0, 0, uid)
1053 self.ExpectNoRoute(addr, 0, 0, 0)
1055 def testIPv4RouteGet(self):
1056 self.CheckGetRoute(4, net_test.IPV4_ADDR)
1058 def testIPv6RouteGet(self):
1059 self.CheckGetRoute(6, net_test.IPV6_ADDR)
1062 class RulesTest(net_test.NetworkTest):
1064 RULE_PRIORITY = 99999
1067 self.iproute = iproute.IPRoute()
1068 for version in [4, 6]:
1069 self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
1072 for version in [4, 6]:
1073 self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
1075 def testRuleDeletionMatchesTable(self):
1076 for version in [4, 6]:
1077 # Add rules with mark 300 pointing at tables 301 and 302.
1078 # This checks for a kernel bug where deletion request for tables > 256
1079 # ignored the table.
1080 self.iproute.FwmarkRule(version, True, 300, 301,
1081 priority=self.RULE_PRIORITY)
1082 self.iproute.FwmarkRule(version, True, 300, 302,
1083 priority=self.RULE_PRIORITY)
1084 # Delete rule with mark 300 pointing at table 302.
1085 self.iproute.FwmarkRule(version, False, 300, 302,
1086 priority=self.RULE_PRIORITY)
1087 # Check that the rule pointing at table 301 is still around.
1088 attributes = [a for _, a in self.iproute.DumpRules(version)
1089 if a.get("FRA_PRIORITY", 0) == self.RULE_PRIORITY]
1090 self.assertEquals(1, len(attributes))
1091 self.assertEquals(301, attributes[0]["FRA_TABLE"])
1094 if __name__ == "__main__":