summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2007-04-12 18:57:31 +0000
committerLennart Poettering <lennart@poettering.net>2007-04-12 18:57:31 +0000
commitf10b82b26456f44d03d0c28d93c5118b859d3891 (patch)
tree1f56d1c77f8a461a162e3259c0110c9a7a9079d2
parent6befbfaeb84cea2fa36d144f812b183400dd02ea (diff)
port avahi-autoipd to FreeBSD (original patch from Bruce M Simpson)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1402 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
-rw-r--r--avahi-autoipd/Makefile.am19
-rwxr-xr-xavahi-autoipd/avahi-autoipd.action.bsd50
-rwxr-xr-xavahi-autoipd/avahi-autoipd.action.linux (renamed from avahi-autoipd/avahi-autoipd.action)0
-rw-r--r--avahi-autoipd/iface-bsd.c416
-rw-r--r--avahi-autoipd/main.c344
5 files changed, 787 insertions, 42 deletions
diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am
index 11e6493..7589688 100644
--- a/avahi-autoipd/Makefile.am
+++ b/avahi-autoipd/Makefile.am
@@ -36,15 +36,30 @@ sbin_PROGRAMS = avahi-autoipd
avahi_autoipd_SOURCES = \
main.c main.h \
../avahi-daemon/setproctitle.c ../avahi-daemon/setproctitle.h \
- iface.h iface-linux.c \
+ iface.h \
../avahi-common/malloc.h ../avahi-common/malloc.c \
../avahi-common/timeval.h ../avahi-common/timeval.c
avahi_autoipd_CFLAGS = $(AM_CFLAGS) $(LIBDAEMON_CFLAGS)
avahi_autoipd_LDADD = $(AM_LDADD) $(LIBDAEMON_LIBS)
+if TARGET_FREEBSD
+avahi_autoipd_SOURCES += iface-bsd.c
+avahi_autoipd_LDADD += -lpcap
+else
+avahi_autoipd_SOURCES += iface-linux.c
+endif
+
pkgsysconf_SCRIPTS=avahi-autoipd.action
+if TARGET_FREEBSD
+avahi-autoipd.action: avahi-autoipd.action.bsd
+ cp $< $@
+else
+avahi-autoipd.action: avahi-autoipd.action.linux
+ cp $< $@
+endif
+
if TARGET_DEBIAN
noinst_SCRIPTS = dhclient-enter-hook dhclient-exit-hook
@@ -78,4 +93,4 @@ endif
EXTRA_DIST=avahi-autoipd.action dhclient-enter-hook.in dhclient-exit-hook.in
-CLEANFILES=dhclient-enter-hook dhclient-exit-hook
+CLEANFILES=dhclient-enter-hook dhclient-exit-hook avahi-autoipd.action
diff --git a/avahi-autoipd/avahi-autoipd.action.bsd b/avahi-autoipd/avahi-autoipd.action.bsd
new file mode 100755
index 0000000..db39c4e
--- /dev/null
+++ b/avahi-autoipd/avahi-autoipd.action.bsd
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# $Id$
+#
+# This file is part of avahi.
+#
+# avahi is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# avahi is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with avahi; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+set -e
+
+# Command line arguments:
+# $1 event that happened:
+# BIND: Successfully claimed address
+# CONFLICT: An IP address conflict happened
+# UNBIND: The IP address is no longer needed
+# STOP: The daemon is terminating
+# $2 interface name
+# $3 IP adddress
+
+# We have the BSD ifconfig tool
+
+case "$1" in
+BIND)
+ ifconfig "$2" "$3"/16
+ ;;
+
+CONFLICT|STOP|UNBIND)
+ ifconfig "$2" "$3"/16 delete
+ ;;
+
+*)
+ echo "Unknown event $1" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/avahi-autoipd/avahi-autoipd.action b/avahi-autoipd/avahi-autoipd.action.linux
index 27f00bb..27f00bb 100755
--- a/avahi-autoipd/avahi-autoipd.action
+++ b/avahi-autoipd/avahi-autoipd.action.linux
diff --git a/avahi-autoipd/iface-bsd.c b/avahi-autoipd/iface-bsd.c
new file mode 100644
index 0000000..23c02dd
--- /dev/null
+++ b/avahi-autoipd/iface-bsd.c
@@ -0,0 +1,416 @@
+/* rcs tags go here */
+/* Original author: Bruce M. Simpson <bms@FreeBSD.org> */
+
+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#include <libdaemon/dlog.h>
+
+#include <avahi-common/llist.h>
+#include <avahi-common/malloc.h>
+
+#include "iface.h"
+
+#ifndef IN_LINKLOCAL
+#define IN_LINKLOCAL(i) (((u_int32_t)(i) & (0xffff0000)) == (0xa9fe0000))
+#endif
+
+#ifndef elementsof
+#define elementsof(array) (sizeof(array)/sizeof(array[0]))
+#endif
+
+#ifndef so_set_nonblock
+#define so_set_nonblock(s, val) \
+ do { \
+ int __flags; \
+ __flags = fcntl((s), F_GETFL); \
+ if (__flags == -1) \
+ break; \
+ if (val != 0) \
+ __flags |= O_NONBLOCK; \
+ else \
+ __flags &= ~O_NONBLOCK; \
+ (void)fcntl((s), F_SETFL, __flags); \
+ } while (0)
+#endif
+
+#define MAX_RTMSG_SIZE 2048
+
+struct rtm_dispinfo {
+ u_char *di_buf;
+ ssize_t di_buflen;
+ ssize_t di_len;
+};
+
+union rtmunion {
+ struct rt_msghdr rtm;
+ struct if_msghdr ifm;
+ struct ifa_msghdr ifam;
+ struct ifma_msghdr ifmam;
+ struct if_announcemsghdr ifan;
+};
+typedef union rtmunion rtmunion_t;
+
+struct Address;
+typedef struct Address Address;
+
+struct Address {
+ in_addr_t address;
+ AVAHI_LLIST_FIELDS(Address, addresses);
+};
+
+static int rtm_dispatch(void);
+static int rtm_dispatch_newdeladdr(struct rtm_dispinfo *di);
+static int rtm_dispatch_ifannounce(struct rtm_dispinfo *di);
+static struct sockaddr *next_sa(struct sockaddr *sa);
+
+static int fd = -1;
+static int ifindex = -1;
+static AVAHI_LLIST_HEAD(Address, addresses) = NULL;
+
+int
+iface_init(int idx)
+{
+
+ fd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (fd == -1) {
+ daemon_log(LOG_ERR, "socket(PF_ROUTE): %s", strerror(errno));
+ return (-1);
+ }
+
+ so_set_nonblock(fd, 1);
+
+ ifindex = idx;
+
+ return (fd);
+}
+
+int
+iface_get_initial_state(State *state)
+{
+ int mib[6];
+ char *buf;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ char *lim;
+ char *next;
+ struct sockaddr *sa;
+ size_t len;
+ int naddrs;
+
+ assert(state != NULL);
+ assert(fd != -1);
+
+ naddrs = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+
+ if (sysctl(mib, elementsof(mib), NULL, &len, NULL, 0) != 0) {
+ daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ daemon_log(LOG_ERR, "malloc(%d): %s", len, strerror(errno));
+ return (-1);
+ }
+
+ if (sysctl(mib, elementsof(mib), buf, &len, NULL, 0) != 0) {
+ daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
+ strerror(errno));
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_NEWADDR) {
+ ifam = (struct ifa_msghdr *)next;
+ sa = (struct sockaddr *)(ifam + 1);
+ if (sa->sa_family != AF_INET)
+ continue;
+ ++naddrs;
+ }
+ }
+ free(buf);
+
+ *state = (naddrs > 0) ? STATE_SLEEPING : STATE_START;
+
+ return (0);
+}
+
+int
+iface_process(Event *event)
+{
+ int b;
+
+ assert(fd != -1);
+
+ b = !!addresses;
+
+ if (rtm_dispatch() == -1)
+ return (-1);
+
+ if (b && !addresses)
+ *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
+ else if (!b && addresses)
+ *event = EVENT_ROUTABLE_ADDR_CONFIGURED;
+
+ return (0);
+}
+
+void
+iface_done(void)
+{
+ Address *a;
+
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+
+ while ((a = addresses) != NULL) {
+ AVAHI_LLIST_REMOVE(Address, addresses, addresses, a);
+ avahi_free(a);
+ }
+}
+
+/*
+ * Dispatch kernel routing socket messages.
+ */
+static int
+rtm_dispatch(void)
+{
+ struct msghdr mh;
+ struct iovec iov[1];
+ struct rt_msghdr *rtm;
+ struct rtm_dispinfo *di;
+ ssize_t len;
+ int retval;
+
+ di = malloc(sizeof(*di));
+ if (di == NULL) {
+ daemon_log(LOG_ERR, "malloc(%d): %s", sizeof(*di),
+ strerror(errno));
+ return (-1);
+ }
+ di->di_buflen = MAX_RTMSG_SIZE;
+ di->di_buf = calloc(MAX_RTMSG_SIZE, 1);
+ if (di->di_buf == NULL) {
+ free(di);
+ daemon_log(LOG_ERR, "calloc(%d): %s", MAX_RTMSG_SIZE,
+ strerror(errno));
+ return (-1);
+ }
+
+ memset(&mh, 0, sizeof(mh));
+ iov[0].iov_base = di->di_buf;
+ iov[0].iov_len = di->di_buflen;
+ mh.msg_iov = iov;
+ mh.msg_iovlen = 1;
+
+ retval = 0;
+ for (;;) {
+ len = recvmsg(fd, &mh, MSG_DONTWAIT);
+ if (len == -1) {
+ if (errno == EWOULDBLOCK)
+ break;
+ else {
+ daemon_log(LOG_ERR, "recvmsg(): %s",
+ strerror(errno));
+ retval = -1;
+ break;
+ }
+ }
+
+ rtm = (void *)di->di_buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ daemon_log(LOG_ERR,
+ "unknown routing socket message (version %d)\n",
+ rtm->rtm_version);
+ /* this is non-fatal; just ignore it for now. */
+ continue;
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ retval = rtm_dispatch_newdeladdr(di);
+ break;
+ case RTM_IFANNOUNCE:
+ retval = rtm_dispatch_ifannounce(di);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If we got an error; assume our position on the call
+ * stack is enclosed by a level-triggered event loop,
+ * and signal the error condition.
+ */
+ if (retval != 0)
+ break;
+ }
+ free(di->di_buf);
+ free(di);
+
+ return (retval);
+}
+
+/* handle link coming or going away */
+static int
+rtm_dispatch_ifannounce(struct rtm_dispinfo *di)
+{
+ rtmunion_t *rtm = (void *)di->di_buf;
+
+ assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE);
+
+ switch (rtm->ifan.ifan_what) {
+ case IFAN_ARRIVAL:
+ if (rtm->ifan.ifan_index == ifindex) {
+ daemon_log(LOG_ERR,
+"RTM_IFANNOUNCE IFAN_ARRIVAL, for ifindex %d, which we already manage.",
+ ifindex);
+ return (-1);
+ }
+ break;
+ case IFAN_DEPARTURE:
+ if (rtm->ifan.ifan_index == ifindex) {
+ daemon_log(LOG_ERR, "Interface vanished.");
+ return (-1);
+ }
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+
+ return (0);
+}
+
+static struct sockaddr *
+next_sa(struct sockaddr *sa)
+{
+ void *p;
+ size_t sa_size;
+
+ sa_size = sa->sa_len;
+ if (sa_size < sizeof(u_long))
+ sa_size = sizeof(u_long);
+ p = ((char *)sa) + sa_size;
+
+ return (struct sockaddr *)p;
+}
+
+/* handle address coming or going away */
+static int
+rtm_dispatch_newdeladdr(struct rtm_dispinfo *di)
+{
+ Address *ap;
+ rtmunion_t *rtm;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+
+/* macro to skip to next RTA; has side-effects */
+#define SKIPRTA(rtmsgp, rta, sa) \
+ do { \
+ if ((rtmsgp)->rtm_addrs & (rta)) \
+ (sa) = next_sa((sa)); \
+ } while (0)
+
+ rtm = (void *)di->di_buf;
+
+ assert(rtm->rtm.rtm_type == RTM_NEWADDR ||
+ rtm->rtm.rtm_type == RTM_DELADDR);
+
+ if (rtm->rtm.rtm_index != ifindex)
+ return (0);
+
+ if (!(rtm->rtm.rtm_addrs & RTA_IFA)) {
+ daemon_log(LOG_ERR, "ifa msg has no RTA_IFA.");
+ return (0);
+ }
+
+ /* skip over rtmsg padding correctly */
+ sa = (struct sockaddr *)((&rtm->ifam) + 1);
+ SKIPRTA(&rtm->rtm, RTA_DST, sa);
+ SKIPRTA(&rtm->rtm, RTA_GATEWAY, sa);
+ SKIPRTA(&rtm->rtm, RTA_NETMASK, sa);
+ SKIPRTA(&rtm->rtm, RTA_GENMASK, sa);
+ SKIPRTA(&rtm->rtm, RTA_IFP, sa);
+
+ /*
+ * sa now points to RTA_IFA sockaddr; we are only interested
+ * in updates for link-local addresses.
+ */
+ if (sa->sa_family != AF_INET)
+ return (0);
+ sin = (struct sockaddr_in *)sa;
+ if (!IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
+ return (0);
+
+ for (ap = addresses; ap; ap = ap->addresses_next) {
+ if (ap->address == sin->sin_addr.s_addr)
+ break;
+ }
+ if (rtm->rtm.rtm_type == RTM_DELADDR && ap != NULL) {
+ AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap);
+ avahi_free(ap);
+ }
+ if (rtm->rtm.rtm_type == RTM_NEWADDR && ap == NULL) {
+ ap = avahi_new(Address, 1);
+ ap->address = sin->sin_addr.s_addr;
+ AVAHI_LLIST_PREPEND(Address, addresses, addresses, ap);
+ }
+
+ return (0);
+#undef SKIPRTA
+}
diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c
index 60fc8aa..58811a3 100644
--- a/avahi-autoipd/main.c
+++ b/avahi-autoipd/main.c
@@ -23,32 +23,49 @@
#include <config.h>
#endif
-#include <stdlib.h>
-#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
+#include <sys/wait.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+
+#ifdef __linux__
#include <netpacket/packet.h>
+#endif
#include <net/ethernet.h>
-#include <fcntl.h>
-#include <time.h>
+#include <net/if.h>
+#ifdef __FreeBSD__
+#include <net/if_dl.h>
+#include <net/route.h>
+#endif
+#include <arpa/inet.h>
+
#include <assert.h>
#include <errno.h>
-#include <string.h>
#include <inttypes.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
-#include <sys/ioctl.h>
-#include <poll.h>
-#include <net/if.h>
+#include <fcntl.h>
+#include <stdlib.h>
#include <stdio.h>
-#include <getopt.h>
#include <signal.h>
-#include <sys/wait.h>
-#include <pwd.h>
+#include <string.h>
+#include <time.h>
+#include <getopt.h>
+
#include <grp.h>
+#include <poll.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#ifndef __linux__
+#include <pcap.h>
+#endif
#include <avahi-common/malloc.h>
#include <avahi-common/timeval.h>
-
#include <avahi-daemon/setproctitle.h>
#include <libdaemon/dfork.h>
@@ -60,10 +77,6 @@
#include "main.h"
#include "iface.h"
-#ifndef __linux__
-#error "avahi-autoipd is only available on Linux for now"
-#endif
-
/* An implementation of RFC 3927 */
/* Constants from the RFC */
@@ -84,6 +97,7 @@
#define IPV4LL_BROADCAST 0xA9FEFFFFL
#define ETHER_ADDRLEN 6
+#define ETHER_HDR_SIZE (2+2*ETHER_ADDRLEN)
#define ARP_PACKET_SIZE (8+4+4+2*ETHER_ADDRLEN)
typedef enum ArpOperation {
@@ -98,6 +112,11 @@ typedef struct ArpPacketInfo {
uint8_t sender_hw_address[ETHER_ADDRLEN], target_hw_address[ETHER_ADDRLEN];
} ArpPacketInfo;
+typedef struct ArpPacket {
+ uint8_t *ether_header;
+ uint8_t *ether_payload;
+} ArpPacket;
+
static State state = STATE_START;
static int n_iteration = 0;
static int n_conflict = 0;
@@ -249,16 +268,44 @@ fail:
return -1;
}
-static void* packet_new(const ArpPacketInfo *info, size_t *packet_len) {
+/*
+ * Allocate a buffer with two pointers in front, one of which is
+ * guaranteed to point ETHER_HDR_SIZE bytes into it.
+ */
+static ArpPacket* packet_new(size_t packet_len) {
+ ArpPacket *p;
+ uint8_t *b;
+
+ assert(packet_len > 0);
+
+#ifdef __linux__
+ b = avahi_new0(uint8_t, sizeof(struct ArpPacket) + packet_len);
+ p = (ArpPacket*) b;
+ p->ether_header = NULL;
+ p->ether_payload = b + sizeof(struct ArpPacket);
+
+#else
+ b = avahi_new0(uint8_t, sizeof(struct ArpPacket) + ETHER_HDR_SIZE + packet_len);
+ p = (ArpPacket*) b;
+ p->ether_header = b + sizeof(struct ArpPacket);
+ p->ether_payload = b + sizeof(struct ArpPacket) + ETHER_HDR_SIZE;
+#endif
+
+ return p;
+}
+
+static ArpPacket* packet_new_with_info(const ArpPacketInfo *info, size_t *packet_len) {
+ ArpPacket *p = NULL;
uint8_t *r;
assert(info);
- assert(packet_len);
assert(info->operation == ARP_REQUEST || info->operation == ARP_RESPONSE);
+ assert(packet_len != NULL);
*packet_len = ARP_PACKET_SIZE;
- r = avahi_new0(uint8_t, *packet_len);
-
+ p = packet_new(*packet_len);
+ r = p->ether_payload;
+
r[1] = 1; /* HTYPE */
r[2] = 8; /* PTYPE */
r[4] = ETHER_ADDRLEN; /* HLEN */
@@ -270,10 +317,10 @@ static void* packet_new(const ArpPacketInfo *info, size_t *packet_len) {
memcpy(r+18, info->target_hw_address, ETHER_ADDRLEN);
memcpy(r+24, &info->target_ip_address, 4);
- return r;
+ return p;
}
-static void *packet_new_probe(uint32_t ip_address, const uint8_t*hw_address, size_t *packet_len) {
+static ArpPacket *packet_new_probe(uint32_t ip_address, const uint8_t*hw_address, size_t *packet_len) {
ArpPacketInfo info;
memset(&info, 0, sizeof(info));
@@ -281,10 +328,10 @@ static void *packet_new_probe(uint32_t ip_address, const uint8_t*hw_address, siz
memcpy(info.sender_hw_address, hw_address, ETHER_ADDRLEN);
info.target_ip_address = ip_address;
- return packet_new(&info, packet_len);
+ return packet_new_with_info(&info, packet_len);
}
-static void *packet_new_announcement(uint32_t ip_address, const uint8_t* hw_address, size_t *packet_len) {
+static ArpPacket *packet_new_announcement(uint32_t ip_address, const uint8_t* hw_address, size_t *packet_len) {
ArpPacketInfo info;
memset(&info, 0, sizeof(info));
@@ -293,13 +340,15 @@ static void *packet_new_announcement(uint32_t ip_address, const uint8_t* hw_addr
info.target_ip_address = ip_address;
info.sender_ip_address = ip_address;
- return packet_new(&info, packet_len);
+ return packet_new_with_info(&info, packet_len);
}
-static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info) {
- const uint8_t *p = data;
+static int packet_parse(const ArpPacket *packet, size_t packet_len, ArpPacketInfo *info) {
+ const uint8_t *p;
- assert(data);
+ assert(packet);
+ p = (uint8_t *)packet->ether_payload;
+ assert(p);
if (packet_len < ARP_PACKET_SIZE)
return -1;
@@ -392,6 +441,10 @@ fail:
return -1;
}
+#ifdef __linux__
+
+/* Linux 'packet socket' specific implementation */
+
static int open_socket(int iface, uint8_t *hw_address) {
int fd = -1;
struct sockaddr_ll sa;
@@ -437,7 +490,7 @@ fail:
return -1;
}
-static int send_packet(int fd, int iface, void *packet, size_t packet_len) {
+static int send_packet(int fd, int iface, ArpPacket *packet, size_t packet_len) {
struct sockaddr_ll sa;
assert(fd >= 0);
@@ -459,7 +512,7 @@ static int send_packet(int fd, int iface, void *packet, size_t packet_len) {
return 0;
}
-static int recv_packet(int fd, void **packet, size_t *packet_len) {
+static int recv_packet(int fd, ArpPacket **packet, size_t *packet_len) {
int s;
struct sockaddr_ll sa;
socklen_t sa_len;
@@ -479,10 +532,10 @@ static int recv_packet(int fd, void **packet, size_t *packet_len) {
if (s <= 0)
s = 4096;
- *packet = avahi_new(uint8_t, s);
+ *packet = packet_new(s);
sa_len = sizeof(sa);
- if ((r = recvfrom(fd, *packet, s, 0, (struct sockaddr*) &sa, &sa_len)) < 0) {
+ if ((r = recvfrom(fd, (*packet)->ether_payload, s, 0, (struct sockaddr*) &sa, &sa_len)) < 0) {
daemon_log(LOG_ERR, "recvfrom() failed: %s", strerror(errno));
goto fail;
}
@@ -499,7 +552,214 @@ fail:
return -1;
}
-
+
+static void
+close_socket(int fd) {
+ close(fd);
+}
+
+#else /* !__linux__ */
+/* PCAP-based implementation */
+
+static pcap_t *__pp;
+static char __pcap_errbuf[PCAP_ERRBUF_SIZE];
+static uint8_t __lladdr[ETHER_ADDRLEN];
+
+#ifndef elementsof
+#define elementsof(array) (sizeof(array)/sizeof(array[0]))
+#endif
+
+static int
+__get_ether_addr(int ifindex, u_char *lladdr)
+{
+ int mib[6];
+ char *buf;
+ struct if_msghdr *ifm;
+ char *lim;
+ char *next;
+ struct sockaddr_dl *sdl;
+ size_t len;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+
+ if (sysctl(mib, elementsof(mib), NULL, &len, NULL, 0) != 0) {
+ daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ daemon_log(LOG_ERR, "malloc(%d): %s", len, strerror(errno));
+ return (-1);
+ }
+
+ if (sysctl(mib, elementsof(mib), buf, &len, NULL, 0) != 0) {
+ daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
+ strerror(errno));
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ memcpy(lladdr, LLADDR(sdl), ETHER_ADDRLEN);
+ }
+ }
+ free(buf);
+
+ return (0);
+}
+
+static int
+open_socket(int iface, uint8_t *hw_address)
+{
+ struct bpf_program bpf;
+ char ifname[IFNAMSIZ];
+ pcap_t *pp;
+ int err;
+ int fd;
+
+ assert(__pp == NULL);
+
+ if (interface_up(iface) < 0) {
+ return (-1);
+ }
+ if (__get_ether_addr(iface, __lladdr) == -1) {
+ return (-1);
+ }
+ if (if_indextoname(iface, ifname) == NULL) {
+ return (-1);
+ }
+
+ pp = pcap_open_live(ifname, 1500, 0, 0, __pcap_errbuf);
+ if (pp == NULL) {
+ return (-1);
+ }
+ err = pcap_set_datalink(pp, DLT_EN10MB);
+ if (err == -1) {
+ daemon_log(LOG_ERR, "pcap_set_datalink: %s", pcap_geterr(pp));
+ pcap_close(pp);
+ return (-1);
+ }
+ err = pcap_setdirection(pp, PCAP_D_IN);
+ if (err == -1) {
+ daemon_log(LOG_ERR, "pcap_setdirection: %s", pcap_geterr(pp));
+ pcap_close(pp);
+ return (-1);
+ }
+
+ fd = pcap_get_selectable_fd(pp);
+ if (fd == -1) {
+ pcap_close(pp);
+ return (-1);
+ }
+#if 0
+ /* XXX: can we use this with pcap_next_ex() ? */
+ err = pcap_setnonblock(pp, 1, __pcap_errbuf);
+ if (err == -1) {
+ pcap_close(pp);
+ return (-1);
+ }
+#endif
+
+ err = pcap_compile(pp, &bpf,
+ "arp and ether dst ff:ff:ff:ff:ff:ff", 1, 0);
+ if (err == -1) {
+ daemon_log(LOG_ERR, "pcap_compile: %s", pcap_geterr(pp));
+ pcap_close(pp);
+ return (-1);
+ }
+ err = pcap_setfilter(pp, &bpf);
+ if (err == -1) {
+ daemon_log(LOG_ERR, "pcap_setfilter: %s", pcap_geterr(pp));
+ pcap_close(pp);
+ return (-1);
+ }
+ pcap_freecode(&bpf);
+
+ /* Stash pcap-specific context away. */
+ memcpy(hw_address, __lladdr, ETHER_ADDRLEN);
+ __pp = pp;
+
+ return (fd);
+}
+
+static void
+close_socket(int fd __unused)
+{
+
+ assert(__pp != NULL);
+ pcap_close(__pp);
+ __pp = NULL;
+}
+
+/*
+ * We trick avahi into allocating sizeof(packet) + sizeof(ether_header),
+ * and prepend the required ethernet header information before sending.
+ */
+static int
+send_packet(int fd __unused, int iface __unused, ArpPacket *packet,
+ size_t packet_len)
+{
+ struct ether_header *eh;
+
+ assert(__pp != NULL);
+ assert(packet != NULL);
+
+ eh = (struct ether_header *)packet->ether_header;
+ memset(eh->ether_dhost, 0xFF, ETHER_ADDRLEN);
+ memcpy(eh->ether_shost, __lladdr, ETHER_ADDRLEN);
+ eh->ether_type = htons(0x0806);
+
+ return (pcap_inject(__pp, (void *)eh, packet_len + sizeof(*eh)));
+}
+
+static int
+recv_packet(int fd __unused, ArpPacket **packet, size_t *packet_len)
+{
+ struct pcap_pkthdr *ph;
+ u_char *pd;
+ ArpPacket *ap;
+ int err;
+ int retval;
+
+ assert(__pp != NULL);
+ assert(packet != NULL);
+ assert(packet_len != NULL);
+
+ *packet = NULL;
+ *packet_len = 0;
+ retval = -1;
+
+ err = pcap_next_ex(__pp, &ph, (const u_char **)&pd);
+ if (err == 1 && ph->caplen <= ph->len) {
+ ap = packet_new(ph->caplen);
+ memcpy(ap->ether_header, pd, ph->caplen);
+ *packet = ap;
+ *packet_len = (ph->caplen - sizeof(struct ether_header));
+ retval = 0;
+ } else {
+ if (err == 1) {
+ daemon_log(LOG_ERR, "pcap len > caplen");
+ } else {
+ daemon_log(LOG_ERR, "pcap_next_ex: %s",
+ pcap_geterr(__pp));
+ }
+ }
+
+ return (retval);
+}
+#endif /* __linux__ */
+
int is_ll_address(uint32_t addr) {
return
(ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK &&
@@ -507,6 +767,7 @@ int is_ll_address(uint32_t addr) {
ntohl(addr) != IPV4LL_BROADCAST;
}
+
static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned jitter) {
assert(tv);
@@ -656,6 +917,9 @@ static int drop_privs(void) {
int r;
mode_t u;
+ pw = NULL;
+ gr = NULL;
+
/* Get user/group ID */
if (!no_drop_root) {
@@ -780,13 +1044,13 @@ static int loop(int iface, uint32_t addr) {
struct timeval next_wakeup;
int next_wakeup_valid = 0;
char buf[64];
- void *in_packet = NULL;
+ ArpPacket *in_packet = NULL;
size_t in_packet_len;
- void *out_packet = NULL;
+ ArpPacket *out_packet = NULL;
size_t out_packet_len;
uint8_t hw_address[ETHER_ADDRLEN];
struct pollfd pollfds[FD_MAX];
- int iface_fd;
+ int iface_fd = -1;
Event event = EVENT_NULL;
int retval_sent = !daemonize;
State st;
@@ -1054,7 +1318,7 @@ static int loop(int iface, uint32_t addr) {
if (pollfds[FD_ARP].revents == POLLERR) {
/* The interface is probably down, let's recreate our socket */
- close(fd);
+ close_socket(fd);
if ((fd = open_socket(iface, hw_address)) < 0)
goto fail;
@@ -1124,7 +1388,7 @@ fail:
avahi_free(in_packet);
if (fd >= 0)
- close(fd);
+ close_socket(fd);
if (iface_fd >= 0)
iface_done();