]> git.meshlink.io Git - catta/commitdiff
add netlink support
authorLennart Poettering <lennart@poettering.net>
Wed, 30 Aug 2006 19:51:54 +0000 (19:51 +0000)
committerLennart Poettering <lennart@poettering.net>
Wed, 30 Aug 2006 19:51:54 +0000 (19:51 +0000)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1287 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe

avahi-autoipd/Makefile.am
avahi-autoipd/iface-linux.c [new file with mode: 0644]
avahi-autoipd/iface.h [new file with mode: 0644]
avahi-autoipd/main.c
avahi-autoipd/main.h [new file with mode: 0644]

index 33df1f54ce8e5afc05a608ac31e83f5d2142b158..adfe70d03ed58b5b97d1b063ca4e2e55837012d3 100644 (file)
@@ -28,7 +28,7 @@ AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
 
 sbin_PROGRAMS = avahi-autoipd
 
-avahi_autoipd_SOURCES = main.c 
+avahi_autoipd_SOURCES = main.c  ../avahi-daemon/setproctitle.c iface.h main.h iface-linux.c
 avahi_autoipd_CFLAGS = $(AM_CFLAGS) $(LIBDAEMON_CFLAGS)
 avahi_autoipd_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la $(LIBDAEMON_LIBS)
 
diff --git a/avahi-autoipd/iface-linux.c b/avahi-autoipd/iface-linux.c
new file mode 100644 (file)
index 0000000..c2f24df
--- /dev/null
@@ -0,0 +1,297 @@
+/* $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.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/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+
+#include <libdaemon/dlog.h>
+
+#include <avahi-common/llist.h>
+#include <avahi-common/malloc.h>
+
+#include "iface.h"
+
+static int fd = -1;
+static int ifindex = -1;
+
+typedef struct Address Address;
+
+struct Address {
+    uint32_t address;
+    AVAHI_LLIST_FIELDS(Address, addresses);
+};
+
+AVAHI_LLIST_HEAD(Address, addresses) = NULL;
+
+int iface_init(int i) {
+    struct sockaddr_nl addr;
+    
+    if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+        daemon_log(LOG_ERR, "socket(PF_NETLINK): %s", strerror(errno));
+        goto fail;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_groups =  RTMGRP_LINK|RTMGRP_IPV4_IFADDR;
+    addr.nl_pid = getpid();
+
+    if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        daemon_log(LOG_ERR, "bind(): %s", strerror(errno));
+        goto fail;
+    }
+
+    ifindex = i;
+    
+    return fd;
+    
+fail:
+    if (fd >= 0) {
+        close(fd);
+        fd = -1;
+    }
+    
+    return -1;
+}
+
+static int process_nlmsg(struct nlmsghdr *n) {
+    assert(n);
+
+    if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
+        /* A link appeared or was removed */
+
+        struct ifinfomsg *ifi;
+        ifi = NLMSG_DATA(n);
+
+        if (ifi->ifi_family != AF_UNSPEC || ifi->ifi_index != ifindex)
+            return 0;
+        
+        if (n->nlmsg_type == RTM_DELLINK) {
+            daemon_log(LOG_ERR, "Interface vanished.");
+            return -1;
+        }
+
+        assert(n->nlmsg_type == RTM_NEWLINK);
+        
+        if ((ifi->ifi_flags & IFF_LOOPBACK) ||
+            (ifi->ifi_flags & IFF_NOARP) ||
+            !(ifi->ifi_flags & IFF_UP) ||
+            ifi->ifi_type != ARPHRD_ETHER) {
+            daemon_log(LOG_ERR, "Interface not suitable.");
+            return -1;
+        }
+
+    } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
+
+        /* An address was added or removed */
+
+        struct rtattr *a = NULL;
+        struct ifaddrmsg *ifa;
+        int l;
+        uint32_t address = 0;
+        Address *i;
+        char buf[32];
+        
+        ifa = NLMSG_DATA(n);
+
+        if (ifa->ifa_family != AF_INET || ifa->ifa_index != ifindex)
+            return 0;
+
+        l = NLMSG_PAYLOAD(n, sizeof(*ifa));
+        a = IFLA_RTA(ifa);
+
+        while(RTA_OK(a, l)) {
+
+            switch(a->rta_type) {
+                case IFA_LOCAL:
+                case IFA_ADDRESS:
+                    assert(RTA_PAYLOAD(a) == 4);
+                    memcpy(&address, RTA_DATA(a), sizeof(uint32_t));
+                    break;
+            }
+            
+            a = RTA_NEXT(a, l);
+        }
+
+        daemon_log(LOG_INFO, "%s", inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+
+        if (!address || is_ll_address(address))
+            return 0;
+
+        for (i = addresses; i; i = i->addresses_next)
+            if (i->address == address)
+                break;
+
+        if (n->nlmsg_type == RTM_DELADDR && i) {
+            AVAHI_LLIST_REMOVE(Address, addresses, addresses, i);
+            avahi_free(i);
+        } if (n->nlmsg_type == RTM_NEWADDR && !i) {
+            i = avahi_new(Address, 1);
+            i->address = address;
+            AVAHI_LLIST_PREPEND(Address, addresses, addresses, i);
+        }
+    }
+
+    return 0;
+}
+
+static int process_response(int wait_for_done, unsigned seq) {
+    assert(fd >= 0);
+    
+    do {
+        size_t bytes;
+        ssize_t r;
+        char replybuf[2048];
+        struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
+
+        if ((r = recv(fd, replybuf, sizeof(replybuf), 0)) < 0) {
+            daemon_log(LOG_ERR, "recv() failed: %s", strerror(errno));
+            return -1;
+        }
+
+        bytes = (size_t) r;
+        
+        for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+
+            if (!NLMSG_OK(p, bytes) || bytes < sizeof(struct nlmsghdr) || bytes < p->nlmsg_len) {
+                daemon_log(LOG_ERR, "Netlink packet too small.");
+                return -1;
+            }
+
+            if (p->nlmsg_type == NLMSG_DONE && wait_for_done && p->nlmsg_seq == seq)
+                return 0;
+
+            if (p->nlmsg_type == NLMSG_ERROR) {
+                struct nlmsgerr *e = (struct nlmsgerr *) NLMSG_DATA (p);
+
+                if (e->error) {
+                    daemon_log(LOG_ERR, "Netlink error: %s", strerror(-e->error));
+                    return -1;
+                }
+            }
+
+            if ((pid_t) p->nlmsg_pid != getpid())
+                continue;
+
+            if (process_nlmsg(p) < 0)
+                return -1;
+        }
+    } while (wait_for_done);
+
+    return 0;
+}
+
+int iface_get_initial_state(State *state) {
+    struct nlmsghdr *n;
+    struct ifinfomsg *ifi;
+    struct ifaddrmsg *ifa;
+    uint8_t req[1024];
+    int seq = 0;
+
+    assert(fd >= 0);
+    assert(state);
+    
+    memset(&req, 0, sizeof(req));
+    n = (struct nlmsghdr*) req;
+    n->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+    n->nlmsg_type = RTM_GETLINK;
+    n->nlmsg_seq = seq;
+    n->nlmsg_flags = NLM_F_MATCH|NLM_F_REQUEST|NLM_F_ACK;
+    n->nlmsg_pid = 0;
+
+    ifi = NLMSG_DATA(n);
+    ifi->ifi_family = AF_UNSPEC;
+    ifi->ifi_change = -1;
+
+    if (send(fd, n, n->nlmsg_len, 0) < 0) {
+        daemon_log(LOG_ERR, "send(): %s", strerror(errno));
+        return -1;
+    }
+
+    if (process_response(1, 0) < 0)
+        return -1;
+    
+    n->nlmsg_type = RTM_GETADDR;
+    n->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa));
+    n->nlmsg_seq = ++seq;
+
+    ifa = NLMSG_DATA(n);
+    ifa->ifa_family = AF_INET;
+    ifa->ifa_index = ifindex;
+    
+    if (send(fd, n, n->nlmsg_len, 0) < 0) {
+        daemon_log(LOG_ERR, "send(): %s", strerror(errno));
+        return -1;
+    }
+
+    if (process_response(1, seq) < 0)
+        return -1;
+
+    *state = addresses ? STATE_SLEEPING : STATE_START;
+    
+    return 0;
+}
+
+int iface_process(Event *event) {
+    int b;
+    assert(fd >= 0);
+
+    b = !!addresses;
+    
+    if (process_response(0, 0) < 0)
+        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 >= 0) {
+        close(fd);
+        fd = -1;
+    }
+
+    while ((a = addresses)) {
+        AVAHI_LLIST_REMOVE(Address, addresses, addresses, a);
+        avahi_free(a);
+    }
+}
+
+
diff --git a/avahi-autoipd/iface.h b/avahi-autoipd/iface.h
new file mode 100644 (file)
index 0000000..4480c97
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef fooavahiifacehfoo
+#define fooavahiifacehfoo
+
+/* $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.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.
+***/
+
+#include <inttypes.h>
+
+#include "main.h"
+
+int iface_init(int ifindex);
+int iface_process(Event *event);
+void iface_done(void);
+
+int iface_get_initial_state(State *state);
+
+#endif
index 8ce7da1ed8d402009791213e131af48e8be1ee5c..200cab048f24ba87f79114692b432c83cee073bc 100644 (file)
 #include <avahi-common/malloc.h>
 #include <avahi-common/timeval.h>
 
+#include <avahi-daemon/setproctitle.h>
+
 #include <libdaemon/dfork.h>
 #include <libdaemon/dsignal.h>
 #include <libdaemon/dlog.h>
 #include <libdaemon/dpid.h>
 #include <libdaemon/dexec.h>
 
+#include "main.h"
+#include "iface.h"
+
 #ifndef __linux__
 #error "avahi-autoipd is only available on Linux for now"
 #endif
 #define ETHER_ADDRLEN 6
 #define ARP_PACKET_SIZE (8+4+4+2*ETHER_ADDRLEN)
 
-typedef enum State {
-    STATE_INVALID,
-    STATE_WAITING_PROBE,
-    STATE_PROBING,
-    STATE_WAITING_ANNOUNCE,
-    STATE_ANNOUNCING,
-    STATE_RUNNING,
-    STATE_SLEEPING,
-    STATE_MAX
-} State;
-
 typedef enum ArpOperation {
     ARP_REQUEST = 1,
     ARP_RESPONSE = 2
@@ -97,7 +91,7 @@ typedef struct ArpPacketInfo {
     uint8_t sender_hw_address[ETHER_ADDRLEN], target_hw_address[ETHER_ADDRLEN];
 } ArpPacketInfo;
 
-static State state = STATE_INVALID;
+static State state = STATE_START;
 static int n_iteration = 0;
 static int n_conflict = 0;
 
@@ -212,7 +206,7 @@ static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info
 
 static void set_state(State st, int reset_counter) {
     const char* const state_table[] = {
-        [STATE_INVALID] = "INVALID",
+        [STATE_START] = "START",
         [STATE_WAITING_PROBE] = "WAITING_PROBE",
         [STATE_PROBING] = "PROBING",
         [STATE_WAITING_ANNOUNCE] = "WAITING_ANNOUNCE", 
@@ -347,7 +341,7 @@ fail:
     return -1;
 }
  
-static int is_ll_address(uint32_t addr) {
+int is_ll_address(uint32_t addr) {
     return (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK;
 }
 
@@ -366,6 +360,12 @@ static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned j
 }
 
 static int loop(int iface, uint32_t addr) {
+    enum {
+        FD_ARP,
+        FD_IFACE,
+        FD_MAX
+    };
+
     int fd = -1, ret = -1;
     struct timeval next_wakeup;
     int next_wakeup_valid = 0;
@@ -375,17 +375,19 @@ static int loop(int iface, uint32_t addr) {
     void *out_packet = NULL;
     size_t out_packet_len;
     uint8_t hw_address[ETHER_ADDRLEN];
-    struct pollfd pollfds[1];
+    struct pollfd pollfds[FD_MAX];
+    int iface_fd;
+    Event event = EVENT_NULL;
 
-    enum {
-        EVENT_NULL,
-        EVENT_PACKET,
-        EVENT_TIMEOUT
-    } event = EVENT_NULL;
-    
     if ((fd = open_socket(iface, hw_address)) < 0)
         goto fail;
 
+    if ((iface_fd = iface_init(iface)) < 0)
+        goto fail;
+
+    if (iface_get_initial_state(&state) < 0)
+        goto fail;
+    
     if (addr && !is_ll_address(addr)) {
         daemon_log(LOG_WARNING, "Requested address %s is not from IPv4LL range 169.254/16, ignoring.", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
         addr = 0;
@@ -403,15 +405,20 @@ static int loop(int iface, uint32_t addr) {
 
     daemon_log(LOG_INFO, "Starting with address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
 
+    if (state == STATE_SLEEPING)
+        daemon_log(LOG_INFO, "Routable address already assigned, sleeping.");
+
     memset(pollfds, 0, sizeof(pollfds));
-    pollfds[0].fd = fd;
-    pollfds[0].events = POLLIN;
+    pollfds[FD_ARP].fd = fd;
+    pollfds[FD_ARP].events = POLLIN;
+    pollfds[FD_IFACE].fd = iface_fd;
+    pollfds[FD_IFACE].events = POLLIN;
     
     for (;;) {
         int r, timeout;
         AvahiUsec usec;
 
-        if (state == STATE_INVALID) {
+        if (state == STATE_START) {
 
             /* First, wait a random time */
             set_state(STATE_WAITING_PROBE, 1);
@@ -488,10 +495,10 @@ static int loop(int iface, uint32_t addr) {
 
                     daemon_log(LOG_INFO, "Trying address %s", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
 
-                    set_state(STATE_WAITING_PROBE, 1);
-
                     n_conflict++;
 
+                    set_state(STATE_WAITING_PROBE, 1);
+                    
                     if (n_conflict >= MAX_CONFLICTS) {
                         daemon_log(LOG_WARNING, "Got too many conflicts, rate limiting new probes.");
                         elapse_time(&next_wakeup, RATE_LIMIT_INTERVAL*1000, PROBE_WAIT*1000);
@@ -502,6 +509,25 @@ static int loop(int iface, uint32_t addr) {
                 } else
                     daemon_log(LOG_DEBUG, "Ignoring ARP packet.");
             }
+            
+        } else if (event == EVENT_ROUTABLE_ADDR_CONFIGURED) {
+
+            daemon_log(LOG_INFO, "A routable address has been configured.");
+
+            set_state(STATE_SLEEPING, 1);
+            
+            if (state == STATE_RUNNING || state == STATE_ANNOUNCING)
+                remove_address(iface, addr);
+            
+        } else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING) {
+
+            daemon_log(LOG_INFO, "No longer a routable address configured, restarting probe process.");
+
+            set_state(STATE_WAITING_PROBE, 1);
+
+            elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
+            next_wakeup_valid = 1;
+            
         }
         
         if (out_packet) {
@@ -519,6 +545,7 @@ static int loop(int iface, uint32_t addr) {
             in_packet = NULL;
         }
 
+        event = EVENT_NULL;
         timeout = -1;
         
         if (next_wakeup_valid) {
@@ -528,7 +555,7 @@ static int loop(int iface, uint32_t addr) {
 
         daemon_log(LOG_DEBUG, "sleeping %ims", timeout);
                     
-        while ((r = poll(pollfds, 1, timeout)) < 0 && errno == EINTR)
+        while ((r = poll(pollfds, FD_MAX, timeout)) < 0 && errno == EINTR)
             ;
 
         if (r < 0) {
@@ -538,13 +565,21 @@ static int loop(int iface, uint32_t addr) {
             event = EVENT_TIMEOUT;
             next_wakeup_valid = 0;
         } else {
-            assert(pollfds[0].revents == POLLIN);
 
-            if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
-                goto fail;
+            if (pollfds[FD_ARP].revents == POLLIN) {
+                if (recv_packet(fd, &in_packet, &in_packet_len) < 0)
+                    goto fail;
+                
+                if (in_packet)
+                    event = EVENT_PACKET;
+            }
 
-            if (in_packet)
-                event = EVENT_PACKET;
+            if (event == EVENT_NULL &&
+                pollfds[FD_IFACE].revents == POLLIN) {
+                
+                if (iface_process(&event) < 0)
+                    goto fail;
+            }
         }
     }
 
@@ -557,6 +592,9 @@ fail:
     
     if (fd >= 0)
         close(fd);
+
+    if (iface_fd >= 0)
+        iface_done();
     
     return ret;
 }
@@ -594,6 +632,8 @@ int main(int argc, char*argv[]) {
     int ifindex;
     uint32_t addr = 0;
 
+    avahi_init_proc_title(argc, argv);
+    
     init_rand_seed();
 
     if ((ifindex = get_ifindex(argc >= 2 ? argv[1] : "eth0")) < 0)
@@ -624,5 +664,6 @@ fail:
 - signals
 - store last used address
 - cmdline
+- setproctitle
 
 */
diff --git a/avahi-autoipd/main.h b/avahi-autoipd/main.h
new file mode 100644 (file)
index 0000000..61d5ed6
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef fooavahimainhfoo
+#define fooavahimainhfoo
+
+/* $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.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.
+***/
+
+typedef enum Event {
+    EVENT_NULL,
+    EVENT_PACKET,
+    EVENT_TIMEOUT,
+    EVENT_ROUTABLE_ADDR_CONFIGURED,
+    EVENT_ROUTABLE_ADDR_UNCONFIGURED,
+} Event;
+
+typedef enum State {
+    STATE_START,
+    STATE_WAITING_PROBE,
+    STATE_PROBING,
+    STATE_WAITING_ANNOUNCE,
+    STATE_ANNOUNCING,
+    STATE_RUNNING,
+    STATE_SLEEPING,
+    STATE_MAX
+} State;
+
+int is_ll_address(uint32_t addr);
+
+#endif