diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2013-11-04 09:48:33 +0100 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2013-11-04 09:48:33 +0100 |
commit | a03e95ff50e9d63f7415c5f67c1b51d250a51ca7 (patch) | |
tree | c5b8ce0dff735edfccec92a9b81f231e25319212 | |
parent | c1fe726970fc85118730f041c1721598eeed2f68 (diff) |
owfd: dhcp: add dhcp helpers skeleton
All known dhcp daemons have horrible interfaces if you want to use them
for short-lived ad-hoc networks like Wifi-P2P. Therefore, we use our own
helper with gdhcp as DHCP implementation.
openwfd_dhcp implements a DHCP server and daemon. Depending in the given
command-line argument, it is run as either. All configuration parameters
are passed via command-line.
Note that openwfd_dhcp is a workaround and should only be used as such.
Once network-managers pick up proper Wifi-P2P APIs, we can remove
openwfd_p2pd (including openwfd_dhcp) and start using their APIs. Until
then, we need this small hack.
This patch only adds the dhcp skeleton. No real functionality is added,
yet.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 24 | ||||
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | src/dhcp.c | 205 | ||||
-rw-r--r-- | src/dhcp.h | 66 | ||||
-rw-r--r-- | src/dhcp_config.c | 354 |
6 files changed, 657 insertions, 1 deletions
@@ -21,6 +21,7 @@ config.status configure libtool m4/ +openwfd_dhcp openwfd_ie openwfd_p2pd stamp-h1 diff --git a/Makefile.am b/Makefile.am index d1aa135..6f78a38 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,7 +62,8 @@ AM_LDFLAGS = \ AM_CPPFLAGS += \ -DBUILD_ENABLE_DEBUG \ - "-DBUILD_BINDIR_WPA_SUPPLICANT=\"/bin\"" + "-DBUILD_BINDIR_WPA_SUPPLICANT=\"/bin\"" \ + "-DBUILD_BINDIR_IP=\"/bin\"" # # SHL - Static Helper Library @@ -142,6 +143,27 @@ openwfd_p2pd_LDADD = \ openwfd_p2pd_LDFLAGS = $(AM_LDFLAGS) # +# openwfd_dhcp +# + +bin_PROGRAMS += openwfd_dhcp + +openwfd_dhcp_SOURCES = \ + src/dhcp.h \ + src/dhcp.c \ + src/dhcp_config.c + +openwfd_dhcp_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(DHCP_CFLAGS) +openwfd_dhcp_LDADD = \ + $(DHCP_LIBS) \ + libgdhcp.la \ + libowfd.la \ + libshl.la +openwfd_dhcp_LDFLAGS = $(AM_LDFLAGS) + +# # Tools # diff --git a/configure.ac b/configure.ac index d1f635f..7883ba3 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,14 @@ AC_SUBST(GDHCP_CFLAGS) AC_SUBST(GDHCP_LIBS) # +# Test for dhcp dependencies +# + +PKG_CHECK_MODULES([DHCP], [glib-2.0]) +AC_SUBST(DHCP_CFLAGS) +AC_SUBST(DHCP_LIBS) + +# # Test for "check" which we use for our test-suite. If not found, we disable # all tests. # diff --git a/src/dhcp.c b/src/dhcp.c new file mode 100644 index 0000000..3894563 --- /dev/null +++ b/src/dhcp.c @@ -0,0 +1,205 @@ +/* + * OpenWFD - Open-Source Wifi-Display Implementation + * + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <glib.h> +#include <netinet/in.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/epoll.h> +#include <sys/signalfd.h> +#include <sys/socket.h> +#include <unistd.h> +#include "dhcp.h" +#include "gdhcp/gdhcp.h" +#include "shl_log.h" + +struct owfd_dhcp { + struct owfd_dhcp_config config; + GMainLoop *loop; + + int sfd; + GIOChannel *sfd_chan; + guint sfd_id; +}; + +static gboolean owfd_dhcp_sfd_fn(GIOChannel *chan, GIOCondition mask, + gpointer data) +{ + struct owfd_dhcp *dhcp = data; + ssize_t l; + struct signalfd_siginfo info; + + if (mask & (G_IO_HUP | G_IO_ERR)) { + log_vEPIPE(); + g_main_loop_quit(dhcp->loop); + return FALSE; + } + + l = read(dhcp->sfd, &info, sizeof(info)); + if (l < 0) { + log_vERRNO(); + g_main_loop_quit(dhcp->loop); + return FALSE; + } else if (l != sizeof(info)) { + log_vEFAULT(); + return TRUE; + } + + log_notice("received signal %d: %s", + info.ssi_signo, strsignal(info.ssi_signo)); + + g_main_loop_quit(dhcp->loop); + return FALSE; +} + +static int owfd_dhcp_run(struct owfd_dhcp *dhcp) +{ + g_main_loop_run(dhcp->loop); + + return 0; +} + +static void owfd_dhcp_teardown(struct owfd_dhcp *dhcp) +{ + if (dhcp->sfd >= 0) { + g_source_remove(dhcp->sfd_id); + g_io_channel_unref(dhcp->sfd_chan); + close(dhcp->sfd); + } + + if (dhcp->loop) + g_main_loop_unref(dhcp->loop); +} + +static void sig_dummy(int sig) +{ +} + +static int owfd_dhcp_setup(struct owfd_dhcp *dhcp) +{ + static const int sigs[] = { + SIGINT, + SIGTERM, + SIGQUIT, + SIGHUP, + SIGPIPE, + 0 + }; + int r, i; + sigset_t mask; + struct sigaction sig; + + dhcp->loop = g_main_loop_new(NULL, FALSE); + + sigemptyset(&mask); + memset(&sig, 0, sizeof(sig)); + sig.sa_handler = sig_dummy; + sig.sa_flags = SA_RESTART; + + for (i = 0; sigs[i]; ++i) { + sigaddset(&mask, sigs[i]); + r = sigaction(sigs[i], &sig, NULL); + if (r < 0) { + r = log_ERRNO(); + goto error; + } + } + + r = sigprocmask(SIG_BLOCK, &mask, NULL); + if (r < 0) { + r = log_ERRNO(); + goto error; + } + + dhcp->sfd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); + if (dhcp->sfd < 0) { + r = log_ERRNO(); + goto error; + } + + dhcp->sfd_chan = g_io_channel_unix_new(dhcp->sfd); + dhcp->sfd_id = g_io_add_watch(dhcp->sfd_chan, + G_IO_HUP | G_IO_ERR | G_IO_IN, + owfd_dhcp_sfd_fn, + dhcp); + + return 0; + +error: + owfd_dhcp_teardown(dhcp); + return r; +} + +int main(int argc, char **argv) +{ + struct owfd_dhcp dhcp; + int r; + + memset(&dhcp, 0, sizeof(dhcp)); + dhcp.sfd = -1; + owfd_dhcp_init_config(&dhcp.config); + + r = owfd_dhcp_parse_argv(&dhcp.config, argc, argv); + if (r < 0) + goto err_out; + + if (dhcp.config.debug) + log_max_sev = LOG_DEBUG; + else if (dhcp.config.verbose) + log_max_sev = LOG_INFO; + else if (dhcp.config.silent) + log_max_sev = LOG_ERROR; + + if (dhcp.config.silent) + log_debug("-"); + else + log_format(LOG_DEFAULT_BASE, NULL, LOG_SEV_NUM, + "openwfd_dhcp - revision %s %s %s", + "some-rev-TODO-xyz", __DATE__, __TIME__); + + log_info("initializing"); + r = owfd_dhcp_setup(&dhcp); + if (r < 0) + goto err_conf; + + log_info("running"); + r = owfd_dhcp_run(&dhcp); + + owfd_dhcp_teardown(&dhcp); +err_conf: + owfd_dhcp_clear_config(&dhcp.config); + if (r < 0) { + errno = -r; + log_error("initialization failed (%d): %m", r); + } + log_info("exiting"); +err_out: + return -r; +} diff --git a/src/dhcp.h b/src/dhcp.h new file mode 100644 index 0000000..64aa261 --- /dev/null +++ b/src/dhcp.h @@ -0,0 +1,66 @@ +/* + * OpenWFD - Open-Source Wifi-Display Implementation + * + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef OWFD_DHCP_H +#define OWFD_DHCP_H + +#include <stdbool.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* config handling */ + +struct owfd_dhcp_config { + unsigned int verbose : 1; + unsigned int silent : 1; + unsigned int debug : 1; + + unsigned int client : 1; + unsigned int server : 1; + + char *interface; + char *ip_binary; + + char *local; + char *gateway; + char *dns; + char *subnet; + char *ip_from; + char *ip_to; +}; + +void owfd_dhcp_init_config(struct owfd_dhcp_config *conf); +void owfd_dhcp_clear_config(struct owfd_dhcp_config *conf); +int owfd_dhcp_parse_argv(struct owfd_dhcp_config *conf, + int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* OWFD_DHCP_H */ diff --git a/src/dhcp_config.c b/src/dhcp_config.c new file mode 100644 index 0000000..28d050c --- /dev/null +++ b/src/dhcp_config.c @@ -0,0 +1,354 @@ +/* + * OpenWFD - Open-Source Wifi-Display Implementation + * + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <getopt.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include "dhcp.h" + +#define LONG_OPT_OFFSET 10000 + +enum { + OPT_HELP, + OPT_VERBOSE, + OPT_SILENT, + OPT_DEBUG, + + OPT_CLIENT, + OPT_SERVER, + + OPT_INTERFACE, + OPT_IP_BINARY, + + OPT_LOCAL, + OPT_GATEWAY, + OPT_DNS, + OPT_SUBNET, + OPT_IP_FROM, + OPT_IP_TO, +}; + +const char short_options[] = ":hvcsi:"; + +#define OPT(_name, _arg, _val) \ + { .name = _name, .has_arg = _arg, .val = LONG_OPT_OFFSET + _val } +const struct option long_options[] = { + OPT("help", 0, OPT_HELP), + OPT("verbose", 0, OPT_VERBOSE), + OPT("silent", 0, OPT_SILENT), + OPT("debug", 0, OPT_DEBUG), + + OPT("client", 0, OPT_CLIENT), + OPT("server", 0, OPT_SERVER), + + OPT("interface", 1, OPT_INTERFACE), + OPT("ip-binary", 1, OPT_IP_BINARY), + + OPT("local", 1, OPT_LOCAL), + OPT("gateway", 1, OPT_GATEWAY), + OPT("dns", 1, OPT_DNS), + OPT("subnet", 1, OPT_SUBNET), + OPT("ip-from", 1, OPT_IP_FROM), + OPT("ip-to", 1, OPT_IP_TO), + + OPT(NULL, 0, 0), +}; +#undef OPT + +void owfd_dhcp_init_config(struct owfd_dhcp_config *conf) +{ + memset(conf, 0, sizeof(*conf)); +} + +void owfd_dhcp_clear_config(struct owfd_dhcp_config *conf) +{ + free(conf->interface); + free(conf->ip_binary); + + free(conf->local); + free(conf->gateway); + free(conf->dns); + free(conf->subnet); + free(conf->ip_from); + free(conf->ip_to); +} + +static void show_help(void) +{ + /* + * Usage/Help information + * This should be scaled to a maximum of 80 characters per line: + * + * 80 char line: + * | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | + * "12345678901234567890123456789012345678901234567890123456789012345678901234567890\n" + * 80 char line starting with tab: + * |10| 20 | 30 | 40 | 50 | 60 | 70 | 80 | + * "\t901234567890123456789012345678901234567890123456789012345678901234567890\n" + */ + fprintf(stderr, + "Usage:\n" + "\t%1$s [options]\n" + "\t%1$s -h [options]\n" + "\n" + "All addresses must be given as IPv6 address. If you want to pass an IPv4\n" + "address, use '::FFFF:<ipv4>' as usual.\n" + "\n" + "General Options:\n" + "\t-h, --help [off] Print this help and exit\n" + "\t-v, --verbose [off] Print verbose messages\n" + "\t --debug [off] Enable debug mode\n" + "\t --silent [off] Suppress notices and warnings\n" + "\n" + "Modus Options:\n" + "\t-c, --client [off] Run as DHCP client\n" + "\t-s, --server [off] Run as DHCP server\n" + "\n" + "Network Options:\n" + "\t-i, --interface <wlan0> [] Wireless interface to run on\n" + "\t --ip-binary </path> [%2$s]\n" + "\t Path to 'ip' binary\n" + "\n" + "Server Options:\n" + "\t --local <addr> [] Local IPv6 address\n" + "\t --gateway <addr> [] Gateway IPv6 address\n" + "\t --dns <addr> [] DNS-Server IPv6 address\n" + "\t --subnet <mask> [] Subnet mask\n" + "\t --ip-from <addr> [] Server IPv6-range start address\n" + "\t --ip-to <addr> [] Server IPv6-range end address\n" + , "openwfd_dhcp", + BUILD_BINDIR_IP "/ip"); + /* + * 80 char line: + * | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | + * "12345678901234567890123456789012345678901234567890123456789012345678901234567890\n" + * 80 char line starting with tab: + * |10| 20 | 30 | 40 | 50 | 60 | 70 | 80 | + * "\t901234567890123456789012345678901234567890123456789012345678901234567890\n" + */ +} + +static int OOM(void) +{ + fprintf(stderr, "out of memory\n"); + return -ENOMEM; +} + +static int verify_address(const char *argname, const char *argval) +{ + int r; + struct in6_addr addr; + + if (!argval) { + fprintf(stderr, "no value given for %s\n", argname); + return -EINVAL; + } + + r = inet_pton(AF_INET6, argval, &addr); + if (r != 1) { + fprintf(stderr, "invalid IPv6 address for %s\n", argname); + return -EINVAL; + } + + return 0; +} + +int owfd_dhcp_parse_argv(struct owfd_dhcp_config *conf, int argc, char **argv) +{ + int c; + bool help = false; + char *t; + int r; + + opterr = 0; + while (1) { + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c <= 0) { + break; + } else if (c == ':') { + fprintf(stderr, "missing argument for: %s\n", + argv[optind - 1]); + return -EINVAL; + } else if (c == '?') { + if (optopt && optopt < LONG_OPT_OFFSET) + fprintf(stderr, "unknown argument: -%c\n", + optopt); + else if (!optopt) + fprintf(stderr, "unknown argument: %s\n", + argv[optind - 1]); + else + fprintf(stderr, "option takes no arg: %s\n", + argv[optind - 1]); + return -EINVAL; + } + +#define OPT(_num) LONG_OPT_OFFSET + _num + switch (c) { + case 'h': + case OPT(OPT_HELP): + help = true; + break; + case 'v': + case OPT(OPT_VERBOSE): + conf->verbose = 1; + break; + case OPT(OPT_SILENT): + conf->silent = 1; + break; + case OPT(OPT_DEBUG): + conf->debug = 1; + break; + + case 'c': + case OPT(OPT_CLIENT): + conf->server = 0; + conf->client = 1; + break; + case 's': + case OPT(OPT_SERVER): + conf->client = 0; + conf->server = 1; + break; + + case 'i': + case OPT(OPT_INTERFACE): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->interface); + conf->interface = t; + break; + case OPT(OPT_IP_BINARY): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->ip_binary); + conf->ip_binary = t; + break; + + case OPT(OPT_LOCAL): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->local); + conf->local = t; + break; + case OPT(OPT_GATEWAY): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->gateway); + conf->gateway = t; + break; + case OPT(OPT_DNS): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->dns); + conf->dns = t; + break; + case OPT(OPT_SUBNET): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->subnet); + conf->subnet = t; + break; + case OPT(OPT_IP_FROM): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->ip_from); + conf->ip_from = t; + break; + case OPT(OPT_IP_TO): + t = strdup(optarg); + if (!t) + return OOM(); + free(conf->ip_to); + conf->ip_to = t; + break; + } +#undef OPT + } + + if (help) { + show_help(); + return -EAGAIN; + } + + if (optind < argc) { + fprintf(stderr, + "unparsed remaining arguments starting with: %s\n", + argv[optind]); + return -EINVAL; + } + + if (!conf->client && !conf->server) { + fprintf(stderr, + "no --client or --server given\n"); + return -EINVAL; + } + + if (!conf->interface) { + fprintf(stderr, "no interface given, use: -i <iface>\n"); + return -EINVAL; + } + + if (!conf->ip_binary) { + conf->ip_binary = strdup(BUILD_BINDIR_IP "/ip"); + if (!conf->ip_binary) + return OOM(); + } + + if (conf->server) { + r = verify_address("--local", conf->local); + if (r < 0) + return r; + r = verify_address("--gateway", conf->gateway); + if (r < 0) + return r; + r = verify_address("--dns", conf->dns); + if (r < 0) + return r; + r = verify_address("--subnet", conf->subnet); + if (r < 0) + return r; + r = verify_address("--ip-from", conf->ip_from); + if (r < 0) + return r; + r = verify_address("--ip-to", conf->ip_to); + if (r < 0) + return r; + } + + return 0; +} |