summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2014-05-11 20:32:20 +0900
committerLorenzo Colitti <lorenzo@google.com>2015-02-02 17:47:28 +0900
commita608717fb126073d5b117f5f3b1d2f8fa5767e8a (patch)
tree8e1ae274d409a05c30e6c8c9baec4f4341e96c1b /tests
parentd73faf93478dd7e735b4d878fd96954e85c024d5 (diff)
downloadextras-a608717fb126073d5b117f5f3b1d2f8fa5767e8a.tar.gz
Better tests for Path MTU discovery.
Add PMTU tests for unconnected sockets, and test PMTUD when routing using all methods, not just using socket marking. Change-Id: I8f0f6fc00afa95b8e57792c51e955e2150ef29dc
Diffstat (limited to 'tests')
-rwxr-xr-xtests/net_test/mark_test.py144
1 files changed, 109 insertions, 35 deletions
diff --git a/tests/net_test/mark_test.py b/tests/net_test/mark_test.py
index ec70b5e7..2e2e5b47 100755
--- a/tests/net_test/mark_test.py
+++ b/tests/net_test/mark_test.py
@@ -74,6 +74,16 @@ def HaveUidRouting():
return result
+def LinuxVersion():
+ # Example: "3.4.67-00753-gb7a556f".
+ # Get the part before the dash.
+ version = os.uname()[2].split("-")[0]
+ # Convert it into a tuple such as (3, 4, 67). That allows comparing versions
+ # using < and >, since tuples are compared lexicographically.
+ version = tuple(int(i) for i in version.split("."))
+ return version
+
+
AUTOCONF_TABLE_SYSCTL = "/proc/sys/net/ipv6/conf/default/accept_ra_rt_table"
IPV4_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv4/fwmark_reflect"
IPV6_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv6/fwmark_reflect"
@@ -1398,59 +1408,123 @@ class RATest(MultiNetworkTest):
self.assertEquals(num_routes, GetNumRoutes())
-class PMTUTest(MultiNetworkTest):
+class PMTUTest(InboundMarkingTest):
PAYLOAD_SIZE = 1400
+
+ # Socket options to change PMTU behaviour.
IP_MTU_DISCOVER = 10
- IP_MTU = 14
IP_PMTUDISC_DO = 1
- IPV6_PATHMTU = 61
IPV6_DONTFRAG = 62
+ # Socket options to get the MTU.
+ IP_MTU = 14
+ IPV6_PATHMTU = 61
+
def GetSocketMTU(self, version, s):
if version == 6:
ip6_mtuinfo = s.getsockopt(net_test.SOL_IPV6, self.IPV6_PATHMTU, 32)
- mtu = struct.unpack("=28sI", ip6_mtuinfo)
- return mtu[1]
+ unused_sockaddr, mtu = struct.unpack("=28sI", ip6_mtuinfo)
+ return mtu
else:
return s.getsockopt(net_test.SOL_IP, self.IP_MTU)
- def CheckPMTU(self, version):
+ def DisableFragmentationAndReportErrors(self, version, s):
+ if version == 4:
+ s.setsockopt(net_test.SOL_IP, self.IP_MTU_DISCOVER, self.IP_PMTUDISC_DO)
+ s.setsockopt(net_test.SOL_IP, net_test.IP_RECVERR, 1)
+ else:
+ s.setsockopt(net_test.SOL_IPV6, self.IPV6_DONTFRAG, 1)
+ s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1)
+
+ def CheckPMTU(self, version, use_connect, modes):
for netid in self.tuns:
- s = net_test.UDPSocket(self.GetProtocolFamily(version))
+ for mode in modes:
+ s = self.BuildSocket(version, net_test.UDPSocket, netid, mode)
+ self.DisableFragmentationAndReportErrors(version, s)
- srcaddr = self.MyAddress(version, netid)
- dst_prefix, intermediate = {
- 4: ("172.19.", "172.16.9.12"),
- 6: ("2001:db8::", "2001:db8::1")
- }[version]
- dstaddr = self.GetRandomDestination(dst_prefix)
+ srcaddr = self.MyAddress(version, netid)
+ dst_prefix, intermediate = {
+ 4: ("172.19.", "172.16.9.12"),
+ 6: ("2001:db8::", "2001:db8::1")
+ }[version]
+ dstaddr = self.GetRandomDestination(dst_prefix)
- # So the packet has somewhere to go.
- self.SetSocketMark(s, netid)
- s.connect((dstaddr, 1234))
- self.assertEquals(1500, self.GetSocketMTU(version, s))
-
- s.send(self.PAYLOAD_SIZE * "a")
- packets = self.ReadAllPacketsOn(netid)
- self.assertEquals(1, len(packets))
- _, toobig = Packets.ICMPPacketTooBig(version, intermediate, srcaddr,
- packets[0])
- self.ReceivePacketOn(netid, toobig)
- self.assertEquals(1280, self.GetSocketMTU(version, s))
- s.close()
+ if use_connect:
+ s.connect((dstaddr, 1234))
+
+ payload = self.PAYLOAD_SIZE * "a"
+
+ def SendBigPacket():
+ if use_connect:
+ s.send(payload)
+ else:
+ self.SendOnNetid(version, s, dstaddr, 1234, netid, payload, [])
+
+ # Send a packet and receive a packet too big.
+ SendBigPacket()
+ packets = self.ReadAllPacketsOn(netid)
+ self.assertEquals(1, len(packets))
+ _, toobig = Packets.ICMPPacketTooBig(version, intermediate, srcaddr,
+ packets[0])
+ self.ReceivePacketOn(netid, toobig)
+
+ # Check that another send on the same socket returns EMSGSIZE.
+ self.assertRaisesErrno(errno.EMSGSIZE, SendBigPacket)
+
+ # If this is a connected socket, make sure the socket MTU was set.
+ # Note that in IPv4 this only started working in Linux 3.6!
+ if use_connect and (version == 6 or LinuxVersion() >= (3, 6)):
+ self.assertEquals(1280, self.GetSocketMTU(version, s))
+
+ s.close()
+
+ # Check that other sockets pick up the PMTU we have been told about by
+ # connecting another socket to the same destination and getting its MTU.
+ # This new socket can use any method to select its outgoing interface;
+ # here we use a mark for simplicity.
+ s2 = self.BuildSocket(version, net_test.UDPSocket, netid, "mark")
+ s2.connect((dstaddr, 1234))
+ self.assertEquals(1280, self.GetSocketMTU(version, s2))
+
+ def testIPv4BasicPMTU(self):
+ self.CheckPMTU(4, True, ["mark", "oif"])
+ self.CheckPMTU(4, False, ["mark", "oif"])
+
+ def testIPv6BasicPMTU(self):
+ self.CheckPMTU(6, True, ["mark", "oif"])
+ self.CheckPMTU(6, False, ["mark", "oif"])
+
+ @unittest.skipUnless(HAVE_EXPERIMENTAL_UID_ROUTING, "no UID routing")
+ def testIPv4UIDPMTU(self):
+ self.CheckPMTU(4, True, ["uid"])
+ self.CheckPMTU(4, False, ["uid"])
+
+ @unittest.skipUnless(HAVE_EXPERIMENTAL_UID_ROUTING, "no UID routing")
+ def testIPv6ConnectedSocketUIDPMTU(self):
+ self.CheckPMTU(6, True, ["uid"])
+ self.CheckPMTU(6, False, ["uid"])
- # Open another socket to ensure the path MTU is cached.
- s2 = net_test.UDPSocket(self.GetProtocolFamily(version))
- self.BindToDevice(s2, self.GetInterfaceName(netid))
- s2.connect((dstaddr, 1234))
- self.assertEquals(1280, self.GetSocketMTU(version, s2))
+ # Making Path MTU Discovery work on unmarked sockets requires that mark
+ # reflection be enabled. Otherwise the kernel has no way to know what routing
+ # table the original packet used, and thus it won't be able to clone the
+ # correct route.
- def testIPv4PMTU(self):
- self.CheckPMTU(4)
+ @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+ def testIPv4UnmarkedSocketPMTU(self):
+ self.SetMarkReflectSysctls(1)
+ try:
+ self.CheckPMTU(4, False, [None])
+ finally:
+ self.SetMarkReflectSysctls(0)
- def testIPv6PMTU(self):
- self.CheckPMTU(6)
+ @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+ def testIPv6UnmarkedSocketPMTU(self):
+ self.SetMarkReflectSysctls(1)
+ try:
+ self.CheckPMTU(6, False, [None])
+ finally:
+ self.SetMarkReflectSysctls(0)
@unittest.skipUnless(HAVE_EXPERIMENTAL_UID_ROUTING, "no UID routing")