• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/netifd/proto-ext.c

  1 /*
  2  * netifd - network interface daemon
  3  * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License version 2
  7  * as published by the Free Software Foundation
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  */
 14 #define _GNU_SOURCE
 15 
 16 #include <string.h>
 17 #include <stdlib.h>
 18 #include <stdio.h>
 19 #include <signal.h>
 20 
 21 #include <arpa/inet.h>
 22 #include <netinet/in.h>
 23 
 24 #include "proto-ext.h"
 25 #include "system.h"
 26 #include "handler.h"
 27 
 28 static void
 29 proto_ext_check_dependencies(struct proto_ext_state *state)
 30 {
 31         struct proto_ext_dep *dep;
 32         bool available = true;
 33 
 34         list_for_each_entry(dep, &state->deps, list) {
 35                 if (dep->dep.iface)
 36                         continue;
 37 
 38                 available = false;
 39                 break;
 40         }
 41 
 42         interface_set_available(state->proto.iface, available);
 43 }
 44 
 45 static void
 46 proto_ext_if_up_cb(struct interface_user *dep, struct interface *iface,
 47                    enum interface_event ev);
 48 static void
 49 proto_ext_if_down_cb(struct interface_user *dep, struct interface *iface,
 50                      enum interface_event ev);
 51 
 52 static void
 53 proto_ext_update_host_dep(struct proto_ext_dep *dep)
 54 {
 55         struct interface *iface = NULL;
 56 
 57         if (dep->dep.iface)
 58                 goto out;
 59 
 60         if (dep->interface[0]) {
 61                 iface = vlist_find(&interfaces, dep->interface, iface, node);
 62 
 63                 if (!iface || iface->state != IFS_UP)
 64                         goto out;
 65         }
 66 
 67         if (!dep->any)
 68                 iface = interface_ip_add_target_route(&dep->host, dep->v6, iface, false);
 69 
 70         if (!iface)
 71                 goto out;
 72 
 73         interface_remove_user(&dep->dep);
 74         dep->dep.cb = proto_ext_if_down_cb;
 75         interface_add_user(&dep->dep, iface);
 76 
 77 out:
 78         proto_ext_check_dependencies(dep->proto);
 79 }
 80 
 81 static void
 82 proto_ext_clear_host_dep(struct proto_ext_state *state)
 83 {
 84         struct proto_ext_dep *dep, *tmp;
 85 
 86         list_for_each_entry_safe(dep, tmp, &state->deps, list) {
 87                 interface_remove_user(&dep->dep);
 88                 list_del(&dep->list);
 89                 free(dep);
 90         }
 91 }
 92 
 93 static void
 94 proto_ext_if_up_cb(struct interface_user *dep, struct interface *iface,
 95                    enum interface_event ev)
 96 {
 97         struct proto_ext_dep *pdep;
 98 
 99         if (ev != IFEV_UP && ev != IFEV_UPDATE)
100                 return;
101 
102         pdep = container_of(dep, struct proto_ext_dep, dep);
103         proto_ext_update_host_dep(pdep);
104 }
105 
106 static void
107 proto_ext_if_down_cb(struct interface_user *dep, struct interface *iface,
108                      enum interface_event ev)
109 {
110         struct proto_ext_dep *pdep;
111         struct proto_ext_state *state;
112 
113         if (ev != IFEV_UP_FAILED && ev != IFEV_DOWN && ev != IFEV_FREE)
114                 return;
115 
116         pdep = container_of(dep, struct proto_ext_dep, dep);
117         interface_remove_user(dep);
118         dep->cb = proto_ext_if_up_cb;
119         interface_add_user(dep, NULL);
120 
121         state = pdep->proto;
122         if (state->sm == S_IDLE) {
123                 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
124                 state->proto.cb(&state->proto, PROTO_CMD_TEARDOWN, false);
125         }
126 }
127 
128 static void
129 proto_ext_task_finish(struct proto_ext_state *state,
130                       struct netifd_process *task)
131 {
132         switch (state->sm) {
133         case S_IDLE:
134                 if (task == &state->proto_task)
135                         state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
136                 fallthrough;
137         case S_SETUP:
138                 if (task == &state->proto_task)
139                         state->proto.cb(&state->proto, PROTO_CMD_TEARDOWN,
140                                         false);
141                 else if (task == &state->script_task) {
142                         if (state->restart_pending)
143                                 state->proto.cb(&state->proto,
144                                                 PROTO_CMD_RESTART, false);
145                         else if (state->renew_pending)
146                                 state->proto.cb(&state->proto,
147                                                 PROTO_CMD_RENEW, false);
148                         else if (!(state->proto.handler->flags & PROTO_FLAG_NO_TASK) &&
149                                  !state->proto_task.uloop.pending &&
150                                  state->sm == S_SETUP)
151                                 state->proto.cb(&state->proto,
152                                                 PROTO_CMD_TEARDOWN,
153                                                 false);
154 
155                         if (state->sm == S_SETUP && state->checkup_interval > 0) {
156                                 uloop_timeout_set(&state->checkup_timeout,
157                                                   state->checkup_interval * 1000);
158                         }
159                 }
160                 break;
161 
162         case S_SETUP_ABORT:
163                 if (state->script_task.uloop.pending ||
164                     state->proto_task.uloop.pending)
165                         break;
166 
167                 uloop_timeout_cancel(&state->teardown_timeout);
168                 uloop_timeout_cancel(&state->checkup_timeout);
169                 state->sm = S_IDLE;
170                 state->proto.cb(&state->proto, PROTO_CMD_TEARDOWN, false);
171                 break;
172 
173         case S_TEARDOWN:
174                 if (state->script_task.uloop.pending)
175                         break;
176 
177                 if (state->proto_task.uloop.pending) {
178                         if (!state->proto_task_killed)
179                                 kill(state->proto_task.uloop.pid, SIGTERM);
180                         break;
181                 }
182 
183                 uloop_timeout_cancel(&state->teardown_timeout);
184                 uloop_timeout_cancel(&state->checkup_timeout);
185                 state->sm = S_IDLE;
186                 state->proto.proto_event(&state->proto, IFPEV_DOWN);
187                 break;
188         }
189 }
190 
191 static void
192 proto_ext_teardown_timeout_cb(struct uloop_timeout *timeout)
193 {
194         struct proto_ext_state *state;
195 
196         state = container_of(timeout, struct proto_ext_state, teardown_timeout);
197 
198         netifd_kill_process(&state->script_task);
199         netifd_kill_process(&state->proto_task);
200         proto_ext_task_finish(state, NULL);
201 }
202 
203 static void
204 proto_ext_script_cb(struct netifd_process *p, int ret)
205 {
206         struct proto_ext_state *state;
207 
208         state = container_of(p, struct proto_ext_state, script_task);
209         proto_ext_task_finish(state, p);
210 }
211 
212 static void
213 proto_ext_task_cb(struct netifd_process *p, int ret)
214 {
215         struct proto_ext_state *state;
216 
217         state = container_of(p, struct proto_ext_state, proto_task);
218 
219         if (state->sm == S_IDLE || state->sm == S_SETUP)
220                 state->last_error = WEXITSTATUS(ret);
221 
222         proto_ext_task_finish(state, p);
223 }
224 
225 void
226 proto_ext_free(struct interface_proto_state *proto)
227 {
228         struct proto_ext_state *state;
229 
230         state = container_of(proto, struct proto_ext_state, proto);
231         uloop_timeout_cancel(&state->teardown_timeout);
232         uloop_timeout_cancel(&state->checkup_timeout);
233         proto_ext_clear_host_dep(state);
234         netifd_kill_process(&state->script_task);
235         netifd_kill_process(&state->proto_task);
236         free(state->config);
237         free(state);
238 }
239 
240 static void
241 proto_ext_parse_route_list(struct interface *iface, struct blob_attr *attr,
242                            bool v6)
243 {
244         struct blob_attr *cur;
245         size_t rem;
246 
247         blobmsg_for_each_attr(cur, attr, rem) {
248                 if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
249                         D(INTERFACE, "Ignore wrong route type: %d", blobmsg_type(cur));
250                         continue;
251                 }
252 
253                 interface_ip_add_route(iface, cur, v6);
254         }
255 }
256 
257 static void
258 proto_ext_parse_neighbor_list(struct interface *iface, struct blob_attr *attr,
259                               bool v6)
260 {
261         struct blob_attr *cur;
262         size_t rem;
263 
264         blobmsg_for_each_attr(cur, attr, rem) {
265                 if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
266                         D(INTERFACE, "Ignore wrong neighbor type: %d", blobmsg_type(cur));
267                         continue;
268                 }
269 
270                 interface_ip_add_neighbor(iface, cur, v6);
271         }
272 }
273 
274 static void
275 proto_ext_parse_data(struct interface *iface, struct blob_attr *attr)
276 {
277         struct blob_attr *cur;
278         size_t rem;
279 
280         blobmsg_for_each_attr(cur, attr, rem)
281                 interface_add_data(iface, cur);
282 }
283 
284 static struct device *
285 proto_ext_create_tunnel(const char *name, struct blob_attr *attr)
286 {
287         struct device *dev;
288         struct blob_buf b;
289 
290         memset(&b, 0, sizeof(b));
291         blob_buf_init(&b, 0);
292         blob_put(&b, 0, blobmsg_data(attr), blobmsg_data_len(attr));
293         dev = device_create(name, &tunnel_device_type, blob_data(b.head));
294         blob_buf_free(&b);
295 
296         return dev;
297 }
298 
299 enum {
300         NOTIFY_ACTION,
301         NOTIFY_ERROR,
302         NOTIFY_COMMAND,
303         NOTIFY_ENV,
304         NOTIFY_SIGNAL,
305         NOTIFY_AVAILABLE,
306         NOTIFY_LINK_UP,
307         NOTIFY_IFNAME,
308         NOTIFY_ADDR_EXT,
309         NOTIFY_ROUTES,
310         NOTIFY_ROUTES6,
311         NOTIFY_TUNNEL,
312         NOTIFY_DATA,
313         NOTIFY_KEEP,
314         NOTIFY_HOST,
315         NOTIFY_DNS,
316         NOTIFY_DNS_SEARCH,
317         NOTIFY_NEIGHBORS,
318         NOTIFY_NEIGHBORS6,
319         __NOTIFY_LAST
320 };
321 
322 static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
323         [NOTIFY_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_INT32 },
324         [NOTIFY_ERROR] = { .name = "error", .type = BLOBMSG_TYPE_ARRAY },
325         [NOTIFY_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_ARRAY },
326         [NOTIFY_ENV] = { .name = "env", .type = BLOBMSG_TYPE_ARRAY },
327         [NOTIFY_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
328         [NOTIFY_AVAILABLE] = { .name = "available", .type = BLOBMSG_TYPE_BOOL },
329         [NOTIFY_LINK_UP] = { .name = "link-up", .type = BLOBMSG_TYPE_BOOL },
330         [NOTIFY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
331         [NOTIFY_ADDR_EXT] = { .name = "address-external", .type = BLOBMSG_TYPE_BOOL },
332         [NOTIFY_ROUTES] = { .name = "routes", .type = BLOBMSG_TYPE_ARRAY },
333         [NOTIFY_ROUTES6] = { .name = "routes6", .type = BLOBMSG_TYPE_ARRAY },
334         [NOTIFY_TUNNEL] = { .name = "tunnel", .type = BLOBMSG_TYPE_TABLE },
335         [NOTIFY_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
336         [NOTIFY_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL },
337         [NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
338         [NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
339         [NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
340         [NOTIFY_NEIGHBORS]= {.name = "neighbor", .type = BLOBMSG_TYPE_ARRAY},
341         [NOTIFY_NEIGHBORS6]= {.name = "neighbor6", .type = BLOBMSG_TYPE_ARRAY},
342 };
343 
344 static int
345 proto_ext_update_link(struct proto_ext_state *state, struct blob_attr *data, struct blob_attr **tb)
346 {
347         struct interface *iface = state->proto.iface;
348         struct blob_attr *cur;
349         struct device *dev;
350         const char *devname;
351         int dev_create = 1;
352         bool addr_ext = false;
353         bool keep = false;
354         bool up;
355 
356         if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
357                 return UBUS_STATUS_PERMISSION_DENIED;
358 
359         if (!tb[NOTIFY_LINK_UP])
360                 return UBUS_STATUS_INVALID_ARGUMENT;
361 
362         up = blobmsg_get_bool(tb[NOTIFY_LINK_UP]);
363         if (!up) {
364                 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
365                 return 0;
366         }
367 
368         if ((cur = tb[NOTIFY_KEEP]) != NULL)
369                 keep = blobmsg_get_bool(cur);
370 
371         if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL) {
372                 addr_ext = blobmsg_get_bool(cur);
373                 if (addr_ext)
374                         dev_create = 2;
375         }
376 
377         if (iface->state != IFS_UP || !iface->l3_dev.dev)
378                 keep = false;
379 
380         if (!keep) {
381                 dev = iface->main_dev.dev;
382                 if (tb[NOTIFY_IFNAME]) {
383                         keep = false;
384                         devname = blobmsg_data(tb[NOTIFY_IFNAME]);
385                         if (tb[NOTIFY_TUNNEL])
386                                 dev = proto_ext_create_tunnel(devname, tb[NOTIFY_TUNNEL]);
387                         else
388                                 dev = device_get(devname, dev_create);
389                 }
390 
391                 if (!dev)
392                         return UBUS_STATUS_INVALID_ARGUMENT;
393 
394                 interface_set_l3_dev(iface, dev);
395                 if (device_claim(&iface->l3_dev) < 0)
396                         return UBUS_STATUS_UNKNOWN_ERROR;
397 
398                 device_set_present(dev, true);
399         }
400 
401         interface_update_start(iface, keep);
402 
403         proto_apply_ip_settings(iface, data, addr_ext);
404 
405         if ((cur = tb[NOTIFY_ROUTES]) != NULL)
406                 proto_ext_parse_route_list(state->proto.iface, cur, false);
407 
408         if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
409                 proto_ext_parse_route_list(state->proto.iface, cur, true);
410 
411         if ((cur = tb[NOTIFY_NEIGHBORS]) != NULL)
412                 proto_ext_parse_neighbor_list(state->proto.iface, cur, false);
413 
414         if ((cur = tb[NOTIFY_NEIGHBORS6]) != NULL)
415                 proto_ext_parse_neighbor_list(state->proto.iface, cur, true);
416 
417         if ((cur = tb[NOTIFY_DNS]))
418                 interface_add_dns_server_list(&iface->proto_ip, cur);
419 
420         if ((cur = tb[NOTIFY_DNS_SEARCH]))
421                 interface_add_dns_search_list(&iface->proto_ip, cur);
422 
423         if ((cur = tb[NOTIFY_DATA]))
424                 proto_ext_parse_data(state->proto.iface, cur);
425 
426         interface_update_complete(state->proto.iface);
427 
428         if ((state->sm != S_SETUP_ABORT) && (state->sm != S_TEARDOWN)) {
429                 state->proto.proto_event(&state->proto, IFPEV_UP);
430                 state->sm = S_IDLE;
431         }
432 
433         return 0;
434 }
435 
436 static bool
437 fill_string_list(struct blob_attr *attr, char **argv, int max)
438 {
439         struct blob_attr *cur;
440         int argc = 0;
441         size_t rem;
442 
443         if (!attr)
444                 goto out;
445 
446         blobmsg_for_each_attr(cur, attr, rem) {
447                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
448                         return false;
449 
450                 if (!blobmsg_check_attr(cur, false))
451                         return false;
452 
453                 argv[argc++] = blobmsg_data(cur);
454                 if (argc == max - 1)
455                         return false;
456         }
457 
458 out:
459         argv[argc] = NULL;
460         return true;
461 }
462 
463 static int
464 proto_ext_run_command(struct proto_ext_state *state, struct blob_attr **tb)
465 {
466         static char *argv[64];
467         static char *env[32];
468 
469         if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
470                 return UBUS_STATUS_PERMISSION_DENIED;
471 
472         if (!tb[NOTIFY_COMMAND])
473                 goto error;
474 
475         if (!fill_string_list(tb[NOTIFY_COMMAND], argv, ARRAY_SIZE(argv)))
476                 goto error;
477 
478         if (!fill_string_list(tb[NOTIFY_ENV], env, ARRAY_SIZE(env)))
479                 goto error;
480 
481         state->proto_task_killed = false;
482         netifd_start_process((const char **) argv, (char **) env, &state->proto_task);
483 
484         return 0;
485 
486 error:
487         return UBUS_STATUS_INVALID_ARGUMENT;
488 }
489 
490 static int
491 proto_ext_kill_command(struct proto_ext_state *state, struct blob_attr **tb)
492 {
493         unsigned int signal = ~0;
494 
495         if (tb[NOTIFY_SIGNAL])
496                 signal = blobmsg_get_u32(tb[NOTIFY_SIGNAL]);
497 
498         if (signal > 31)
499                 signal = SIGTERM;
500 
501         if (state->proto_task.uloop.pending) {
502                 if (signal == SIGTERM || signal == SIGKILL)
503                         state->proto_task_killed = true;
504                 kill(state->proto_task.uloop.pid, signal);
505         }
506 
507         return 0;
508 }
509 
510 static int
511 proto_ext_notify_error(struct proto_ext_state *state, struct blob_attr **tb)
512 {
513         struct blob_attr *cur;
514         char *data[16];
515         int n_data = 0;
516         size_t rem;
517 
518         if (!tb[NOTIFY_ERROR])
519                 return UBUS_STATUS_INVALID_ARGUMENT;
520 
521         blobmsg_for_each_attr(cur, tb[NOTIFY_ERROR], rem) {
522                 if (n_data + 1 == ARRAY_SIZE(data))
523                         goto error;
524 
525                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
526                         goto error;
527 
528                 if (!blobmsg_check_attr(cur, false))
529                         goto error;
530 
531                 data[n_data++] = blobmsg_data(cur);
532         }
533 
534         if (!n_data)
535                 goto error;
536 
537         interface_add_error(state->proto.iface, state->proto.handler->name,
538                         data[0], (const char **) &data[1], n_data - 1);
539 
540         return 0;
541 
542 error:
543         return UBUS_STATUS_INVALID_ARGUMENT;
544 }
545 
546 static int
547 proto_ext_block_restart(struct proto_ext_state *state, struct blob_attr **tb)
548 {
549         state->proto.iface->autostart = false;
550         return 0;
551 }
552 
553 static int
554 proto_ext_set_available(struct proto_ext_state *state, struct blob_attr **tb)
555 {
556         if (!tb[NOTIFY_AVAILABLE])
557                 return UBUS_STATUS_INVALID_ARGUMENT;
558 
559         interface_set_available(state->proto.iface, blobmsg_get_bool(tb[NOTIFY_AVAILABLE]));
560         return 0;
561 }
562 
563 static int
564 proto_ext_add_host_dependency(struct proto_ext_state *state, struct blob_attr **tb)
565 {
566         struct proto_ext_dep *dep;
567         const char *ifname = tb[NOTIFY_IFNAME] ? blobmsg_data(tb[NOTIFY_IFNAME]) : "";
568         const char *host = tb[NOTIFY_HOST] ? blobmsg_data(tb[NOTIFY_HOST]) : "";
569 
570         if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
571                 return UBUS_STATUS_PERMISSION_DENIED;
572 
573         dep = calloc(1, sizeof(*dep) + strlen(ifname) + 1);
574         if (!dep)
575                 return UBUS_STATUS_UNKNOWN_ERROR;
576 
577         if (!host[0] && ifname[0]) {
578                 dep->any = true;
579         } else if (inet_pton(AF_INET, host, &dep->host) < 1) {
580                 if (inet_pton(AF_INET6, host, &dep->host) < 1) {
581                         free(dep);
582                         return UBUS_STATUS_INVALID_ARGUMENT;
583                 } else {
584                         dep->v6 = true;
585                 }
586         }
587 
588         dep->proto = state;
589         strcpy(dep->interface, ifname);
590 
591         dep->dep.cb = proto_ext_if_up_cb;
592         interface_add_user(&dep->dep, NULL);
593         list_add(&dep->list, &state->deps);
594         proto_ext_update_host_dep(dep);
595         if (!dep->dep.iface)
596                 return UBUS_STATUS_NOT_FOUND;
597 
598         return 0;
599 }
600 
601 static int
602 proto_ext_setup_failed(struct proto_ext_state *state)
603 {
604         int ret = 0;
605 
606         switch (state->sm) {
607         case S_IDLE:
608                 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
609                 fallthrough;
610         case S_SETUP:
611                 state->proto.cb(&state->proto, PROTO_CMD_TEARDOWN, false);
612                 break;
613         case S_SETUP_ABORT:
614         case S_TEARDOWN:
615         default:
616                 ret = UBUS_STATUS_PERMISSION_DENIED;
617                 break;
618         }
619         return ret;
620 }
621 
622 int
623 proto_ext_notify(struct interface_proto_state *proto, struct blob_attr *attr)
624 {
625         struct proto_ext_state *state;
626         struct blob_attr *tb[__NOTIFY_LAST];
627 
628         state = container_of(proto, struct proto_ext_state, proto);
629 
630         blobmsg_parse_attr(notify_attr, __NOTIFY_LAST, tb, attr);
631         if (!tb[NOTIFY_ACTION])
632                 return UBUS_STATUS_INVALID_ARGUMENT;
633 
634         switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
635         case 0:
636                 return proto_ext_update_link(state, attr, tb);
637         case 1:
638                 return proto_ext_run_command(state, tb);
639         case 2:
640                 return proto_ext_kill_command(state, tb);
641         case 3:
642                 return proto_ext_notify_error(state, tb);
643         case 4:
644                 return proto_ext_block_restart(state, tb);
645         case 5:
646                 return proto_ext_set_available(state, tb);
647         case 6:
648                 return proto_ext_add_host_dependency(state, tb);
649         case 7:
650                 return proto_ext_setup_failed(state);
651         default:
652                 return UBUS_STATUS_INVALID_ARGUMENT;
653         }
654 }
655 
656 static void
657 proto_ext_checkup_timeout_cb(struct uloop_timeout *timeout)
658 {
659         struct proto_ext_state *state = container_of(timeout, struct
660                         proto_ext_state, checkup_timeout);
661         struct interface_proto_state *proto = &state->proto;
662         struct interface *iface = proto->iface;
663 
664         if (!iface->autostart)
665                 return;
666 
667         if (iface->state == IFS_UP)
668                 return;
669 
670         D(INTERFACE, "Interface '%s' is not up after %d sec",
671                         iface->name, state->checkup_interval);
672         state->proto.cb(proto, PROTO_CMD_TEARDOWN, false);
673 }
674 
675 static void
676 proto_ext_checkup_attach(struct proto_ext_state *state,
677                 const struct blob_attr *attr)
678 {
679         struct blob_attr *tb;
680         struct blobmsg_policy checkup_policy = {
681                 .name = "checkup_interval",
682                 .type = BLOBMSG_TYPE_INT32
683         };
684 
685         blobmsg_parse_attr(&checkup_policy, 1, &tb, (struct blob_attr *)attr);
686         if (!tb) {
687                 state->checkup_interval = -1;
688                 state->checkup_timeout.cb = NULL;
689         } else {
690                 state->checkup_interval = blobmsg_get_u32(tb);
691                 state->checkup_timeout.cb = proto_ext_checkup_timeout_cb;
692         }
693 }
694 
695 void
696 proto_ext_state_init(struct proto_ext_state *state,
697                      struct interface *iface, struct blob_attr *attr,
698                      int dir_fd)
699 {
700         INIT_LIST_HEAD(&state->deps);
701 
702         state->config = malloc(blob_pad_len(attr));
703         if (!state->config)
704                 return;
705 
706         memcpy(state->config, attr, blob_pad_len(attr));
707         proto_ext_checkup_attach(state, state->config);
708         state->proto.free = proto_ext_free;
709         state->proto.notify = proto_ext_notify;
710         state->teardown_timeout.cb = proto_ext_teardown_timeout_cb;
711         state->script_task.cb = proto_ext_script_cb;
712         state->script_task.dir_fd = dir_fd;
713         state->script_task.log_prefix = iface->name;
714         state->proto_task.cb = proto_ext_task_cb;
715         state->proto_task.dir_fd = dir_fd;
716         state->proto_task.log_prefix = iface->name;
717 }
718 
719 int
720 proto_ext_run(struct proto_ext_state *state,
721               enum interface_proto_cmd cmd, bool force,
722               proto_ext_handler_cb start_cb)
723 {
724         struct interface_proto_state *proto = &state->proto;
725         static char error_buf[32];
726         char *envp[2];
727         const char *action;
728         char *config;
729         int ret, j = 0;
730 
731         if (cmd == PROTO_CMD_SETUP) {
732                 switch (state->sm) {
733                 case S_IDLE:
734                         action = "setup";
735                         state->last_error = -1;
736                         proto_ext_clear_host_dep(state);
737                         state->sm = S_SETUP;
738                         break;
739 
740                 default:
741                         return -1;
742                 }
743         } else if (cmd == PROTO_CMD_RENEW) {
744                 if (!(proto->handler->flags & PROTO_FLAG_RENEW_AVAILABLE))
745                         return 0;
746 
747                 /*
748                  * A teardown is already in progress and its completion paths do
749                  * not consume pending flags; a flag queued here would go stale
750                  * and fire against the freshly started client after the next
751                  * setup. The client is being bounced anyway, so drop the
752                  * request.
753                  */
754                 if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
755                         return 0;
756 
757                 if (state->script_task.uloop.pending) {
758                         state->renew_pending = true;
759                         return 0;
760                 }
761 
762                 state->renew_pending = false;
763                 action = "renew";
764         } else if (cmd == PROTO_CMD_RESTART) {
765                 if (!(proto->handler->flags & PROTO_FLAG_RESTART_AVAILABLE))
766                         return 0;
767 
768                 if (state->sm == S_TEARDOWN || state->sm == S_SETUP_ABORT)
769                         return 0;
770 
771                 if (state->script_task.uloop.pending) {
772                         state->restart_pending = true;
773                         return 0;
774                 }
775 
776                 state->restart_pending = false;
777                 action = "restart";
778         } else {
779                 switch (state->sm) {
780                 case S_SETUP:
781                         if (state->script_task.uloop.pending) {
782                                 uloop_timeout_set(&state->teardown_timeout, 1000);
783                                 kill(state->script_task.uloop.pid, SIGTERM);
784                                 if (state->proto_task.uloop.pending)
785                                         kill(state->proto_task.uloop.pid, SIGTERM);
786                                 state->renew_pending = false;
787                                 state->restart_pending = false;
788                                 state->sm = S_SETUP_ABORT;
789                                 return 0;
790                         }
791                 fallthrough;
792                 case S_IDLE:
793                         action = "teardown";
794                         state->renew_pending = false;
795                         state->restart_pending = false;
796                         state->sm = S_TEARDOWN;
797                         if (state->last_error >= 0) {
798                                 snprintf(error_buf, sizeof(error_buf), "ERROR=%d", state->last_error);
799                                 envp[j++] = error_buf;
800                         }
801                         uloop_timeout_set(&state->teardown_timeout, 5000);
802                         break;
803 
804                 case S_TEARDOWN:
805                         return 0;
806 
807                 default:
808                         return -1;
809                 }
810         }
811 
812         D(INTERFACE, "run %s for interface '%s'", action, proto->iface->name);
813         config = blobmsg_format_json(state->config, true);
814         if (!config) {
815                 ret = -1;
816                 goto error;
817         }
818 
819         envp[j] = NULL;
820 
821         ret = start_cb(state, action, config, envp);
822         free(config);
823         if (ret)
824                 goto error;
825 
826         return 0;
827 
828 error:
829         /*
830          * Roll back the setup transition: nothing is running that could
831          * advance the state machine, and teardown on an interface that never
832          * came up does not reach the proto handler, so a stuck S_SETUP would
833          * block every future setup attempt
834          */
835         if (cmd == PROTO_CMD_SETUP)
836                 state->sm = S_IDLE;
837 
838         return ret;
839 }
840 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt