/* * Copyright (c) 2016 Chris Johns . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Handle the networking directives found in rc.conf. * - ifconfig_* * - cloned_interfaces * - autobridge_interfaces * - autobridge_bridge* * - defaultrouter */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Default defaultroute_delay is 30seconds. */ static int defaultroute_delay_secs = 30; /* * Show a result. */ static void show_result(const char* label, int r) { if (r < 0) fprintf(stderr, "error: %s: %s\n", label, strerror(errno)); } /* * cloned_interfaces * * eg cloned_interfaces="vlan0 bridge0 tap1 tap2" * * See 'man rc.conf(5)' on FreeBSD. */ static int cloned_interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) { int arg; int r = 0; r = rtems_bsd_rc_conf_find(rc_conf, "cloned_interfaces", aa); if (r < 0) { if (errno == ENOENT) r = 0; return r; } for (arg = 1; arg < aa->argc; ++arg) { const char* ifconfg_args[] = { "ifconfig", aa->argv[arg], "create", NULL }; rtems_bsd_rc_conf_print_cmd(rc_conf, "cloning_interfaces", 3, ifconfg_args); rtems_bsd_command_ifconfig(3, (char**) ifconfg_args); } return 0; } /* * create_args_'interface' * * eg create_args_myvlan="vlan 102" * * See 'man rc.conf(5)' on FreeBSD. */ typedef struct { rtems_chain_node node; const char* label; int argc; const char** argv; } create_args_item; static RTEMS_CHAIN_DEFINE_EMPTY(create_args_items); static int load_create_args(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) { int r = 0; r = rtems_bsd_rc_conf_find(rc_conf, "create_args_.*", aa); while (r == 0) { rtems_chain_node* node; const char* label; create_args_item* item; int arg; rtems_bsd_rc_conf_print_cmd(rc_conf, "create_args_", aa->argc, aa->argv); label = aa->argv[0] + strlen("create_args_"); node = rtems_chain_first(&create_args_items); while (!rtems_chain_is_tail(&create_args_items, node)) { item = (create_args_item*) node; if (strcasecmp(item->label, label) == 0) { fprintf(stderr, "error: %s:%d: duplicate create args entry: %s\n", rtems_bsd_rc_conf_name(rc_conf), rtems_bsd_rc_conf_line(rc_conf), aa->argv[0]); errno = EEXIST; return -1; } node = rtems_chain_next(node); } item = calloc(1, sizeof(*item)); if (item == NULL) { errno = ENOMEM; fprintf(stderr, "error: %s:%d: %s\n", rtems_bsd_rc_conf_name(rc_conf), rtems_bsd_rc_conf_line(rc_conf), strerror(errno)); return -1; } item->argc = aa->argc; item->label = strdup(label); if (item->label == NULL) { free(item); errno = ENOMEM; fprintf(stderr, "error: %s:%d: %s\n", rtems_bsd_rc_conf_name(rc_conf), rtems_bsd_rc_conf_line(rc_conf), strerror(errno)); return -1; } item->argv = calloc(aa->argc + 1, sizeof(char*)); if (item->argv == NULL) { free((void*) item->label); free(item); errno = ENOMEM; fprintf(stderr, "error: %s:%d: %s\n", rtems_bsd_rc_conf_name(rc_conf), rtems_bsd_rc_conf_line(rc_conf), strerror(errno)); return -1; } for (arg = 0; arg < aa->argc; ++arg) { item->argv[arg] = strdup(aa->argv[0]); if (item->argv[arg] == NULL) { int a; for (a = 0; a < arg; ++a) free((void*) item->argv[a]); free(item->argv); free((void*) item->label); free(item); errno = ENOMEM; fprintf(stderr, "error: %s:%d: %s\n", rtems_bsd_rc_conf_name(rc_conf), rtems_bsd_rc_conf_line(rc_conf), strerror(errno)); return -1; } } rtems_chain_append(&create_args_items, &item->node); r = rtems_bsd_rc_conf_find_next(rc_conf, aa); } /* * ignore not found. */ if (r < 0 && errno == ENOENT) r = 0; return 0; } /* * ifconfig_'interface' * * eg ifconfig_em0="inet 10.10.5.33 netmask 255.255.255.0" * * See 'man rc.conf(5)' on FreeBSD. */ static int ifconfig_(rtems_bsd_rc_conf* rc_conf, const char* ifname, int argc, const char** argv) { const char** args; int arg; int r; const char const* ifconfig_show[] = { "ifconfig", ifname, NULL }; for (arg = 1; arg < argc; ++arg) { if (strcasecmp(argv[arg], "NOAUTO") == 0) return 0; } args = calloc(argc + 3, sizeof(char*)); if (args == NULL) { errno = ENOMEM; return -1; } args[0] = "ifconfig"; args[1] = ifname; for (arg = 1; arg < argc; ++arg) args[arg + 1] = argv[arg]; args[argc + 1] = "up"; rtems_bsd_rc_conf_print_cmd(rc_conf, "ifconfig", argc + 2, args); r = rtems_bsd_command_ifconfig(argc + 2, (char**) args); free(args); if (r != EX_OK) { errno = ECANCELED; return -1; } r = rtems_bsd_command_ifconfig(2, (char**) ifconfig_show); return r; } /* * hostname * * eg hostname="myhost" * * See 'man rc.conf(5)' on FreeBSD. */ static int hostname(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) { const char** argv; int r; const char const* default_argv[] = { "hostname", "Amnesiac", NULL }; r = rtems_bsd_rc_conf_find(rc_conf, "hostname", aa); if (r < 0 && errno != ENOENT) return -1; if (r < 0 || (r == 0 && aa->argc != 2)) { argv = default_argv; } else { argv = aa->argv; } fprintf(stdout, "Setting hostname: %s.\n", argv[1]); return sethostname(argv[1], strlen(argv[1])); } /* * defaultrouter * * eg defaultrouter="1.2.3.4" * * See 'man rc.conf(5)' on FreeBSD. */ static int defaultrouter(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) { int r; r = rtems_bsd_rc_conf_find(rc_conf, "defaultrouter", aa); if (r < 0 && errno != ENOENT) return -1; if (r == 0) { if (aa->argc > 2) { errno = EINVAL; return -1; } if (strcasecmp(aa->argv[1], "NO") != 0) { const char* args[] = { "route", "add", "default", aa->argv[1], NULL }; int r; rtems_bsd_rc_conf_print_cmd(rc_conf, "defaultrouter", 4, args); r = rtems_bsd_command_route(4, (char**) args); if (r != EX_OK) { errno = ECANCELED; return -1; } } } return 0; } /* * defaultroute_delay * * eg defaultroute=120 * * See 'man rc.conf(5)' on FreeBSD. */ static int defaultroute_delay(rtems_bsd_rc_conf* rc_conf, int argc, const char** argv) { int value; char* end = NULL; if (argc != 2) { errno = EINVAL; return -1; } value = strtol(argv[1], &end, 10); if (end == NULL) { const char* args[] = { "defaultrouter_delay", argv[1], NULL }; rtems_bsd_rc_conf_print_cmd(rc_conf, "defaultrouter", 2, args); defaultroute_delay_secs = value; } else { errno = EINVAL; return -1; } return 0; } static int show_interfaces(const char* msg, struct ifaddrs* ifap) { struct ifaddrs* ifa; fprintf(stdout, msg); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcasecmp("lo0", ifa->ifa_name) == 0) { fprintf(stdout, "%s ", ifa->ifa_name); break; } } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcasecmp("lo0", ifa->ifa_name) != 0) { fprintf(stdout, "%s ", ifa->ifa_name); } } fprintf(stdout, "\b.\n"); return 0; } static int setup_lo0(rtems_bsd_rc_conf* rc_conf, struct ifaddrs* ifap) { struct ifaddrs* ifa; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcasecmp("lo0", ifa->ifa_name) == 0) { const char* lo0_argv[] = { "ifconfig_lo0", "inet", "127.0.0.1", "netmask", "255.0.0.0", NULL }; show_result("lo0", ifconfig_(rc_conf, "lo0", 5, lo0_argv)); return 0; } } fprintf(stderr, "warning: no loopback interface found\n"); return -1; } static int setup_interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa, struct ifaddrs* ifap) { struct ifaddrs* ifa; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcasecmp("lo0", ifa->ifa_name) != 0) { char iface[64]; int r; snprintf(iface, sizeof(iface), "ifconfig_%s", ifa->ifa_name); r = rtems_bsd_rc_conf_find(rc_conf, iface, aa); if (r == 0) { show_result(iface, ifconfig_(rc_conf, ifa->ifa_name, aa->argc, aa->argv)); } } } return 0; } static int setup_vlans(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa, struct ifaddrs* ifap) { rtems_bsd_rc_conf_argc_argv* vaa; struct ifaddrs* ifa; vaa = rtems_bsd_rc_conf_argc_argv_create(); if (vaa == NULL) return -1; show_result("create_args", load_create_args(rc_conf, aa)); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcasecmp("lo0", ifa->ifa_name) != 0) { char expr[128]; int r; /* * Look for vlans_'iface'="101 102 103" */ snprintf(expr, sizeof(expr), "vlans_%s", ifa->ifa_name); r = rtems_bsd_rc_conf_find(rc_conf, expr, aa); if (r == 0) { int arg; for (arg = 1; arg < aa->argc; ++arg) { char vlan_name[64]; const char* vlan_create[] = { "ifconfig", vlan_name, "create", NULL }; /* * Create the VLAN name as 'iface'.'vlan'. */ snprintf(vlan_name, sizeof(vlan_name), "%s.%s", ifa->ifa_name, aa->argv[arg]); rtems_bsd_rc_conf_print_cmd(rc_conf, "vlan", 3, vlan_create); r = rtems_bsd_command_ifconfig(3, (char**) vlan_create); if (r == 0) { /* * Look for ifconfig_'iface'_'vlan'="..." */ snprintf(expr, sizeof(expr), "ifconfig_%s_%s", ifa->ifa_name, aa->argv[arg]); r = rtems_bsd_rc_conf_find(rc_conf, expr, vaa); if (r == 0) { show_result(vlan_name, ifconfig_(rc_conf, vlan_name, vaa->argc, vaa->argv)); } } } } } } rtems_bsd_rc_conf_argc_argv_destroy(vaa); return 0; } static int interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) { struct ifaddrs* ifap; if (getifaddrs(&ifap) != 0) { fprintf(stderr, "error: interfaces: getifaddrs: %s\n", strerror(errno)); return -1; } show_interfaces("Starting network: ", ifap); show_result("cloned_interfaces", cloned_interfaces(rc_conf, aa)); show_result("lo0", setup_lo0(rc_conf, ifap)); show_result("ifaces", setup_interfaces(rc_conf, aa, ifap)); show_result("vlans", setup_vlans(rc_conf, aa, ifap)); free(ifap); return 0; } static int network_service(rtems_bsd_rc_conf* rc_conf) { rtems_bsd_rc_conf_argc_argv* aa; int r; aa = rtems_bsd_rc_conf_argc_argv_create(); if (aa == NULL) return -1; show_result("hostname", hostname(rc_conf, aa)); r = interfaces(rc_conf, aa); if (r < 0) { rtems_bsd_rc_conf_argc_argv_destroy(aa); return -1; } show_result("defaultrouter", defaultrouter(rc_conf, aa)); rtems_bsd_rc_conf_argc_argv_destroy(aa); return 0; } void rc_conf_net_init(void* arg) { int r; r = rtems_bsd_rc_conf_service_add("network", "after:first;", network_service); if (r < 0) fprintf(stderr, "error: network service add failed: %s\n", strerror(errno)); }