Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/base/network_change_notifier_netlink_linux.h"
      6 
      7 #include <fcntl.h>
      8 // socket.h is needed to define types for the linux kernel header netlink.h
      9 // so it needs to come before netlink.h.
     10 #include <sys/socket.h>
     11 #include <linux/netlink.h>
     12 #include <linux/rtnetlink.h>
     13 #include <string.h>
     14 #include <unistd.h>
     15 
     16 #include "base/logging.h"
     17 
     18 namespace {
     19 
     20 // Return true on success, false on failure.
     21 // Too small a function to bother putting in a library?
     22 bool SetNonBlocking(int fd) {
     23   int flags = fcntl(fd, F_GETFL, 0);
     24   if (-1 == flags)
     25     return false;
     26   return fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0 ? true : false;
     27 }
     28 
     29 bool IsIPv6Update(const struct nlmsghdr* netlink_message_header) {
     30   const struct ifaddrmsg* address_message =
     31       reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message_header));
     32   return address_message->ifa_family == AF_INET6;
     33 }
     34 
     35 bool IsDuplicateIPv6AddressUpdate(
     36     const struct nlmsghdr* netlink_message_header) {
     37   const struct ifaddrmsg* address_message =
     38       reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message_header));
     39   int address_message_length = IFA_PAYLOAD(netlink_message_header);
     40   const struct rtattr* route_attribute =
     41       reinterpret_cast<struct rtattr*>(IFA_RTA(address_message));
     42   DCHECK_EQ(address_message->ifa_family, AF_INET6);
     43 
     44   // Look for a cacheinfo attribute, and ignore new address broadcasts
     45   // where the updated time stamp is newer than the created time stamp.
     46   while (RTA_OK(route_attribute, address_message_length)) {
     47     if (route_attribute->rta_type == IFA_CACHEINFO) {
     48       struct ifa_cacheinfo* cache_info =
     49           reinterpret_cast<struct ifa_cacheinfo*>(RTA_DATA(route_attribute));
     50       if (cache_info->cstamp != cache_info->tstamp)
     51         return true;
     52     }
     53     route_attribute = RTA_NEXT(route_attribute, address_message_length);
     54   }
     55   return false;
     56 }
     57 
     58 }  // namespace
     59 
     60 int InitializeNetlinkSocket() {
     61   int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
     62   if (sock < 0) {
     63     PLOG(ERROR) << "Error creating netlink socket";
     64     return -1;
     65   }
     66 
     67   if (!SetNonBlocking(sock)) {
     68     PLOG(ERROR) << "Failed to set netlink socket to non-blocking mode.";
     69     if (close(sock) != 0)
     70       PLOG(ERROR) << "Failed to close socket";
     71     return -1;
     72   }
     73 
     74   struct sockaddr_nl local_addr;
     75   memset(&local_addr, 0, sizeof(local_addr));
     76   local_addr.nl_family = AF_NETLINK;
     77   local_addr.nl_pid = getpid();
     78   local_addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
     79                          RTMGRP_NOTIFY;
     80   int ret = bind(sock, reinterpret_cast<struct sockaddr*>(&local_addr),
     81                  sizeof(local_addr));
     82   if (ret < 0) {
     83     PLOG(ERROR) << "Error binding netlink socket";
     84     if (close(sock) != 0)
     85       PLOG(ERROR) << "Failed to close socket";
     86     return -1;
     87   }
     88 
     89   return sock;
     90 }
     91 
     92 bool HandleNetlinkMessage(char* buf, size_t len) {
     93   const struct nlmsghdr* netlink_message_header =
     94       reinterpret_cast<struct nlmsghdr*>(buf);
     95   DCHECK(netlink_message_header);
     96   for (; NLMSG_OK(netlink_message_header, len);
     97        netlink_message_header = NLMSG_NEXT(netlink_message_header, len)) {
     98     int netlink_message_type = netlink_message_header->nlmsg_type;
     99     switch (netlink_message_type) {
    100       case NLMSG_DONE:
    101         NOTREACHED()
    102             << "This is a monitoring netlink socket.  It should never be done.";
    103         return false;
    104       case NLMSG_ERROR:
    105         LOG(ERROR) << "Unexpected netlink error.";
    106         return false;
    107       // During IP address changes, we will see all these messages.  Only fire
    108       // the notification when we get a new address or remove an address.  We
    109       // may still end up notifying observers more than strictly necessary, but
    110       // if the primary interface goes down and back up, then this is necessary.
    111       case RTM_NEWADDR:
    112         if (IsIPv6Update(netlink_message_header) &&
    113             IsDuplicateIPv6AddressUpdate(netlink_message_header))
    114           return false;
    115         return true;
    116       case RTM_DELADDR:
    117         return true;
    118       case RTM_NEWLINK:
    119       case RTM_DELLINK:
    120         return false;
    121       default:
    122         LOG(DFATAL) << "Received unexpected netlink message type: "
    123                     << netlink_message_type;
    124         return false;
    125     }
    126   }
    127 
    128   return false;
    129 }
    130