From c9657e695068b4325ba49b7d039021b14728a321 Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Wed, 20 Sep 2017 06:40:48 -0700 Subject: [PATCH 1/7] Consildate Ipv6 Features for Upstream --- Makefile | 3 + lib/nerves_network_interface.ex | 32 ++ src/erlcmd.c | 15 +- src/netif.c | 886 +++++++++++++++++++++++++++++++- 4 files changed, 923 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index e345628..62430e3 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,9 @@ else CFLAGS += -std=gnu99 endif +#We need this for netinet/in.h IN6_* macros +CFLAGS += -D_GNU_SOURCE + # If not cross-compiling, then run sudo by default ifeq ($(origin CROSSCOMPILE), undefined) SUDO_ASKPASS ?= /usr/bin/ssh-askpass diff --git a/lib/nerves_network_interface.ex b/lib/nerves_network_interface.ex index 3d2870f..6e898fd 100644 --- a/lib/nerves_network_interface.ex +++ b/lib/nerves_network_interface.ex @@ -33,6 +33,11 @@ defmodule Nerves.NetworkInterface do need to change any parameters or bring up or down an interface, you should ensure that the port process is running as a privileged user. """ + + @type interface_name :: String.t + @type registration :: + {:ok, pid} | + {:error, {:already_registered, pid}} @doc """ Return the list of network interfaces on this machine. @@ -93,4 +98,31 @@ defmodule Nerves.NetworkInterface do Returns `:ok` on success or `{:error, reason}` if an error occurs. """ defdelegate setup(ifname, options), to: Nerves.NetworkInterface.Worker + + @doc """ + Register for Nerves.NetworkInterface events on a specific interface + + The calling process is the process that will be registered for + all events. The events can be handled by implementing a `handle_info\2`. + + `def handle_info({Nerves.NetworkInterface, :ifchanged, ifstate} = event, state)` + + Use :all to register for events from all interfaces. + + The registration registers for messages dispatched out of `Registry`. + + For information on how `Registry` works please see that module's + documentation. + """ + @spec register(:all | interface_name) :: + registration | + [registration] | + [] + def register(:all) do + Enum.each(interfaces(), ®ister/1) + end + + def register(ifname) do + Registry.register(Nerves.NetworkInterface, ifname, []) + end end diff --git a/src/erlcmd.c b/src/erlcmd.c index b781ec5..e2dd570 100644 --- a/src/erlcmd.c +++ b/src/erlcmd.c @@ -250,8 +250,21 @@ int erlcmd_encode_errno_error(char *buf, int *index, int err) case EROFS: reason = "erofs"; break; case EMLINK: reason = "emlink"; break; case EPIPE: reason = "epipe"; break; + case ENETDOWN: reason = "enetdown"; break; + case ENETUNREACH: reason = "enetunreach"; break; + case ENETRESET: reason = "enetreset"; break; + case ECONNABORTED: reason = "econnaborted"; break; + case ECONNRESET: reason = "econnreset"; break; case EADDRNOTAVAIL: reason = "eaddrnotavail"; break; - default: reason = "unknown"; break; + case ENOBUFS: reason = "enobufs"; break; + case EISCONN: reason = "eisconn"; break; + case ENOTCONN: reason = "enotconn"; break; + case ETOOMANYREFS: reason = "etoomanyrefs"; break; + case ETIMEDOUT: reason = "etimedout"; break; + case ECONNREFUSED: reason = "econnrefused"; break; + case EHOSTDOWN: reason = "ehostdown"; break; + case EHOSTUNREACH: reason = "ehostunreach"; break; + default: reason = "unknown"; break; } return erlcmd_encode_error_tuple(buf, index, reason); } diff --git a/src/netif.c b/src/netif.c index 9581199..5d367c5 100644 --- a/src/netif.c +++ b/src/netif.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include // In Ubuntu 16.04, it seems that the new compat logic handling is preventing // IFF_LOWER_UP from being defined properly. It looks like a bug, so define it @@ -60,6 +62,9 @@ struct netif { // AF_INET socket for ioctls int inet_fd; + // AF_INET6 socket for ioctls + int inet6_fd; + // Netlink buffering char nlbuf[8192]; // See MNL_SOCKET_BUFFER_SIZE @@ -103,6 +108,10 @@ static void netif_init(struct netif *nb) if (nb->inet_fd < 0) err(EXIT_FAILURE, "socket"); + nb->inet6_fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (nb->inet6_fd < 0) + err(EXIT_FAILURE, "socket6"); + nb->seq = 1; } @@ -202,6 +211,11 @@ static void encode_kv_string(struct netif *nb, const char *key, const char *str) ei_encode_atom(nb->resp, &nb->resp_index, key); encode_string(nb->resp, &nb->resp_index, str); } +static void encode_kv_atom(struct netif *nb, const char *key, const char *str) +{ + ei_encode_atom(nb->resp, &nb->resp_index, key); + ei_encode_atom(nb->resp, &nb->resp_index, str); +} static void encode_kv_macaddr(struct netif *nb, const char *key, const unsigned char *macaddr) { @@ -565,6 +579,25 @@ static int check_default_gateway(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } +static int check_default_gateway6(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RTA_MAX + 1]; + memset(tb, 0, sizeof(tb)); + struct rtmsg *rm = mnl_nlmsg_get_payload(nlh); + mnl_attr_parse(nlh, sizeof(*rm), collect_route_attrs, tb); + + struct fdg_data *fdg = (struct fdg_data *) data; + if (rm->rtm_scope == 0 && + tb[RTA_OIF] && + (int) mnl_attr_get_u32(tb[RTA_OIF]) == fdg->oif && + tb[RTA_GATEWAY]) { + // Found it. + inet_ntop(AF_INET6, mnl_attr_get_payload(tb[RTA_GATEWAY]), fdg->result, INET6_ADDRSTRLEN); + } + + return MNL_CB_OK; +} + static void find_default_gateway(struct netif *nb, int oif, char *result) @@ -602,6 +635,41 @@ static void find_default_gateway(struct netif *nb, err(EXIT_FAILURE, "mnl_socket_recvfrom"); } +static void find_default_gateway6(struct netif *nb, + int oif, + char *result) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(nb->nlbuf); + struct rtmsg *rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); + unsigned int portid = 0; + unsigned int seq = 0; + ssize_t ret = 0; + struct fdg_data fdg = { .oif = oif, .result = result }; + + nlh->nlmsg_type = RTM_GETROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = nb->seq++; + + rtm->rtm_family = AF_INET6; + + if (mnl_socket_sendto(nb->nl, nlh, nlh->nlmsg_len) < 0) + err(EXIT_FAILURE, "mnl_socket_send"); + + mnl_socket_get_portid(nb->nl); + + fdg.result[0] = '\0'; + + ret = mnl_socket_recvfrom(nb->nl, nb->nlbuf, sizeof(nb->nlbuf)); + while (ret > 0) { + ret = mnl_cb_run(nb->nlbuf, ret, seq, portid, check_default_gateway6, &fdg); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nb->nl, nb->nlbuf, sizeof(nb->nlbuf)); + } + if (ret == -1) + err(EXIT_FAILURE, "mnl_socket_recvfrom"); +} + struct ip_setting_handler { const char *name; int (*prep)(const struct ip_setting_handler *handler, struct netif *nb, void **context); @@ -619,20 +687,54 @@ static int get_mac_address_ioctl(const struct ip_setting_handler *handler, struc static int prep_ipaddr_ioctl(const struct ip_setting_handler *handler, struct netif *nb, void **context); static int set_ipaddr_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); static int get_ipaddr_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int set_ipaddr6_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_ipaddr6(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int remove_ipaddr6_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int prep_ipaddr6_ioctl(const struct ip_setting_handler *handler, struct netif *nb, void **context); static int prep_default_gateway(const struct ip_setting_handler *handler, struct netif *nb, void **context); static int set_default_gateway(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); static int get_default_gateway(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int prep_default_gateway6(const struct ip_setting_handler *handler, struct netif *nb, void **context); +static int set_default_gateway6(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_default_gateway6(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int prep_ipv6_autoconf(const struct ip_setting_handler *handler, struct netif *nb, void **context); +static int set_ipv6_autoconf(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_ipv6_autoconf(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int prep_ipv6_accept_ra(const struct ip_setting_handler *handler, struct netif *nb, void **context); +static int set_ipv6_accept_ra(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_ipv6_accept_ra(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int ifname_to_index(struct netif *nb, const char *ifname); +static int add_default_gateway6(struct netif *nb, const char *ifname, const char *gateway_ip); +static int prep_ipv6_disable(const struct ip_setting_handler *handler, struct netif *nb, void **context); +static int set_ipv6_disable(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_ipv6_disable(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int prep_ipv6_accept_ra_pinfo(const struct ip_setting_handler *handler, struct netif *nb, void **context); +static int set_ipv6_accept_ra_pinfo(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_ipv6_accept_ra_pinfo(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); +static int prep_ipv6_forwarding(const struct ip_setting_handler *handler, struct netif *nb, void **context); +static int set_ipv6_forwarding(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context); +static int get_ipv6_forwarding(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname); + + +static const struct ip_handlers_descr *find_descriptor(const char *name); // These handlers are listed in the order that they should be invoked when // configuring the interface. For example, "ipv4_gateway" is listed at the end // so that it is set after the address and subnet_mask. If this is not done, // setting the gateway may fail since Linux thinks that it is on the wrong subnet. static const struct ip_setting_handler handlers[] = { - { "ipv4_address", prep_ipaddr_ioctl, set_ipaddr_ioctl, get_ipaddr_ioctl, SIOCSIFADDR, SIOCGIFADDR }, - { "ipv4_subnet_mask", prep_ipaddr_ioctl, set_ipaddr_ioctl, get_ipaddr_ioctl, SIOCSIFNETMASK, SIOCGIFNETMASK }, - { "ipv4_broadcast", prep_ipaddr_ioctl, set_ipaddr_ioctl, get_ipaddr_ioctl, SIOCSIFBRDADDR, SIOCGIFBRDADDR }, - { "ipv4_gateway", prep_default_gateway, set_default_gateway, get_default_gateway, 0, 0 }, - { "mac_address", prep_mac_address_ioctl, set_mac_address_ioctl, get_mac_address_ioctl, SIOCSIFHWADDR, SIOCGIFHWADDR }, + { "ipv4_address", prep_ipaddr_ioctl, set_ipaddr_ioctl, get_ipaddr_ioctl, SIOCSIFADDR, SIOCGIFADDR }, + { "ipv6_address", prep_ipaddr6_ioctl, set_ipaddr6_ioctl, get_ipaddr6, SIOCSIFADDR, 0 }, + { "ipv4_subnet_mask", prep_ipaddr_ioctl, set_ipaddr_ioctl, get_ipaddr_ioctl, SIOCSIFNETMASK, SIOCGIFNETMASK }, + { "ipv4_broadcast", prep_ipaddr_ioctl, set_ipaddr_ioctl, get_ipaddr_ioctl, SIOCSIFBRDADDR,SIOCGIFBRDADDR }, + { "ipv4_gateway", prep_default_gateway, set_default_gateway, get_default_gateway, 0, 0 }, + { "ipv6_gateway", prep_default_gateway6, set_default_gateway6, get_default_gateway6, 0, 0 }, + { "mac_address", prep_mac_address_ioctl, set_mac_address_ioctl, get_mac_address_ioctl, SIOCSIFHWADDR, SIOCGIFHWADDR }, + { "ipv6_autoconf", prep_ipv6_autoconf, set_ipv6_autoconf, get_ipv6_autoconf, 0, 0 }, + { "ipv6_disable", prep_ipv6_disable, set_ipv6_disable, get_ipv6_disable, 0, 0 }, + { "ipv6_accept_ra", prep_ipv6_accept_ra, set_ipv6_accept_ra, get_ipv6_accept_ra, 0, 0 }, + { "ipv6_accept_ra_pinfo", prep_ipv6_accept_ra_pinfo, set_ipv6_accept_ra_pinfo, get_ipv6_accept_ra_pinfo, 0, 0 }, + { "ipv6_forwarding", prep_ipv6_forwarding, set_ipv6_forwarding, get_ipv6_forwarding, 0, 0 }, }; #define HANDLER_COUNT (sizeof(handlers) / sizeof(handlers[0])) @@ -652,6 +754,411 @@ static int prep_mac_address_ioctl(const struct ip_setting_handler *handler, stru return 0; } +struct ipv6_procfs_ctx { + char token[10]; /* ProcFs atom string true/false/override */ +}; +#define member_size(type, member) sizeof( ((type *) 0)->member) + +static int ipv6_read_integer_from_file(const struct ip_setting_handler *handler, struct netif *nb, const char *fname, int *val) +{ + FILE *f = NULL; + + (void) handler; + + if((f = fopen(fname, "r")) == NULL) { + debug("Unable to open file '%s' for '%s'", fname, handler->name); + nb->last_error = errno; + return -1; + } + + if(fscanf(f, "%d", val) < 0) { + nb->last_error = EIO; + fclose(f); + return -1; + } + + fclose(f); + return 0; +} + +static int ipv6_write_integer_to_file(const struct ip_setting_handler *handler, struct netif *nb, const char *fname, const int val) +{ + FILE *f = NULL; + + (void) handler; + + if ((f = fopen(fname, "w")) == NULL) { + debug("Unable to open file '%s' for '%s'", fname, handler->name); + nb->last_error = errno; + return -1; + } + + if (fprintf(f, "%d", val) < 0) { + nb->last_error = EIO; + fclose(f); + return -1; + } + + fclose(f); + + return 0; +} + + +static int prep_ipv6_autoconf(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + struct ipv6_procfs_ctx *ac_ctx = *context = malloc(sizeof(struct ipv6_procfs_ctx)); + + if(*context == NULL) { + errx(EXIT_FAILURE, "Unable to allocate memory for '%s'", handler->name); + } + + if ( erlcmd_decode_atom(nb->req, &nb->req_index, &ac_ctx->token[0], member_size(struct ipv6_procfs_ctx, token) ) < 0) + errx(EXIT_FAILURE, "Autoconf value true/false required for '%s'", handler->name); + + return 0; +} + +static int prep_ipv6_forwarding(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + struct ipv6_procfs_ctx *ac_ctx = *context = malloc(sizeof(struct ipv6_procfs_ctx)); + + if(*context == NULL) { + errx(EXIT_FAILURE, "Unable to allocate memory for '%s'", handler->name); + } + + if ( erlcmd_decode_atom(nb->req, &nb->req_index, &ac_ctx->token[0], member_size(struct ipv6_procfs_ctx, token) ) < 0) + errx(EXIT_FAILURE, "Autoconf value true/false required for '%s'", handler->name); + + return 0; +} + +static int prep_ipv6_disable(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + struct ipv6_procfs_ctx *ac_ctx = *context = malloc(sizeof(struct ipv6_procfs_ctx)); + + if(*context == NULL) { + errx(EXIT_FAILURE, "Unable to allocate memory for '%s'", handler->name); + } + + if ( erlcmd_decode_atom(nb->req, &nb->req_index, &ac_ctx->token[0], member_size(struct ipv6_procfs_ctx, token) ) < 0) + errx(EXIT_FAILURE, "Autoconf value true/false required for '%s'", handler->name); + + return 0; +} + +static int prep_ipv6_accept_ra_pinfo(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + struct ipv6_procfs_ctx *ac_ctx = *context = malloc(sizeof(struct ipv6_procfs_ctx)); + + if(*context == NULL) { + errx(EXIT_FAILURE, "Unable to allocate memory for '%s'", handler->name); + } + + if ( erlcmd_decode_atom(nb->req, &nb->req_index, &ac_ctx->token[0], member_size(struct ipv6_procfs_ctx, token) ) < 0) + errx(EXIT_FAILURE, "Autoconf value true/false required for '%s'", handler->name); + + return 0; +} + +static int prep_ipv6_accept_ra(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + struct ipv6_procfs_ctx *ac_ctx = *context = malloc(sizeof(struct ipv6_procfs_ctx)); + + if(*context == NULL) { + errx(EXIT_FAILURE, "Unable to allocate memory for '%s'", handler->name); + } + + if ( erlcmd_decode_atom(nb->req, &nb->req_index, &ac_ctx->token[0], member_size(struct ipv6_procfs_ctx, token) ) < 0) + errx(EXIT_FAILURE, "Autoconf value true/false required for '%s'", handler->name); + + return 0; +} + +static int ipv6_bool_atom_to_integer(const struct ip_setting_handler *handler, struct netif *nb, const char *str, int *val) { + if (strcmp(str, "true") == 0) { + *val = 1; + } + else if (strcmp(str, "false") == 0) { + *val = 0; + } + else { + debug("Unsupported value of '%s' for '%s'", str, handler->name); + return -1; + } + + return 0; +} + +static int ipv6_tri_state_atom_to_integer(const struct ip_setting_handler *handler, struct netif *nb, const char *str, int *val) { + if (strcmp(str, "true") == 0) { + *val = 1; + } + else if (strcmp(str, "false") == 0) { + *val = 0; + } + else if (strcmp(str, "override") == 0) { + *val = 2; + } + else { + debug("Unsupported value of '%s' for '%s'", str, handler->name); + return -1; + } + + return 0; +} + +static const char *ipv6_tri_state_integer_to_atom(const int val) { + static const char *atoms[] = { + "false", /* 0 */ + "true", /* 1 */ + "override", /* 2 */ + "" /* 3 */ + }; + + switch(val) { + case 0: + case 1: + case 2: + return atoms[val]; + default: + break; + }; + + return atoms[3]; +} + +static int ipv6_create_procfs_file_name(const struct ip_setting_handler *handler, struct netif *nb, char *dest, const size_t max_len, const char *ifname, const char *sys_fname) +{ + (void) handler; + + if ((unsigned int) snprintf(dest, max_len, "/proc/sys/net/ipv6/conf/%s/%s", ifname, sys_fname) >= max_len) { + debug("The file name truncated! Setting not performed for '%s'", handler->name); + nb->last_error = ENOMEM; + return -1; + } + + return 0; +} + +/* The proc file's path is in the format: /proc/sys/net/ipv6/conf //accept_ra - hence we need + * IFNAMSIZ + 34 bytes for the remainder '/proc/sys/net/ipv6/conf//accept_ra' +1 for '\0' string termination */ +#define NETIF_IPV6_PROC_FILENAME_MAXLEN (IFNAMSIZ+60+1) + +static int set_ipv6_autoconf(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct ipv6_procfs_ctx *ac_ctx = (struct ipv6_procfs_ctx *) context; + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int autoconf = 0; + + if(ipv6_create_procfs_file_name(handler, nb, fname, sizeof(fname), ifname, "autoconf") < 0) { + return -1; + } + + if (ipv6_bool_atom_to_integer(handler, nb, ac_ctx->token, &autoconf) < 0) { + nb->last_error = EOPNOTSUPP; + return -1; + } + + if(ipv6_write_integer_to_file(handler, nb, fname, autoconf) < 0){ + return -1; + } + + return 0; +} + +static int set_ipv6_forwarding(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct ipv6_procfs_ctx *ac_ctx = (struct ipv6_procfs_ctx *) context; + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int forwarding = 0; + + if(ipv6_create_procfs_file_name(handler, nb, fname, sizeof(fname), ifname, "forwarding") < 0) { + return -1; + } + + if (ipv6_bool_atom_to_integer(handler, nb, ac_ctx->token, &forwarding) < 0) { + nb->last_error = EOPNOTSUPP; + return -1; + } + + if(ipv6_write_integer_to_file(handler, nb, fname, forwarding) < 0){ + return -1; + } + + return 0; +} + +static int set_ipv6_disable(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct ipv6_procfs_ctx *ac_ctx = (struct ipv6_procfs_ctx *) context; + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int disable = 0; + + if(ipv6_create_procfs_file_name(handler, nb, fname, sizeof(fname), ifname, "disable_ipv6") < 0) { + return -1; + } + + if (ipv6_bool_atom_to_integer(handler, nb, ac_ctx->token, &disable) < 0) { + nb->last_error = EOPNOTSUPP; + return -1; + } + + if(ipv6_write_integer_to_file(handler, nb, fname, disable) < 0){ + return -1; + } + + return 0; +} + +static int set_ipv6_accept_ra_pinfo(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct ipv6_procfs_ctx *ac_ctx = (struct ipv6_procfs_ctx *) context; + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int accept_ra_pinfo = 0; + + if(ipv6_create_procfs_file_name(handler, nb, fname, sizeof(fname), ifname, "accept_ra_pinfo") < 0) { + return -1; + } + + if (ipv6_bool_atom_to_integer(handler, nb, ac_ctx->token, &accept_ra_pinfo) < 0) { + nb->last_error = EOPNOTSUPP; + return -1; + } + + if(ipv6_write_integer_to_file(handler, nb, fname, accept_ra_pinfo) < 0){ + return -1; + } + + return 0; +} + +static int get_ipv6_autoconf(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + /* The proc file's path is in the format: /proc/sys/net/ipv6/conf //accept_ra - hence we need + * IFNAMSIZ + 34 bytes for the remainder '/proc/sys/net/ipv6/conf//accept_ra' +1 for '\0' string termination */ + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int autoconf = 0; + + if(ipv6_create_procfs_file_name(handler, nb, &fname[0], sizeof(fname), ifname, "autoconf") < 0) { + debug("[%s %d] generated fname = '%s'\r\n", __FILE__, __LINE__, fname); + return -1; + } + + if(ipv6_read_integer_from_file(handler, nb, fname, &autoconf) < 0) { + return -1; + } + + encode_kv_bool(nb, handler->name, autoconf); + + return 0; +} + +static int get_ipv6_forwarding(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + /* The proc file's path is in the format: /proc/sys/net/ipv6/conf //accept_ra - hence we need + * IFNAMSIZ + 34 bytes for the remainder '/proc/sys/net/ipv6/conf//accept_ra' +1 for '\0' string termination */ + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int forwarding = 0; + + if(ipv6_create_procfs_file_name(handler, nb, &fname[0], sizeof(fname), ifname, "forwarding") < 0) { + debug("[%s %d] generated fname = '%s'\r\n", __FILE__, __LINE__, fname); + return -1; + } + + if(ipv6_read_integer_from_file(handler, nb, fname, &forwarding) < 0) { + return -1; + } + + encode_kv_bool(nb, handler->name, forwarding); + + return 0; +} + +static int get_ipv6_disable(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + /* The proc file's path is in the format: /proc/sys/net/ipv6/conf //accept_ra - hence we need + * IFNAMSIZ + 34 bytes for the remainder '/proc/sys/net/ipv6/conf//accept_ra' +1 for '\0' string termination */ + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int disable = 0; + + if(ipv6_create_procfs_file_name(handler, nb, &fname[0], sizeof(fname), ifname, "disable_ipv6") < 0) { + debug("[%s %d] generated fname = '%s'\r\n", __FILE__, __LINE__, fname); + return -1; + } + + if(ipv6_read_integer_from_file(handler, nb, fname, &disable) < 0) { + return -1; + } + + encode_kv_bool(nb, handler->name, disable); + + return 0; +} + +static int get_ipv6_accept_ra_pinfo(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + /* The proc file's path is in the format: /proc/sys/net/ipv6/conf //accept_ra - hence we need + * IFNAMSIZ + 34 bytes for the remainder '/proc/sys/net/ipv6/conf//accept_ra' +1 for '\0' string termination */ + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int accept_ra_pinfo = 0; + + if(ipv6_create_procfs_file_name(handler, nb, &fname[0], sizeof(fname), ifname, "accept_ra_pinfo") < 0) { + debug("[%s %d] generated fname = '%s'\r\n", __FILE__, __LINE__, fname); + return -1; + } + + if(ipv6_read_integer_from_file(handler, nb, fname, &accept_ra_pinfo) < 0) { + return -1; + } + + encode_kv_bool(nb, handler->name, accept_ra_pinfo); + + return 0; +} + +static int set_ipv6_accept_ra(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct ipv6_procfs_ctx *ac_ctx = (struct ipv6_procfs_ctx *) context; + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int accept_ra = 0; + + if(ipv6_create_procfs_file_name(handler, nb, fname, sizeof(fname), ifname, "accept_ra") < 0) { + return -1; + } + + if (ipv6_tri_state_atom_to_integer(handler, nb, ac_ctx->token, &accept_ra) < 0) { + nb->last_error = EOPNOTSUPP; + return -1; + } + + if(ipv6_write_integer_to_file(handler, nb, fname, accept_ra) < 0){ + return -1; + } + + return 0; +} + +static int get_ipv6_accept_ra(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + /* The proc file's path is in the format: /proc/sys/net/ipv6/conf //accept_ra - hence we need + * IFNAMSIZ + 34 bytes for the remainder '/proc/sys/net/ipv6/conf//accept_ra' +1 for '\0' string termination */ + char fname[NETIF_IPV6_PROC_FILENAME_MAXLEN] = {'\0', }; + int accept_ra = 0; + + if(ipv6_create_procfs_file_name(handler, nb, &fname[0], sizeof(fname), ifname, "accept_ra") < 0) { + return -1; + } + + if(ipv6_read_integer_from_file(handler, nb, fname, &accept_ra) < 0) { + return -1; + } + + encode_kv_atom(nb, handler->name, ipv6_tri_state_integer_to_atom(accept_ra) ); + + return 0; +} + static int set_mac_address_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) { @@ -718,6 +1225,66 @@ static int prep_ipaddr_ioctl(const struct ip_setting_handler *handler, struct ne return 0; } +#define access_setting_handler(ptr) ((const struct ip_setting_handler *) ptr) +static int prep_ipaddr6_ioctl(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + struct in6_ifreq *ifr6 = malloc(sizeof(struct in6_ifreq)); + char *prefix_ptr = (void *) NULL; + char ipaddr[INET6_ADDRSTRLEN] = {0, }; + + if(ifr6 == NULL) { + debug("Unable to allocate memory for '%s'", handler->name); + nb->last_error = ENOMEM; + return -1; + } + + *context = (void *) ifr6; + + memset(ifr6, 0, sizeof(*ifr6)); + + if (erlcmd_decode_string(nb->req, &nb->req_index, ipaddr, INET6_ADDRSTRLEN) < 0) + errx(EXIT_FAILURE, "ip address parameter required for '%s'", handler->name); + + /* Let's check whether prefix was provided as part of the address */ + prefix_ptr = strchr(ipaddr, (int) '/'); + + if(prefix_ptr != NULL) { + char *end_ptr = NULL; + long int retval = 0; + + /* Let's terminate the preceeding IPv6 address so we would be able to parse it later on */ + *prefix_ptr = '\0'; + + /* Parse the prefix */ + retval = strtol(&prefix_ptr[1], &end_ptr, 0); + + /* There were no digits to parse */ + if(&prefix_ptr[1] == end_ptr) { + nb->last_error = EINVAL; + return -1; + } + + /* Of course prefix cannot be a negative number and longer than the IPv6 address itself */ + if((retval < 0) || (retval > 128)) { + nb->last_error = EINVAL; + return -1; + } + + ifr6->ifr6_prefixlen = (int) retval; + } + + if (inet_pton(AF_INET6, ipaddr, &ifr6->ifr6_addr) <= 0) { + debug("Bad IP address for '%s': %s", handler->name, ipaddr); + nb->last_error = EINVAL; + return -1; + } + + /* We are restoring the original '/' caracter we temporarily replaced with '\0' */ + *prefix_ptr = '/'; + + return 0; +} + static int set_ipaddr_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) { const char *ipaddr = (const char *) context; @@ -772,6 +1339,198 @@ static int get_ipaddr_ioctl(const struct ip_setting_handler *handler, struct net return 0; } +static int get_if_index(struct netif *nb, const char *ifname, int * const if_index) +{ + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(nb->inet_fd, SIOGIFINDEX, &ifr) < 0) { + nb->last_error = errno; + return -1; + } + + *if_index = ifr.ifr_ifindex; + return 0; +} + +#if defined(DEBUG) +static void byte_debug(char * caption, void *buf, int len) { + int i = 0; + fprintf(stderr, "======================== %s ========================\r\n", caption); + for(; i < len; i++) + fprintf(stderr, "%02x ", (int) ((unsigned char *) buf)[i]); +} +#else +# define byte_debug(caption, buf, len) +#endif /* DEBUG */ + +static int set_ipaddr6_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct in6_ifreq *ifr6 = (struct in6_ifreq *) context; + + debug("[%s %d]: set_ipaddr6_ioctl\r\n", __FILE__, __LINE__); + + byte_debug("ifr6_addr", &ifr6->ifr6_addr, sizeof(ifr6->ifr6_addr)); + + if(get_if_index(nb, ifname, &ifr6->ifr6_ifindex) != 0) { + debug("Unable to obtain netif index '%s': %d", handler->name, ifr6->ifr6_ifindex); + nb->last_error = EINVAL; + return -1; + } + + debug("netif index '%s': %d", handler->name, ifr6->ifr6_ifindex); + + if (ioctl(nb->inet6_fd, handler->ioctl_set, ifr6) < 0) { + debug("ioctl(0x%04x) failed for setting '%s': %s", handler->ioctl_set, handler->name, strerror(errno)); + nb->last_error = errno; + return -1; + } + + return 0; +} + +static int remove_ipaddr6_ioctl(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + struct in6_ifreq *ifr6 = (struct in6_ifreq *) context; + + debug("[%s %d]: remove_ipaddr6_ioctl\r\n", __FILE__, __LINE__); + + byte_debug("ifr6_addr", &ifr6->ifr6_addr, sizeof(ifr6->ifr6_addr)); + + if(get_if_index(nb, ifname, &ifr6->ifr6_ifindex) != 0) { + debug("Unable to obtain netif index '%s': %d", handler->name, ifr6->ifr6_ifindex); + nb->last_error = EINVAL; + return -1; + } + + debug("netif index '%s': %d", handler->name, ifr6->ifr6_ifindex); + + if (ioctl(nb->inet6_fd, handler->ioctl_set, ifr6) < 0) { + debug("ioctl(0x%04x) failed for setting '%s': %s", handler->ioctl_set, handler->name, strerror(errno)); + nb->last_error = errno; + return -1; + } + + return 0; +} + +#define SCOPE_STR_MAX_LEN (16) + +static char * get_ip6_address_scope(char * restrict scope_str, const struct in6_addr *addr) { + if(IN6_IS_ADDR_LINKLOCAL(addr)) { + return strncpy(scope_str, "link-local", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_SITELOCAL(addr)) { + return strncpy(scope_str, "site-local", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_UNSPECIFIED(addr)) { + return strncpy(scope_str, "unspecified", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_LOOPBACK(addr)) { + return strncpy(scope_str, "loopback", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_V4MAPPED(addr)) { + return strncpy(scope_str, "v4-mapped", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_V4COMPAT(addr)) { + return strncpy(scope_str, "v4-compat", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_MC_NODELOCAL(addr)) { + return strncpy(scope_str, "node-local", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_MC_LINKLOCAL(addr)) { + return strncpy(scope_str, "link-local", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_MC_SITELOCAL(addr)) { + return strncpy(scope_str, "site-local", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_MC_ORGLOCAL(addr)) { + return strncpy(scope_str, "org-local", SCOPE_STR_MAX_LEN); + } else if(IN6_IS_ADDR_MC_GLOBAL(addr)) { + return strncpy(scope_str, "global", SCOPE_STR_MAX_LEN); + } else { + return strncpy(scope_str, "global", SCOPE_STR_MAX_LEN); + } + return scope_str; +} + +/* Encodes the erlang list of IPv6 addresses' strings in the form of [ a | [b | [c] ] bound to a network interface if the ifname name */ +static int get_ipaddr6(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + FILE *f = fopen("/proc/net/if_inet6", "r"); + struct in6_addr ip6 = in6addr_any; + char parsed_ifname[IFNAMSIZ] = {'\0', }; + char addr_scope[SCOPE_STR_MAX_LEN] = {'\0', }; + unsigned int prefix = 0; + unsigned int scope = 0; + + if(f == NULL) { + debug("[%s %d]: Unable to open file for reading!!", __FILE__, __LINE__); + nb->last_error = errno; + return -1; + } + /* A sample entry for an interface fe80000000000000020c29fffe9c009e 02 40 20 80 eth0 */ + /* 00000000000000000000000000000001 01 80 10 80 lo + +------------------------------+ ++ ++ ++ ++ ++ + | | | | | | + 1 2 3 4 5 6 + + 1. IPv6 address displayed in 32 hexadecimal chars without colons as separator + 2. Netlink device number (interface index) in hexadecimal (see ”ip addr” , too) + 3. Prefix length in hexadecimal + 4. Scope value (see kernel source ” include/net/ipv6.h” and ”net/ipv6/addrconf.c” for more) + 5. Interface flags (see ”include/linux/rtnetlink.h” and ”net/ipv6/addrconf.c” for more) + 6. Device name + */ + ei_encode_atom(nb->resp, &nb->resp_index, handler->name); + while(19 == fscanf(f, "%2hhx%2hhx%2hhx%2hhx""%2hhx%2hhx%2hhx%2hhx""%2hhx%2hhx%2hhx%2hhx""%2hhx%2hhx%2hhx%2hhx""%*x %x %x %*x %s", + &ip6.s6_addr[0], + &ip6.s6_addr[1], + &ip6.s6_addr[2], + &ip6.s6_addr[3], + &ip6.s6_addr[4], + &ip6.s6_addr[5], + &ip6.s6_addr[6], + &ip6.s6_addr[7], + &ip6.s6_addr[8], + &ip6.s6_addr[9], + &ip6.s6_addr[10], + &ip6.s6_addr[11], + &ip6.s6_addr[12], + &ip6.s6_addr[13], + &ip6.s6_addr[14], + &ip6.s6_addr[15], + &prefix, &scope, &parsed_ifname[0])) { + char address[INET6_ADDRSTRLEN] = {'\0', }; + char prefix_str[4] = {'\0', }; + + if(strcmp(parsed_ifname, ifname) != 0) { + debug("[%s %d]: Parsed ifname '%s' neq ifname = '%s' skipping...", __FILE__, __LINE__, parsed_ifname, ifname); + continue; + } + + if(inet_ntop(AF_INET6, &ip6, address, sizeof(address)) == NULL) { + debug("[%s %d]: Invalid IP address skipping...", __FILE__, __LINE__); + continue; + } else { + /* Decode Scope */ + (void) get_ip6_address_scope(&addr_scope[0], &ip6); + debug("[%s %d]: Address scope = '%s'", __FILE__, __LINE__, addr_scope); + } + + sprintf(prefix_str, "/%d", prefix); + + debug("[%s %d]: address = '%s'", __FILE__, __LINE__, address); + debug("[%s %d]: prefix = '%s'", __FILE__, __LINE__, prefix_str); + strcat(address, prefix_str); + debug("[%s %d]: Result address = '%s'", __FILE__, __LINE__, address); + /* Let's concat the prefix length in the IPv6 address string */ + ei_encode_list_header(nb->resp, &nb->resp_index, 1); + ei_encode_map_header(nb->resp, &nb->resp_index, 2); + ei_encode_atom(nb->resp, &nb->resp_index, "address"); + encode_string(nb->resp, &nb->resp_index, address); + ei_encode_atom(nb->resp, &nb->resp_index, "scope"); + encode_string(nb->resp, &nb->resp_index, addr_scope); + } /* while (19 == fscanf(...) */ + ei_encode_empty_list(nb->resp, &nb->resp_index); + + fclose(f); + + return 0; +} + static int remove_all_gateways(struct netif *nb, const char *ifname) { struct rtentry route; @@ -808,6 +1567,35 @@ static int remove_all_gateways(struct netif *nb, const char *ifname) } } +static int remove_all_gateways6(struct netif *nb, const char *ifname) +{ + struct in6_rtmsg route = {0, }; + struct in6_addr *gw = (struct in6_addr *) &route.rtmsg_gateway; + struct in6_addr *dst = (struct in6_addr *) &route.rtmsg_dst; + + *gw = in6addr_any; + *dst = in6addr_any; + + route.rtmsg_dst_len = 0; + route.rtmsg_ifindex = ifname_to_index(nb, ifname); + route.rtmsg_flags = RTF_UP; /* if the RTF_GATEWAY flag is set the gateway ip must exactly mach the one in the fib table */ + route.rtmsg_metric = 0; + + /* There may be more than one gateway. Remove all of them. */ + for (;;) { + int rc = ioctl(nb->inet6_fd, SIOCDELRT, &route); + if (rc < 0) { + if (errno == ESRCH) { + return 0; + } else { + nb->last_error = errno; + debug("Removing GW returnt rc = %d\r\n", rc); + return -1; + } + } + } +} + static int add_default_gateway(struct netif *nb, const char *ifname, const char *gateway_ip) { struct rtentry route; @@ -854,14 +1642,88 @@ static int prep_default_gateway(const struct ip_setting_handler *handler, struct return 0; } +static int prep_default_gateway6(const struct ip_setting_handler *handler, struct netif *nb, void **context) +{ + char gateway[INET6_ADDRSTRLEN] = {'\0', }; + if (erlcmd_decode_string(nb->req, &nb->req_index, gateway, INET6_ADDRSTRLEN) < 0) + errx(EXIT_FAILURE, "ip address parameter required for '%s'", handler->name); + + *context = strdup(gateway); + return 0; +} + +static int set_default_gateway6(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) +{ + (void) handler; + const char *gateway = context; + + // Before one can be set, any configured gateways need to be removed. + if (remove_all_gateways6(nb, ifname) < 0) { + debug("remove_all_gateways6 failed for '%s' : %s", handler->name, gateway); + return -1; + } + + // If no gateway was specified, then we're done. + if (*gateway == '\0') + return 0; + + return add_default_gateway6(nb, ifname, gateway); +} + +static int get_default_gateway6(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname) +{ + int oif = ifname_to_index(nb, ifname); + char gateway_ip[INET6_ADDRSTRLEN] = {'\0', }; + + if (oif < 0) + return -1; + + find_default_gateway6(nb, oif, gateway_ip); + debug("[%s %d]: gateway_ip = '%s'\r\n", __FILE__, __LINE__, gateway_ip); + + // If the gateway isn't found, then the empty string is what we want. + encode_kv_string(nb, handler->name, gateway_ip); + return 0; +} + +static int add_default_gateway6(struct netif *nb, const char *ifname, const char *gateway_ip) +{ + struct in6_rtmsg route = {0, }; + struct in6_addr *dst = (struct in6_addr *) &route.rtmsg_dst; + struct in6_addr *gw = (struct in6_addr *) &route.rtmsg_gateway; + + *dst = in6addr_any; + + if (inet_pton(AF_INET6, gateway_ip, (void *) gw) <= 0) { + debug("Bad IP address for the default gateway v6: %s", gateway_ip); + nb->last_error = EINVAL; + return -1; + } + + route.rtmsg_dst_len = 0; /* router does not have to have a prefix */ + route.rtmsg_flags = RTF_UP | RTF_GATEWAY; + route.rtmsg_metric = 0; + route.rtmsg_ifindex = ifname_to_index(nb, ifname); + + int rc = ioctl(nb->inet6_fd, SIOCADDRT, &route); + if (rc < 0 && errno != EEXIST) { + debug("IOCTL failed for the default gateway v6: %s", gateway_ip); + nb->last_error = errno; + return -1; + } + return 0; +} + static int set_default_gateway(const struct ip_setting_handler *handler, struct netif *nb, const char *ifname, void *context) { (void) handler; const char *gateway = context; // Before one can be set, any configured gateways need to be removed. - if (remove_all_gateways(nb, ifname) < 0) + if (remove_all_gateways(nb, ifname) < 0) { + debug("remove_all_gateways failed for '%s' : %s", handler->name, gateway); return -1; + } // If no gateway was specified, then we're done. if (*gateway == '\0') @@ -903,11 +1765,11 @@ static void netif_handle_get(struct netif *nb, start_response(nb); int original_resp_index = nb->resp_index; - ei_encode_tuple_header(nb->resp, &nb->resp_index, 2); - ei_encode_atom(nb->resp, &nb->resp_index, "ok"); + ei_encode_tuple_header(nb->resp, &nb->resp_index, 2); + ei_encode_atom(nb->resp, &nb->resp_index, "ok"); ei_encode_map_header(nb->resp, &nb->resp_index, HANDLER_COUNT); - nb->last_error = 0; + nb->last_error = 0; for (size_t i = 0; i < HANDLER_COUNT; i++) { const struct ip_setting_handler *handler = &handlers[i]; if (handler->get(handler, nb, ifname) < 0) @@ -927,8 +1789,8 @@ static const struct ip_setting_handler *find_handler(const char *name) const struct ip_setting_handler *handler = &handlers[i]; if (strcmp(handler->name, name) == 0) return handler; - } - return NULL; + } + return NULL; } static void netif_handle_set(struct netif *nb, @@ -958,7 +1820,7 @@ static void netif_handle_set(struct netif *nb, handler->prep(handler, nb, &handler_context[handler - handlers]); else ei_skip_term(nb->req, &nb->req_index); - } + } // If no errors, then set everything if (!nb->last_error) { From 2ebaff72408b22be621d631a417efce810fa259a Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Fri, 16 Feb 2018 06:33:50 -0800 Subject: [PATCH 2/7] Adding configuring the list of interfaces to be managed --- lib/nerves_network_interface.ex | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/nerves_network_interface.ex b/lib/nerves_network_interface.ex index 6e898fd..2f4e887 100644 --- a/lib/nerves_network_interface.ex +++ b/lib/nerves_network_interface.ex @@ -33,7 +33,7 @@ defmodule Nerves.NetworkInterface do need to change any parameters or bring up or down an interface, you should ensure that the port process is running as a privileged user. """ - + @type interface_name :: String.t @type registration :: {:ok, pid} | @@ -98,19 +98,19 @@ defmodule Nerves.NetworkInterface do Returns `:ok` on success or `{:error, reason}` if an error occurs. """ defdelegate setup(ifname, options), to: Nerves.NetworkInterface.Worker - + @doc """ Register for Nerves.NetworkInterface events on a specific interface - + The calling process is the process that will be registered for all events. The events can be handled by implementing a `handle_info\2`. - + `def handle_info({Nerves.NetworkInterface, :ifchanged, ifstate} = event, state)` - + Use :all to register for events from all interfaces. - + The registration registers for messages dispatched out of `Registry`. - + For information on how `Registry` works please see that module's documentation. """ @@ -121,7 +121,7 @@ defmodule Nerves.NetworkInterface do def register(:all) do Enum.each(interfaces(), ®ister/1) end - + def register(ifname) do Registry.register(Nerves.NetworkInterface, ifname, []) end From 26f9551a51165be229cfb8371f196ca5d23f670a Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Fri, 16 Feb 2018 06:37:51 -0800 Subject: [PATCH 3/7] Adding configuring the list of interfaces to be managed --- lib/network_interface/worker.ex | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/network_interface/worker.ex b/lib/network_interface/worker.ex index bd732bd..e5096d8 100644 --- a/lib/network_interface/worker.ex +++ b/lib/network_interface/worker.ex @@ -71,6 +71,7 @@ defmodule Nerves.NetworkInterface.Worker do is_broadcast: boolean, is_lower_up: boolean, is_multicast: boolean, + "is_all-multicast": boolean, is_up: boolean, is_running: boolean, mac_address: mac_address, @@ -125,8 +126,37 @@ defmodule Nerves.NetworkInterface.Worker do { :ok, %Nerves.NetworkInterface.Worker{port: port} } end + #Returns intersection of lists a and b + defp intersect(a, b), do: a -- (a -- b) + + # Returns list of interfaces to be managed by Nerves.NetworkInterface and Nerves.Network modules + # By default this is list of ALL network interfaces available in the system. It can be reduced + # by specifying a list of interfaces we want to be managed by Nerves.Network sub-system in the + # .../config/config.exs file. + defp get_managed_interfaces(available_interfaces) do + managed_interfaces = Application.get_env(:nerves_network_interface, :managed_interfaces, []) + Logger.debug "#{__MODULE__}: managed_interfaces = #{inspect managed_interfaces}" + + case managed_interfaces do + "all" -> available_interfaces + nil -> available_interfaces + [] -> available_interfaces + _ -> managed_interfaces + end + end + + def handle_call(:all_interfaces, _from, state) do + available_interfaces = call_port(state, :interfaces, []) + {:reply, available_interfaces, state } + end + def handle_call(:interfaces, _from, state) do - response = call_port(state, :interfaces, []) + available_interfaces = call_port(state, :interfaces, []) + response = + get_managed_interfaces(available_interfaces) + |> intersect(available_interfaces) + Logger.debug "#{__MODULE__}: response: #{inspect response}" + IO.puts "#{__MODULE__}: response: #{inspect response}" {:reply, response, state } end def handle_call({:status, ifname}, _from, state) do From ab2422a42bdd2fce958344472c2d72d42afa813b Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Fri, 16 Feb 2018 06:40:27 -0800 Subject: [PATCH 4/7] Adding configuration for the list of interfaces to be managed --- config/config.exs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 config/config.exs diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..b4fcb11 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,9 @@ +use Mix.Config + +#config :nerves_network_interface, :managed_interfaces, +# ["eth0", "ens38"] + +# Other possible configurations: +# 1. Manage all available interfaces - explicitly said implicit equivalen of no :managed interfaces provided +# config :nerves_network_interface, :managed_interfaces, +# "all" From a0ff2e5dd8784fb9df1ac2de9fa9653bf57ec83d Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Fri, 16 Feb 2018 06:45:05 -0800 Subject: [PATCH 5/7] Adding the allmulti flag to the network interface --- src/netif.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/netif.c b/src/netif.c index 5d367c5..0468347 100644 --- a/src/netif.c +++ b/src/netif.c @@ -46,9 +46,11 @@ //#define DEBUG #ifdef DEBUG -#define debug(...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\r\n"); } while(0) +#define debug(format, ...) fprintf(stderr, format, __VA_ARGS__); fprintf(stderr, "\r\n") +#define debugf(string) fprintf(stderr, string); fprintf(stderr, "\r\n") #else -#define debug(...) +#define debug(format, ...) +#define debugf(string) #endif struct netif { @@ -274,7 +276,7 @@ static int netif_build_ifinfo(const struct nlmsghdr *nlh, void *data) struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); if (mnl_attr_parse(nlh, sizeof(*ifm), collect_if_attrs, tb) != MNL_CB_OK) { - debug("Error from mnl_attr_parse"); + debugf("Error from mnl_attr_parse"); return MNL_CB_ERROR; } @@ -296,6 +298,7 @@ static int netif_build_ifinfo(const struct nlmsghdr *nlh, void *data) encode_kv_bool(nb, "is_running", ifm->ifi_flags & IFF_RUNNING); encode_kv_bool(nb, "is_lower_up", ifm->ifi_flags & WORKAROUND_IFF_LOWER_UP); encode_kv_bool(nb, "is_multicast", ifm->ifi_flags & IFF_MULTICAST); + encode_kv_bool(nb, "is_all-multicast", ifm->ifi_flags & IFF_ALLMULTI); if (tb[IFLA_MTU]) encode_kv_ulong(nb, "mtu", mnl_attr_get_u32(tb[IFLA_MTU])); @@ -467,7 +470,7 @@ static void netif_handle_status_callback(struct netif *nb, int bytecount) ei_encode_tuple_header(nb->resp, &nb->resp_index, 2); ei_encode_atom(nb->resp, &nb->resp_index, "ok"); if (mnl_cb_run(nb->nlbuf, bytecount, nb->response_seq, nb->response_portid, netif_build_ifinfo, nb) < 0) { - debug("error from or mnl_cb_run?"); + debugf("error from or mnl_cb_run?"); nb->resp_index = original_resp_index; erlcmd_encode_errno_error(nb->resp, &nb->resp_index, errno); } @@ -1202,7 +1205,7 @@ static int get_mac_address_ioctl(const struct ip_setting_handler *handler, struc if (addr->sin_family == AF_UNIX) { encode_kv_macaddr(nb, handler->name, (unsigned char *) &ifr.ifr_hwaddr.sa_data); } else { - debug("got unexpected sin_family %d for '%s'", addr->sin_family, handler->name); + debug("Got unexpected sin_family %d for '%s'", addr->sin_family, handler->name); nb->last_error = EINVAL; return -1; } @@ -1332,7 +1335,7 @@ static int get_ipaddr_ioctl(const struct ip_setting_handler *handler, struct net } encode_kv_string(nb, handler->name, addrstr); } else { - debug("got unexpected sin_family %d for '%s'", addr->sin_family, handler->name); + debug("Got unexpected sin_family %d for '%s'", addr->sin_family, handler->name); nb->last_error = EINVAL; return -1; } @@ -1584,6 +1587,7 @@ static int remove_all_gateways6(struct netif *nb, const char *ifname) /* There may be more than one gateway. Remove all of them. */ for (;;) { int rc = ioctl(nb->inet6_fd, SIOCDELRT, &route); + debug("Removing GW returnt rc = %d\r\n", rc); if (rc < 0) { if (errno == ESRCH) { return 0; @@ -1598,6 +1602,8 @@ static int remove_all_gateways6(struct netif *nb, const char *ifname) static int add_default_gateway(struct netif *nb, const char *ifname, const char *gateway_ip) { + debug("add_default_gateway %s %s", ifname, gateway_ip); + struct rtentry route; memset(&route, 0, sizeof(route)); @@ -1629,6 +1635,8 @@ static int add_default_gateway(struct netif *nb, const char *ifname, const char nb->last_error = errno; return -1; } + + debug("add_default_gateway ok %s", ifname); return 0; } @@ -1719,8 +1727,11 @@ static int set_default_gateway(const struct ip_setting_handler *handler, struct (void) handler; const char *gateway = context; + debug("set_default_gateway %s", ifname); + // Before one can be set, any configured gateways need to be removed. if (remove_all_gateways(nb, ifname) < 0) { + debug("remove_all_gateways failed for '%s' : %s", handler->name, gateway); return -1; } @@ -1864,7 +1875,7 @@ static void netif_request_handler(const char *req, void *cookie) errx(EXIT_FAILURE, "expecting command atom"); if (strcmp(cmd, "interfaces") == 0) { - debug("interfaces"); + debugf("interfaces"); netif_handle_interfaces(nb); } else if (strcmp(cmd, "status") == 0) { if (erlcmd_decode_string(nb->req, &nb->req_index, ifname, IFNAMSIZ) < 0) From c200b8b806e988b9c258906c857fc42b0a0fa915 Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Sun, 18 Feb 2018 10:53:34 +0000 Subject: [PATCH 6/7] Update netif.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing solution to problems like: 'usr/include/linux/if.h:143:8: error: redefinition of ‘struct ifmap’ struct ifmap { ^' --- src/netif.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/netif.c b/src/netif.c index 0468347..c64eddc 100644 --- a/src/netif.c +++ b/src/netif.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include From c5a76fe329a6bd078a42a29e249decab16104395 Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Sun, 18 Feb 2018 11:27:08 +0000 Subject: [PATCH 7/7] Update netif.c revoking the test change --- src/netif.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/netif.c b/src/netif.c index c64eddc..0468347 100644 --- a/src/netif.c +++ b/src/netif.c @@ -27,8 +27,6 @@ #include #include #include -#include -#include #include #include #include