GSOC 2017: Implementing RFC 8229 "TCP Encapsulation of IKE and IPsec Packets" for Libreswan

From Libreswan
Revision as of 17:16, 27 August 2017 by Mayank Totale (talk | contribs)
Jump to navigation Jump to search

My GSOC 2017 project aimed at implementing the RFC 8229 - TCP Encapsulation of IKE and IPsec Packets. According to this, we add support for TCP encapsulation of packets in Pluto to route out of stringent networks allowing only TCP traffic.

The project is in its initial stages right now and we are working on figuring out the features from the draft that we need to implement. This page documents the project.

These are some points from the RFC that we wanted to implement:

  • The SPI field in ESP header must be non-zero.
  • Before sending any of the IKE or ESP packet streams, a peer must send a fixed sequence of six bytes "IKETCP" so that IKE/ESP traffic is distinguishable. This is only required at the start of the TCP connection. The responder must wait to receive all six bytes before parsing other packets.
  • A responder peer must always keep listening on the configured TCP port in case a session is initiated. And if TCP encapsulation is used then the subsequent IKE SA and Child SAs must be sent over this TCP connection.
  • When the TCP connection is torn down for any reason, the TCP originator should create a new connection sending the "IKETCP" bytes at the start. The responder must receive traffic for old SAs on this new TCP connection even if there's a change of ports.
  • A partially received message due to a broken connection must always be discarded. In case a peer can't recognize a stream, it must tear down the tcp connection but should only tear down the IKE SA if the issue is with the IKE packet syntax.
  • If MOBIKE is also being used, pluto must support dynamically changing between UDP and TCP when interfaces change.
  • A peer must silently drop NAT keep-alives when using TCP encapsulation. TCP/TLS keep alives may be used by peers but it must not be an indicator of IKE liveliness, for that IKE informational packets should be used.
  • Multiple IKE SAs must not share a single TCP connection, unless one is a rekey of an existing SA.

As of the completion of the project duration, I have been able to implement the following functionalities into Pluto (The Libreswan IKE daemon):

  • Listen on a TCP port for connections using the config parameter listen-tcp = portno. As of now, this should always be 4500.
  • Tell Pluto to connect to a server over TCP directly with the conn parameter tcponly = yes and specify tcp server port with tcp-remoteport = portno.
  • If we want to try over UDP first and then fallback to TCP, then set tcponly = no (default).
  • Currently rekey does work, but it tears down the old TCP connection and creates a new one.
  • In all cases where the TCP connection is torn down, right now we create a new IKE connection.

These are the things that are yet to be incorporated from the RFC into pluto:

  • Listen on multiple ports apart from 4500.
  • Add TLS support and maybe a configuration option to initiate TLS directly.
  • Any kind of support from the kernel. We are waiting for the kernel guys to implement this, and once that is in place, I'll make changes for it as well.
  • Mobike support

Implementation:

We have to make sure that TCP only inititates when UDP has completely failed. So, as of now, 1 keying attempt by the initiator with all the retransmits failing is enough to fall back to trying over TCP. After UDP fails, the next consequent attempts are made over TCP. But if the connection is loaded again or pluto restarts, it will try over UDP first. If a client wants to inititate over TCP from the start, it can set tcponly option as yes, in which case we will directly start with TCP encap. In both of these cases, just when the inititate function is called, I create a new TCP interface and inititate a connection to the server. And the state now stores the information about this interface in the subsequent exchanges.

On the server side, I create a TCP socket and put it in listen mode using libevent's evconnlistener to receive connections. When a new connection is received, a bufferevent is created and then all communication on that connection is handled by bufferevent callbacks. This includes handling the initial stream prefix of "IKETCP" and accept or reject the connection based on that. The read callback for bufferevent handles the received packet in the same function as UDP would handle it, just does receiving through bufferevent buffer.