bzr branch
http://bzr.recompile.se/loggerhead/mandos/trunk
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 1 | /*  -*- coding: utf-8 -*- */
 | 
| 2 | /* 
 | |
| 3 |  * iprouteadddel - Add or delete direct route to a local IP address
 | |
| 4 |  * 
 | |
| 1259
by Teddy Hogeborn Update copyright year | 5 |  * Copyright © 2015-2018, 2021-2022 Teddy Hogeborn
 | 
| 6 |  * Copyright © 2015-2018, 2021-2022 Björn Påhlsson
 | |
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 7 |  * 
 | 
| 907
by Teddy Hogeborn Alter copyright notices slightly. Actual license is unchanged! | 8 |  * This file is part of Mandos.
 | 
| 9 |  * 
 | |
| 10 |  * Mandos is free software: you can redistribute it and/or modify it
 | |
| 11 |  * under the terms of the GNU General Public License as published by
 | |
| 12 |  * the Free Software Foundation, either version 3 of the License, or
 | |
| 13 |  * (at your option) any later version.
 | |
| 14 |  * 
 | |
| 15 |  * Mandos is distributed in the hope that it will be useful, but
 | |
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 16 |  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
| 17 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| 18 |  * General Public License for more details.
 | |
| 19 |  * 
 | |
| 20 |  * You should have received a copy of the GNU General Public License
 | |
| 907
by Teddy Hogeborn Alter copyright notices slightly. Actual license is unchanged! | 21 |  * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 22 |  * 
 | 
| 23 |  * Contact the authors at <mandos@recompile.se>.
 | |
| 24 |  */
 | |
| 25 | ||
| 938
by Teddy Hogeborn Correct comments | 26 | #define _GNU_SOURCE /* program_invocation_short_name */ | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 27 | #include <stdbool.h> /* bool, false, true */ | 
| 1233
by Teddy Hogeborn Fix #include headers | 28 | #include <argp.h> /* argp_program_version, | 
| 29 | argp_program_bug_address, | |
| 30 | struct argp_option, | |
| 31 | struct argp_state, ARGP_KEY_ARG, | |
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 32 | argp_usage(), ARGP_KEY_END, | 
| 33 | ARGP_ERR_UNKNOWN, struct argp, | |
| 1233
by Teddy Hogeborn Fix #include headers | 34 | argp_parse(), ARGP_IN_ORDER */ | 
| 35 | #include <errno.h> /* errno, | |
| 36 | program_invocation_short_name, | |
| 37 | error_t, EINVAL, ENOMEM */ | |
| 38 | #include <stdio.h> /* fprintf(), stderr, perror(), FILE, | |
| 39 | vfprintf() */ | |
| 40 | #include <stdarg.h> /* va_list, va_start(), vfprintf() */ | |
| 41 | #include <stdlib.h> /* EXIT_SUCCESS */ | |
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 42 | #include <netlink/netlink.h> /* struct nl_addr, nl_addr_parse(), | 
| 43 | nl_geterror(), | |
| 1233
by Teddy Hogeborn Fix #include headers | 44 | nl_addr_get_family(), NLM_F_EXCL, | 
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 45 | nl_addr_put() */ | 
| 1233
by Teddy Hogeborn Fix #include headers | 46 | #include <stddef.h> /* NULL */ | 
| 47 | #include <netlink/route/route.h>/* struct rtnl_route, | |
| 48 | struct rtnl_nexthop, NETLINK_ROUTE, | |
| 49 | rtnl_route_alloc(), | |
| 50 | rtnl_route_set_family(), | |
| 51 | rtnl_route_set_protocol(), | |
| 52 | RTPROT_BOOT, | |
| 53 | rtnl_route_set_scope(), | |
| 54 | RT_SCOPE_LINK, | |
| 55 | rtnl_route_set_type(), RTN_UNICAST, | |
| 56 | rtnl_route_set_dst(), | |
| 57 | rtnl_route_set_table(), | |
| 58 | RT_TABLE_MAIN, | |
| 59 | rtnl_route_nh_alloc(), | |
| 60 | rtnl_route_nh_set_ifindex(), | |
| 61 | rtnl_route_add_nexthop(), | |
| 62 | rtnl_route_add(), | |
| 63 | rtnl_route_delete(), | |
| 64 | rtnl_route_put(), | |
| 65 | rtnl_route_nh_free() */ | |
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 66 | #include <netlink/socket.h> /* struct nl_sock, nl_socket_alloc(), | 
| 67 | nl_connect(), nl_socket_free() */ | |
| 1233
by Teddy Hogeborn Fix #include headers | 68 | #include <strings.h> /* strcasecmp() */ | 
| 69 | #include <sys/socket.h> /* AF_UNSPEC, AF_INET6, AF_INET */ | |
| 70 | #include <sysexits.h> /* EX_USAGE, EX_OSERR */ | |
| 71 | #include <netlink/route/link.h> /* struct rtnl_link, | |
| 72 | rtnl_link_get_kernel(), | |
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 73 | rtnl_link_get_ifindex(), | 
| 74 | rtnl_link_put() */ | |
| 1233
by Teddy Hogeborn Fix #include headers | 75 | #include <netinet/in.h> /* sa_family_t */ | 
| 76 | #include <inttypes.h> /* PRIdMAX, intmax_t */ | |
| 77 | #include <stdint.h> /* uint8_t */ | |
| 78 | ||
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 79 | |
| 80 | bool debug = false; | |
| 81 | const char *argp_program_version = "mandos-client-iprouteadddel " VERSION; | |
| 82 | const char *argp_program_bug_address = "<mandos@recompile.se>"; | |
| 83 | ||
| 84 | /* Function to use when printing errors */
 | |
| 85 | void perror_plus(const char *print_text){ | |
| 86 | int e = errno; | |
| 87 | fprintf(stderr, "Mandos plugin helper %s: ", | |
| 88 | program_invocation_short_name); | |
| 89 | errno = e; | |
| 90 | perror(print_text); | |
| 91 | }
 | |
| 92 | ||
| 93 | __attribute__((format (gnu_printf, 2, 3), nonnull)) | |
| 94 | int fprintf_plus(FILE *stream, const char *format, ...){ | |
| 95 | va_list ap; | |
| 1235
by Teddy Hogeborn Remove superfluous whitespace | 96 | va_start(ap, format); | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 97 |   
 | 
| 98 | fprintf(stream, "Mandos plugin helper %s: ", | |
| 99 | program_invocation_short_name); | |
| 100 | return vfprintf(stream, format, ap); | |
| 101 | }
 | |
| 102 | ||
| 103 | int main(int argc, char *argv[]){ | |
| 104 | int ret; | |
| 105 | int exitcode = EXIT_SUCCESS; | |
| 106 | struct arguments { | |
| 107 | bool add; /* true: add, false: delete */ | |
| 108 | char *address; /* IP address as string */ | |
| 109 | struct nl_addr *nl_addr; /* Netlink IP address */ | |
| 110 | char *interface; /* interface name */ | |
| 111 | } arguments = { .add = true, .address = NULL, .interface = NULL }; | |
| 112 | struct argp_option options[] = { | |
| 113 | { .name = "debug", .key = 128, | |
| 114 | .doc = "Debug mode" }, | |
| 115 | { .name = NULL } | |
| 116 | }; | |
| 117 | struct rtnl_route *route = NULL; | |
| 118 | struct rtnl_nexthop *nexthop = NULL; | |
| 119 | struct nl_sock *sk = NULL; | |
| 120 |   
 | |
| 121 | error_t parse_opt(int key, char *arg, struct argp_state *state){ | |
| 122 | int lret; | |
| 123 | errno = 0; | |
| 124 | switch(key){ | |
| 125 | case 128: /* --debug */ | |
| 126 | debug = true; | |
| 127 | break; | |
| 128 | case ARGP_KEY_ARG: | |
| 129 | switch(state->arg_num){ | |
| 130 | case 0: | |
| 131 | if(strcasecmp(arg, "add") == 0){ | |
| 132 | ((struct arguments *)(state->input))->add = true; | |
| 133 | } else if(strcasecmp(arg, "delete") == 0){ | |
| 134 | ((struct arguments *)(state->input))->add = false; | |
| 135 | } else { | |
| 136 | fprintf_plus(stderr, "Unrecognized command: %s\n", arg); | |
| 137 | argp_usage(state); | |
| 138 | } | |
| 139 | break; | |
| 140 | case 1: | |
| 141 | ((struct arguments *)(state->input))->address = arg; | |
| 142 | lret = nl_addr_parse(arg, AF_UNSPEC, &(((struct arguments *) | |
| 143 | (state->input)) | |
| 144 | ->nl_addr)); | |
| 145 | if(lret != 0){ | |
| 146 | fprintf_plus(stderr, "Failed to parse address %s: %s\n", | |
| 147 | arg, nl_geterror(lret)); | |
| 148 | argp_usage(state); | |
| 149 | } | |
| 150 | break; | |
| 151 | case 2: | |
| 152 | ((struct arguments *)(state->input))->interface = arg; | |
| 153 | break; | |
| 154 | default: | |
| 155 | argp_usage(state); | |
| 156 | } | |
| 157 | break; | |
| 158 | case ARGP_KEY_END: | |
| 159 | if(state->arg_num < 3){ | |
| 160 | argp_usage(state); | |
| 161 | } | |
| 162 | break; | |
| 163 | default: | |
| 164 | return ARGP_ERR_UNKNOWN; | |
| 165 | } | |
| 166 | return errno; | |
| 167 | } | |
| 168 |   
 | |
| 169 | struct argp argp = { .options = options, .parser = parse_opt, | |
| 170 | .args_doc = "[ add | delete ] ADDRESS INTERFACE", | |
| 171 | .doc = "Mandos client helper -- Add or delete" | |
| 172 | " local route to IP address on interface" }; | |
| 173 |   
 | |
| 174 | ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &arguments); | |
| 175 | switch(ret){ | |
| 176 | case 0: | |
| 177 | break; | |
| 178 | case EINVAL: | |
| 179 | exit(EX_USAGE); | |
| 180 | case ENOMEM: | |
| 181 | default: | |
| 182 | errno = ret; | |
| 183 | perror_plus("argp_parse"); | |
| 184 | exitcode = EX_OSERR; | |
| 185 | goto end; | |
| 186 | } | |
| 187 | /* Get netlink socket */ | |
| 188 | sk = nl_socket_alloc(); | |
| 189 | if(sk == NULL){ | |
| 190 | fprintf_plus(stderr, "Failed to allocate netlink socket: %s\n", | |
| 191 | nl_geterror(ret)); | |
| 192 | exitcode = EX_OSERR; | |
| 193 | goto end; | |
| 194 | } | |
| 195 | /* Connect socket to netlink */ | |
| 196 | ret = nl_connect(sk, NETLINK_ROUTE); | |
| 197 | if(ret < 0){ | |
| 198 | fprintf_plus(stderr, "Failed to connect socket to netlink: %s\n", | |
| 199 | nl_geterror(ret)); | |
| 200 | exitcode = EX_OSERR; | |
| 201 | goto end; | |
| 202 | } | |
| 203 | /* Get link object of specified interface */ | |
| 204 | struct rtnl_link *link = NULL; | |
| 205 | ret = rtnl_link_get_kernel(sk, 0, arguments.interface, &link); | |
| 206 | if(ret < 0){ | |
| 207 | fprintf_plus(stderr, "Failed to use interface %s: %s\n", | |
| 208 | arguments.interface, nl_geterror(ret)); | |
| 209 | exitcode = EX_OSERR; | |
| 210 | goto end; | |
| 211 | } | |
| 212 | /* Get netlink route object */ | |
| 213 | route = rtnl_route_alloc(); | |
| 214 | if(route == NULL){ | |
| 215 | fprintf_plus(stderr, "Failed to get netlink route:\n"); | |
| 216 | exitcode = EX_OSERR; | |
| 217 | goto end; | |
| 218 | } | |
| 219 | /* Get address family of specified address */ | |
| 220 | sa_family_t af = (sa_family_t)nl_addr_get_family(arguments.nl_addr); | |
| 221 | if(debug){ | |
| 222 | fprintf_plus(stderr, "Address family of %s is %s (%" PRIdMAX | |
| 223 | ")\n", arguments.address, | |
| 224 | af == AF_INET6 ? "AF_INET6" : | |
| 225 | ( af == AF_INET ? "AF_INET" : "UNKNOWN"), | |
| 226 | (intmax_t)af); | |
| 227 | } | |
| 228 | /* Set route parameters: */ | |
| 229 | rtnl_route_set_family(route, (uint8_t)af); /* Address family */ | |
| 230 | rtnl_route_set_protocol(route, RTPROT_BOOT); /* protocol - see | |
| 231 | 						  ip-route(8) */
 | |
| 232 | rtnl_route_set_scope(route, RT_SCOPE_LINK); /* link scope */ | |
| 233 | rtnl_route_set_type(route, RTN_UNICAST); /* normal unicast | |
| 234 | 						 address route */
 | |
| 235 | rtnl_route_set_dst(route, arguments.nl_addr); /* Destination | |
| 236 | 						   address */
 | |
| 237 | rtnl_route_set_table(route, RT_TABLE_MAIN); /* "main" routing | |
| 238 | 						 table */
 | |
| 239 | /* Create nexthop */ | |
| 240 | nexthop = rtnl_route_nh_alloc(); | |
| 241 | if(nexthop == NULL){ | |
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 242 | fprintf_plus(stderr, "Failed to get netlink route nexthop\n"); | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 243 | exitcode = EX_OSERR; | 
| 244 | goto end; | |
| 245 | } | |
| 246 | /* Get index number of specified interface */ | |
| 247 | int ifindex = rtnl_link_get_ifindex(link); | |
| 248 | if(debug){ | |
| 249 | fprintf_plus(stderr, "ifindex of %s is %d\n", arguments.interface, | |
| 250 | ifindex); | |
| 251 | } | |
| 252 | /* Set interface index number on nexthop object */ | |
| 253 | rtnl_route_nh_set_ifindex(nexthop, ifindex); | |
| 938
by Teddy Hogeborn Correct comments | 254 | /* Set route to use nexthop object */ | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 255 | rtnl_route_add_nexthop(route, nexthop); | 
| 256 | /* Add or delete route? */ | |
| 257 | if(arguments.add){ | |
| 258 | ret = rtnl_route_add(sk, route, NLM_F_EXCL); | |
| 259 | } else { | |
| 260 | ret = rtnl_route_delete(sk, route, 0); | |
| 261 | } | |
| 262 | if(ret < 0){ | |
| 263 | fprintf_plus(stderr, "Failed to %s route: %s\n", | |
| 264 | arguments.add ? "add" : "delete", | |
| 265 | nl_geterror(ret)); | |
| 266 | exitcode = EX_OSERR; | |
| 267 | goto end; | |
| 268 | } | |
| 269 | end: | |
| 270 | /* Deallocate route */ | |
| 271 | if(route){ | |
| 272 | rtnl_route_put(route); | |
| 273 | } else if(nexthop) { | |
| 274 | /* Deallocate route nexthop */ | |
| 275 | rtnl_route_nh_free(nexthop); | |
| 276 | } | |
| 277 | /* Deallocate parsed address */ | |
| 278 | if(arguments.nl_addr){ | |
| 279 | nl_addr_put(arguments.nl_addr); | |
| 280 | } | |
| 281 | /* Deallocate link struct */ | |
| 282 | if(link){ | |
| 283 | rtnl_link_put(link); | |
| 284 | } | |
| 285 | /* Deallocate netlink socket struct */ | |
| 286 | if(sk){ | |
| 287 | nl_socket_free(sk); | |
| 288 | } | |
| 289 | return exitcode; | |
| 290 | }
 |