summaryrefslogtreecommitdiffstats
path: root/extras/strongswan/vpp_sswan/README.rst
blob: 57b30f452e535af5b658523b4ad7a0b5522d1223 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
.. _vpp_sswan_doc:

VPP-SSWAN
=======================

``VPP-SSWAN`` is a StrongSwan plugin that helps offloading Strongswan IPsec ESP
process from Linux Kernel to ``VPP``.

The ``VPP-SSWAN`` takes advantage of ``StrongSwan`` extendable plugin design
and translates ``StrongSwan`` SA creation/deletion and routing
update operations into ``VPP`` C API calls. The successful execution of the
API calls means the operations shall be performed by VPP smoothly.

Inside ``VPP-SSWAN``, the kernel-vpp plugin is an interface to the IPsec and
networking backend for `VPP <https://wiki.fd.io/view/VPP>`__ platform using
the `VPP C API <https://wiki.fd.io/view/VPP/How_To_Use_The_C_API>`__.
It provides address and routing lookup functionality and installs routes for
IPsec traffic.

The plugin also installs and maintains Security Associations and Policies to
the `VPP IPsec <https://wiki.fd.io/view/VPP/IPSec_and_IKEv2#IPSec>`__.

Since ``StrongSwan`` expects both IKE and IPsec traffic coming through the
same network protected interfaces, the ``VPP-SSWAN`` expects the IKE traffic
being diverted to Linux Kernel through the help of
`VPP Linux Control Plane <https://s3-docs.fd.io/vpp/22.10/developer/plugins/
lcp.html>`__. It is important to notice that due to LCP is a Tun/Tap interface,
the IPsec performance will be limited by it if Transport mode of IPsec is used.

Prerequisites
-------------

``VPP`` in release mode should be built before compiling ``vpp-swan plugin``.
User may install ``StrongSwan`` prior to compile the plugin. However the
plugin requires downloading ``StrongSwan`` source to include some of its
header files to compile ``VPP-SSWAN``. In addition ``libsystemd-dev``
should be installed prior to compile the plugin.

Please Note: ONLY Strongswan version ``5.9.5`` and ``5.9.6`` were tested with
this plugin.

Build VPP Strongswan Plugin
-------------

``VPP-SSWAN`` requires ``StrongSwan`` source to compile. To obtain
``StrongSwan`` the simplest way is to run the following commands:

::

   cd path/to/vpp/external/strongswan/vpp_swan/
   make all

Or you may download ``StrongSwan``  from its github page. It is recommended to
use ``Strongswan`` version ``5.9.6`` or ``5.9.5`` for ``VPP-SSWAN`` to be
compiled and integrate. The following steps are required for manually download
``Strongswan`` source:

- download strongswan source code to:
``path/to/vpp/build/external/downloads``

- unzip source code strongswan to:
``path/to/vpp/build-root/build-vpp-native/external/sswan``

- check if you have installed packages: ``libsystemd-dev`` on your OS

- configure strongswan by:
``./autogen.sh``
``./configure --prefix=/usr --sysconfdir=/etc --enable-libipsec
--enable-systemd --enable-swanctl --disable-gmp --enable-openssl``

- compile ``vpp-swan plugin`` by:

::

   cd path/to/vpp/external/strongswan/vpp_swan/
   make

Build/install Strongswan (Optional)
-------------

In case you haven't installed ``Strongswan`` yet, you may use the following
simple command to compile and install ``Strongswan`` from the downloaded source.

::

   cd path/to/vpp/external/strongswan/vpp_swan/
   make pull-swan
   make install-swan

Install VPP-SWAN plugin into StrongSwan
-------------

After the ``VPP-SSWAN`` plugin has been built and ``Strongswan`` was installed,
the following command will install the ``VPP-SSWAN`` plugin into ``Strongswan``.

::

   cd path/to/vpp/external/strongswan/vpp_swan/
   make install

Or you can manually copy ``libstrongswan-kernel-vpp.so`` into:
``/usr/lib/ipsec/plugins``,
and also ``kernel-vpp.conf`` into: ``/etc/strongswan.d/charon/``

Now you can restart ``Strongswan`` by executing the following command:

::

   systemctl restart strongswan.service

Configuration Strongswan
-------------

As an example, ``swanctl.conf`` file provides an example configuration to
initialize connections between two endpoints.

You may update the file based on your need and Copy into:
``/etc/swanctl/conf.d/swanctl.conf``

Configuration VPP
-------------

Some special treatment to VPP are required in your VPP ``startup.conf``.
Since we use ``Strongswan`` to process IKE messages, we should disable VPP's
IKEv2 plugin. Also as mentioned ``Linux Control Plane`` plugin is needed to
route the traffic between VPP interface and Tun/Tap interface. To do so, simply
adding the following commands:

::

   plugins {
     plugin linux_cp_plugin.so { enable }
     plugin ikev2_plugin.so { disable }
    }

   linux-cp {
      lcp-sync
   }

Running VPP
-------------

Based on the provided sample ``swanctl.conf``, the following commands are
required to be executed in ``VPP``:

::

   lcp create eth2 host-if eth2
   set interface state eth2 up
   set interface ip address eth2 192.168.0.2/24
   set int state eth1 up
   set int ip addr eth1 192.168.200.1/24

In the commands above we assume ``eth2`` is the WAN interface to receive both
IKE message and ESP encapsulated packets, and ``eth1`` is the LAN interface to
receive plain packets to be encrypted. With the commands a ``Linux CP`` interface
is created to mirror the ``eth2`` interface to Linux Kernel, and both interfaces
were set the IP addresses followed by the ``swanctl.conf``.

With the commands successfully executed and the security policy is succesfully
agreed between two IKE daemons (one with VPP as IPsec processing engine), you may
see the packets are encrypted/decrypted by VPP smoothly.

Misc
-------------
This plugin is based on:
`https://github.com/matfabia/strongswan
<https://github.com/matfabia/strongswan>`__

Author: Matus Fabian <matfabia@cisco.com>
is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Copyright (c) 2001, 2002, 2003, 2005 Eliot Dresselhaus Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include <sys/un.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <string.h> /* strchr */ #include <vppinfra/mem.h> #include <vppinfra/vec.h> #include <vppinfra/socket.h> #include <vppinfra/format.h> #include <vppinfra/error.h> void clib_socket_tx_add_formatted (clib_socket_t * s, char *fmt, ...) { va_list va; va_start (va, fmt); clib_socket_tx_add_va_formatted (s, fmt, &va); va_end (va); } /* Return and bind to an unused port. */ static word find_free_port (word sock) { word port; for (port = IPPORT_USERRESERVED; port < 1 << 16; port++) { struct sockaddr_in a; memset (&a, 0, sizeof (a)); /* Warnings be gone */ a.sin_family = PF_INET; a.sin_addr.s_addr = INADDR_ANY; a.sin_port = htons (port); if (bind (sock, (struct sockaddr *) &a, sizeof (a)) >= 0) break; } return port < 1 << 16 ? port : -1; } /* Convert a config string to a struct sockaddr and length for use with bind or connect. */ static clib_error_t * socket_config (char *config, void *addr, socklen_t * addr_len, u32 ip4_default_address) { clib_error_t *error = 0; if (!config) config = ""; /* Anything that begins with a / is a local PF_LOCAL socket. */ if (config[0] == '/') { struct sockaddr_un *su = addr; su->sun_family = PF_LOCAL; clib_memcpy (&su->sun_path, config, clib_min (sizeof (su->sun_path), 1 + strlen (config))); *addr_len = sizeof (su[0]); } /* Hostname or hostname:port or port. */ else { char *host_name; int port = -1; struct sockaddr_in *sa = addr; host_name = 0; port = -1; if (config[0] != 0) { unformat_input_t i; unformat_init_string (&i, config, strlen (config)); if (unformat (&i, "%s:%d", &host_name, &port) || unformat (&i, "%s:0x%x", &host_name, &port)) ; else if (unformat (&i, "%s", &host_name)) ; else error = clib_error_return (0, "unknown input `%U'", format_unformat_error, &i); unformat_free (&i); if (error) goto done; } sa->sin_family = PF_INET; *addr_len = sizeof (sa[0]); if (port != -1) sa->sin_port = htons (port); else sa->sin_port = 0; if (host_name) { struct in_addr host_addr; /* Recognize localhost to avoid host lookup in most common cast. */ if (!strcmp (host_name, "localhost")) sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK); else if (inet_aton (host_name, &host_addr)) sa->sin_addr = host_addr; else if (host_name && strlen (host_name) > 0) { struct hostent *host = gethostbyname (host_name); if (!host) error = clib_error_return (0, "unknown host `%s'", config); else clib_memcpy (&sa->sin_addr.s_addr, host->h_addr_list[0], host->h_length); } else sa->sin_addr.s_addr = htonl (ip4_default_address); vec_free (host_name); if (error) goto done; } } done: return error; } static clib_error_t * default_socket_write (clib_socket_t * s) { clib_error_t *err = 0; word written = 0; word fd = 0; word tx_len; fd = s->fd; /* Map standard input to standard output. Typically, fd is a socket for which read/write both work. */ if (fd == 0) fd = 1; tx_len = vec_len (s->tx_buffer); written = write (fd, s->tx_buffer, tx_len); /* Ignore certain errors. */ if (written < 0 && !unix_error_is_fatal (errno)) written = 0; /* A "real" error occurred. */ if (written < 0) { err = clib_error_return_unix (0, "write %wd bytes", tx_len); vec_free (s->tx_buffer); goto done; } /* Reclaim the transmitted part of the tx buffer on successful writes. */ else if (written > 0) { if (written == tx_len) _vec_len (s->tx_buffer) = 0; else vec_delete (s->tx_buffer, written, 0); } /* If a non-fatal error occurred AND the buffer is full, then we must free it. */ else if (written == 0 && tx_len > 64 * 1024) { vec_free (s->tx_buffer); } done: return err; } static clib_error_t * default_socket_read (clib_socket_t * sock, int n_bytes) { word fd, n_read; u8 *buf; /* RX side of socket is down once end of file is reached. */ if (sock->flags & SOCKET_RX_END_OF_FILE) return 0; fd = sock->fd; n_bytes = clib_max (n_bytes, 4096); vec_add2 (sock->rx_buffer, buf, n_bytes); if ((n_read = read (fd, buf, n_bytes)) < 0) { n_read = 0; /* Ignore certain errors. */ if (!unix_error_is_fatal (errno)) goto non_fatal; return clib_error_return_unix (0, "read %d bytes", n_bytes); } /* Other side closed the socket. */ if (n_read == 0) sock->flags |= SOCKET_RX_END_OF_FILE; non_fatal: _vec_len (sock->rx_buffer) += n_read - n_bytes; return 0; } static clib_error_t * default_socket_close (clib_socket_t * s) { if (close (s->fd) < 0) return clib_error_return_unix (0, "close"); return 0; } static void socket_init_funcs (clib_socket_t * s) { if (!s->write_func) s->write_func = default_socket_write; if (!s->read_func) s->read_func = default_socket_read; if (!s->close_func) s->close_func = default_socket_close; } clib_error_t * clib_socket_init (clib_socket_t * s) { union { struct sockaddr sa; struct sockaddr_un su; } addr; socklen_t addr_len = 0; clib_error_t *error = 0; word port; error = socket_config (s->config, &addr.sa, &addr_len, (s->flags & SOCKET_IS_SERVER ? INADDR_LOOPBACK : INADDR_ANY)); if (error) goto done; socket_init_funcs (s); s->fd = socket (addr.sa.sa_family, SOCK_STREAM, 0); if (s->fd < 0) { error = clib_error_return_unix (0, "socket"); goto done; } port = 0; if (addr.sa.sa_family == PF_INET) port = ((struct sockaddr_in *) &addr)->sin_port; if (s->flags & SOCKET_IS_SERVER) { uword need_bind = 1; if (addr.sa.sa_family == PF_INET) { if (port == 0) { port = find_free_port (s->fd); if (port < 0) { error = clib_error_return (0, "no free port"); goto done; } need_bind = 0; } } if (addr.sa.sa_family == PF_LOCAL) unlink (((struct sockaddr_un *) &addr)->sun_path); /* Make address available for multiple users. */ { int v = 1; if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof (v)) < 0) clib_unix_warning ("setsockopt SO_REUSEADDR fails"); } if (need_bind && bind (s->fd, &addr.sa, addr_len) < 0) { error = clib_error_return_unix (0, "bind"); goto done; } if (listen (s->fd, 5) < 0) { error = clib_error_return_unix (0, "listen"); goto done; } if (addr.sa.sa_family == PF_LOCAL && s->flags & SOCKET_ALLOW_GROUP_WRITE) { struct stat st = { 0 }; stat (((struct sockaddr_un *) &addr)->sun_path, &st); st.st_mode |= S_IWGRP; chmod (((struct sockaddr_un *) &addr)->sun_path, st.st_mode); } } else { if ((s->flags & SOCKET_NON_BLOCKING_CONNECT) && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0) { error = clib_error_return_unix (0, "fcntl NONBLOCK"); goto done; } if (connect (s->fd, &addr.sa, addr_len) < 0 && !((s->flags & SOCKET_NON_BLOCKING_CONNECT) && errno == EINPROGRESS)) { error = clib_error_return_unix (0, "connect"); goto done; } } return error; done: if (s->fd > 0) close (s->fd); return error; } clib_error_t * clib_socket_accept (clib_socket_t * server, clib_socket_t * client) { clib_error_t *err = 0; socklen_t len = 0; memset (client, 0, sizeof (client[0])); /* Accept the new socket connection. */ client->fd = accept (server->fd, 0, 0); if (client->fd < 0) return clib_error_return_unix (0, "accept"); /* Set the new socket to be non-blocking. */ if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0) { err = clib_error_return_unix (0, "fcntl O_NONBLOCK"); goto close_client; } /* Get peer info. */ len = sizeof (client->peer); if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0) { err = clib_error_return_unix (0, "getpeername"); goto close_client; } client->flags = SOCKET_IS_CLIENT; socket_init_funcs (client); return 0; close_client: close (client->fd); return err; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */