Home | History | Annotate | Download | only in avahi-autoipd
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <sys/socket.h>
     25 #include <errno.h>
     26 #include <string.h>
     27 #include <unistd.h>
     28 #include <arpa/inet.h>
     29 
     30 #include <linux/types.h>
     31 #include <linux/netlink.h>
     32 #include <linux/rtnetlink.h>
     33 #include <linux/if.h>
     34 #include <linux/if_arp.h>
     35 
     36 #include <libdaemon/dlog.h>
     37 
     38 #include <avahi-common/llist.h>
     39 #include "avahi-common/avahi-malloc.h"
     40 
     41 #ifndef IFLA_RTA
     42 #include <linux/if_addr.h>
     43 #define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
     44 #endif
     45 
     46 #ifndef IFA_RTA
     47 #include <linux/if_addr.h>
     48 #define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
     49 #endif
     50 
     51 #include "iface.h"
     52 
     53 static int fd = -1;
     54 static int ifindex = -1;
     55 
     56 typedef struct Address Address;
     57 
     58 struct Address {
     59     uint32_t address;
     60     AVAHI_LLIST_FIELDS(Address, addresses);
     61 };
     62 
     63 AVAHI_LLIST_HEAD(Address, addresses) = NULL;
     64 
     65 int iface_init(int i) {
     66     struct sockaddr_nl addr;
     67     int on = 1;
     68 
     69     if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
     70         daemon_log(LOG_ERR, "socket(PF_NETLINK): %s", strerror(errno));
     71         goto fail;
     72     }
     73 
     74     memset(&addr, 0, sizeof(addr));
     75     addr.nl_family = AF_NETLINK;
     76     addr.nl_groups =  RTMGRP_LINK|RTMGRP_IPV4_IFADDR;
     77     addr.nl_pid = getpid();
     78 
     79     if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
     80         daemon_log(LOG_ERR, "bind(): %s", strerror(errno));
     81         goto fail;
     82     }
     83 
     84     if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
     85         daemon_log(LOG_ERR, "SO_PASSCRED: %s", strerror(errno));
     86         goto fail;
     87     }
     88 
     89     ifindex = i;
     90 
     91     return fd;
     92 
     93 fail:
     94     if (fd >= 0) {
     95         close(fd);
     96         fd = -1;
     97     }
     98 
     99     return -1;
    100 }
    101 
    102 static int process_nlmsg(struct nlmsghdr *n) {
    103     assert(n);
    104 
    105     if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
    106         /* A link appeared or was removed */
    107 
    108         struct ifinfomsg *ifi;
    109         ifi = NLMSG_DATA(n);
    110 
    111         if (ifi->ifi_family != AF_UNSPEC || (int) ifi->ifi_index != ifindex)
    112             return 0;
    113 
    114         if (n->nlmsg_type == RTM_DELLINK) {
    115             daemon_log(LOG_ERR, "Interface vanished.");
    116             return -1;
    117         }
    118 
    119         assert(n->nlmsg_type == RTM_NEWLINK);
    120 
    121         if ((ifi->ifi_flags & IFF_LOOPBACK) ||
    122             (ifi->ifi_flags & IFF_NOARP) ||
    123             ifi->ifi_type != ARPHRD_ETHER) {
    124             daemon_log(LOG_ERR, "Interface not suitable.");
    125             return -1;
    126         }
    127 
    128     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
    129 
    130         /* An address was added or removed */
    131 
    132         struct rtattr *a = NULL;
    133         struct ifaddrmsg *ifa;
    134         int l;
    135         uint32_t address = 0;
    136         Address *i;
    137 
    138         ifa = NLMSG_DATA(n);
    139 
    140         if (ifa->ifa_family != AF_INET || (int) ifa->ifa_index != ifindex)
    141             return 0;
    142 
    143         l = NLMSG_PAYLOAD(n, sizeof(*ifa));
    144         a = IFLA_RTA(ifa);
    145 
    146         while(RTA_OK(a, l)) {
    147 
    148             switch(a->rta_type) {
    149                 case IFA_LOCAL:
    150                 case IFA_ADDRESS:
    151                     assert(RTA_PAYLOAD(a) == 4);
    152                     memcpy(&address, RTA_DATA(a), sizeof(uint32_t));
    153                     break;
    154             }
    155 
    156             a = RTA_NEXT(a, l);
    157         }
    158 
    159         if (!address || is_ll_address(address))
    160             return 0;
    161 
    162         for (i = addresses; i; i = i->addresses_next)
    163             if (i->address == address)
    164                 break;
    165 
    166         if (n->nlmsg_type == RTM_DELADDR && i) {
    167             AVAHI_LLIST_REMOVE(Address, addresses, addresses, i);
    168             avahi_free(i);
    169         } if (n->nlmsg_type == RTM_NEWADDR && !i) {
    170             i = avahi_new(Address, 1);
    171             i->address = address;
    172             AVAHI_LLIST_PREPEND(Address, addresses, addresses, i);
    173         }
    174     }
    175 
    176     return 0;
    177 }
    178 
    179 static int process_response(int wait_for_done, unsigned seq) {
    180     assert(fd >= 0);
    181 
    182     do {
    183         size_t bytes;
    184         ssize_t r;
    185         char replybuf[8*1024];
    186         char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
    187         struct msghdr msghdr;
    188         struct cmsghdr *cmsghdr;
    189         struct ucred *ucred;
    190         struct iovec iov;
    191         struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
    192 
    193         memset(&iov, 0, sizeof(iov));
    194         iov.iov_base = replybuf;
    195  	iov.iov_len = sizeof(replybuf);
    196 
    197         memset(&msghdr, 0, sizeof(msghdr));
    198         msghdr.msg_name = (void*) NULL;
    199         msghdr.msg_namelen = 0;
    200         msghdr.msg_iov = &iov;
    201         msghdr.msg_iovlen = 1;
    202         msghdr.msg_control = cred_msg;
    203         msghdr.msg_controllen = sizeof(cred_msg);
    204  	msghdr.msg_flags = 0;
    205 
    206         if ((r = recvmsg(fd, &msghdr, 0)) < 0) {
    207             daemon_log(LOG_ERR, "recvmsg() failed: %s", strerror(errno));
    208             return -1;
    209         }
    210 
    211         if (!(cmsghdr = CMSG_FIRSTHDR(&msghdr)) || cmsghdr->cmsg_type != SCM_CREDENTIALS) {
    212             daemon_log(LOG_WARNING, "No sender credentials received, ignoring data.");
    213             return -1;
    214         }
    215 
    216         ucred = (struct ucred*) CMSG_DATA(cmsghdr);
    217 
    218         if (ucred->uid != 0)
    219             return -1;
    220 
    221         bytes = (size_t) r;
    222 
    223         for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
    224 
    225             if (!NLMSG_OK(p, bytes) || bytes < sizeof(struct nlmsghdr) || bytes < p->nlmsg_len) {
    226                 daemon_log(LOG_ERR, "Netlink packet too small.");
    227                 return -1;
    228             }
    229 
    230             if (p->nlmsg_type == NLMSG_DONE && wait_for_done && p->nlmsg_seq == seq && (pid_t) p->nlmsg_pid == getpid())
    231                 return 0;
    232 
    233             if (p->nlmsg_type == NLMSG_ERROR) {
    234                 struct nlmsgerr *e = (struct nlmsgerr *) NLMSG_DATA (p);
    235 
    236                 if (e->error) {
    237                     daemon_log(LOG_ERR, "Netlink error: %s", strerror(-e->error));
    238                     return -1;
    239                 }
    240             }
    241 
    242             if (process_nlmsg(p) < 0)
    243                 return -1;
    244         }
    245     } while (wait_for_done);
    246 
    247     return 0;
    248 }
    249 
    250 int iface_get_initial_state(State *state) {
    251     struct nlmsghdr *n;
    252     struct ifinfomsg *ifi;
    253     struct ifaddrmsg *ifa;
    254     uint8_t req[1024];
    255     int seq = 0;
    256 
    257     assert(fd >= 0);
    258     assert(state);
    259 
    260     memset(&req, 0, sizeof(req));
    261     n = (struct nlmsghdr*) req;
    262     n->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
    263     n->nlmsg_type = RTM_GETLINK;
    264     n->nlmsg_seq = seq;
    265     n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
    266     n->nlmsg_pid = 0;
    267 
    268     ifi = NLMSG_DATA(n);
    269     ifi->ifi_family = AF_UNSPEC;
    270     ifi->ifi_change = -1;
    271 
    272     if (send(fd, n, n->nlmsg_len, 0) < 0) {
    273         daemon_log(LOG_ERR, "send(): %s", strerror(errno));
    274         return -1;
    275     }
    276 
    277     if (process_response(1, 0) < 0)
    278         return -1;
    279 
    280     n->nlmsg_type = RTM_GETADDR;
    281     n->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa));
    282     n->nlmsg_seq = ++seq;
    283 
    284     ifa = NLMSG_DATA(n);
    285     ifa->ifa_family = AF_INET;
    286     ifa->ifa_index = ifindex;
    287 
    288     if (send(fd, n, n->nlmsg_len, 0) < 0) {
    289         daemon_log(LOG_ERR, "send(): %s", strerror(errno));
    290         return -1;
    291     }
    292 
    293     if (process_response(1, seq) < 0)
    294         return -1;
    295 
    296     *state = addresses ? STATE_SLEEPING : STATE_START;
    297 
    298     return 0;
    299 }
    300 
    301 int iface_process(Event *event) {
    302     int b;
    303     assert(fd >= 0);
    304 
    305     b = !!addresses;
    306 
    307     if (process_response(0, 0) < 0)
    308         return -1;
    309 
    310     if (b && !addresses)
    311         *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
    312     else if (!b && addresses)
    313         *event = EVENT_ROUTABLE_ADDR_CONFIGURED;
    314 
    315     return 0;
    316 }
    317 
    318 void iface_done(void) {
    319     Address *a;
    320 
    321     if (fd >= 0) {
    322         close(fd);
    323         fd = -1;
    324     }
    325 
    326     while ((a = addresses)) {
    327         AVAHI_LLIST_REMOVE(Address, addresses, addresses, a);
    328         avahi_free(a);
    329     }
    330 }
    331 
    332 
    333