Commit ab5d88f4 authored by Stephane Glondu's avatar Stephane Glondu

Imported Upstream version 0.50

parents
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.
PREFIX = /usr/local
CDEBUGFLAGS = -Os -g -Wall
DEFINES = $(PLATFORM_DEFINES)
CFLAGS = $(CDEBUGFLAGS) $(DEFINES) $(EXTRA_DEFINES)
SRCS = ahcpd.c monotonic.c transport.c config.c lease.c
OBJS = ahcpd.o monotonic.o transport.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|<a href='babel.8'|<a href=\"../babel/babel.html\"|" \
-e "s|<a href='\\(ahcp[-a-z]*\\).8'|<a href=\"\1.html\"|" \
-e "s|<a href='[^']*8'>\\(.*(8)\\)</a>|\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 /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 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. Your
server should run something like
$ ahcpd -S fde6:20f5:c9ac:358::,192.168.4.128,192.168.4.254,/var/lib/leases \
wlan0
where fde6:20f5:c9ac:358:: is your IPv6 prefix, 192.168.4.128 through
192.168.4.254 is the range of IPv4 addresses you want to give out to
clients, and /var/lib/leases is the location of the persistent lease
database.
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. Simply run
something like
# ahcpd wlan0
and you should get IPv4 and IPv6 addresses in a few seconds.
Juliusz Chroboczek
<jch@pps.jussieu.fr>
#!/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" = "Darwin" ]; then
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
}
else
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"
}
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
This diff is collapsed.
/*
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.
*/
#undef MAX
#undef MIN
#define MAX(x,y) ((x)<=(y)?(y):(x))
#define MIN(x,y) ((x)<=(y)?(x):(y))
extern int nodns, af;
extern char *config_script;
extern int debug_level;
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();
.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"
Speficy the link-local multicast address to be used by AHCP.
.TP
.BI \-p " port"
Specify the UDP port number to be used by AHCP.
.TP
.B \-n
Participate in the flooding protocol, but don't actually perform any
configuration.
.TP
.B \-4
Only attempt to configure IPv4 addresses.
.TP
.B \-4
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.
.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 \-S " ipv6,first,last,dir,name-server,ntp-server"
Run as an AHCP server rather than a client.
.I Ipv6
specifies the IPv6 prefix.
.I First
and
.I last
represent the bounds of the range of IPv4 addresses available; active
leases will be stored in the directory
.IR dir ,
which should be in persistent storage (not a RAM disk).
.B Name-server
and
.B ntp-server
can be IPv4 or IPv6 addresses, and specify respectively the address of the
DNS server and of the NTP server to configure clients with.
.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.
.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 babel (8),
.BR olsrd (8),
.BR ntpd (8),
.IR "The Ad-Hoc Configuration Protocol" .
.SH AUTHOR
Juliusz Chroboczek.
This diff is collapsed.
/*
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.
*/
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];
};
/* 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;
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;
void prefix_list_extract4(unsigned char *dest, struct prefix_list *p);
void prefix_list_extract6(unsigned char *dest, struct prefix_list *p);
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,
unsigned char *ipv6_prefix,
unsigned char *name_server,
int name_server_len,
unsigned char *ntp_server,
int ntp_server_len,
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);
This diff is collapsed.
/*
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);
/*
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 <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#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 <sys/timex.h>
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 <sys/timex.h>
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</