diff --git a/src/libcharon/plugins/kernel_netlink/.gitignore b/src/libcharon/plugins/kernel_netlink/.gitignore index e05064df6e..5d4f4c8975 100644 --- a/src/libcharon/plugins/kernel_netlink/.gitignore +++ b/src/libcharon/plugins/kernel_netlink/.gitignore @@ -1 +1,2 @@ kernel_netlink_tests +xfrmi diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.am b/src/libcharon/plugins/kernel_netlink/Makefile.am index 0e39c0d24d..1600f8ece5 100644 --- a/src/libcharon/plugins/kernel_netlink/Makefile.am +++ b/src/libcharon/plugins/kernel_netlink/Makefile.am @@ -24,6 +24,12 @@ libstrongswan_kernel_netlink_la_LIBADD = $(DLLIB) libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version +ipsec_PROGRAMS = xfrmi +xfrmi_SOURCES = xfrmi.c +xfrmi_LDADD = \ + libstrongswan-kernel-netlink.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libcharon/libcharon.la TESTS = kernel_netlink_tests diff --git a/src/libcharon/plugins/kernel_netlink/xfrmi.c b/src/libcharon/plugins/kernel_netlink/xfrmi.c new file mode 100644 index 0000000000..0555ac4ce0 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/xfrmi.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2019 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_netlink_shared.h" + +#ifndef IFLA_XFRM_MAX +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + __IFLA_XFRM_MAX +}; +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) +#endif + +#define NLMSG_TAIL(nlh) ((void*)(((char*)nlh) + NLMSG_ALIGN(nlh->nlmsg_len))) + +/** + * Create an XFRM interface with the given ID and underlying interface + */ +static int add_xfrm_interface(char *name, uint32_t xfrm_id, uint32_t ifindex) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct ifinfomsg *msg; + struct rtattr *linkinfo, *info_data; + netlink_socket_t *socket; + int status = 1; + + socket = netlink_socket_create(NETLINK_ROUTE, NULL, FALSE); + if (!socket) + { + return 1; + } + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; + hdr->nlmsg_type = RTM_NEWLINK; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + msg = NLMSG_DATA(hdr); + msg->ifi_family = AF_UNSPEC; + + netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name), + sizeof(request)); + + /* the following attributes are nested under this one */ + linkinfo = netlink_reserve(hdr, sizeof(request), IFLA_LINKINFO, 0); + linkinfo = (void*)linkinfo - RTA_LENGTH(0); + + netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), + sizeof(request)); + + /* the following attributes are nested under this one */ + info_data = netlink_reserve(hdr, sizeof(request), IFLA_INFO_DATA, 0); + info_data = (void*)info_data - RTA_LENGTH(0); + + netlink_add_attribute(hdr, IFLA_XFRM_IF_ID, chunk_from_thing(xfrm_id), + sizeof(request)); + netlink_add_attribute(hdr, IFLA_XFRM_LINK, chunk_from_thing(ifindex), + sizeof(request)); + + info_data->rta_len = NLMSG_TAIL(hdr) - (void*)info_data; + + linkinfo->rta_len = NLMSG_TAIL(hdr) - (void*)linkinfo; + + switch (socket->send_ack(socket, hdr)) + { + case SUCCESS: + status = 0; + break; + case ALREADY_DONE: + fprintf(stderr, "XFRM interface already exists\n"); + break; + default: + fprintf(stderr, "failed to create XFRM interface\n"); + break; + } + + socket->destroy(socket); + return status; +} + +static void usage(FILE *out, char *name) +{ + fprintf(out, "Create XFRM interfaces\n\n"); + fprintf(out, "%s [OPTIONS]\n\n", name); + fprintf(out, "Options:\n"); + fprintf(out, " -h, --help print this help.\n"); + fprintf(out, " -v, --debug set debug level, default: 1.\n"); + fprintf(out, " -n, --name=NAME name of the XFRM interface.\n"); + fprintf(out, " -i, --id=ID optional numeric XFRM ID.\n"); + fprintf(out, " -d, --dev=DEVICE underlying physical interface.\n"); + fprintf(out, "\n"); +} + +int main(int argc, char *argv[]) +{ + char *name = NULL, *dev = NULL, *end; + uint32_t xfrm_id = 0; + u_int ifindex; + + while (true) + { + struct option long_opts[] = { + {"help", no_argument, NULL, 'h' }, + {"debug", no_argument, NULL, 'v' }, + {"name", required_argument, NULL, 'n' }, + {"id", required_argument, NULL, 'i' }, + {"dev", required_argument, NULL, 'd' }, + {0,0,0,0 }, + }; + switch (getopt_long(argc, argv, "hvn:i:d:", long_opts, NULL)) + { + case EOF: + break; + case 'h': + usage(stdout, argv[0]); + return 0; + case 'v': + dbg_default_set_level(atoi(optarg)); + continue; + case 'n': + name = optarg; + continue; + case 'i': + errno = 0; + xfrm_id = strtoul(optarg, &end, 0); + if (errno || *end) + { + fprintf(stderr, "invalid XFRM ID: %s\n", + errno ? strerror(errno) : end); + return 1; + } + continue; + case 'd': + dev = optarg; + continue; + default: + usage(stderr, argv[0]); + return 1; + } + break; + } + + if (!name || !dev) + { + fprintf(stderr, "please specify a name and a physical interface\n"); + return 1; + } + ifindex = if_nametoindex(dev); + if (!ifindex) + { + fprintf(stderr, "physical interface %s not found\n", dev); + return 1; + } + + library_init(NULL, "xfrmi"); + atexit(library_deinit); + + return add_xfrm_interface(name, xfrm_id, ifindex); +}