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 |  * 
 | |
| 923
by Teddy Hogeborn Update copyright year to 2018 | 5 |  * Copyright © 2015-2018 Teddy Hogeborn
 | 
| 6 |  * Copyright © 2015-2018 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 */ | 
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 28 | #include <stdio.h> /* fprintf(), stderr, FILE, vfprintf */ | 
| 29 | #include <errno.h> /* program_invocation_short_name, | |
| 30 | errno, perror(), EINVAL, ENOMEM */ | |
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 31 | #include <stdarg.h> /* va_list, va_start */ | 
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 32 | #include <stdlib.h> /* EXIT_SUCCESS */ | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 33 | #include <argp.h> /* struct argp_option, error_t, struct | 
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 34 | argp_state, ARGP_KEY_ARG, | 
| 35 | argp_usage(), ARGP_KEY_END, | |
| 36 | ARGP_ERR_UNKNOWN, struct argp, | |
| 37 | argp_parse() */ | |
| 38 | #include <sysexits.h> /* EX_USAGE, EX_OSERR */ | |
| 39 | #include <netinet/ip.h> /* sa_family_t, AF_INET6, AF_INET */ | |
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 40 | #include <inttypes.h> /* PRIdMAX, intmax_t */ | 
| 41 | ||
| 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(), | |
| 44 | nl_addr_get_family(), | |
| 45 | nl_addr_put() */ | |
| 46 | #include <netlink/route/route.h> /* struct rtnl_route, | |
| 47 | struct rtnl_nexthop, | |
| 48 | rtnl_route_alloc(), | |
| 49 | rtnl_route_set_family(), | |
| 50 | rtnl_route_set_protocol(), | |
| 51 | RTPROT_BOOT, | |
| 52 | rtnl_route_set_scope(), | |
| 53 | RT_SCOPE_LINK, | |
| 54 | rtnl_route_set_type(), | |
| 55 | 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() */ | |
| 66 | #include <netlink/socket.h> /* struct nl_sock, nl_socket_alloc(), | |
| 67 | nl_connect(), nl_socket_free() */ | |
| 68 | #include <netlink/route/link.h> /* rtnl_link_get_kernel(), | |
| 69 | rtnl_link_get_ifindex(), | |
| 70 | rtnl_link_put() */ | |
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 71 | |
| 72 | bool debug = false; | |
| 73 | const char *argp_program_version = "mandos-client-iprouteadddel " VERSION; | |
| 74 | const char *argp_program_bug_address = "<mandos@recompile.se>"; | |
| 75 | ||
| 76 | /* Function to use when printing errors */
 | |
| 77 | void perror_plus(const char *print_text){ | |
| 78 | int e = errno; | |
| 79 | fprintf(stderr, "Mandos plugin helper %s: ", | |
| 80 | program_invocation_short_name); | |
| 81 | errno = e; | |
| 82 | perror(print_text); | |
| 83 | }
 | |
| 84 | ||
| 85 | __attribute__((format (gnu_printf, 2, 3), nonnull)) | |
| 86 | int fprintf_plus(FILE *stream, const char *format, ...){ | |
| 87 | va_list ap; | |
| 88 | va_start (ap, format); | |
| 89 |   
 | |
| 90 | fprintf(stream, "Mandos plugin helper %s: ", | |
| 91 | program_invocation_short_name); | |
| 92 | return vfprintf(stream, format, ap); | |
| 93 | }
 | |
| 94 | ||
| 95 | int main(int argc, char *argv[]){ | |
| 96 | int ret; | |
| 97 | int exitcode = EXIT_SUCCESS; | |
| 98 | struct arguments { | |
| 99 | bool add; /* true: add, false: delete */ | |
| 100 | char *address; /* IP address as string */ | |
| 101 | struct nl_addr *nl_addr; /* Netlink IP address */ | |
| 102 | char *interface; /* interface name */ | |
| 103 | } arguments = { .add = true, .address = NULL, .interface = NULL }; | |
| 104 | struct argp_option options[] = { | |
| 105 | { .name = "debug", .key = 128, | |
| 106 | .doc = "Debug mode" }, | |
| 107 | { .name = NULL } | |
| 108 | }; | |
| 109 | struct rtnl_route *route = NULL; | |
| 110 | struct rtnl_nexthop *nexthop = NULL; | |
| 111 | struct nl_sock *sk = NULL; | |
| 112 |   
 | |
| 113 | error_t parse_opt(int key, char *arg, struct argp_state *state){ | |
| 114 | int lret; | |
| 115 | errno = 0; | |
| 116 | switch(key){ | |
| 117 | case 128: /* --debug */ | |
| 118 | debug = true; | |
| 119 | break; | |
| 120 | case ARGP_KEY_ARG: | |
| 121 | switch(state->arg_num){ | |
| 122 | case 0: | |
| 123 | if(strcasecmp(arg, "add") == 0){ | |
| 124 | ((struct arguments *)(state->input))->add = true; | |
| 125 | } else if(strcasecmp(arg, "delete") == 0){ | |
| 126 | ((struct arguments *)(state->input))->add = false; | |
| 127 | } else { | |
| 128 | fprintf_plus(stderr, "Unrecognized command: %s\n", arg); | |
| 129 | argp_usage(state); | |
| 130 | } | |
| 131 | break; | |
| 132 | case 1: | |
| 133 | ((struct arguments *)(state->input))->address = arg; | |
| 134 | lret = nl_addr_parse(arg, AF_UNSPEC, &(((struct arguments *) | |
| 135 | (state->input)) | |
| 136 | ->nl_addr)); | |
| 137 | if(lret != 0){ | |
| 138 | fprintf_plus(stderr, "Failed to parse address %s: %s\n", | |
| 139 | arg, nl_geterror(lret)); | |
| 140 | argp_usage(state); | |
| 141 | } | |
| 142 | break; | |
| 143 | case 2: | |
| 144 | ((struct arguments *)(state->input))->interface = arg; | |
| 145 | break; | |
| 146 | default: | |
| 147 | argp_usage(state); | |
| 148 | } | |
| 149 | break; | |
| 150 | case ARGP_KEY_END: | |
| 151 | if(state->arg_num < 3){ | |
| 152 | argp_usage(state); | |
| 153 | } | |
| 154 | break; | |
| 155 | default: | |
| 156 | return ARGP_ERR_UNKNOWN; | |
| 157 | } | |
| 158 | return errno; | |
| 159 | } | |
| 160 |   
 | |
| 161 | struct argp argp = { .options = options, .parser = parse_opt, | |
| 162 | .args_doc = "[ add | delete ] ADDRESS INTERFACE", | |
| 163 | .doc = "Mandos client helper -- Add or delete" | |
| 164 | " local route to IP address on interface" }; | |
| 165 |   
 | |
| 166 | ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &arguments); | |
| 167 | switch(ret){ | |
| 168 | case 0: | |
| 169 | break; | |
| 170 | case EINVAL: | |
| 171 | exit(EX_USAGE); | |
| 172 | case ENOMEM: | |
| 173 | default: | |
| 174 | errno = ret; | |
| 175 | perror_plus("argp_parse"); | |
| 176 | exitcode = EX_OSERR; | |
| 177 | goto end; | |
| 178 | } | |
| 179 | /* Get netlink socket */ | |
| 180 | sk = nl_socket_alloc(); | |
| 181 | if(sk == NULL){ | |
| 182 | fprintf_plus(stderr, "Failed to allocate netlink socket: %s\n", | |
| 183 | nl_geterror(ret)); | |
| 184 | exitcode = EX_OSERR; | |
| 185 | goto end; | |
| 186 | } | |
| 187 | /* Connect socket to netlink */ | |
| 188 | ret = nl_connect(sk, NETLINK_ROUTE); | |
| 189 | if(ret < 0){ | |
| 190 | fprintf_plus(stderr, "Failed to connect socket to netlink: %s\n", | |
| 191 | nl_geterror(ret)); | |
| 192 | exitcode = EX_OSERR; | |
| 193 | goto end; | |
| 194 | } | |
| 195 | /* Get link object of specified interface */ | |
| 196 | struct rtnl_link *link = NULL; | |
| 197 | ret = rtnl_link_get_kernel(sk, 0, arguments.interface, &link); | |
| 198 | if(ret < 0){ | |
| 199 | fprintf_plus(stderr, "Failed to use interface %s: %s\n", | |
| 200 | arguments.interface, nl_geterror(ret)); | |
| 201 | exitcode = EX_OSERR; | |
| 202 | goto end; | |
| 203 | } | |
| 204 | /* Get netlink route object */ | |
| 205 | route = rtnl_route_alloc(); | |
| 206 | if(route == NULL){ | |
| 207 | fprintf_plus(stderr, "Failed to get netlink route:\n"); | |
| 208 | exitcode = EX_OSERR; | |
| 209 | goto end; | |
| 210 | } | |
| 211 | /* Get address family of specified address */ | |
| 212 | sa_family_t af = (sa_family_t)nl_addr_get_family(arguments.nl_addr); | |
| 213 | if(debug){ | |
| 214 | fprintf_plus(stderr, "Address family of %s is %s (%" PRIdMAX | |
| 215 | ")\n", arguments.address, | |
| 216 | af == AF_INET6 ? "AF_INET6" : | |
| 217 | ( af == AF_INET ? "AF_INET" : "UNKNOWN"), | |
| 218 | (intmax_t)af); | |
| 219 | } | |
| 220 | /* Set route parameters: */ | |
| 221 | rtnl_route_set_family(route, (uint8_t)af); /* Address family */ | |
| 222 | rtnl_route_set_protocol(route, RTPROT_BOOT); /* protocol - see | |
| 223 | 						  ip-route(8) */
 | |
| 224 | rtnl_route_set_scope(route, RT_SCOPE_LINK); /* link scope */ | |
| 225 | rtnl_route_set_type(route, RTN_UNICAST); /* normal unicast | |
| 226 | 						 address route */
 | |
| 227 | rtnl_route_set_dst(route, arguments.nl_addr); /* Destination | |
| 228 | 						   address */
 | |
| 229 | rtnl_route_set_table(route, RT_TABLE_MAIN); /* "main" routing | |
| 230 | 						 table */
 | |
| 231 | /* Create nexthop */ | |
| 232 | nexthop = rtnl_route_nh_alloc(); | |
| 233 | if(nexthop == NULL){ | |
| 738.1.5
by Teddy Hogeborn plugin-helpers/mandos-client-iprouteadddel.c: Fix #include lines. | 234 | 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. | 235 | exitcode = EX_OSERR; | 
| 236 | goto end; | |
| 237 | } | |
| 238 | /* Get index number of specified interface */ | |
| 239 | int ifindex = rtnl_link_get_ifindex(link); | |
| 240 | if(debug){ | |
| 241 | fprintf_plus(stderr, "ifindex of %s is %d\n", arguments.interface, | |
| 242 | ifindex); | |
| 243 | } | |
| 244 | /* Set interface index number on nexthop object */ | |
| 245 | rtnl_route_nh_set_ifindex(nexthop, ifindex); | |
| 938
by Teddy Hogeborn Correct comments | 246 | /* Set route to use nexthop object */ | 
| 738.1.4
by Teddy Hogeborn Add plugin for mandos-client to add and delete local routes. | 247 | rtnl_route_add_nexthop(route, nexthop); | 
| 248 | /* Add or delete route? */ | |
| 249 | if(arguments.add){ | |
| 250 | ret = rtnl_route_add(sk, route, NLM_F_EXCL); | |
| 251 | } else { | |
| 252 | ret = rtnl_route_delete(sk, route, 0); | |
| 253 | } | |
| 254 | if(ret < 0){ | |
| 255 | fprintf_plus(stderr, "Failed to %s route: %s\n", | |
| 256 | arguments.add ? "add" : "delete", | |
| 257 | nl_geterror(ret)); | |
| 258 | exitcode = EX_OSERR; | |
| 259 | goto end; | |
| 260 | } | |
| 261 | end: | |
| 262 | /* Deallocate route */ | |
| 263 | if(route){ | |
| 264 | rtnl_route_put(route); | |
| 265 | } else if(nexthop) { | |
| 266 | /* Deallocate route nexthop */ | |
| 267 | rtnl_route_nh_free(nexthop); | |
| 268 | } | |
| 269 | /* Deallocate parsed address */ | |
| 270 | if(arguments.nl_addr){ | |
| 271 | nl_addr_put(arguments.nl_addr); | |
| 272 | } | |
| 273 | /* Deallocate link struct */ | |
| 274 | if(link){ | |
| 275 | rtnl_link_put(link); | |
| 276 | } | |
| 277 | /* Deallocate netlink socket struct */ | |
| 278 | if(sk){ | |
| 279 | nl_socket_free(sk); | |
| 280 | } | |
| 281 | return exitcode; | |
| 282 | }
 |