• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Copyright 2015 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 itertools
18import random
19import unittest
20
21from socket import *
22
23import multinetwork_base
24import net_test
25import packets
26
27class ForwardingTest(multinetwork_base.MultiNetworkBaseTest):
28  TCP_TIME_WAIT = 6
29
30  def ForwardBetweenInterfaces(self, enabled, iface1, iface2):
31    for iif, oif in itertools.permutations([iface1, iface2]):
32      self.iproute.IifRule(6, enabled, self.GetInterfaceName(iif),
33                           self._TableForNetid(oif), self.PRIORITY_IIF)
34
35  def setUp(self):
36    self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1)
37
38  def tearDown(self):
39    self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0)
40
41  """Checks that IPv6 forwarding works for UDP packets and is not broken by early demux.
42
43  Relevant kernel commits:
44    upstream:
45      5425077d73e0c8e net: ipv6: Add early demux handler for UDP unicast
46      0bd84065b19bca1 net: ipv6: Fix UDP early demux lookup with udp_l3mdev_accept=0
47      Ifa9c2ddfaa5b51 net: ipv6: reset daddr and dport in sk if connect() fails
48  """
49  def CheckForwardingUdp(self, netid, iface1, iface2):
50    # TODO: Make a test for IPv4
51    # 1. Make version as an argument. Pick address to bind from array based
52    #    on version.
53    # 2. The prefix length of the address is hardcoded to /64. Use the subnet
54    #    mask there instead.
55    # 3. We recreate the address with SendRA, which obviously only works for
56    #    IPv6. Use AddAddress for IPv4.
57
58    # Create a UDP socket and bind to it
59    version = 6
60    s = net_test.UDPSocket(AF_INET6)
61    self.SetSocketMark(s, netid)
62    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
63    s.bind(("::", 53))
64
65    remoteaddr = self.GetRemoteAddress(version)
66    myaddr = self.MyAddress(version, netid)
67
68    try:
69      # Delete address and check if packet is forwarded
70      # (and not dropped because an incorrect socket match happened)
71      self.iproute.DelAddress(myaddr, 64, self.ifindices[netid])
72      hoplimit = 39
73      desc, udp_pkt = packets.UDPWithOptions(version, myaddr, remoteaddr, 53)
74      # Decrements the hoplimit of a packet to simulate forwarding.
75      desc_fwded, udp_fwd = packets.UDPWithOptions(version, myaddr, remoteaddr,
76                                                   53, hoplimit - 1)
77      msg = "Sent %s, expected %s" % (desc, desc_fwded)
78      self.ReceivePacketOn(iface1, udp_pkt)
79      self.ExpectPacketOn(iface2, msg, udp_fwd)
80    finally:
81      # Recreate the address.
82      self.SendRA(netid)
83      s.close()
84
85  """Checks that IPv6 forwarding doesn't crash the system.
86
87  Relevant kernel commits:
88    upstream net-next:
89      e7eadb4 ipv6: inet6_sk() should use sk_fullsock()
90    android-3.10:
91      feee3c1 ipv6: inet6_sk() should use sk_fullsock()
92      cdab04e net: add sk_fullsock() helper
93    android-3.18:
94      8246f18 ipv6: inet6_sk() should use sk_fullsock()
95      bea19db net: add sk_fullsock() helper
96  """
97  def CheckForwardingCrashTcp(self, netid, iface1, iface2):
98    version = 6
99    listensocket = net_test.IPv6TCPSocket()
100    self.SetSocketMark(listensocket, netid)
101    listenport = net_test.BindRandomPort(version, listensocket)
102
103    remoteaddr = self.GetRemoteAddress(version)
104    myaddr = self.MyAddress(version, netid)
105
106    desc, syn = packets.SYN(listenport, version, remoteaddr, myaddr)
107    synack_desc, synack = packets.SYNACK(version, myaddr, remoteaddr, syn)
108    msg = "Sent %s, expected %s" % (desc, synack_desc)
109    reply = self._ReceiveAndExpectResponse(netid, syn, synack, msg)
110
111    establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1]
112    self.ReceivePacketOn(netid, establishing_ack)
113    accepted, peer = listensocket.accept()
114    remoteport = accepted.getpeername()[1]
115
116    accepted.close()
117    desc, fin = packets.FIN(version, myaddr, remoteaddr, establishing_ack)
118    self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin)
119
120    desc, finack = packets.FIN(version, remoteaddr, myaddr, fin)
121    self.ReceivePacketOn(netid, finack)
122
123    # Check our socket is now in TIME_WAIT.
124    sockets = self.ReadProcNetSocket("tcp6")
125    mysrc = "%s:%04X" % (net_test.FormatSockStatAddress(myaddr), listenport)
126    mydst = "%s:%04X" % (net_test.FormatSockStatAddress(remoteaddr), remoteport)
127    state = None
128    sockets = [s for s in sockets if s[0] == mysrc and s[1] == mydst]
129    self.assertEqual(1, len(sockets))
130    self.assertEqual("%02X" % self.TCP_TIME_WAIT, sockets[0][2])
131
132    # Remove our IP address.
133    try:
134      self.iproute.DelAddress(myaddr, 64, self.ifindices[netid])
135
136      self.ReceivePacketOn(iface1, finack)
137      self.ReceivePacketOn(iface1, establishing_ack)
138      self.ReceivePacketOn(iface1, establishing_ack)
139      # No crashes? Good.
140
141    finally:
142      # Put back our IP address.
143      self.SendRA(netid)
144      listensocket.close()
145
146  def CheckForwardingHandlerByProto(self, protocol, netid, iif, oif):
147    if protocol == IPPROTO_UDP:
148      self.CheckForwardingUdp(netid, iif, oif)
149    elif protocol == IPPROTO_TCP:
150      self.CheckForwardingCrashTcp(netid, iif, oif)
151    else:
152      raise NotImplementedError
153
154  def CheckForwardingByProto(self, proto):
155    # Run the test a few times as it doesn't crash/hang the first time.
156    for netids in itertools.permutations(self.tuns):
157      # Pick an interface to send traffic on and two to forward traffic between.
158      netid, iface1, iface2 = random.sample(netids, 3)
159      self.ForwardBetweenInterfaces(True, iface1, iface2)
160      try:
161        self.CheckForwardingHandlerByProto(proto, netid, iface1, iface2)
162      finally:
163        self.ForwardBetweenInterfaces(False, iface1, iface2)
164
165  def testForwardingUdp(self):
166    self.CheckForwardingByProto(IPPROTO_UDP)
167
168  def testForwardingCrashTcp(self):
169    self.CheckForwardingByProto(IPPROTO_TCP)
170
171if __name__ == "__main__":
172  unittest.main()
173