/* * 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 /** * 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)); linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO); netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), sizeof(request)); info_data = netlink_nested_start(hdr, sizeof(request), IFLA_INFO_DATA); 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)); netlink_nested_end(hdr, info_data); netlink_nested_end(hdr, 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; } /** * Parse attributes nested in IFLA_INFO_DATA */ static void parse_info_data(struct rtattr *rta, size_t rtasize, char *phys, uint32_t *if_id) { uint32_t ifindex; while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { case IFLA_XFRM_IF_ID: if (RTA_PAYLOAD(rta) == sizeof(*if_id)) { *if_id = *(uint32_t*)RTA_DATA(rta); } break; case IFLA_XFRM_LINK: if (RTA_PAYLOAD(rta) == sizeof(ifindex)) { ifindex = *(uint32_t*)RTA_DATA(rta); if_indextoname(ifindex, phys); } break; default: break; } rta = RTA_NEXT(rta, rtasize); } } /** * Parse attributes nested in IFLA_LINKINFO */ static void parse_linkinfo(struct rtattr *rta, size_t rtasize, char *phys, uint32_t *if_id) { while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { case IFLA_INFO_DATA: parse_info_data(RTA_DATA(rta), RTA_PAYLOAD(rta), phys, if_id); break; default: break; } rta = RTA_NEXT(rta, rtasize); } } /** * List all installed XFRM interfaces */ static int list_xfrm_interfaces() { netlink_buf_t request; struct nlmsghdr *hdr, *out, *current; struct ifinfomsg *msg; struct rtattr *linkinfo; netlink_socket_t *socket; size_t len; int status = 0; 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_DUMP; hdr->nlmsg_type = RTM_GETLINK; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); msg = NLMSG_DATA(hdr); msg->ifi_family = AF_UNSPEC; linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO); netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), sizeof(request)); netlink_nested_end(hdr, linkinfo); if (socket->send(socket, hdr, &out, &len) != SUCCESS) { return FAILED; } current = out; while (NLMSG_OK(current, len)) { switch (current->nlmsg_type) { case NLMSG_DONE: break; case RTM_NEWLINK: msg = NLMSG_DATA(current); struct rtattr *rta = IFLA_RTA(msg); size_t rtasize = IFLA_PAYLOAD(current); char *name = NULL, phys[IF_NAMESIZE] = {}; uint32_t if_id = 0; while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { case IFLA_IFNAME: name = RTA_DATA(rta); break; case IFLA_LINKINFO: parse_linkinfo(RTA_DATA(rta), RTA_PAYLOAD(rta), phys, &if_id); break; default: break; } rta = RTA_NEXT(rta, rtasize); } if (name) { printf("%2u: %-16s dev %-8s if_id 0x%.8x [%u]\n", msg->ifi_index, name, phys, if_id, if_id); } /* fall through */ default: current = NLMSG_NEXT(current, len); continue; } break; } free(out); 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, " -l, --list list XFRM interfaces.\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; library_init(NULL, "xfrmi"); atexit(library_deinit); while (true) { struct option long_opts[] = { {"help", no_argument, NULL, 'h' }, {"debug", no_argument, NULL, 'v' }, {"list", no_argument, NULL, 'l' }, {"name", required_argument, NULL, 'n' }, {"id", required_argument, NULL, 'i' }, {"dev", required_argument, NULL, 'd' }, {0,0,0,0 }, }; switch (getopt_long(argc, argv, "hvln:i:d:", long_opts, NULL)) { case EOF: break; case 'h': usage(stdout, argv[0]); return 0; case 'l': list_xfrm_interfaces(); 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; } return add_xfrm_interface(name, xfrm_id, ifindex); }