/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

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, 2021 Teddy Hogeborn
6
 
 * Copyright © 2015-2018, 2021 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 <argp.h>               /* argp_program_version,
29
 
                                   argp_program_bug_address,
30
 
                                   struct argp_option,
31
 
                                   struct argp_state, ARGP_KEY_ARG,
32
 
                                   argp_usage(), ARGP_KEY_END,
33
 
                                   ARGP_ERR_UNKNOWN, struct argp,
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 */
42
 
#include <netlink/netlink.h>    /* struct nl_addr, nl_addr_parse(),
43
 
                                   nl_geterror(),
44
 
                                   nl_addr_get_family(), NLM_F_EXCL,
45
 
                                   nl_addr_put() */
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() */
66
 
#include <netlink/socket.h>     /* struct nl_sock, nl_socket_alloc(),
67
 
                                   nl_connect(), nl_socket_free() */
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(),
73
 
                                   rtnl_link_get_ifindex(),
74
 
                                   rtnl_link_put() */
75
 
#include <netinet/in.h>         /* sa_family_t */
76
 
#include <inttypes.h>           /* PRIdMAX, intmax_t */
77
 
#include <stdint.h>             /* uint8_t */
78
 
 
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;
96
 
  va_start(ap, format);
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){
242
 
    fprintf_plus(stderr, "Failed to get netlink route nexthop\n");
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);
254
 
  /* Set route to use nexthop object */
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
 
}