/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugin-helpers/mandos-client-iprouteadddel.c

  • Committer: Teddy Hogeborn
  • Date: 2019-03-12 20:13:34 UTC
  • Revision ID: teddy@recompile.se-20190312201334-my3htrprewjosuw5
mandos-ctl: Refactor

* mandos-ctl: Reorder everything into logical order; put main() first,
              and put every subsequent definition as soon as possible
              after its first use, except superclasses which need to
              be placed before the classes inheriting from them.
              Reorder all tests to match.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  -*- coding: utf-8 -*- */
 
2
/* 
 
3
 * iprouteadddel - Add or delete direct route to a local IP address
 
4
 * 
 
5
 * Copyright © 2015-2018 Teddy Hogeborn
 
6
 * Copyright © 2015-2018 Björn Påhlsson
 
7
 * 
 
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
 
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
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
22
 * 
 
23
 * Contact the authors at <mandos@recompile.se>.
 
24
 */
 
25
 
 
26
#define _GNU_SOURCE             /* program_invocation_short_name */
 
27
#include <stdbool.h>            /* bool, false, true */
 
28
#include <stdio.h>              /* fprintf(), stderr, FILE, vfprintf */
 
29
#include <errno.h>              /* program_invocation_short_name,
 
30
                                   errno, perror(), EINVAL, ENOMEM */
 
31
#include <stdarg.h>             /* va_list, va_start */
 
32
#include <stdlib.h>             /* EXIT_SUCCESS */
 
33
#include <argp.h>               /* struct argp_option, error_t, struct
 
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 */
 
40
#include <inttypes.h>           /* PRIdMAX, intmax_t */
 
41
 
 
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() */
 
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){
 
234
    fprintf_plus(stderr, "Failed to get netlink route nexthop\n");
 
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);
 
246
  /* Set route to use nexthop object */
 
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
}