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