pax_global_header 0000666 0000000 0000000 00000000064 11670236476 0014526 g ustar 00root root 0000000 0000000 52 comment=690c927eb22d8b0759391024b1d02ac57fcaad7f
ahcpd-0.53/ 0000775 0000000 0000000 00000000000 11670236476 0012534 5 ustar 00root root 0000000 0000000 ahcpd-0.53/CHANGES 0000664 0000000 0000000 00000004215 11670236476 0013531 0 ustar 00root root 0000000 0000000 8 December 2011: ahcpd 0.53
* Fixed a longstanding bug that would cause a client to immediately
deconfigure itself after successfully renewing a lease.
* Set AHCP packets' TOS field as network control (thanks to Dave Taht).
28 July 2011: ahcpd 0.52
* Port to OpenBSD, by Vincent Gross.
* Tweaks for compatibility Mac OS X, thanks to Matthieu Boutier.
* Fixed incorrect testing of the prefix length of received IPv6
prefixes, thanks to Gabriel Kerneis.
29 January 2010: ahcpd 0.51
* Servers are now configured with a plain-text configuration file.
* Implemented client-side support for prefix delegation (-P).
* Made requesting state more persistent, to deal with packet loss.
* Fixed a typo that prevented the -I option from working.
* Fixed compilation on BSD systems.
* Fixed a bug that could cause an expired lease to be discarded when
older ones are available.
11 August 2009: ahcpd 0.50
* Complete redesign of AHCP as a client-server protocol layered over
a built-in multicast routing layer. This protocol (version 1) is
completely incompatible with the old (version 0) protocol.
25 May 2008: ahcpd 0.5
* Made ahcpd gracefully survive interfaces going up/down or being
renumbered, as usually happens when tunnels or VPNs are started or
stopped.
* Implemented the ability to run as a daemon.
* Implemented the ability to try multiple stateful servers.
* Implemented the flag -r, which prevents the routing protocol from
being started.
17 February 2008: ahcpd 0.4
* Implemented stateful configuration for IPv4.
* Tweaked timing to avoid hopping between competing configurations.
* Fixed conversion of Babel hello intervals to seconds.
28 October 2007: ahcpd 0.3
* Moved user-serviceable parts to /etc/ahcp/
* Added support for configuring an NTP server.
* Made ahcp print warnings when a clock inconsistency is detected.
* Fixed error handling when the configuration script dies unexpectedly.
27 September 2007: ahcpd 0.2
* Fixed a bug in computation of validity time that would cause ahcpd
to be much more chatty than necessary.
22 August 2007: ahcpd 0.1
* Initial public release.
ahcpd-0.53/LICENCE 0000664 0000000 0000000 00000002057 11670236476 0013525 0 ustar 00root root 0000000 0000000 Copyright (c) 2007-2009 by Juliusz Chroboczek
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.
ahcpd-0.53/Makefile 0000664 0000000 0000000 00000002572 11670236476 0014202 0 ustar 00root root 0000000 0000000 PREFIX = /usr/local
CDEBUGFLAGS = -Os -g -Wall
DEFINES = $(PLATFORM_DEFINES)
CFLAGS = $(CDEBUGFLAGS) $(DEFINES) $(EXTRA_DEFINES)
SRCS = ahcpd.c monotonic.c transport.c prefix.c configure.c config.c lease.c
OBJS = ahcpd.o monotonic.o transport.o prefix.o configure.o config.o lease.o
LDLIBS = -lrt
ahcpd: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o ahcpd $(OBJS) $(LDLIBS)
.SUFFIXES: .man .html
.man.html:
rman -f html $< | \
sed -e "s|\\(.*(8)\\)|\1|" \
> $@
ahcpd.html: ahcpd.man
.PHONY: all install.minimal install
all: ahcpd
install.minimal: all
mkdir -p $(TARGET)$(PREFIX)/bin/
-rm -f $(TARGET)$(PREFIX)/bin/ahcpd
cp ahcpd $(TARGET)$(PREFIX)/bin/
mkdir -p $(TARGET)/etc/ahcp/
-rm -f $(TARGET)/etc/ahcp/ahcp-config.sh
cp ahcp-config.sh $(TARGET)/etc/ahcp/
chmod +x $(TARGET)/etc/ahcp/ahcp-config.sh
install: all install.minimal
mkdir -p $(TARGET)$(PREFIX)/man/man8/
cp -f ahcpd.man $(TARGET)$(PREFIX)/man/man8/ahcpd.8
.PHONY: uninstall
uninstall:
-rm -f $(TARGET)$(PREFIX)/bin/ahcpd
-rm -f $(TARGET)$(PREFIX)/bin/ahcp-config.sh
-rm -f $(TARGET)$(PREFIX)/bin/ahcp-dummy-config.sh
-rm -f $(TARGET)$(PREFIX)/man/man8/ahcpd.8
.PHONY: clean
clean:
-rm -f ahcpd
-rm -f *.o *~ core TAGS gmon.out
-rm -f ahcpd.html
ahcpd-0.53/README 0000664 0000000 0000000 00000003672 11670236476 0013424 0 ustar 00root root 0000000 0000000 Ahcpd
=====
Ahcpd is a daemon for configuring an IPv6 network using the Ad-Hoc
Configuration Protocol (AHCP). AHCP is designed for wireless mesh
networks, where DHCP and IPv6 autoconfiguration do not work, but could in
principle also be used on wired networks.
Installation
============
$ make
# make install
If compiling for OpenWRT, you will probably want to say something like
$ make CC=mipsel-linux-gcc PLATFORM_DEFINES='-march=mips32'
You can reduce the size of the ahcpd binary by omitting server
functionality; to do so, specify
$ make EXTRA_DEFINES=-DNO_SERVER
Setting up a server
===================
You need to set up one or more authoritative servers in your network. The
server should be run as
$ ahcpd -c /etc/ahcpd.conf wlan0
where the configuration file /etc/ahcpd.conf should say something like
mode server
prefix fde6:20f5:c9ac:358::/64
prefix 192.168.4.128/25
lease-dir /var/lib/leases
name-server fde6:20f5:c9ac:358::1
name-server 192.168.4.1
ntp-server 192.168.4.2
If you want ahcpd to fork into the background, pass it the ``-D'' flag.
The server does not need to run ahcpd as root, as long as it can write the
lease database and the pidfile.
The server should synchronise its clock using NTP. If ahcpd doesn't detect
time synchronisation, it will only give out leases for short periods of
time, and will be extremely conservative about releasing them. Note that
ahcpd actually checks with the kernel for time synchronisation, so most
SNTP clients will not do; you will most probably need Mills' implementation
of NTP.
For redundancy, you may set up multiple servers in a single network as long
as they serve disjoint IPv4 address ranges.
Setting up a client
===================
Unlike the server, the client needs to run ahcpd as root. No configuration
file is needed, just run
# ahcpd wlan0
and you should get IPv4 and IPv6 addresses in a few seconds.
--Juliusz Chroboczek
ahcpd-0.53/ahcp-config.sh 0000775 0000000 0000000 00000005077 11670236476 0015262 0 ustar 00root root 0000000 0000000 #!/bin/sh
die() {
echo "$@" >&2
exit 1
}
findcmd() {
type "$1" > /dev/null 2>&1 || \
die "Couldn't find $1, please install ${2:-it} or fix your path."
}
first() {
echo "$1"
}
nl='
'
usage="Usage: $0 (start|stop)"
debuglevel=${AHCP_DEBUG_LEVEL:-1}
if [ $debuglevel -ge 2 ]; then
env | grep ^AHCP
fi
[ $debuglevel -ge 3 ] && set -x
interfaces="$AHCP_INTERFACES"
if [ -z "$interfaces" ]; then die "No interface set"; fi
first_if=$(first $interfaces)
ipv4_address="$AHCP_IPv4_ADDRESS"
ipv6_address="$AHCP_IPv6_ADDRESS"
platform=`uname`
if [ "$platform" = "Linux" ]; then
findcmd ip iproute
(ip -6 addr show dev lo | grep -q 'inet6') || \
die "No IPv6 address on lo, please modprobe ipv6"
add_ipv6_address() {
ip -6 addr add "$2/$3" dev "$1"
}
del_ipv6_address() {
ip -6 addr del "$2/$3" dev "$1"
}
add_ipv4_address() {
ip addr add "$2/$3" dev "$1"
}
del_ipv4_address() {
ip addr del "$2/$3" dev "$1"
}
else
add_ipv6_address() {
ifconfig "$1" inet6 "$2/$3"
}
del_ipv6_address() {
ifconfig "$1" inet6 "$2/$3" delete
}
add_ipv4_address() {
ifconfig "$1" inet "$2/$3"
}
del_ipv4_address() {
ifconfig "$1" inet "$2/$3" delete
}
fi
add_addresses() {
for i in $interfaces; do
for a in $ipv6_address; do
add_ipv6_address $i $a 128
done
for a in $ipv4_address; do
add_ipv4_address $i $a 32
done
done
}
del_addresses() {
for i in $interfaces; do
for a in $ipv6_address; do
del_ipv6_address $i $a 128
done
for a in $ipv4_address; do
del_ipv4_address $i $a 32
done
done
}
nameserver_start() {
if [ ! -z "$AHCP_NAMESERVER" ]; then
info=''
for n in $AHCP_NAMESERVER; do
info="${info}nameserver $n$nl"
done
if [ -x /sbin/resolvconf ]; then
echo -n "$info" | /sbin/resolvconf -a "$first_if"
else
mv /etc/resolv.conf /etc/resolv.conf.orig
echo -n "$info" > /etc/resolv.conf
fi
fi
}
nameserver_stop() {
if [ ! -z "$AHCP_NAMESERVER" ]; then
if [ -x /sbin/resolvconf ]; then
/sbin/resolvconf -d "$first_if"
else
mv /etc/resolv.conf.orig /etc/resolv.conf
fi
fi
}
case $1 in
start)
add_addresses
nameserver_start
;;
stop)
nameserver_stop
del_addresses
;;
*)
die "$usage"
;;
esac
[ -x /etc/ahcp/ahcp-local.sh ] && /etc/ahcp/ahcp-local.sh $1
exit 0
ahcpd-0.53/ahcpd.c 0000664 0000000 0000000 00000106367 11670236476 0013774 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ahcpd.h"
#include "monotonic.h"
#include "protocol.h"
#include "transport.h"
#include "prefix.h"
#include "config.h"
#include "configure.h"
#include "lease.h"
#define BUFFER_SIZE 2048
struct timeval now;
const struct timeval zero = {0, 0};
static volatile sig_atomic_t exiting = 0, dumping = 0, changed = 0;
struct in6_addr protocol_group;
unsigned int protocol_port = 5359;
int protocol_socket = -1;
unsigned char myid[8];
char *unique_id_file = "/var/lib/ahcpd-unique-id";
int nodns = 0, af = 3, request_prefix_delegation = 0;
char *config_script = "/etc/ahcp/ahcp-config.sh";
int debug = 1;
int do_daemonise = 0;
char *logfile = NULL, *pidfile = "/var/run/ahcpd.pid";
#define CHECK_NETWORKS 1
#define MESSAGE 2
struct network networks[MAXNETWORKS];
int numnetworks;
char *interfaces[MAXNETWORKS + 1];
struct timeval check_networks_time = {0, 0};
struct timeval message_time = {0, 0};
const unsigned char zeroes[16] = {0};
const unsigned char ones[16] =
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static void init_signals(void);
static int check_network(struct network *net);
int ahcp_socket(int port);
int ahcp_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen);
static int send_unicast_packet(unsigned char *server_id,
struct config_data *config, int index,
void *buf, int buflen);
int timeval_compare(const struct timeval *s1, const struct timeval *s2);
static int reopen_logfile(void);
static int daemonise(void);
static void set_timeout(int which, int msecs, int override);
unsigned roughly(unsigned value);
/* Client states */
enum state {
STATE_IDLE, /* we're not a client */
STATE_INIT, /* searching for server */
STATE_REQUESTING, /* found a server */
STATE_RENEWING_UNICAST, /* configured and starting to get nervous */
STATE_RENEWING, /* configured but panicking */
STATE_BOUND /* configured and cool */
};
int
main(int argc, char **argv)
{
char *multicast = "ff02::cca6:c0f9:e182:5359";
char *config_file = NULL;
struct sockaddr_in6 sin6;
int opt, fd, rc, i, net;
unsigned int seed;
enum state state = STATE_IDLE;
int count = 0;
int server_hopcount = 0;
unsigned char selected_server[8];
unsigned char last_ipv4[4] = {0};
unsigned lease_time = 3666;
while(1) {
opt = getopt(argc, argv, "m:p:nN46s:d:i:t:P:c:C:DL:I:");
if(opt < 0)
break;
switch(opt) {
case 'm':
multicast = optarg;
break;
case 'p':
protocol_port = atoi(optarg);
if(protocol_port <= 0 || protocol_port > 0xFFFF)
goto usage;
break;
case 'n':
client_config = 0;
break;
case 'N':
nodns = 1;
break;
case '4':
af = 1;
break;
case '6':
af = 2;
break;
case 's':
config_script = optarg;
break;
case 'd':
debug = atoi(optarg);
break;
case 'i':
unique_id_file = optarg;
break;
case 't':
lease_time = atoi(optarg);
if(lease_time < 300 || lease_time > 365 * 24 * 3600)
goto usage;
break;
case 'P':
request_prefix_delegation = 1;
break;
case 'c':
config_file = optarg;
break;
case 'C':
rc = parse_config_from_string(optarg);
if(rc < 0) {
fprintf(stderr,
"Couldn't parse configuration from command line.\n");
exit(1);
}
break;
case 'D':
do_daemonise = 1;
break;
case 'L':
logfile = optarg;
break;
case 'I':
pidfile = optarg;
break;
default:
goto usage;
}
}
if(optind >= argc)
goto usage;
if(config_file) {
rc = parse_config_from_file(config_file);
if(rc < 0) {
fprintf(stderr,
"Couldn't parse configuration from file %s.\n",
config_file);
exit(1);
}
}
for(i = optind; i < argc; i++) {
if(i - optind >= MAXNETWORKS) {
fprintf(stderr, "Too many interfaces.\n");
exit(1);
}
interfaces[i - optind] = argv[i];
}
numnetworks = i - optind;
interfaces[i - optind] = NULL;
rc = inet_pton(AF_INET6, multicast, &protocol_group);
if(rc <= 0)
goto usage;
time_init();
if(do_daemonise) {
if(logfile == NULL)
logfile = "/var/log/ahcpd.log";
}
rc = reopen_logfile();
if(rc < 0) {
perror("reopen_logfile()");
exit(1);
}
fd = open("/dev/null", O_RDONLY);
if(fd < 0) {
perror("open(null)");
exit(1);
}
rc = dup2(fd, 0);
if(rc < 0) {
perror("dup2(null, 0)");
exit(1);
}
close(fd);
if(do_daemonise) {
rc = daemonise();
if(rc < 0) {
perror("daemonise");
exit(1);
}
}
if(pidfile) {
int pfd, len;
char buf[100];
len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
if(len < 0 || len >= 100) {
perror("snprintf(getpid)");
exit(1);
}
pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
if(pfd < 0) {
perror("creat(pidfile)");
exit(1);
}
rc = write(pfd, buf, len);
if(rc < len) {
perror("write(pidfile)");
goto fail;
}
close(pfd);
}
gettime(&now, NULL);
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0) {
struct timeval tv;
perror("open(random)");
get_real_time(&tv, NULL);
seed = tv.tv_sec ^ tv.tv_usec;
} else {
rc = read(fd, &seed, sizeof(unsigned int));
if(rc < sizeof(unsigned int)) {
perror("read(random)");
goto fail;
}
close(fd);
}
srandom(seed);
myseqno = random();
if(unique_id_file && unique_id_file[0] != '\0') {
fd = open(unique_id_file, O_RDONLY);
if(fd >= 0) {
rc = read(fd, myid, 8);
if(rc == 8) {
close(fd);
goto unique_id_done;
}
close(fd);
}
}
for(i = 0; i < numnetworks; i++) {
rc = if_eui64(interfaces[i], myid);
if(rc >= 0)
goto write_unique_id;
}
rc = random_eui64(myid);
if(rc < 0) {
fprintf(stderr, "Couldn't generate unique id.\n");
goto fail;
}
write_unique_id:
if(unique_id_file && unique_id_file[0] != '\0') {
fd = open(unique_id_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if(fd < 0) {
perror("creat(unique_id)");
} else {
rc = write(fd, myid, 8);
if(rc != 8) {
perror("write(unique_id)");
unlink(unique_id_file);
}
close(fd);
}
}
unique_id_done:
if(server_config) {
#ifndef NO_SERVER
if(server_config->lease_first[0] != 0) {
if(server_config->lease_dir == NULL) {
fprintf(stderr, "No lease directory configured!\n");
goto fail;
}
rc = lease_init(server_config->lease_dir,
server_config->lease_first,
server_config->lease_last,
debug >= 2);
if(rc < 0) {
fprintf(stderr, "Couldn't initialise lease database.\n");
goto fail;
}
}
#else
abort();
#endif
}
protocol_socket = ahcp_socket(protocol_port);
if(protocol_socket < 0) {
perror("ahcp_socket");
goto fail;
}
for(i = 0; i < numnetworks; i++) {
networks[i].ifname = interfaces[i];
check_network(&networks[i]);
if(networks[i].ifindex <= 0) {
fprintf(stderr, "Warning: unknown interface %s.\n",
networks[i].ifname);
continue;
}
}
init_signals();
set_timeout(CHECK_NETWORKS, 30000, 1);
/* The client state machine. */
#define SWITCH(new) \
do { \
debugf(2, "Switching to state %d.\n", new); \
state = new; \
if(state == STATE_IDLE || state == STATE_BOUND) \
set_timeout(MESSAGE, 0, 1); \
else \
set_timeout(MESSAGE, 300, 1); \
count = 0; \
} while(0)
if(client_config) {
server_hopcount = 0;
SWITCH(STATE_INIT);
}
debugf(2, "Entering main loop.\n");
while(1) {
fd_set readfds;
struct timeval tv;
assert((config_data != NULL) == (state == STATE_BOUND ||
state == STATE_RENEWING_UNICAST ||
state == STATE_RENEWING));
FD_ZERO(&readfds);
tv = check_networks_time;
timeval_min(&tv, &message_time);
if(config_data) {
timeval_min_sec(&tv, config_data->expires_m);
if(state == STATE_BOUND)
timeval_min_sec(&tv, config_renew_time());
}
gettime(&now, NULL);
if(timeval_compare(&tv, &now) > 0) {
timeval_minus(&tv, &tv, &now);
FD_SET(protocol_socket, &readfds);
debugf(3, "Sleeping for %d.%03ds, state=%d.\n",
(int)tv.tv_sec, (int)(tv.tv_usec / 1000), (int)state);
rc = select(protocol_socket + 1, &readfds, NULL, NULL, &tv);
if(rc < 0 && errno != EINTR) {
perror("select");
sleep(5);
continue;
}
}
gettime(&now, NULL);
if(exiting)
break;
if(dumping) {
int clock_status;
time_t stable;
struct timeval tv, real;
dumping = 0;
get_real_time(&real, &clock_status);
gettime(&tv, &stable);
printf("Clock status %d, stable for at least %ld seconds.\n",
(int)clock_status, (long)stable);
printf("Forwarder forwarding.\n");
if(server_config)
printf("Server serving.\n");
if(client_config) {
printf("Client in state %d, ", (int)state);
if(memcmp(selected_server, zeroes, 8) != 0)
printf("server selected, ");
if(config_data) {
printf("configuration valid for "
"%lds (originally %lds since %lds).",
(long)((long)config_data->expires_m - now.tv_sec),
(long)config_data->expires,
(long)config_data->origin);
} else {
printf("not configured.\n");
}
}
printf("\n");
fflush(stdout);
}
if(changed) {
changed = 0;
for(i = 0; i < numnetworks; i++)
check_network(&networks[i]);
set_timeout(CHECK_NETWORKS, 30000, 1);
rc = reopen_logfile();
if(rc < 0) {
perror("reopen_logfile");
goto fail;
}
}
if(FD_ISSET(protocol_socket, &readfds)) {
unsigned char buf[BUFFER_SIZE];
int len;
struct sockaddr *psin;
int sinlen;
len = ahcp_recv(protocol_socket, buf, BUFFER_SIZE,
(struct sockaddr*)&sin6, sizeof(sin6));
if(len < 0) {
if(errno != EAGAIN && errno != EINTR) {
perror("recv");
sleep(5);
}
continue;
}
psin = (struct sockaddr*)&sin6;
sinlen = sizeof(sin6);
if(IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
for(net = 0; net < numnetworks; net++) {
if(networks[net].ifindex <= 0)
continue;
if(networks[net].ifindex == sin6.sin6_scope_id)
break;
}
if(net >= numnetworks) {
fprintf(stderr, "Received packet on unknown network.\n");
continue;
}
} else {
net = -1;
}
rc = handle_packet(net >= 0, buf, len);
gettime(&now, NULL);
if(rc == 2) {
unsigned char *body = buf + 24;
int bodylen = len - 24;
if(len < 28) {
fprintf(stderr, "Received truncated packet (%d).\n", rc);
continue;
}
debugf(2, "Received packet %d in state %d.\n",
body[0], (int)state);
switch(state) {
case STATE_IDLE:
break;
case STATE_INIT:
if(body[0] == AHCP_OFFER) {
struct config_data *config;
config = parse_message(0, body, bodylen, interfaces);
if(config &&
(((af & 1) && config->ipv4_address) ||
((af & 2) && config->ipv6_prefix))) {
if((af & 1) && config->ipv4_address)
prefix_list_extract4(last_ipv4,
config->ipv4_address);
server_hopcount = buf[3];
memcpy(selected_server, buf + 8, 8);
SWITCH(STATE_REQUESTING);
}
if(config)
free_config_data(config);
}
break;
case STATE_REQUESTING:
case STATE_RENEWING:
case STATE_RENEWING_UNICAST:
if(body[0] == AHCP_ACK &&
memcmp(buf + 8, selected_server, 8) == 0) {
struct config_data *config;
config = parse_message(1, body, bodylen, interfaces);
if(!config_data) {
/* Something went wrong, such as the config script
failing. Reset everything, but set a large
timeout. */
server_hopcount = 0;
memset(selected_server, 0, 8);
SWITCH(STATE_INIT);
set_timeout(MESSAGE, 30000, 1);
} else if(config) {
if((af & 1) && config->ipv4_address)
prefix_list_extract4(last_ipv4,
config->ipv4_address);
server_hopcount = buf[3];
free_config_data(config);
SWITCH(STATE_BOUND);
} else {
fprintf(stderr,
"Eek! Configured, but config null.\n");
}
} else if(body[0] == AHCP_NACK &&
memcmp(buf + 8, selected_server, 8) == 0) {
if(config_data)
unconfigure(interfaces);
server_hopcount = 0;
memset(selected_server, 0, 8);
SWITCH(STATE_INIT);
}
break;
case STATE_BOUND:
break;
default:
abort();
}
#ifndef NO_SERVER
if(server_config) {
if(body[0] == AHCP_DISCOVER ||
body[0] == AHCP_REQUEST ||
body[0] == AHCP_RELEASE) {
struct config_data *config;
unsigned client_lease_time;
unsigned char reply[BUFFER_SIZE];
int hopcount;
unsigned char ipv4[4] = {0};
config = parse_message(-1, body, bodylen, interfaces);
if(!config) {
fprintf(stderr, "Unparseable client message.\n");
continue;
}
hopcount = buf[3];
if(config->ipv4_address)
prefix_list_extract4(ipv4, config->ipv4_address);
client_lease_time =
config->expires ?
config->expires + roughly(120) :
4 * 3600 + roughly(120);
free_config_data(config);
if(body[0] == AHCP_RELEASE) {
if(memcmp(ipv4, zeroes, 4) != 0) {
rc = release_lease(buf + 8, 8, ipv4);
if(rc < 0) {
char a[INET_ADDRSTRLEN];
inet_ntop(AF_INET, ipv4, a,
INET_ADDRSTRLEN);
fprintf(stderr,
"Couldn't release lease for %s.\n",
a);
}
}
continue;
}
if((config->ipv4_mandatory &&
!server_config->lease_first[0]) ||
(config->ipv6_mandatory &&
!server_config->ipv6_prefix) ||
config->ipv4_delegation_mandatory ||
config->ipv6_delegation_mandatory) {
/* We won't be able to satisfy the client's
mandatory constraints. */
rc = -1;
} else if(server_config->lease_first[0] &&
config->ipv4_address) {
rc = take_lease(buf + 8, 8,
memcmp(ipv4, zeroes, 4) == 0 ?
ipv4 : NULL,
ipv4, &client_lease_time,
body[0] == AHCP_REQUEST);
} else {
rc = 0;
}
/* If the client is in the initial state, there's
no point in notifying it about failures -- it
will time out and fall back to another server */
if(rc < 0 && body[0] == AHCP_DISCOVER)
continue;
config =
make_config_data(client_lease_time,
ipv4, server_config,
interfaces);
if(config == NULL) {
fprintf(stderr, "Couldn't build config data.\n");
continue;
}
rc = server_body(rc < 0 ? AHCP_NACK :
body[0] == AHCP_DISCOVER ?
AHCP_OFFER : AHCP_ACK,
config, reply, BUFFER_SIZE);
if(rc < 0) {
fprintf(stderr, "Couldn't build reply.\n");
} else {
debugf(2, "Sending %d (%d bytes, %d hops).\n",
reply[0], rc, hopcount);
usleep(roughly(50000));
send_packet(psin, sinlen, buf + 8, hopcount,
reply, rc);
gettime(&now, NULL);
}
free_config_data(config);
}
}
#endif
}
}
if(config_data) {
if(now.tv_sec >= config_data->expires_m) {
unconfigure(interfaces);
server_hopcount = 0;
SWITCH(STATE_INIT);
}
}
if(state == STATE_BOUND) {
if(now.tv_sec >= config_renew_time())
SWITCH(STATE_RENEWING_UNICAST);
}
if(message_time.tv_sec > 0 && now.tv_sec >= message_time.tv_sec) {
unsigned char buf[BUFFER_SIZE];
int i;
switch(state) {
case STATE_IDLE:
fprintf(stderr, "Attempted to send message in IDLE state.\n");
set_timeout(MESSAGE, 0, 1);
break;
case STATE_INIT:
server_hopcount++;
i = query_body(AHCP_DISCOVER, lease_time + roughly(120),
memcmp(last_ipv4, zeroes, 4) ? NULL : last_ipv4,
buf, BUFFER_SIZE);
if(i >= 0) {
debugf(2, "Sending %d (%d bytes, %d hops).\n",
buf[0], i, server_hopcount);
send_packet(NULL, 0, NULL, server_hopcount, buf, i);
} else {
fprintf(stderr, "Couldn't build body.\n");
}
count++;
set_timeout(MESSAGE, 1000 * count, 1);
break;
case STATE_RENEWING:
server_hopcount++;
/* Fall through */
case STATE_REQUESTING:
case STATE_RENEWING_UNICAST:
i = query_body(AHCP_REQUEST, lease_time + roughly(120),
memcmp(last_ipv4, zeroes, 4) ? NULL : last_ipv4,
buf, BUFFER_SIZE);
if(i < 0) {
fprintf(stderr, "Couldn't build body.\n");
break;
}
if(state == STATE_REQUESTING && count > 7) {
debugf(2, "Giving up on request.\n");
server_hopcount = 0;
memset(selected_server, 0, 8);
SWITCH(STATE_INIT);
} else if(state == STATE_RENEWING_UNICAST) {
debugf(2, "Sending %d (%d bytes, unicast)\n",
buf[0], i);
rc = send_unicast_packet(selected_server, config_data,
count / 3,
buf, i);
if(rc == 0) {
SWITCH(STATE_RENEWING);
} else {
/* A failure probably indicates we couldn't route
to this address, no point in delaying. */
count++;
set_timeout(MESSAGE, rc < 0 ? 300 : 10000, 1);
}
} else {
debugf(2, "Sending %d (%d bytes, %d hops).\n",
buf[0], i, server_hopcount);
send_packet(NULL, 0, selected_server, server_hopcount,
buf, i);
count++;
/* At this point we have a fair idea of the server
hopcount, so we should get a reply in one, at most
two requests. Use generous timeouts. */
set_timeout(MESSAGE,
state == STATE_REQUESTING ?
2000 * count : 10000 * count, 1);
}
break;
case STATE_BOUND:
fprintf(stderr, "Attempted to send message in BOUND state.\n");
set_timeout(MESSAGE, 0, 1);
break;
default: abort();
}
}
if(check_networks_time.tv_sec > 0 &&
timeval_compare(&check_networks_time, &now) <= 0) {
for(i = 0; i < numnetworks; i++)
check_network(&networks[i]);
set_timeout(CHECK_NETWORKS, 30000, 1);
}
}
/* Clean up */
if(config_data) {
unsigned char buf[BUFFER_SIZE];
int len;
len = query_body(AHCP_RELEASE, 0,
memcmp(last_ipv4, zeroes, 4) ? NULL : last_ipv4,
buf, BUFFER_SIZE);
if(len >= 0) {
int index = 0;
int success = 0;
while(1) {
debugf(2, "Sending %d (%d bytes, unicast).\n", buf[0], i);
rc = send_unicast_packet(selected_server, config_data, index,
buf, len);
if(rc > 0) {
success = 1;
break;
} else if(rc == 0) {
break;
}
index++;
}
if(!success) {
debugf(2, "Sending %d (%d bytes, %d hops).\n",
buf[0], i, server_hopcount);
send_packet(NULL, 0, NULL, server_hopcount + 2, buf, len);
}
}
unconfigure(interfaces);
SWITCH(STATE_INIT);
}
if(pidfile)
unlink(pidfile);
return 0;
usage:
fprintf(stderr,
"Syntax: ahcpd "
"[-m group] [-p port] [-n] [-4] [-6] [-N]\n"
" "
"[-i file] [-s script] [-D] [-I pidfile] [-L logfile]\n"
" "
"[-C statement] [-c filename]"
"interface...\n");
exit(1);
fail:
if(pidfile)
unlink(pidfile);
exit(1);
}
unsigned
roughly(unsigned value)
{
return value * 3 / 4 + random() % (value / 4);
}
static void
set_timeout(int which, int msecs, int override)
{
struct timeval *tv;
int ms = msecs == 0 ? 0 : roughly(msecs);
switch(which) {
case MESSAGE: tv = &message_time; break;
case CHECK_NETWORKS: tv = &check_networks_time; break;
default: abort();
}
/* (0, 0) represents never */
if(override || tv->tv_sec == 0 || tv->tv_sec > now.tv_sec + ms / 1000) {
if(msecs <= 0) {
tv->tv_usec = 0;
tv->tv_sec = 0;
} else {
tv->tv_usec =
(now.tv_usec + ms * 1000) % 1000000 + random() % 1000;
tv->tv_sec = now.tv_sec + (now.tv_usec / 1000 + ms) / 1000;
}
}
}
static int
check_network(struct network *net)
{
int ifindex, rc;
struct ipv6_mreq mreq;
ifindex = if_nametoindex(net->ifname);
if(ifindex != net->ifindex) {
net->ifindex = ifindex;
if(net->ifindex > 0) {
memset(&mreq, 0, sizeof(mreq));
memcpy(&mreq.ipv6mr_multiaddr, &protocol_group, 16);
mreq.ipv6mr_interface = net->ifindex;
rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char*)&mreq, sizeof(mreq));
if(rc < 0) {
perror("setsockopt(IPV6_JOIN_GROUP)");
net->ifindex = 0;
goto fail;
}
return 1;
}
}
fail:
return 0;
}
static void
sigexit(int signo)
{
exiting = 1;
}
static void
sigdump(int signo)
{
dumping = 1;
}
static void
sigchanged(int signo)
{
changed = 1;
}
static void
init_signals(void)
{
struct sigaction sa;
sigset_t ss;
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGHUP, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigdump;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigchanged;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGUSR2, &sa, NULL);
#ifdef SIGINFO
sigemptyset(&ss);
sa.sa_handler = sigdump;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGINFO, &sa, NULL);
#endif
}
int
ahcp_socket(int port)
{
struct sockaddr_in6 sin6;
int s, rc;
int saved_errno;
int one = 1, zero = 0;
const int ds = 0xc0; /* CS6 - Network Control */
s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
perror("setsockopt(SO_REUSEADDR)");
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&zero, sizeof(zero));
if(rc < 0)
perror("setsockopt(IPV6_MULTICAST_LOOP)");
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
perror("setsockopt(IPV6_MULTICAST_HOPS)");
#ifdef IPV6_V6ONLY
rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
&zero, sizeof(zero));
if(rc < 0)
perror("setsockopt(IPV6_V6ONLY)");
#endif
#ifdef IPV6_TCLASS
rc = setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &ds, sizeof(ds));
#else
rc = -1;
errno = ENOSYS;
#endif
if (rc < 0)
perror("setsockopt(IPV6_TCLASS)");
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
int
ahcp_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
{
struct iovec iovec;
struct msghdr msg;
int rc;
memset(&msg, 0, sizeof(msg));
iovec.iov_base = buf;
iovec.iov_len = buflen;
msg.msg_name = sin;
msg.msg_namelen = slen;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
rc = recvmsg(s, &msg, 0);
return rc;
}
/* Attempts to contact the server over unicast, on its indexth address.
Returns 1 if a packet was successfully sent, 0 if index was beyond the
number of addresses, -1 in case of failure to send the packet. */
static int
send_unicast_packet(unsigned char *server_id, struct config_data *config,
int index, void *buf, int buflen)
{
int rc;
unsigned char *address;
struct sockaddr_in6 sin;
int num6 = config->server_ipv6 ? config->server_ipv6->n : 0;
int num4 = config->server_ipv4 ? config->server_ipv4->n : 0;
if(!config_data)
return 0;
if(index < num6)
address = config->server_ipv6->l[index].p;
else if(index < num6 + num4)
address = config->server_ipv4->l[index - num6].p;
else
return 0;
memset(&sin, 0, sizeof(sin));
sin.sin6_family = AF_INET6;
memcpy(&sin.sin6_addr, address, 16);
sin.sin6_port = htons(protocol_port);
rc = send_packet((struct sockaddr*)&sin, sizeof(sin), server_id, 1,
buf, buflen);
return (rc >= 0) ? 1 : -1;
}
int
timeval_compare(const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_sec < s2->tv_sec)
return -1;
else if(s1->tv_sec > s2->tv_sec)
return 1;
else if(s1->tv_usec < s2->tv_usec)
return -1;
else if(s1->tv_usec > s2->tv_usec)
return 1;
else
return 0;
}
/* {0, 0} represents infinity */
void
timeval_min(struct timeval *d, const struct timeval *s)
{
if(s->tv_sec == 0)
return;
if(d->tv_sec == 0 || timeval_compare(d, s) > 0) {
*d = *s;
}
}
void
timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_usec >= s2->tv_usec) {
d->tv_usec = s1->tv_usec - s2->tv_usec;
d->tv_sec = s1->tv_sec - s2->tv_sec;
} else {
d->tv_usec = s1->tv_usec + 1000000 - s2->tv_usec;
d->tv_sec = s1->tv_sec - s2->tv_sec - 1;
}
}
void
timeval_min_sec(struct timeval *d, int secs)
{
if(d->tv_sec == 0 || d->tv_sec > secs) {
d->tv_sec = secs;
d->tv_usec = random() % 1000000;
}
}
static int
reopen_logfile()
{
int lfd, rc;
if(logfile == NULL)
return 0;
lfd = open(logfile, O_CREAT | O_WRONLY | O_APPEND, 0644);
if(lfd < 0)
return -1;
fflush(stdout);
fflush(stderr);
rc = dup2(lfd, 1);
if(rc < 0)
return -1;
rc = dup2(lfd, 2);
if(rc < 0)
return -1;
if(lfd == 0 || lfd > 2)
close(lfd);
return 1;
}
static int
daemonise()
{
int rc;
fflush(stdout);
fflush(stderr);
rc = fork();
if(rc < 0)
return -1;
if(rc > 0)
exit(0);
rc = setsid();
if(rc < 0)
return -1;
return 1;
}
void
do_debugf(int level, const char *format, ...)
{
va_list args;
va_start(args, format);
if(debug >= level) {
vfprintf(stderr, format, args);
fflush(stderr);
}
va_end(args);
}
ahcpd-0.53/ahcpd.h 0000664 0000000 0000000 00000007470 11670236476 0013774 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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.
*/
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* nothing */
#elif defined(__GNUC__)
#define inline __inline
#if (__GNUC__ >= 3)
#define restrict __restrict
#else
#define restrict /**/
#endif
#else
#define inline /**/
#define restrict /**/
#endif
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define ATTRIBUTE(x) __attribute__ (x)
#define LIKELY(_x) __builtin_expect(!!(_x), 1)
#define UNLIKELY(_x) __builtin_expect(!!(_x), 0)
#else
#define ATTRIBUTE(x) /**/
#define LIKELY(_x) !!(_x)
#define UNLIKELY(_x) !!(_x)
#endif
#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
#define COLD __attribute__ ((cold))
#else
#define COLD /**/
#endif
#undef MAX
#undef MIN
#ifdef HAVE_VALGRIND
#include
#else
#ifndef VALGRIND_MAKE_MEM_UNDEFINED
#define VALGRIND_MAKE_MEM_UNDEFINED(a, b) do {} while(0)
#endif
#ifndef VALGRIND_CHECK_MEM_IS_DEFINED
#define VALGRIND_CHECK_MEM_IS_DEFINED(a, b) do {} while(0)
#endif
#endif
#define MAX(x,y) ((x)<=(y)?(y):(x))
#define MIN(x,y) ((x)<=(y)?(x):(y))
extern int nodns, af, request_prefix_delegation;
extern char *config_script;
extern int debug;
extern unsigned char myid[8];
extern int numnetworks;
struct network {
char *ifname;
int ifindex;
};
#define MAXNETWORKS 20
extern struct network networks[MAXNETWORKS];
extern struct in6_addr protocol_group;
extern unsigned int protocol_port;
extern int protocol_socket;
extern const unsigned char zeroes[16], ones[16];
void timeval_min(struct timeval *d, const struct timeval *s);
void timeval_min_sec(struct timeval *d, int secs);
void timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2);
int timeval_minus_msec(const struct timeval *s1, const struct timeval *s2);
void timeval_plus_msec(struct timeval *d,
const struct timeval *s, int msecs);
int timeval_compare(const struct timeval *s1, const struct timeval *s2);
int clock_stepped();
void do_debugf(int level, const char *format, ...)
ATTRIBUTE ((format (printf, 2, 3))) COLD;
#if defined NO_DEBUG
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define debugf(_level, ...) do {} while(0)
#elif defined __GNUC__
#define debugf(_level, _args...) do {} while(0)
#else
static inline void debugf(_level, const char *format, ...) { return; }
#endif
#else
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define debugf(_level, ...) \
do { \
if(UNLIKELY(debug >= _level)) do_debugf(_level, __VA_ARGS__); \
} while(0)
#elif defined __GNUC__
#define debugf(_level, _args...) \
do { \
if(UNLIKELY(debug >= _level)) do_debugf(_level, _args); \
} while(0)
#else
static inline void debugf(int _level, const char *format, ...) { return; }
#endif
#endif
ahcpd-0.53/ahcpd.man 0000664 0000000 0000000 00000011054 11670236476 0014311 0 ustar 00root root 0000000 0000000 .TH AHCPD 8
.SH NAME
ahcpd \- ad-hoc configuration daemon
.SH SYNOPSIS
.B ahcpd
.IR option ...
[
.B \-\-
]
.IR interface ...
.SH DESCRIPTION
AHCP is a configuration protocol that can replace DHCP on networks without
transitive connectivity, such as mesh networks.
.SH OPTIONS
.TP
.BI \-m " multicast-address"
Specify the link-local multicast address to be used by AHCP. The
default is ff02::cca6:c0f9:e182:5359.
.TP
.BI \-p " port"
Specify the UDP port number to be used by AHCP. The default is 5359.
.TP
.B \-n
Operate as a forwarder: participate in the flooding protocol, but don't
actually perform any configuration.
.TP
.B \-4
Only attempt to configure IPv4 addresses.
.TP
.B \-6
Only attempt to configure IPv6 addresses.
.TP
.B \-N
Do not configure DNS.
.TP
.BI \-t " time"
Specify the time, in seconds, for which leases are requested. The default
is slightly over one hour. Must be between five minutes and a year.
.TP
.BI \-s " script"
Specify the configuration script to run. The default is
.BR /etc/ahcp/ahcp-config.sh .
.TP
.BI \-d " level"
Set the debug level to
.I level
(default 1).
.TP
.BI \-i " filename"
Specify the filename containing this host's unique id. The default is
.BR /var/lib/ahcp\-unique\-id .
If it doesn't exist, it will be created by
.BR ahcpd .
.TP
.BI \-c " filename"
Specify the name of the configuration file.
.TP
.BI \-C " statement"
Specify a configuration statement directly on the command line.
.TP
.B \-D
Daemonise at startup.
.TP
.BI \-L " logfile"
Specify a file to log random ``how do you do?'' messages to. This
defaults to standard error if not daemonising, and to
.B /var/log/ahcpd.log
otherwise.
.TP
.BI \-I " pidfile"
Specify a file to write our process id to. The default is
.B /var/run/ahcpd.pid.
.SH CONFIGURATION FILE FORMAT
The configuration is a sequence of lines, each of which starts with
one of the keywords below. Blank lines are ignored. Comments are
introduced with an octothorp
.RB `` # ''
and terminate at the end of the line.
The following keywords are recognised:
.TP
.BR mode " " server | client | forwarder
Specifies whether the daemon operates as a server, a client, or
a forwarder. If omitted, the default is to operate as a client, unless
the
.B \-n
flag is present on the command line. If present, this must be the first
line in the configuration file.
.TP
.BI prefix " prefix"
Specifies a prefix to use for configuring clients. This keyword is only
valid in server configurations, and may be specified twice, once for
IPv4 and once for IPv6.
.TP
.BI lease-dir " directory"
Specifies a directory to store lease files. This keyword is only valid
in server configurations.
.TP
.BI name-server " address"
Specifies the address of a DNS server to configure clients with. This
keyword is only valid in server configurations, and may be repeated
multiple times.
.TP
.BI ntp-server " address"
Specifies the address of an NTP server to configure clients with. This
keyword is only valid in server configurations, and may be repeated
multiple times.
.SH FILES
.TP
.B /var/lib/ahcp\-unique\-id
An 8-byte long file containing this host's unique id. If it doesn't exist,
a new unique id will be generated from an interface's MAC address.
.TP
.BR /etc/ahcp/ahcp\-config.sh
The script that performs the actual configuration. It will be passed one
argument, which is either
.B start
or
.BR stop .
.TP
.B /etc/ahcp/ahcp\-local.sh
If this is an executable script, it will be called by
.B ahcp\-config.sh
just after configuring or deconfiguring. It will be passed one
argument, which is either
.B start
or
.BR stop .
.SH SIGNALS
.TP
.B SIGUSR1
Print
.BR ahcpd 's
status to standard output or to the log file.
.TP
.B SIGUSR2
Check all interfaces for status changes, then reopen the log file.
.SH NOTES
Since the AHCP protocol is designed for mesh networks, it doesn't have any
provisions for setting routing parameters such as a default gateway, the
IPv4 network mask and the IPv6 list of on-link prefixes; these are expected
to be provided by a full-fledged routing protocol for mesh nodes, and by
router advertisements for ordinary nodes.
The AHCP server should use NTP to synchronise its clock. If a server
instance of
.B ahcpd
doesn't detect time synchronisation, it will only give out leases for
a short period of time, and be extremely conservative about releasing them.
Note that an SNTP client is not enough \[em]
.B ahcpd
actually checks with the kernel for time synchronisation, so real NTP is
necessary.
.SH SEE ALSO
.BR dhcpcd (8),
.BR dhclient (8),
.BR babeld (8),
.BR olsrd (8),
.BR ntpd (8),
.IR "The Ad-Hoc Configuration Protocol" .
.SH AUTHOR
Juliusz Chroboczek.
ahcpd-0.53/config.c 0000664 0000000 0000000 00000017353 11670236476 0014156 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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
#include
#include
#include
#include "prefix.h"
#include "config.h"
int client_config = 1;
struct server_config *server_config = NULL;
/* get_next_char callback */
typedef int (*gnc_t)(void*);
static int
skip_whitespace(int c, gnc_t gnc, void *closure)
{
while(c == ' ' || c == '\t')
c = gnc(closure);
return c;
}
static int
skip_to_eol(int c, gnc_t gnc, void *closure)
{
while(c != '\n' && c >= 0)
c = gnc(closure);
if(c == '\n')
c = gnc(closure);
return c;
}
static int
skip_eol(int c, gnc_t gnc, void *closure)
{
c = skip_whitespace(c, gnc, closure);
if(c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
return c;
} else if(c == -1) {
return -1;
} else {
return -2;
}
}
static int
getword(int c, char **token_r, gnc_t gnc, void *closure)
{
char buf[256];
int i = 0;
c = skip_whitespace(c, gnc, closure);
if(c < 0)
return c;
if(c == '"' || c == '\n')
return -2;
do {
if(i >= 255) return -2;
buf[i++] = c;
c = gnc(closure);
} while(c != ' ' && c != '\t' && c != '\n' && c >= 0);
buf[i] = '\0';
*token_r = strdup(buf);
return c;
}
static int
getstring(int c, char **token_r, gnc_t gnc, void *closure)
{
char buf[256];
int i = 0;
c = skip_whitespace(c, gnc, closure);
if(c < 0)
return c;
if(c == '\n')
return -2;
/* Unquoted strings have the same syntax as words. */
if(c != '"')
return getword(c, token_r, gnc, closure);
c = gnc(closure);
while(1) {
if(i >= 255 || c == '\n') return -2;
if(c == '"') {
c = gnc(closure);
break;
}
if(c == '\\')
c = gnc(closure);
buf[i++] = c;
c = gnc(closure);
}
buf[i] = '\0';
*token_r = strdup(buf);
return c;
}
static int
parse_config(gnc_t gnc, void *closure)
{
int c;
char *token;
c = gnc(closure);
if(c < 2)
return -1;
while(c >= 0) {
c = skip_whitespace(c, gnc, closure);
if(c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
continue;
}
if(c < 0)
break;
c = getword(c, &token, gnc, closure);
if(c < -1)
return -1;
if(strcmp(token, "mode") == 0) {
char *mtoken;
c = getword(c, &mtoken, gnc, closure);
if(c < -1)
return -1;
if(strcmp(mtoken, "server") == 0) {
#ifndef NO_SERVER
client_config = 0;
if(!server_config)
server_config = calloc(1, sizeof(struct server_config));
if(!server_config)
return -1;
#else
return -1;
#endif
} else if(strcmp(mtoken, "client") == 0) {
if(server_config)
return -1;
client_config = 1;
} else if(strcmp(mtoken, "forwarder") == 0) {
if(server_config)
return -1;
client_config = 0;
} else {
return -1;
}
free(mtoken);
c = skip_eol(c, gnc, closure);
if(c < -1)
return -1;
} else if(strcmp(token, "lease-dir") == 0) {
char *dir;
if(!server_config)
return -1;
c = getstring(c, &dir, gnc, closure);
if(c < -1)
return -1;
if(dir[0] != '/')
return -1;
server_config->lease_dir = dir;
c = skip_eol(c, gnc, closure);
if(c < -1)
return -1;
} else if(strcmp(token, "prefix") == 0) {
char *ptoken;
struct prefix_list *prefix;
if(!server_config)
return -1;
c = getword(c, &ptoken, gnc, closure);
if(c < -1)
return -1;
prefix = parse_prefix(ptoken, PREFIX);
if(prefix == NULL || prefix->n != 1)
return -1;
if(prefix_list_v4(prefix)) {
unsigned const char zeroes[4] = {0};
unsigned mask, first, last;
if(memcmp(server_config->lease_first, zeroes, 4) != 0)
return -1;
mask = 0xFFFFFFFF << (128 - prefix->l[0].plen);
first =
(prefix->l[0].p[12] << 24 |
prefix->l[0].p[13] << 16 |
prefix->l[0].p[14] << 8 |
prefix->l[0].p[15] ) & mask;
last = first | (~ mask);
first = htonl(first + 1);
last = htonl(last - 1);
memcpy(server_config->lease_first, &first, 4);
memcpy(server_config->lease_last, &last, 4);
free(prefix);
} else {
server_config->ipv6_prefix =
cat_prefix_list(server_config->ipv6_prefix,
prefix);
}
free(ptoken);
c = skip_eol(c, gnc, closure);
if(c < -1)
return -1;
} else if(strcmp(token, "name-server") == 0 ||
strcmp(token, "ntp-server") == 0) {
char *ptoken;
struct prefix_list *prefix;
if(!server_config)
return -1;
c = getword(c, &ptoken, gnc, closure);
if(c < -1)
return -1;
prefix = parse_prefix(ptoken, ADDRESS);
if(prefix == NULL)
return -1;
if(strcmp(token, "name-server") == 0)
server_config->name_server =
cat_prefix_list(server_config->name_server,
prefix);
else
server_config->ntp_server =
cat_prefix_list(server_config->ntp_server,
prefix);
free(ptoken);
} else {
return -1;
}
free(token);
}
return 1;
}
int
parse_config_from_file(char *filename)
{
FILE *f;
int rc;
f = fopen(filename, "r");
if(f == NULL)
return -1;
rc = parse_config((gnc_t)fgetc, f);
fclose(f);
return rc;
}
struct string_state {
char *string;
int n;
};
static int
gnc_string(struct string_state *s)
{
if(s->string[s->n] == '\0')
return -1;
else
return s->string[s->n++];
}
int
parse_config_from_string(char *string)
{
struct string_state s = { string, 0 };
return parse_config((gnc_t)gnc_string, &s);
}
ahcpd-0.53/config.h 0000664 0000000 0000000 00000002574 11670236476 0014162 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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.
*/
struct server_config {
const char *lease_dir;
struct prefix_list *name_server, *ntp_server, *ipv6_prefix;
unsigned char lease_first[4], lease_last[4];
};
extern int client_config;
extern struct server_config *server_config;
int parse_config_from_string(char *string);
int parse_config_from_file(char *filename);
ahcpd-0.53/configure.c 0000664 0000000 0000000 00000061513 11670236476 0014667 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __linux__
#include
#endif
#include "ahcpd.h"
#include "monotonic.h"
#include "prefix.h"
#include "config.h"
#include "protocol.h"
#include "configure.h"
struct config_data *config_data = NULL;
const unsigned char v4prefix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
static int
interface_ipv4(const char *ifname, unsigned char *addr_r)
{
struct ifreq req;
int s, rc;
s = socket(AF_INET, SOCK_DGRAM, 0);
if(s < 0)
return -1;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
req.ifr_addr.sa_family = AF_INET;
rc = ioctl(s, SIOCGIFADDR, &req);
if(rc < 0) {
close(s);
return -1;
}
if(req.ifr_addr.sa_family != AF_INET)
return -1;
memcpy(addr_r, &((struct sockaddr_in*)&req.ifr_addr)->sin_addr, 4);
close(s);
return 1;
}
static struct prefix_list *
my_ipv4(char **interfaces)
{
struct prefix_list *l;
int i, rc;
l = calloc(1, sizeof(struct prefix_list));
if(l == NULL)
return NULL;
i = 0;
while(l->n < MAX_PREFIX && interfaces[i]) {
l->l[l->n].plen = 0xFF;
memcpy(l->l[l->n].p, v4prefix, 12);
rc = interface_ipv4(interfaces[i], l->l[l->n].p + 12);
if(rc > 0)
l->n++;
i++;
}
if(l->n == 0) {
free(l);
return NULL;
}
return l;
}
static int
run_script(const char *action, struct config_data *config, char **interfaces)
{
pid_t pid;
pid = fork();
if(pid < 0) {
perror("fork");
return -1;
} else if(pid == 0) {
char buf[200];
int i;
snprintf(buf, 50, "%lu", (unsigned long)getppid());
setenv("AHCP_DAEMON_PID", buf, 1);
buf[0] = '\0';
i = 0;
while(interfaces[i]) {
if(i > 0)
strncat(buf, " ", 200);
strncat(buf, interfaces[i], 200);
i++;
}
setenv("AHCP_INTERFACES", buf, 1);
snprintf(buf, 50, "%d", debug);
setenv("AHCP_DEBUG_LEVEL", buf, 1);
if(config->our_ipv6_address && (af & 2))
setenv("AHCP_IPv6_ADDRESS",
format_prefix_list(config->our_ipv6_address, IPv6_ADDRESS),
1);
if(config->ipv4_address && (af & 1))
setenv("AHCP_IPv4_ADDRESS",
format_prefix_list(config->ipv4_address, IPv4_ADDRESS), 1);
if(config->ipv6_prefix_delegation && (af & 2))
setenv("AHCP_IPv6_PREFIX_DELEGATION",
format_prefix_list(config->ipv6_prefix_delegation,
IPv6_PREFIX),
1);
if(config->ipv4_prefix_delegation && (af & 2))
setenv("AHCP_IPv4_PREFIX_DELEGATION",
format_prefix_list(config->ipv4_prefix_delegation,
IPv4_PREFIX),
1);
if(config->name_server && !nodns)
setenv("AHCP_NAMESERVER",
format_prefix_list(config->name_server, ADDRESS), 1);
if(config->ntp_server)
setenv("AHCP_NTP_SERVER",
format_prefix_list(config->ntp_server, ADDRESS), 1);
debugf(1, "Running ``%s %s''\n", config_script, action);
execl(config_script, config_script, action, NULL);
perror("exec failed");
exit(42);
} else {
int status;
again:
pid = waitpid(pid, &status, 0);
if(pid < 0) {
if(errno == EINTR)
goto again;
perror("wait");
return -1;
} else if(!WIFEXITED(status)) {
fprintf(stderr, "Child died violently (%d)\n", status);
return -1;
} else if(WEXITSTATUS(status) != 0) {
fprintf(stderr, "Child returned error status %d\n",
WEXITSTATUS(status));
return -1;
}
}
return 1;
}
unsigned int
config_renew_time(void)
{
return config_data->origin_m + config_data->expires * 5 / 6;
}
void
free_config_data(struct config_data *config)
{
free_prefix_list(config->server_ipv6);
free_prefix_list(config->server_ipv4);
free_prefix_list(config->ipv6_prefix);
free_prefix_list(config->ipv4_prefix);
free_prefix_list(config->ipv6_address);
free_prefix_list(config->ipv4_address);
free_prefix_list(config->ipv6_prefix_delegation);
free_prefix_list(config->ipv4_prefix_delegation);
free_prefix_list(config->name_server);
free_prefix_list(config->ntp_server);
free_prefix_list(config->our_ipv6_address);
free(config);
}
struct config_data *
copy_config_data(struct config_data *config)
{
struct config_data *c = malloc(sizeof(struct config_data));
if(c == NULL)
return NULL;
*c = *config;
#define COPY(field) \
do { if(c->field) c->field = copy_prefix_list(c->field); } while(0)
COPY(server_ipv6);
COPY(server_ipv4);
COPY(ipv6_prefix);
COPY(ipv4_prefix);
COPY(ipv6_address);
COPY(ipv4_address);
COPY(ipv6_prefix_delegation);
COPY(ipv4_prefix_delegation);
COPY(name_server);
COPY(ntp_server);
COPY(our_ipv6_address);
#undef COPY
return c;
}
int
config_data_compatible(struct config_data *config1, struct config_data *config2)
{
if(!!config1->ipv4_address != !!config2->ipv4_address ||
!!config1->ipv6_address != !!config2->ipv6_address ||
!!config1->ipv6_prefix != !!config2->ipv6_prefix ||
!!config1->ipv4_prefix_delegation != !!config2->ipv4_prefix_delegation ||
!!config1->ipv6_prefix_delegation != !!config2->ipv6_prefix_delegation)
return 0;
if(config1->ipv4_address) {
if(!prefix_list_eq(config1->ipv4_address, config2->ipv4_address) != 0)
return 0;
}
if(config1->ipv6_address) {
if(!prefix_list_eq(config1->ipv6_address, config2->ipv6_address) != 0)
return 0;
}
if(config1->ipv6_prefix) {
if(!prefix_list_eq(config1->ipv6_prefix, config2->ipv6_prefix) != 0)
return 0;
}
if(config1->ipv4_prefix_delegation) {
if(!prefix_list_eq(config1->ipv4_prefix_delegation,
config2->ipv4_prefix_delegation) != 0)
return 0;
}
if(config1->ipv6_prefix_delegation) {
if(!prefix_list_eq(config1->ipv6_prefix_delegation,
config2->ipv6_prefix_delegation) != 0)
return 0;
}
return 1;
}
struct config_data *
make_config_data(int expires,
unsigned char *ipv4, struct server_config *server_config,
char **interfaces)
{
struct config_data *config;
struct timeval now, real;
int clock_status;
struct prefix_list *my_address;
gettime(&now, NULL);
get_real_time(&real, &clock_status);
config = calloc(1, sizeof(struct config_data));
if(config == NULL)
return NULL;
config->expires = expires;
config->origin_m = now.tv_sec;
if(clock_status != CLOCK_BROKEN)
config->origin = real.tv_sec;
config->expires_m = config->origin_m + config->expires;
if(ipv4)
config->ipv4_address = raw_prefix_list(ipv4, 4, IPv4_ADDRESS);
if(server_config->ipv6_prefix)
config->ipv6_prefix = copy_prefix_list(server_config->ipv6_prefix);
if(server_config->name_server)
config->name_server = copy_prefix_list(server_config->name_server);
if(server_config->ntp_server)
config->ntp_server = copy_prefix_list(server_config->ntp_server);
my_address = my_ipv4(interfaces);
if(my_address)
config->server_ipv4 = my_address;
return config;
}
/* Parse a message. Configure is 1 to configure, 0 to pretend, and -1 to
omit a number of sanity checks when parsing client messages. */
struct config_data *
parse_message(int configure, const unsigned char *data, int len,
char **interfaces)
{
int bodylen, i, opt, olen, rc;
int mandatory = 0;
const unsigned char *body;
struct config_data *config;
unsigned origin = 0, expires = 0;
struct timeval now, real;
int clock_status;
#define CAT(dst, src) \
do { \
struct prefix_list *pcat; \
pcat = cat_prefix_list(dst, src); \
if(pcat == NULL) \
goto fail; \
dst = pcat; \
} while(0)
if(len < 4)
return NULL;
gettime(&now, NULL);
get_real_time(&real, &clock_status);
body = data + 4;
bodylen = (data[2] << 8) | data[3];
if(bodylen > len - 4)
return NULL;
config = calloc(1, sizeof(struct config_data));
if(config == NULL)
return NULL;
i = 0;
while(i < bodylen) {
opt = body[i];
if(opt == OPT_PAD) {
mandatory = 0;
i++;
continue;
} else if(opt == OPT_MANDATORY) {
mandatory = 1;
i++;
continue;
}
olen = body[i + 1];
if(olen + 2 + i > bodylen) {
fprintf(stderr, "Truncated message.\n");
goto fail;
}
if(opt == OPT_ORIGIN_TIME) {
unsigned int when;
if(olen != 4)
goto fail;
memcpy(&when, body + i + 2, 4);
when = ntohl(when);
if(origin <= 0)
origin = when;
else
origin = MIN(origin, when);
} else if(opt == OPT_EXPIRES) {
unsigned int secs;
if(olen != 4)
goto fail;
memcpy(&secs, body + i + 2, 4);
secs = ntohl(secs);
if(expires <= 0)
expires = secs;
else
expires = MIN(expires, secs);
} else if(opt == OPT_IPv6_PREFIX || opt == OPT_IPv6_PREFIX_DELEGATION) {
struct prefix_list *value;
if(olen % 17 != 0) {
fprintf(stderr, "Unexpected length for prefix.\n");
goto fail;
}
value = raw_prefix_list(body + i + 2, olen, IPv6_PREFIX);
if(opt == OPT_IPv6_PREFIX) {
CAT(config->ipv6_prefix, value);
if(mandatory)
config->ipv6_mandatory = 1;
} else {
CAT(config->ipv6_prefix_delegation, value);
if(mandatory)
config->ipv6_delegation_mandatory = 1;
}
} else if(opt == OPT_IPv4_PREFIX_DELEGATION) {
struct prefix_list *value;
if(olen % 5 != 0) {
fprintf(stderr, "Unexpected length for prefix.\n");
goto fail;
}
value = raw_prefix_list(body + i + 2, olen, IPv4_PREFIX);
CAT(config->ipv4_prefix_delegation, value);
if(mandatory)
config->ipv4_delegation_mandatory = 1;
} else if(opt == OPT_MY_IPv6_ADDRESS || opt == OPT_IPv6_ADDRESS ||
opt == OPT_NAME_SERVER || opt == OPT_NTP_SERVER) {
struct prefix_list *value;
if(olen % 16 != 0) {
fprintf(stderr, "Unexpected length for %s.\n",
opt == OPT_IPv6_ADDRESS ? "address" : "server");
goto fail;
}
value = raw_prefix_list(body + i + 2, olen, IPv6_ADDRESS);
if(opt == OPT_MY_IPv6_ADDRESS) {
CAT(config->server_ipv6, value);
} else if(opt == OPT_IPv6_ADDRESS) {
CAT(config->ipv6_address, value);
if(mandatory)
config->ipv6_mandatory = 1;
} else if(opt == OPT_NAME_SERVER) {
CAT(config->name_server, value);
} else if(opt == OPT_NTP_SERVER) {
CAT(config->ntp_server, value);
} else {
abort();
}
} else if(opt == OPT_MY_IPv4_ADDRESS || opt == OPT_IPv4_ADDRESS) {
struct prefix_list *value;
value = raw_prefix_list(body + i + 2, olen, IPv4_ADDRESS);
if(opt == OPT_MY_IPv4_ADDRESS) {
CAT(config->server_ipv4, value);
} else if(opt == OPT_IPv4_ADDRESS) {
CAT(config->ipv4_address, value);
if(mandatory)
config->ipv4_mandatory = 1;
} else {
abort();
}
} else {
if(mandatory)
debugf(1, "Unsupported option %d\n", opt);
if(mandatory && configure >= 0)
goto fail;
}
mandatory = 0;
i += olen + 2;
}
if(configure >= 0 && expires <= 0)
goto fail;
config->origin_m = now.tv_sec;
config->expires = MIN(25 * 3600, expires);
if(origin >= 0) {
config->origin = origin;
if(configure >= 0) {
if(origin >= real.tv_sec - 300 && origin <= real.tv_sec + 300) {
time_confirm(1);
} else {
fprintf(stderr,
"Detected clock skew (client=%ld, server=%ld).\n",
(long)now.tv_sec, (long)config->origin);
time_confirm(0);
}
}
}
if(configure >= 0) {
config->expires_m = config->origin_m + config->expires;
if(clock_status != CLOCK_BROKEN && config->origin > 0) {
config->expires_m =
MIN(config->expires_m,
config->origin_m + (real.tv_sec - config->origin) +
config->expires);
}
if(config->ipv6_address) {
config->our_ipv6_address = copy_prefix_list(config->ipv6_address);
} else if(config->ipv6_prefix && config->ipv6_prefix->n > 0 &&
config->ipv6_prefix->l[0].plen <= 64) {
unsigned char address[16];
int have_address = 0;
memcpy(address, config->ipv6_prefix->l[0].p, 16);
i = 0;
while(interfaces[i]) {
rc = if_eui64(interfaces[i], address + 8);
if(rc >= 0)
have_address = 1;
i++;
}
if(!have_address) {
rc = random_eui64(address + 8);
if(rc >= 0)
have_address = 1;
}
if(have_address)
config->our_ipv6_address =
raw_prefix_list(address, 16, IPv6_ADDRESS);
if(!config->our_ipv6_address)
fprintf(stderr, "Couldn't generate IPv6 address.\n");
}
}
if(configure > 0 && config_script[0] != '\0' &&
config->expires_m > now.tv_sec + 5) {
if(config_data) {
if(!config_data_compatible(config_data, config))
unconfigure(interfaces);
}
if(!config_data) {
rc = run_script("start", config, interfaces);
if(rc > 0)
config_data = copy_config_data(config);
} else {
config_data->origin = config->origin;
config_data->origin_m = config->origin_m;
config_data->expires = config->expires;
config_data->expires_m = config->expires_m;
}
}
return config;
fail:
free_config_data(config);
return NULL;
#undef CAT
}
int
unconfigure(char **interfaces)
{
int rc;
rc = run_script("stop", config_data, interfaces);
free_config_data(config_data);
config_data = NULL;
return rc;
}
int
query_body(unsigned char opcode, int time, const unsigned char *ipv4,
unsigned char *buf, int buflen)
{
int i, j;
unsigned nowsecs, expires;
struct timeval real;
int clock_status;
get_real_time(&real, &clock_status);
/* Skip header */
i = 4; if(i >= buflen) goto fail;
if(clock_status != CLOCK_BROKEN) {
nowsecs = htonl(real.tv_sec);
buf[i++] = OPT_ORIGIN_TIME; if(i >= buflen) goto fail;
buf[i++] = 4; if(i >= buflen - 4) goto fail;
memcpy(buf + i, &nowsecs, 4);
i += 4;
}
if(time > 0) {
expires = htonl(time);
buf[i++] = OPT_EXPIRES; if(i >= buflen) goto fail;
buf[i++] = 4; if(i >= buflen - 4) goto fail;
memcpy(buf + i, &expires, 4);
i += 4;
}
if((af & 1)) {
buf[i++] = OPT_IPv4_ADDRESS; if(i >= buflen) goto fail;
buf[i++] = ipv4 ? 4 : 0; if(i >= buflen) goto fail;
if(ipv4) {
if(i >= buflen - 4) goto fail;
memcpy(buf + i, ipv4, 4);
i += 4;
}
if(request_prefix_delegation) {
buf[i++] = OPT_IPv4_PREFIX_DELEGATION; if(i >= buflen) goto fail;
buf[i++] = 0; if(i >= buflen) goto fail;
}
}
if((af & 2)) {
buf[i++] = OPT_IPv6_PREFIX; if(i >= buflen) goto fail;
buf[i++] = 0; if(i >= buflen) goto fail;
if(request_prefix_delegation) {
buf[i++] = OPT_IPv6_PREFIX_DELEGATION; if(i >= buflen) goto fail;
buf[i++] = 0; if(i >= buflen) goto fail;
}
}
if(opcode != AHCP_RELEASE) {
if(!nodns) {
buf[i++] = OPT_NAME_SERVER; if(i >= buflen) goto fail;
buf[i++] = 0; if(i >= buflen) goto fail;
}
buf[i++] = OPT_NTP_SERVER; if(i >= buflen) goto fail;
buf[i++] = 0; if(i >= buflen) goto fail;
}
/* Set up header */
j = i - 4;
buf[0] = opcode;
buf[1] = 0;
buf[2] = (j >> 8) & 0xFF;
buf[3] = j & 0xFF;
return i;
fail:
return -1;
}
int
server_body(unsigned char opcode, struct config_data *config,
unsigned char *buf, int buflen)
{
int i, j;
struct timeval now, real;
int clock_status;
unsigned nowsecs, expires;
gettime(&now, NULL);
get_real_time(&real, &clock_status);
/* Skip header */
i = 4; if(i >= buflen) goto fail;
if(now.tv_sec >= config->expires_m)
return -1;
expires = config->expires;
if(clock_status != CLOCK_BROKEN) {
nowsecs = htonl(real.tv_sec);
buf[i++] = OPT_ORIGIN_TIME; if(i >= buflen) goto fail;
buf[i++] = 4; if(i >= buflen - 4) goto fail;
memcpy(buf + i, &nowsecs, 4);
i += 4;
}
expires = htonl(expires);
buf[i++] = OPT_MANDATORY; if(i >= buflen) goto fail;
buf[i++] = OPT_EXPIRES; if(i >= buflen) goto fail;
buf[i++] = 4; if(i >= buflen - 4) goto fail;
memcpy(buf + i, &expires, 4);
i += 4;
if(config->ipv4_address) {
int j;
buf[i++] = OPT_IPv4_ADDRESS; if(i >= buflen) goto fail;
buf[i++] = 4 * config->ipv4_address->n;
if(i >= buflen) goto fail;
for(j = 0; j < config->ipv4_address->n; j++) {
if(i >= buflen - 4) goto fail;
memcpy(buf + i, config->ipv4_address->l[j].p + 12, 4);
i += 4;
}
}
if(config->ipv6_prefix) {
int j;
buf[i++] = OPT_IPv6_PREFIX; if(i >= buflen) goto fail;
buf[i++] = 17 * config->ipv6_prefix->n; if(i >= buflen) goto fail;
for(j = 0; j < config->ipv6_prefix->n; j++) {
if(i >= buflen - 17) goto fail;
memcpy(buf + i, config->ipv6_prefix->l[j].p, 16);
i += 16;
buf[i++] = config->ipv6_prefix->l[j].plen;
}
}
if(config->ipv6_address) {
int j;
buf[i++] = OPT_IPv6_ADDRESS; if(i >= buflen) goto fail;
buf[i++] = 16 * config->ipv6_address->n;
if(i >= buflen) goto fail;
for(j = 0; j < config->ipv6_address->n; j++) {
if(i >= buflen - 16) goto fail;
memcpy(buf + i, config->ipv6_address->l[j].p, 16);
i += 16;
}
}
if(config->name_server) {
int j;
buf[i++] = OPT_NAME_SERVER; if(i >= buflen) goto fail;
buf[i++] = 16 * config->name_server->n;
if(i >= buflen) goto fail;
for(j = 0; j < config->name_server->n; j++) {
if(i >= buflen - 6) goto fail;
memcpy(buf + i, config->name_server->l[j].p, 16);
i += 16;
}
}
if(config->ntp_server) {
int j;
buf[i++] = OPT_NTP_SERVER; if(i >= buflen) goto fail;
buf[i++] = 16 * config->ntp_server->n;
if(i >= buflen) goto fail;
for(j = 0; j < config->ntp_server->n; j++) {
if(i >= buflen - 6) goto fail;
memcpy(buf + i, config->ntp_server->l[j].p, 16);
i += 16;
}
}
if(config->server_ipv6) {
int j;
buf[i++] = OPT_MY_IPv6_ADDRESS; if(i >= buflen) goto fail;
buf[i++] = 16 * config->server_ipv6->n; if(i >= buflen) goto fail;
for(j = 0; j < config->server_ipv6->n; j++) {
if(i >= buflen - 16) goto fail;
memcpy(buf + i, config->server_ipv6->l[j].p, 16);
i += 16;
}
}
if(config->server_ipv4) {
int j;
buf[i++] = OPT_MY_IPv4_ADDRESS; if(i >= buflen) goto fail;
buf[i++] = 4 * config->server_ipv4->n; if(i >= buflen) goto fail;
for(j = 0; j < config->server_ipv4->n; j++) {
if(i >= buflen - 4) goto fail;
memcpy(buf + i, config->server_ipv4->l[j].p + 12, 4);
i += 4;
}
}
/* Set up header */
j = i - 4;
buf[0] = opcode;
buf[1] = 0;
buf[2] = (j >> 8) & 0xFF;
buf[3] = j & 0xFF;
return i;
fail:
return -1;
}
int
address_conflict(struct prefix_list *a, struct prefix_list *b)
{
int i, j;
for(i = 0; i < a->n; i++)
for(j = 0; j < b->n; j++)
if(memcmp(a->l[i].p, b->l[j].p, 16) == 0)
return 1;
return 0;
}
/* Determine an interface's hardware address, in modified EUI-64 format */
#ifdef SIOCGIFHWADDR
int
if_eui64(char *ifname, unsigned char *eui)
{
int s, rc;
struct ifreq ifr;
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if(s < 0) return -1;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
rc = ioctl(s, SIOCGIFHWADDR, &ifr);
if(rc < 0) {
int saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
close(s);
switch(ifr.ifr_hwaddr.sa_family) {
case ARPHRD_ETHER:
case ARPHRD_FDDI:
case ARPHRD_IEEE802_TR:
case ARPHRD_IEEE802: {
unsigned char *mac;
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
/* Check for null address and group and global bits */
if(memcmp(mac, zeroes, 6) == 0 ||
(mac[0] & 1) != 0 || (mac[0] & 2) != 0) {
errno = ENOENT;
return -1;
}
memcpy(eui, mac, 3);
eui[3] = 0xFF;
eui[4] = 0xFE;
memcpy(eui + 5, mac + 3, 3);
eui[0] ^= 2;
return 1;
}
case ARPHRD_EUI64:
case ARPHRD_IEEE1394:
case ARPHRD_INFINIBAND: {
unsigned char *mac;
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
if(memcmp(mac, zeroes, 8) == 0 ||
(mac[0] & 1) != 0 || (mac[0] & 2) != 0) {
errno = ENOENT;
return -1;
}
memcpy(eui, mac, 64);
eui[0] ^= 2;
return 1;
}
default:
errno = ENOENT;
return -1;
}
}
#else
#warning Cannot determine MAC addresses on this platform
int
if_eui64(char *ifname, unsigned char *eui)
{
errno = ENOSYS;
return -1;
}
#endif
int
random_eui64(unsigned char *eui)
{
int fd, rc;
/* If there's no /dev/urandom, this will fail with ENOENT,
which the caller will deal with gracefully. */
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0)
return -1;
rc = read(fd, eui, 8);
if(rc < 8)
return -1;
close(fd);
eui[0] &= ~3;
return 1;
}
ahcpd-0.53/configure.h 0000664 0000000 0000000 00000005521 11670236476 0014671 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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.
*/
/* This is used both to hold the contents of a message and to hold our
current configuration. */
struct config_data {
/* The following fields come from a message */
unsigned origin, origin_m, expires, expires_m;
struct prefix_list *server_ipv6, *server_ipv4;
struct prefix_list *ipv6_prefix, *ipv4_prefix,
*ipv6_address, *ipv4_address,
*ipv6_prefix_delegation, *ipv4_prefix_delegation;
int ipv4_mandatory, ipv6_mandatory,
ipv4_delegation_mandatory, ipv6_delegation_mandatory;
struct prefix_list *name_server, *ntp_server;
/* This field is only in our configuration. */
struct prefix_list *our_ipv6_address;
};
extern struct config_data *config_data;
unsigned int config_renew_time(void);
void free_config_data(struct config_data *config);
int config_data_compatible(struct config_data *config1,
struct config_data *config2);
struct config_data *copy_config_data(struct config_data *config);
struct config_data *make_config_data(int expires,
unsigned char *ipv4,
struct server_config *server_config,
char **interfaces);
struct config_data *parse_message(int configure,
const unsigned char *data, int len,
char **interfaces);
int unconfigure(char **interfaces);
int query_body(unsigned char opcode, int time, const unsigned char *ipv4,
unsigned char *buf, int buflen);
int server_body(unsigned char opcode, struct config_data *config,
unsigned char *buf, int buflen);
int address_conflict(struct prefix_list *a, struct prefix_list *b);
int if_eui64(char *ifname, unsigned char *eui);
int random_eui64(unsigned char *eui);
ahcpd-0.53/lease.c 0000664 0000000 0000000 00000053362 11670236476 0014002 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2008, 2009 by Juliusz Chroboczek
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ahcpd.h"
#include "monotonic.h"
#include "lease.h"
#ifdef NO_SERVER
int
lease_init(const char *dir,
const unsigned char *first, const unsigned char *last, int debug)
{
return -1;
}
int
take_lease(const unsigned char *client_id, int client_len,
const unsigned char *suggested_ipv4,
unsigned char *ipv4_return, unsigned *lease_time, int commit)
{
return -1;
}
int
release_lease(const unsigned char *client_id, int client_len,
const unsigned char *ipv4)
{
return -1;
}
#else
#define LEASE_GRACE_TIME 666
#define LEASE_PURGE_TIME (16 * 24 * 3600 + 666)
static unsigned int first_address = 0, last_address = 0;
const char *lease_directory = NULL;
/* A table mapping known IPs to leases. If an entry is missing, everything
is still safe, although we might be unable to give out leases in some
cases; however, if it is incorrect, then we might incorrectly expire
relative leases. */
#define MAX_LEASE_ENTRIES 16384
struct lease_entry {
unsigned char *id;
int id_len;
unsigned address;
unsigned lease_orig; /* real time, 0 if unknown */
unsigned lease_time;
time_t lease_end_m; /* monotonic time, may be negative if expired */
};
static struct lease_entry *entries = NULL;
static int numentries = 0;
static int maxentries = 0;
static unsigned char *
address_ipv4(unsigned a, unsigned char *ipv4)
{
a = htonl(a);
memcpy(ipv4, &a, 4);
return ipv4;
}
static unsigned
ipv4_address(const unsigned char *ipv4)
{
unsigned a;
memcpy(&a, ipv4, 4);
return ntohl(a);
}
static int
entry_match(struct lease_entry *entry, const unsigned char *id, int id_len)
{
if(entry->id == NULL)
return 0;
return (entry->id_len == id_len && memcmp(entry->id, id, id_len) == 0);
}
static struct lease_entry *
find_entry(unsigned address)
{
int i;
for(i = 0; i < numentries; i++) {
if(entries[i].address == address)
return &entries[i];
}
return NULL;
}
static struct lease_entry *
find_entry_by_id(const unsigned char *id, int id_len)
{
int i;
for(i = 0; i < numentries; i++) {
if(entry_match(&entries[i], id, id_len))
return &entries[i];
}
return NULL;
}
static unsigned int
find_entryless(unsigned first, unsigned last)
{
unsigned a;
int i;
for(a = first; a <= last; a++) {
for(i = 0; i < numentries; i++) {
if(entries[i].address == a)
break;
}
if(i >= numentries)
return a;
}
return 0;
}
struct lease_entry *
find_oldest_entry()
{
int i, j = -1;
unsigned age = 0, a;
struct timeval now;
gettime(&now, NULL);
for(i = 0; i < numentries; i++) {
if(entries[i].id == NULL)
continue;
a = now.tv_sec - entries[i].lease_end_m;
if(a > age) {
age = a;
j = i;
}
}
return j >= 0 ? &entries[j] : NULL;
}
static struct lease_entry *
add_entry(const unsigned char *id, int id_len, unsigned int address,
unsigned lease_orig, unsigned lease_time, time_t lease_end_m)
{
struct lease_entry *entry;
int i;
for(i = 0; i < numentries; i++) {
if(entries[i].address == address) {
if(!entry_match(&entries[i], id, id_len))
return NULL;
entry = &entries[i];
goto done;
}
}
entry = NULL;
for(i = 0; i < numentries; i++) {
if(entries[i].id == NULL) {
entry = &entries[i];
break;
}
}
if(entry == NULL) {
if(numentries >= maxentries) {
if(maxentries < MAX_LEASE_ENTRIES) {
int n = MIN(maxentries * 2, MAX_LEASE_ENTRIES);
struct lease_entry *new =
realloc(entries, n * sizeof(struct lease_entry));
if(new) {
entries = new;
maxentries = n;
}
}
}
if(numentries < maxentries)
entry = &entries[numentries++];
}
if(entry == NULL) {
entry = find_oldest_entry();
if(entry == NULL)
return NULL;
free(entry->id);
entry->id = NULL;
entry->id_len = 0;
entry->address = 0;
entry->lease_orig = 0;
entry->lease_time = 0;
entry->lease_end_m = 0;
}
entry->id = malloc(id_len);
if(entry->id == NULL)
return NULL;
memcpy(entry->id, id, id_len);
entry->id_len = id_len;
entry->address = address;
done:
entry->lease_orig = lease_orig;
entry->lease_time = lease_time;
entry->lease_end_m = lease_end_m;
return entry;
}
static char *
lease_file(const unsigned char *ipv4, char *buf, int bufsize)
{
const char *p;
int n;
n = strlen(lease_directory);
if(n >= bufsize - 2)
return NULL;
memcpy(buf, lease_directory, n);
buf[n++] = '/';
p = inet_ntop(AF_INET, ipv4, buf + n, bufsize - n);
if(p == NULL)
return NULL;
return buf;
}
static int
close_lease_file(int fd, int modified)
{
int rc;
again:
if(modified) {
rc = fsync(fd);
if(rc < 0) {
int save;
if(errno == EINTR) goto again;
save = errno;
close(fd);
errno = save;
return -1;
}
}
rc = close(fd);
if(rc < 1 && errno == EINTR)
goto again;
return rc;
}
static int
read_lease_file(int fd, const unsigned char *ipv4,
unsigned *lease_orig_return, unsigned *lease_time_return,
unsigned char *ipv4_return,
unsigned char *client_buf, int client_len)
{
int rc, i;
struct iovec iov[2];
char head[20], name[INET_ADDRSTRLEN];
unsigned lease_orig, lease_time;
i = 0;
iov[i].iov_base = head;
iov[i++].iov_len = 20;
if(client_len > 0) {
iov[i].iov_base = client_buf;
iov[i++].iov_len = client_len;
}
rc = readv(fd, iov, i);
if(rc < 0) {
perror("read(lease_file)");
return -1;
}
if(rc < 20 || rc > 20 + client_len) {
fprintf(stderr, "Truncated lease file for %s.\n", name);
return -1;
}
if(memcmp("AHCP", head, 4) != 0) {
fprintf(stderr, "Corrupted lease file for %s.\n", name);
return -1;
}
if(memcmp("\1\0\0\0", head + 4, 4) != 0) {
fprintf(stderr, "Lease file %s has wrong version.\n", name);
return -1;
}
if(ipv4 && memcmp(ipv4, head + 8, 4) != 0) {
fprintf(stderr, "Mismatched lease file for %s.\n", name);
return -1;
}
memcpy(&lease_orig, head + 12, 4);
lease_orig = ntohl(lease_orig);
memcpy(&lease_time, head + 16, 4);
lease_time = ntohl(lease_time);
if(lease_orig_return)
*lease_orig_return = lease_orig;
if(lease_time_return)
*lease_time_return = lease_time;
if(ipv4_return)
memcpy(ipv4_return, head + 8, 4);
return rc - 20;
}
static int
write_lease_file(int fd, const unsigned char *ipv4,
unsigned lease_orig, unsigned lease_time,
const unsigned char *client_id, int client_len)
{
struct iovec iov[5];
int i;
int rc;
if(client_len > 650)
return -1;
lease_orig = htonl(lease_orig);
lease_time = htonl(lease_time);
i = 0;
iov[i].iov_base = "AHCP\1\0\0\0";
iov[i++].iov_len = 8;
iov[i].iov_base = (void*)ipv4;
iov[i++].iov_len = 4;
iov[i].iov_base = &lease_orig;
iov[i++].iov_len = 4;
iov[i].iov_base = &lease_time;
iov[i++].iov_len = 4;
iov[i].iov_base = (void*)client_id;
iov[i++].iov_len = client_len;
rc = writev(fd, iov, i);
if(rc < 20 + client_len) {
perror("write(lease_file)");
return -1;
}
return 1;
}
static int
update_lease_file(int fd, unsigned lease_orig, unsigned lease_time)
{
off_t lrc;
int rc, i;
struct iovec iov[2];
lease_orig = htonl(lease_orig);
lease_time = htonl(lease_time);
lrc = lseek(fd, 12, SEEK_SET);
if(lrc < 0) {
perror("lseek(lease_file)");
return -1;
}
i = 0;
iov[i].iov_base = &lease_orig;
iov[i++].iov_len = 4;
iov[i].iov_base = &lease_time;
iov[i++].iov_len = 4;
rc = writev(fd, iov, i);
if(rc < 8) {
perror("write(lease_file)");
return -1;
}
return 1;
}
/* Return 1 if the file was removed. */
static int
purge_lease_file(char *fn, unsigned char *ipv4)
{
int fd, rc;
unsigned lease_orig, lease_time;
struct timeval now, real;
time_t stable;
int clock_status;
gettime(&now, &stable);
get_real_time(&real, &clock_status);
fd = open(fn, O_RDWR);
if(fd < 0)
return 0;
rc = read_lease_file(fd, ipv4, &lease_orig, &lease_time, NULL, NULL, 0);
if(rc < 0) {
close_lease_file(fd, 0);
return 0;
}
if(clock_status == CLOCK_TRUSTED && lease_orig > 0) {
if(lease_orig + lease_time + LEASE_PURGE_TIME < real.tv_sec) {
rc = unlink(fn);
if(rc < 0) perror("unlink(lease_file)");
close_lease_file(fd, 1);
return (rc >= 0);
}
}
close_lease_file(fd, 0);
return 0;
}
/* Make a relative lease absolute. */
static int
mutate_lease(char *fn, const unsigned char *ipv4, struct lease_entry *entry)
{
int fd;
unsigned lease_orig, lease_time;
int rc;
struct timeval now, real;
time_t stable;
int clock_status;
time_t orig;
gettime(&now, &stable);
get_real_time(&real, &clock_status);
if(clock_status != CLOCK_TRUSTED)
return -1;
if(entry && entry->address != ipv4_address(ipv4)) {
fprintf(stderr, "Entry mismatch when mutating!\n");
return -1;
}
fd = open(fn, O_RDWR);
if(fd < 0)
return 0;
rc = read_lease_file(fd, ipv4, &lease_orig, &lease_time, NULL, NULL, 0);
if(rc < 0)
goto fail;
if(lease_orig != 0)
goto fail;
if(entry && stable >= lease_time)
orig = real.tv_sec - (entry->lease_end_m - now.tv_sec);
else
orig = real.tv_sec;
if(orig > 1000000000 && orig <= real.tv_sec + 300)
lease_orig = orig;
else
lease_orig = real.tv_sec;
rc = update_lease_file(fd, lease_orig, lease_time);
if(rc < 0)
goto fail;
if(entry)
entry->lease_orig = lease_orig;
close_lease_file(fd, 1);
return 1;
fail:
close_lease_file(fd, 0);
return 0;
}
static int
lease_expired(const unsigned char *ipv4,
unsigned lease_orig, unsigned lease_time)
{
struct timeval now, real;
time_t stable;
int clock_status;
struct lease_entry *entry;
get_real_time(&real, &clock_status);
if(clock_status == CLOCK_TRUSTED && lease_orig > 0)
return lease_orig + LEASE_GRACE_TIME < real.tv_sec;
gettime(&now, &stable);
if(stable < lease_time)
return 0;
if(!ipv4)
return 0;
entry = find_entry(ipv4_address(ipv4));
if(!entry)
return 0;
return entry->lease_end_m + LEASE_GRACE_TIME > now.tv_sec;
}
static int
get_lease(const unsigned char *client_id, int client_len,
const unsigned char *ipv4, unsigned lease_time,
int commit)
{
unsigned char buf[512];
char fn[256], *p;
int fd, rc;
unsigned lease_orig, old_orig, old_time;
time_t lease_end_m;
int clock_status;
struct timeval now, real;
get_real_time(&real, &clock_status);
gettime(&now, NULL);
lease_orig = clock_status == CLOCK_TRUSTED ? real.tv_sec : 0;
lease_end_m = now.tv_sec + lease_time;
p = lease_file(ipv4, fn, 256);
if(p == NULL)
return -1;
fd = open(fn, commit ? O_RDWR : O_RDONLY);
if(fd < 0) {
if(errno == ENOENT) {
if(commit)
goto create;
else
return 1;
}
perror("open(lease_file)");
return -1;
}
rc = read_lease_file(fd, ipv4, &old_orig, &old_time, NULL, buf, 512);
if(rc < 0) {
fprintf(stderr, "Couldn't read lease file.\n");
goto fail;
}
if(rc == client_len && memcmp(buf, client_id, client_len) == 0) {
struct lease_entry *entry;
entry = find_entry(ipv4_address(ipv4));
if(!entry || !entry_match(entry, client_id, client_len)) {
fprintf(stderr, "Eek! Inconsistent lease entry!\n");
goto fail;
}
/* It would be unsafe to shorten this lease's time. */
if(clock_status == CLOCK_TRUSTED && old_orig > 0 && lease_orig > 0)
lease_time = MAX(lease_time, old_orig + old_time - lease_orig);
else
lease_time = MAX(lease_time, entry->lease_end_m - now.tv_sec);
if(commit) {
rc = update_lease_file(fd, lease_orig, lease_time);
if(rc < 0)
goto fail;
entry->lease_orig = lease_orig;
entry->lease_time = lease_time;
entry->lease_end_m = lease_end_m;
}
} else {
if(!lease_expired(ipv4, old_time, old_orig)) {
if(old_orig == 0 && clock_status == CLOCK_TRUSTED)
goto mutate;
else
goto fail;
}
if(commit) {
rc = unlink(fn);
if(rc < 0) {
perror("unlink(lease_file)");
goto fail;
}
close_lease_file(fd, 1);
goto create;
}
}
return close_lease_file(fd, commit);
fail:
close_lease_file(fd, 0);
return -1;
mutate:
close_lease_file(fd, 0);
mutate_lease(fn, ipv4, find_entry(ipv4_address(ipv4)));
return -1;
create:
fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0644);
if(fd < 0) {
perror("creat(lease_file)");
return -1;
}
rc = write_lease_file(fd, ipv4, lease_orig, lease_time,
client_id, client_len);
if(rc < 0)
goto fail;
add_entry(client_id, client_len, ipv4_address(ipv4),
lease_orig, lease_time, lease_end_m);
return close_lease_file(fd, 1);
}
int
release_lease(const unsigned char *client_id, int client_len,
const unsigned char *ipv4)
{
unsigned char buf[512];
char fn[256], *p;
int fd, rc, clock_status;
unsigned orig;
struct timeval now, real;
struct lease_entry *entry;
if(first_address == 0 || lease_directory == NULL)
return -1;
p = lease_file(ipv4, fn, 256);
if(p == NULL)
return -1;
fd = open(fn, O_RDWR);
if(fd < 0) {
perror("open(lease_file)");
return -1;
}
rc = read_lease_file(fd, ipv4, NULL, NULL, NULL, buf, 512);
if(rc < 0)
goto fail;
if(client_id) {
if(rc != client_len || memcmp(buf, client_id, rc) != 0)
goto fail;
}
gettime(&now, NULL);
get_real_time(&real, &clock_status);
if(clock_status == CLOCK_TRUSTED)
orig = real.tv_sec;
else
orig = 0;
rc = update_lease_file(fd, orig, 0);
if(rc < 0) {
rc = unlink(fn);
if(rc < 0) {
perror("unlink(lease_file)");
goto fail;
}
}
rc = close_lease_file(fd, 1);
if(rc < 0)
goto fail;
entry = find_entry(ipv4_address(ipv4));
entry->lease_orig = orig;
entry->lease_time = 0;
entry->lease_end_m = now.tv_sec;
return 1;
fail:
close_lease_file(fd, 0);
return -1;
}
int
lease_init(const char *dir,
const unsigned char *first, const unsigned char *last, int debug)
{
DIR *d;
struct timeval now, real;
int clock_status;
unsigned fa, la;
fa = ipv4_address(first);
la = ipv4_address(last);
if(fa <= 0x1000000 || fa >= la)
return -1;
entries = malloc(16 * sizeof(struct lease_entry));
if(entries == NULL)
return -1;
numentries = 0;
maxentries = 16;
gettime(&now, NULL);
get_real_time(&real, &clock_status);
d = opendir(dir);
if(d == NULL) {
perror("open(lease_dir)");
return -1;
}
while(1) {
struct dirent *e;
unsigned char ipv4[4], client_buf[512];
char name[INET_ADDRSTRLEN], fn[256];
const char *p;
struct lease_entry *entry;
unsigned lease_orig, lease_time;
int fd, rc, len;
e = readdir(d);
if(e == NULL) break;
if(e->d_name[0] == '.')
continue;
rc = snprintf(fn, 256, "%s/%s", dir, e->d_name);
if(rc < 0 || rc >= 256) {
fprintf(stderr, "Couldn't format filename %s/%s.\n",
dir, e->d_name);
continue;
}
fd = open(fn, O_RDONLY);
if(fd < 0) {
fprintf(stderr, "Inaccessible lease file %s.\n", e->d_name);
continue;
}
len = read_lease_file(fd, NULL, &lease_orig, &lease_time,
ipv4, client_buf, 512);
close(fd);
if(len < 0) {
fprintf(stderr, "Corrupted lease file %s.\n", fn);
continue;
}
p = inet_ntop(AF_INET, ipv4, name, INET_ADDRSTRLEN);
if(p == NULL) {
fprintf(stderr, "Couldn't format address.\n");
continue;
}
if(strcmp(p, e->d_name) != 0) {
fprintf(stderr, "Mis-named lease file %s (should be %s).\n",
fn, p);
continue;
}
debugf(1, "Lease file %s: %u %u.\n",
e->d_name, lease_orig, lease_time);
if(clock_status == CLOCK_TRUSTED) {
if(lease_expired(NULL, lease_orig, lease_time)) {
rc = purge_lease_file(fn, ipv4);
if(rc > 0)
continue;
}
}
entry = add_entry(client_buf, len, ipv4_address(ipv4),
lease_orig, lease_time, now.tv_sec + lease_time);
if(entry && clock_status == CLOCK_TRUSTED) {
if(lease_orig == 0)
mutate_lease(fn, ipv4, entry);
}
}
closedir(d);
if(numentries >= MAX_LEASE_ENTRIES) {
fprintf(stderr, "Warning: lease index full.\n"
"Perhaps you should recompile "
"with a larger value for MAX_LEASE_ENTRIES?");
}
lease_directory = dir;
first_address = fa;
last_address = la;
return 1;
}
int
take_lease(const unsigned char *client_id, int client_len,
const unsigned char *suggested_ipv4,
unsigned char *ipv4_return, unsigned *lease_time, int commit)
{
unsigned int a, a0;
unsigned time;
struct lease_entry *entry;
unsigned char ipv4[4];
struct timeval now, real;
int clock_status;
time_t stable;
if(first_address == 0 || lease_directory == NULL)
return -1;
if(client_len < 1)
return -1;
gettime(&now, &stable);
get_real_time(&real, &clock_status);
time = *lease_time;
if(time > MAX_LEASE_TIME)
time = MAX_LEASE_TIME;
if(clock_status != CLOCK_TRUSTED && time > MAX_RELATIVE_LEASE_TIME)
time = MAX_RELATIVE_LEASE_TIME;
a0 = 0;
/* Client suggested an IP. If it is in range, try that. */
if(suggested_ipv4) {
a0 = ipv4_address(suggested_ipv4);
entry = find_entry(a0);
if(entry) {
if(!entry_match(entry, client_id, client_len) &&
!lease_expired(suggested_ipv4,
entry->lease_orig, entry->lease_time))
a0 = 0;
}
}
/* See if we have an old lease for this client. */
if(a0 < first_address || a0 > last_address) {
entry = find_entry_by_id(client_id, client_len);
if(entry)
a0 = entry->address;
}
/* Choose a free slot. */
if(a0 < first_address || a0 > last_address)
a0 = find_entryless(first_address, last_address);
/* Choose the oldest slot. */
if(a0 < first_address || a0 > last_address) {
entry = find_oldest_entry();
if(entry)
a0 = entry->address;
}
/* Give up, take the first one. */
if(a0 < first_address || a0 > last_address)
a0 = first_address;
/* Now scan all addresses in range sequentially, starting at a0. */
a = a0;
do {
int rc;
if(a > last_address)
a = first_address;
rc = get_lease(client_id, client_len, address_ipv4(a, ipv4),
time, commit);
if(rc >= 0) {
memcpy(ipv4_return, ipv4, 4);
*lease_time = time;
return 1;
}
a++;
} while (a != a0);
return -1;
}
#endif
ahcpd-0.53/lease.h 0000664 0000000 0000000 00000003125 11670236476 0013777 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2008, 2009 by Juliusz Chroboczek
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.
*/
#define MAX_LEASE_TIME (8 * 24 * 3600)
#define MAX_RELATIVE_LEASE_TIME (4 * 3600 + 7)
int lease_init(const char *dir,
const unsigned char *first, const unsigned char *last,
int debug);
int take_lease(const unsigned char *client_id, int client_id_len,
const unsigned char *suggested_ipv4,
unsigned char *ipv4_return, unsigned *lease_time,
int commit);
int release_lease(const unsigned char *client_id, int client_id_len,
const unsigned char *ipv4);
ahcpd-0.53/monotonic.c 0000664 0000000 0000000 00000012125 11670236476 0014706 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2008, 2009 by Juliusz Chroboczek
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.
*/
#define _GNU_SOURCE 1
#define _POSIX_C_SOURCE 200112L
#if defined(__linux__)
#define HAVE_ADJTIMEX
#endif
#if defined(__NetBSD__) || defined(__FreeBSD__)
#define HAVE_NTP_GETTIME
#endif
#include
#include
#include
#include
#include "monotonic.h"
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define UNLIKELY(_x) __builtin_expect(!!(_x), 0)
#else
#define UNLIKELY(_x) !!(_x)
#endif
/* Whether we use CLOCK_MONOTONIC */
static int have_posix_clocks = -1;
/* The clock status, broken, untrusted, or trusted. */
static int clock_status = CLOCK_BROKEN;
/* The (monotonic) time since when monotonic time has been stable. If we
have TIME_MONOTONIC, this is the time at which we inited. */
time_t clock_stable_time;
/* The last (monotonic) time we checked for time sync. */
time_t ntp_check_time = 0;
/* These variables are used for simulating monotonic time when we don't
have CLOCK_MONOTONIC. Offset is the offset to add to real time to get
monotonic time; previous is the previous real time. */
static time_t offset, previous;
#if defined(HAVE_ADJTIMEX)
#include
static int
ntp_sync(void)
{
int rc;
struct timex timex;
timex.modes = 0;
rc = adjtimex(&timex);
return (rc >= 0 && rc != TIME_ERROR);
}
#elif defined(HAVE_NTP_GETTIME)
#include
static int
ntp_sync(void)
{
int rc;
struct ntptimeval ntptv;
rc = ntp_gettime(&ntptv);
return (rc >= 0 && ntptv.time_state != TIME_ERROR);
}
#else
static int
ntp_sync(void)
{
return 0;
}
#endif
static void
fix_clock(struct timeval *real)
{
if(!have_posix_clocks) {
if(previous > real->tv_sec) {
offset += previous - real->tv_sec;
} else if(previous + MAX_SLEEP < real->tv_sec) {
offset += previous + MAX_SLEEP - real->tv_sec;
}
}
if(real->tv_sec < 1200000000)
clock_status = CLOCK_BROKEN;
else if(ntp_sync())
clock_status = CLOCK_TRUSTED;
else
clock_status = CLOCK_UNTRUSTED;
}
void
time_init()
{
struct timeval now, real;
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC)
int rc;
struct timespec ts;
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
have_posix_clocks = (rc >= 0);
#else
have_posix_clocks = 0;
#endif
gettimeofday(&real, NULL);
offset = 0;
previous = real.tv_sec;
fix_clock(&real);
gettime(&now, NULL);
clock_stable_time = now.tv_sec;
}
/* -1: no confirmation, just check NTP status.
0: confirm that our time is broken.
1: confirm that our time seems reasonable. */
void
time_confirm(int confirm)
{
struct timeval tv;
gettimeofday(&tv, NULL);
if(tv.tv_sec < 1200000000 || confirm == 0)
clock_status = CLOCK_BROKEN;
else if(ntp_sync())
clock_status = CLOCK_TRUSTED;
else if(confirm > 0)
clock_status = CLOCK_UNTRUSTED;
}
int
get_real_time(struct timeval *tv, int *status_return)
{
int rc;
rc = gettimeofday(tv, NULL);
if(rc < 0)
return rc;
if(UNLIKELY(tv->tv_sec < previous || tv->tv_sec > previous + MAX_SLEEP)) {
fix_clock(tv);
}
previous = tv->tv_sec;
if(status_return)
*status_return = clock_status;
return rc;
}
int
gettime(struct timeval *tv, time_t *stable)
{
int rc;
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC)
if(have_posix_clocks) {
struct timespec ts;
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
if(rc < 0)
return rc;
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
if(stable)
*stable = tv->tv_sec - clock_stable_time;
} else
#else
#warning No CLOCK_MONOTONIC on this platform
#endif
{
rc = get_real_time(tv, NULL);
if(rc < 0)
return rc;
tv->tv_sec += offset;
}
if(stable)
*stable = tv->tv_sec - clock_stable_time;
if(UNLIKELY(tv->tv_sec - ntp_check_time > 3600)) {
time_confirm(-1);
ntp_check_time = tv->tv_sec;
}
return rc;
}
ahcpd-0.53/monotonic.h 0000664 0000000 0000000 00000002472 11670236476 0014717 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2008, 2009 by Juliusz Chroboczek
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.
*/
#define MAX_SLEEP 120
#define CLOCK_BROKEN 0
#define CLOCK_UNTRUSTED 1
#define CLOCK_TRUSTED 2
void time_init(void);
void time_confirm(int confirm);
int get_real_time(struct timeval *tv, int *status_return);
int gettime(struct timeval *tv, time_t *stable);
ahcpd-0.53/prefix.c 0000664 0000000 0000000 00000016715 11670236476 0014207 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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
#include
#include
#include
#include
#include
#include "prefix.h"
void
free_prefix_list(struct prefix_list *l)
{
free(l);
}
struct prefix_list *
copy_prefix_list(struct prefix_list *l)
{
struct prefix_list *c = malloc(sizeof(struct prefix_list));
if(c == NULL)
return NULL;
memcpy(c, l, sizeof(struct prefix_list));
return c;
}
int
prefix_list_eq(struct prefix_list *l1, struct prefix_list *l2)
{
return l1->n == l2->n &&
memcmp(l1->l, l2->l, l1->n * sizeof(struct prefix)) == 0;
}
int
prefix_list_v4(struct prefix_list *l)
{
int i;
for(i = 0; i < l->n; i++) {
if(l->l[i].plen < 96 ||
memcmp(l->l[i].p, v4prefix, 12) != 0)
return 0;
}
return 1;
}
void
prefix_list_extract6(unsigned char *dest, struct prefix_list *p)
{
if(!p || p->n == 0)
memset(dest, 0, 16);
memcpy(dest, p->l[0].p, 16);
}
void
prefix_list_extract4(unsigned char *dest, struct prefix_list *p)
{
if(!p || p->n == 0)
memset(dest, 0, 4);
memcpy(dest, p->l[0].p + 12, 4);
}
static void
parse_a6(struct prefix *p, const unsigned char *data)
{
memcpy(p->p, data, 16);
p->plen = 0xFF;
}
static void
parse_a4(struct prefix *p, const unsigned char *data)
{
memcpy(p->p, v4prefix, 12);
memcpy(p->p + 12, data, 4);
p->plen = 0xFF;
}
static void
parse_p6(struct prefix *p, const unsigned char *data)
{
memcpy(p->p, data, 16);
p->plen = data[16];
}
static void
parse_p4(struct prefix *p, const unsigned char *data)
{
memcpy(p->p, v4prefix, 12);
memcpy(p->p + 12, data, 4);
p->plen = data[4] + 96;
}
/* Note that this returns an empty prefix list, rather than NULL, when len
is 0. We rely on this in order to keep track of requested options. */
struct prefix_list *
raw_prefix_list(const unsigned char *data, int len, int kind)
{
struct prefix_list *l = calloc(1, sizeof(struct prefix_list));
int i, size;
void (*parser)(struct prefix *, const unsigned char*) = NULL;
if(l == NULL)
return NULL;
switch(kind) {
case IPv6_ADDRESS: size = 16; parser = parse_a6; break;
case IPv4_ADDRESS: size = 4; parser = parse_a4; break;
case IPv6_PREFIX: size = 17; parser = parse_p6; break;
case IPv4_PREFIX: size = 5; parser = parse_p4; break;
default: abort();
}
if(len % size != 0)
return NULL;
i = 0;
while(i < len) {
if(l->n >= MAX_PREFIX)
break;
parser(&l->l[l->n], data + i);
l->n++;
i += size;
}
return l;
}
/* Uses a static buffer. */
char *
format_prefix_list(struct prefix_list *p, int kind)
{
static char buf[120];
int i, j, k;
const char *r;
j = 0;
for(i = 0; i < p->n; i++) {
switch(kind) {
case IPv6_ADDRESS:
r = inet_ntop(AF_INET6, p->l[i].p, buf + j, 120 - j);
if(r == NULL) return NULL;
j += strlen(r);
break;
case IPv4_ADDRESS:
r = inet_ntop(AF_INET, p->l[i].p + 12, buf + j, 120 - j);
if(r == NULL) return NULL;
j += strlen(r);
break;
case ADDRESS:
if(memcmp(p->l[i].p, v4prefix, 12) == 0)
r = inet_ntop(AF_INET, p->l[i].p + 12, buf + j, 120 - j);
else
r = inet_ntop(AF_INET6, p->l[i].p, buf + j, 120 - j);
if(r == NULL) return NULL;
j += strlen(r);
break;
case IPv6_PREFIX:
r = inet_ntop(AF_INET6, p->l[i].p, buf + j, 120 - j);
if(r == NULL) return NULL;
j += strlen(r);
k = snprintf(buf + j, 120 - j, "/%u", p->l[i].plen);
if(k < 0 || k >= 120 - j) return NULL;
j += k;
break;
case IPv4_PREFIX:
r = inet_ntop(AF_INET, p->l[i].p + 12, buf + j, 120 - j);
if(r == NULL) return NULL;
j += strlen(r);
k = snprintf(buf + j, 120 - j, "/%u", p->l[i].plen - 96);
if(k < 0 || k >= 120 - j) return NULL;
j += k;
break;
default: abort();
}
if(j >= 119) return NULL;
if(i + 1 < p->n)
buf[j++] = ' ';
}
if(j >= 120)
return NULL;
buf[j++] = '\0';
return buf;
}
struct prefix_list *
parse_prefix(char *address, int kind)
{
struct prefix_list *list;
unsigned char ipv6[16], ipv4[4];
int plen, rc;
plen = 0xFF;
switch(kind) {
case IPv6_ADDRESS:
rc = inet_pton(AF_INET6, address, ipv6);
if(rc > 0)
goto return_ipv6;
return NULL;
case IPv4_ADDRESS:
rc = inet_pton(AF_INET, address, ipv4);
if(rc > 0)
goto return_ipv4;
case ADDRESS:
rc = inet_pton(AF_INET, address, ipv4);
if(rc > 0)
goto return_ipv4;
rc = inet_pton(AF_INET6, address, ipv6);
if(rc > 0)
goto return_ipv6;
return NULL;
case PREFIX: {
char buf[30];
char *p;
p = strchr(address, '/');
if(p == NULL || p - address >= 30)
return NULL;
plen = atoi(p + 1);
if(plen <= 0 || plen > 128)
return NULL;
memcpy(buf, address, p - address);
buf[p - address] = '\0';
rc = inet_pton(AF_INET, buf, ipv4);
if(rc > 0) {
if(plen > 32)
return NULL;
goto return_ipv4;
}
rc = inet_pton(AF_INET6, buf, ipv6);
if(rc > 0)
goto return_ipv6;
return NULL;
}
default:
abort();
}
return_ipv6:
list = calloc(1, sizeof(struct prefix_list));
if(list == NULL)
return NULL;
list->n = 1;
memcpy(list->l[0].p, ipv6, 16);
list->l[0].plen = plen;
return list;
return_ipv4:
list = calloc(1, sizeof(struct prefix_list));
if(list == NULL)
return NULL;
list->n = 1;
memcpy(list->l[0].p, v4prefix, 12);
memcpy(list->l[0].p + 12, ipv4, 4);
list->l[0].plen = plen == 0xFF ? 0xFF : plen + 96;
return list;
}
struct prefix_list *
cat_prefix_list(struct prefix_list *p1, struct prefix_list *p2)
{
int i;
if(p1 == NULL)
return p2;
if(p2 == NULL)
return p1;
if(p1->n + p2->n > MAX_PREFIX)
return NULL;
for(i = 0; i < p2->n; i++)
p1->l[p1->n + i] = p2->l[i];
p1->n += p2->n;
free(p2);
return p1;
}
ahcpd-0.53/prefix.h 0000664 0000000 0000000 00000004326 11670236476 0014207 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2010 by Juliusz Chroboczek
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.
*/
#define IPv6_ADDRESS 0
#define IPv4_ADDRESS 1
#define ADDRESS 2
#define IPv6_PREFIX 3
#define IPv4_PREFIX 4
#define PREFIX 5
extern const unsigned char v4prefix[16];
/* This holds both IPv6 and IPv4, and both prefixes and addresses. For
IPv4, we use v4prefix above. For addresses, plen is 0xFF. */
struct prefix {
unsigned char p[16];
unsigned char plen;
};
#define MAX_PREFIX 8
struct prefix_list {
int n;
struct prefix l[MAX_PREFIX];
};
void free_prefix_list(struct prefix_list *l);
struct prefix_list *copy_prefix_list(struct prefix_list *l);
int prefix_list_eq(struct prefix_list *l1, struct prefix_list *l2);
int prefix_list_v4(struct prefix_list *l);
void prefix_list_extract4(unsigned char *dest, struct prefix_list *p);
void prefix_list_extract6(unsigned char *dest, struct prefix_list *p);
struct prefix_list *raw_prefix_list(const unsigned char *data, int len,
int kind);
char *format_prefix_list(struct prefix_list *p, int kind);
struct prefix_list *parse_prefix(char *address, int kind);
struct prefix_list *cat_prefix_list(struct prefix_list *p1,
struct prefix_list *p2);
ahcpd-0.53/protocol.h 0000664 0000000 0000000 00000003133 11670236476 0014546 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2007-2009 by Juliusz Chroboczek
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.
*/
/* Opcodes */
#define AHCP_DISCOVER 0
#define AHCP_OFFER 1
#define AHCP_REQUEST 2
#define AHCP_ACK 3
#define AHCP_NACK 4
#define AHCP_RELEASE 5
/* Options */
#define OPT_PAD 0
#define OPT_MANDATORY 1
#define OPT_ORIGIN_TIME 2
#define OPT_EXPIRES 3
#define OPT_MY_IPv6_ADDRESS 4
#define OPT_MY_IPv4_ADDRESS 5
#define OPT_IPv6_PREFIX 6
#define OPT_IPv4_PREFIX 7
#define OPT_IPv6_ADDRESS 8
#define OPT_IPv4_ADDRESS 9
#define OPT_IPv6_PREFIX_DELEGATION 10
#define OPT_IPv4_PREFIX_DELEGATION 11
#define OPT_NAME_SERVER 12
#define OPT_NTP_SERVER 13
ahcpd-0.53/transport.c 0000664 0000000 0000000 00000015171 11670236476 0014741 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2008, 2009 by Juliusz Chroboczek
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ahcpd.h"
#include "monotonic.h"
#include "transport.h"
unsigned myseqno;
#define NUMDUPLICATE 64
struct duplicate {
struct timeval time;
unsigned char src[8];
unsigned char dest[8];
unsigned char nonce[4];
};
struct duplicate duplicate[NUMDUPLICATE];
int next_duplicate = 0;
static int
really_send_packet(struct sockaddr *sin, int sinlen,
unsigned char hopcount, unsigned char original_hopcount,
const unsigned char *nonce,
const unsigned char *src, const unsigned char *dest,
const unsigned char *data, size_t datalen)
{
struct iovec iovec[2];
struct msghdr msg;
unsigned char header[24];
int rc, ret;
header[0] = 43;
header[1] = 1;
header[2] = hopcount;
header[3] = original_hopcount;
memcpy(header + 4, nonce, 4);
memcpy(header + 8, src, 8);
memcpy(header + 16, dest ? dest : ones, 8);
iovec[0].iov_base = (void*)header;
iovec[0].iov_len = sizeof(header);
iovec[1].iov_base = (void*)data;
iovec[1].iov_len = datalen;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iovec;
msg.msg_iovlen = 2;
if(sin) {
msg.msg_name = sin;
msg.msg_namelen = sinlen;
rc = sendmsg(protocol_socket, &msg, 0);
if(rc < 0) {
int save = errno;
perror("send");
errno = save;
return -1;
}
return 1;
} else {
int i, saved_errno = EHOSTUNREACH;
ret = -1;
for(i = 0; i < numnetworks; i++) {
struct sockaddr_in6 sin6;
if(networks[i].ifindex <= 0)
continue;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &protocol_group, 16);
sin6.sin6_port = htons(protocol_port);
sin6.sin6_scope_id = networks[i].ifindex;
msg.msg_name = (struct sockaddr*)&sin6;
msg.msg_namelen = sizeof(sin6);
rc = sendmsg(protocol_socket, &msg, 0);
if(rc < 0) {
saved_errno = errno;
perror("send");
/* but don't reset ret -- only return -1 if all sends failed. */
} else {
ret = 1;
}
errno = saved_errno;
}
return ret;
}
}
int send_packet(struct sockaddr *sin, int sinlen,
const unsigned char *dest, int hopcount,
const unsigned char *buf, size_t bufsize)
{
unsigned char nonce[4];
if(hopcount <= 0)
return 0;
memcpy(nonce, &myseqno, 4);
myseqno++;
return really_send_packet(sin, sinlen,
hopcount, hopcount, nonce, myid, dest,
buf, bufsize);
}
static int
check_duplicate(const unsigned char *header)
{
int i;
struct timeval now;
gettime(&now, NULL);
for(i = 0; i < NUMDUPLICATE; i++) {
if(duplicate[i].time.tv_sec < now.tv_sec - 120)
continue;
if(memcmp(header + 4, duplicate[i].nonce, 4) == 0 &&
memcmp(header + 8, duplicate[i].src, 8) == 0 &&
memcmp(header + 16, duplicate[i].dest, 8) == 0)
return 1;
}
return 0;
}
static void
record_duplicate(const unsigned char *header)
{
memcpy(duplicate[next_duplicate].nonce, header + 4, 4);
memcpy(duplicate[next_duplicate].src, header + 8, 8);
memcpy(duplicate[next_duplicate].dest, header + 16, 8);
gettime(&duplicate[next_duplicate].time, NULL);
next_duplicate = (next_duplicate + 1) % NUMDUPLICATE;
}
/* Take an incoming packet, forward it if necessary, return 2 if it needs
to be handled by the local node. */
int
handle_packet(int ll, const unsigned char *buf, size_t buflen)
{
if(buflen < 2) {
debugf(1, "Received truncated packet.\n");
return 0;
}
if(buf[0] != 43) {
debugf(1, "Received corrupted packet.\n");
return 0;
}
if(buf[1] != 1) {
debugf(2, "Received packet with version %d.\n", buf[1]);
return 0;
}
if(buflen < 24) {
debugf(1, "Received truncated packet.\n");
return 0;
}
if(buf[2] <= 0 || buf[3] <= 0 || buf[2] > buf[3]) {
debugf(1, "Received packet with zero hop count.\n");
return 0;
}
if(memcmp(buf + 8, zeroes, 8) == 0 || memcmp(buf + 8, ones, 8) == 0) {
debugf(1, "Received packet with martian source.\n");
return 0;
}
if(memcmp(buf + 16, zeroes, 8) == 0) {
debugf(1, "Received packet with martian destination.\n");
return 0;
}
/* The following tests do trigger in normal operation. */
if(memcmp(buf + 8, myid, 8) == 0) {
debugf(3, "Suppressed packet from self.\n");
return 0;
}
if(check_duplicate(buf)) {
debugf(3, "Suppressed duplicate.\n");
return 0;
}
record_duplicate(buf);
if(memcmp(buf + 16, myid, 8) == 0)
return 2;
if(buf[2] >= 2) {
debugf(2, "Forwarding packet, %d/%d hops left.\n",
buf[2] - 1, buf[3]);
usleep(random() % 50000);
really_send_packet(NULL, 0,
buf[2] - 1, buf[3],
buf + 4, buf + 8, buf + 16,
buf + 24, buflen - 24);
}
if(memcmp(buf + 16, ones, 8) == 0)
return 2;
else
return 1;
}
ahcpd-0.53/transport.h 0000664 0000000 0000000 00000002473 11670236476 0014747 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2008, 2009 by Juliusz Chroboczek
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.
*/
extern unsigned myseqno;
int send_packet(struct sockaddr *sin, int sinlen,
const unsigned char *dest, int hopcount,
const unsigned char *buf, size_t bufsize);
int handle_packet(int ll, const unsigned char *buf, size_t buflen);