aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Christian Hesse <mail@eworm.de>2013-10-31 16:19:05 +0100
committerGravatar Christian Hesse <mail@eworm.de>2013-10-31 16:19:05 +0100
commitf0ec293fee4f149e0234d0bfccdd6189145370e8 (patch)
treeb914cfefe849b426305825728f64e71bab176099
parent81f33f4d6f59ca1d27d767ed3619ed0e2e73e0d7 (diff)
downloaddyndhcpd-f0ec293fee4f149e0234d0bfccdd6189145370e8.tar.gz
dyndhcpd-f0ec293fee4f149e0234d0bfccdd6189145370e8.tar.zst
initial commit
-rw-r--r--.gitignore5
-rw-r--r--Makefile40
-rw-r--r--config.def.h20
-rw-r--r--dhcpd.conf33
-rw-r--r--dyndhcpd.c244
-rw-r--r--dyndhcpd@.service9
6 files changed, 351 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..22d4a5c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*~
+*.o
+dyndhcpd
+config.h
+README.html
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9ad0a87
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+# dyndhcpd - Dynamically start DHCP Daemon
+
+CC := gcc
+MD := markdown
+INSTALL := install
+RM := rm
+CP := cp
+CFLAGS += -O2 -Wall -Werror
+VERSION := $(shell git describe --tags --long 2>/dev/null)
+# this is just a fallback in case you do not use git but downloaded
+# a release tarball...
+ifeq ($(VERSION),)
+VERSION := 0.0.1
+endif
+
+all: dyndhcpd README.html
+
+config.h:
+ $(CP) config.def.h config.h
+
+dyndhcpd: dyndhcpd.c config.h
+ $(CC) $(CFLAGS) -o dyndhcpd dyndhcpd.c \
+ -DVERSION="\"$(VERSION)\""
+
+README.html: README.md
+ $(MD) README.md > README.html
+
+install: install-bin install-doc
+
+install-bin: netlink-notify
+ $(INSTALL) -D -m0755 dyndhcpd $(DESTDIR)/usr/bin/dyndhcpd
+ $(INSTALL) -D -m0644 dyndhcpd@.service $(DESTDIR)/usr/lib/systemd/system/dyndhcpd@.service
+ $(INSTALL) -D -m0644 dhcpd.conf $(DESTDIR)/etc/dyndhcpd/dhcpd.conf
+
+install-doc: README.html
+ $(INSTALL) -D -m0644 README.md $(DESTDIR)/usr/share/doc/netlink-notify/README.md
+ $(INSTALL) -D -m0644 README.html $(DESTDIR)/usr/share/doc/netlink-notify/README.html
+
+clean:
+ $(RM) -f *.o *~ dyndhcpd README.html
diff --git a/config.def.h b/config.def.h
new file mode 100644
index 0000000..367f05c
--- /dev/null
+++ b/config.def.h
@@ -0,0 +1,20 @@
+/*
+ * (C) 2013 by Christian Hesse <mail@eworm.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/* use this for domain if gethostbyname() fails */
+#define FALLBACKDOMAIN "localdomain"
+
+/* pathes for config file, first is read, second is written */
+#define CONFIG_TEMPLATE "/etc/dyndhcpd/dhcpd.conf"
+#define CONFIG_OUTPUT "/tmp/dhcpd_%s.conf"
+
+#endif /* _CONFIG_H */
+
+// vim: set syntax=c:
diff --git a/dhcpd.conf b/dhcpd.conf
new file mode 100644
index 0000000..6ec2a13
--- /dev/null
+++ b/dhcpd.conf
@@ -0,0 +1,33 @@
+# dhcpd.conf for interface __INTERFACE__
+# generated by dyndhcpd/__VERSION__
+authoritative;
+ddns-update-style none;
+ignore client-updates;
+default-lease-time 21600;
+max-lease-time 43200;
+
+option domain-name "__DOMAINNAME__";
+
+allow booting;
+allow bootp;
+
+subnet __NETADDRESS__ netmask __NETMASK__ {
+ option broadcast-address __BROADCAST__;
+ option routers __ADDRESS__;
+ option domain-name-servers __ADDRESS__;
+ option time-servers __ADDRESS__;
+
+ range dynamic-bootp __MINHOST__ __MAXHOST__;
+}
+
+class "PXEClient" {
+ match if substring(option vendor-class-identifier, 0, 9) = "PXEClient";
+ next-server __ADDRESS__;
+
+ # Options for iPXE
+ if exists user-class and option user-class = "iPXE" {
+ filename "http://__ADDRESS__:3928/default.ipxe";
+ } else {
+ filename "/undionly.kpxe";
+ }
+}
diff --git a/dyndhcpd.c b/dyndhcpd.c
new file mode 100644
index 0000000..7316f5c
--- /dev/null
+++ b/dyndhcpd.c
@@ -0,0 +1,244 @@
+/*
+ * (C) 2013 by Christian Hesse <mail@eworm.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This is an example code skeleton provided by vim-skeleton.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "config.h"
+
+/*** str_replace ***/
+char * str_replace(char * original, const char * pattern, const char * replacement) {
+ size_t const replen = strlen(replacement);
+ size_t const patlen = strlen(pattern);
+ size_t const orilen = strlen(original);
+
+ size_t patcnt = 0;
+ const char * oriptr;
+ const char * patloc;
+
+ /* find how many times the pattern occurs in the original string */
+ for (oriptr = original; (patloc = strstr(oriptr, pattern)); oriptr = patloc + patlen)
+ patcnt++;
+
+ /* allocate memory for the new string */
+ size_t const retlen = orilen + patcnt * (replen - patlen);
+ char * const returned = (char *) malloc(sizeof(char) * (retlen + 1));
+
+ if (returned != NULL) {
+ /* copy the original string,
+ * replacing all the instances of the pattern */
+ char * retptr = returned;
+ for (oriptr = original; (patloc = strstr(oriptr, pattern)); oriptr = patloc + patlen) {
+ size_t const skplen = patloc - oriptr;
+ /* copy the section until the occurence of the pattern */
+ strncpy(retptr, oriptr, skplen);
+ retptr += skplen;
+ /* copy the replacement */
+ strncpy(retptr, replacement, replen);
+ retptr += replen;
+ }
+ /* copy the rest of the string */
+ strcpy(retptr, oriptr);
+ free(original);
+ return returned;
+ }
+ return NULL;
+}
+
+/*** main ***/
+int main(int argc, char ** argv) {
+ int i, rc = EXIT_FAILURE;
+
+ struct ifaddrs *ifaddr = NULL, *ifa;
+ struct sockaddr_in * s4;
+ struct in_addr * v_host, * v_mask;
+ struct in_addr s_broadcast, s_netaddress, s_minhost, s_maxhost;
+ char c_address[INET_ADDRSTRLEN], c_netmask[INET_ADDRSTRLEN], c_netaddress[INET_ADDRSTRLEN], c_broadcast[INET_ADDRSTRLEN], c_minhost[INET_ADDRSTRLEN], c_maxhost[INET_ADDRSTRLEN];
+ char * interface = NULL;
+
+ char hostname[254];
+ char * domainname;
+ struct hostent *hp;
+
+ size_t fsize;
+ char * config = NULL;
+ char * filename = NULL;
+ FILE * configfile;
+
+ printf("Starting dyndhcpd/" VERSION " (compiled: " __DATE__ ", " __TIME__ ")\n");
+
+ for (i = 1; i < argc; i++) {
+ switch ((int)argv[i][0]) {
+ case '-':
+ switch ((int)argv[i][1]) {
+ case 'h':
+ fprintf(stderr, "usage: %s [-h] [-iINTERFACE]\n", argv[0]);
+ return EXIT_SUCCESS;
+ case 'i':
+ interface = argv[i] + 2;
+ if (strlen(interface) == 0) {
+ fprintf(stderr, "No interface given!\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ default:
+ fprintf(stderr, "unknown option: '%s'\n", argv[i]);
+ return EXIT_FAILURE;
+ }
+ break;
+ default:
+ fprintf(stderr, "unknown command line argument: '%s'\n", argv[i]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (getuid() > 0) {
+ fprintf(stderr, "You need to be root!\n");
+ goto out;
+ }
+
+ gethostname(hostname, 254);
+ hp = gethostbyname(hostname);
+ if ((domainname = strchr(hp->h_name, '.')) != NULL)
+ domainname++;
+ else {
+ fprintf(stderr, "Could not get domainname, using '" FALLBACKDOMAIN "\n");
+ domainname = FALLBACKDOMAIN;
+ }
+
+ if (interface == NULL) {
+ fprintf(stderr, "No interface given!\n");
+ return EXIT_FAILURE;
+ }
+
+ if (getifaddrs(&ifaddr) == -1) {
+ fprintf(stderr, "getifaddrs() failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (strcmp(interface, ifa->ifa_name) != 0)
+ continue;
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (!(ifa->ifa_flags & IFF_UP))
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+
+ s4 = (struct sockaddr_in *)ifa->ifa_addr;
+ v_host = &s4->sin_addr;
+
+ if (!inet_ntop(ifa->ifa_addr->sa_family, v_host, c_address, INET_ADDRSTRLEN))
+ fprintf(stderr, "%s: inet_ntop failed!\n", ifa->ifa_name);
+
+ s4 = (struct sockaddr_in *)ifa->ifa_netmask;
+ v_mask = &s4->sin_addr;
+
+ if (!inet_ntop(ifa->ifa_netmask->sa_family, v_mask, c_netmask, INET_ADDRSTRLEN))
+ fprintf(stderr, "%s: inet_ntop failed!\n", ifa->ifa_name);
+
+ s_broadcast.s_addr = v_host->s_addr |~ v_mask->s_addr;
+ s_netaddress.s_addr = v_host->s_addr & v_mask->s_addr;
+
+ if (ntohl(s_broadcast.s_addr) - ntohl(v_host->s_addr) < ntohl(v_host->s_addr) - ntohl(s_netaddress.s_addr)) {
+ s_minhost.s_addr = htonl(ntohl(s_netaddress.s_addr) + 1);
+ s_maxhost.s_addr = htonl(ntohl(v_host->s_addr) - 1);
+ } else {
+ s_minhost.s_addr = htonl(ntohl(v_host->s_addr) + 1);
+ s_maxhost.s_addr = htonl(ntohl(s_broadcast.s_addr) - 1);
+ }
+
+ if (inet_ntop(AF_INET, &s_broadcast, c_broadcast, INET_ADDRSTRLEN) != NULL &&
+ inet_ntop(AF_INET, &s_netaddress, c_netaddress, INET_ADDRSTRLEN) != NULL &&
+ inet_ntop(AF_INET, &s_minhost, c_minhost, INET_ADDRSTRLEN) != NULL &&
+ inet_ntop(AF_INET, &s_maxhost, c_maxhost, INET_ADDRSTRLEN) != NULL) {
+ printf("Interface: %s\n"
+ "Domain: %s\n"
+ "Host Address: %s\n"
+ "Network Address: %s\n"
+ "Broadcast: %s\n"
+ "Netmask: %s\n"
+ "Hosts: %s - %s\n", interface, domainname, c_address, c_netaddress, c_broadcast, c_netmask, c_minhost, c_maxhost);
+
+
+ /* read the template */
+ if ((configfile = fopen(CONFIG_TEMPLATE, "r")) == NULL) {
+ fprintf(stderr, "Failed reading config template.\n");
+ goto out;
+ }
+ fseek(configfile, 0, SEEK_END);
+ fsize = ftell(configfile);
+ fseek(configfile, 0, SEEK_SET);
+
+ config = malloc(fsize + 1);
+ fread(config, fsize, 1, configfile);
+ fclose(configfile);
+ config[fsize] = 0;
+
+ if ((config = str_replace(config, "__INTERFACE__", interface)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__VERSION__", VERSION)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__DOMAINNAME__", domainname)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__ADDRESS__", c_address)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__NETADDRESS__", c_netaddress)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__BROADCAST__", c_broadcast)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__NETMASK__", c_netmask)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__MINHOST__", c_minhost)) == NULL)
+ goto out;
+ if ((config = str_replace(config, "__MAXHOST__", c_maxhost)) == NULL)
+ goto out;
+
+ filename = malloc(strlen(CONFIG_OUTPUT) + strlen(interface) + 1);
+ sprintf(filename, CONFIG_OUTPUT, interface);
+ if ((configfile = fopen(filename, "w")) == NULL) {
+ fprintf(stderr, "Failed opening config file for writing.\n");
+ goto out;
+ }
+ fputs(config, configfile);
+ fclose(configfile);
+
+ execlp("/usr/bin/dhcpd", "dhcpd", "-f", "-4", "-q", "-cf", filename, interface, NULL);
+
+ rc = EXIT_SUCCESS;
+ goto out;
+ } else {
+ fprintf(stderr, "Failed converting number to string\n");
+ goto out;
+ }
+
+ }
+
+ fprintf(stderr, "Interface not found or no address.\n");
+
+out:
+ if (filename != NULL)
+ free(filename);
+ if (config != NULL)
+ free(config);
+ if (ifaddr != NULL)
+ freeifaddrs(ifaddr);
+
+ return rc;
+}
+
+// vim: set syntax=c:
diff --git a/dyndhcpd@.service b/dyndhcpd@.service
new file mode 100644
index 0000000..397116d
--- /dev/null
+++ b/dyndhcpd@.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=IPv4 DHCP server for interface %i
+After=network.target netctl@%i.service
+
+[Service]
+ExecStart=/usr/bin/dyndhcpd -i%i
+
+[Install]
+WantedBy=multi-user.target