// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/network_change_notifier_netlink_linux.h"
#include <fcntl.h>
// socket.h is needed to define types for the linux kernel header netlink.h
// so it needs to come before netlink.h.
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <string.h>
#include <unistd.h>
#include "base/logging.h"
namespace {
// Return true on success, false on failure.
// Too small a function to bother putting in a library?
bool SetNonBlocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (-1 == flags)
return false;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0 ? true : false;
}
bool IsIPv6Update(const struct nlmsghdr* netlink_message_header) {
const struct ifaddrmsg* address_message =
reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message_header));
return address_message->ifa_family == AF_INET6;
}
bool IsDuplicateIPv6AddressUpdate(
const struct nlmsghdr* netlink_message_header) {
const struct ifaddrmsg* address_message =
reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message_header));
int address_message_length = IFA_PAYLOAD(netlink_message_header);
const struct rtattr* route_attribute =
reinterpret_cast<struct rtattr*>(IFA_RTA(address_message));
DCHECK_EQ(address_message->ifa_family, AF_INET6);
// Look for a cacheinfo attribute, and ignore new address broadcasts
// where the updated time stamp is newer than the created time stamp.
while (RTA_OK(route_attribute, address_message_length)) {
if (route_attribute->rta_type == IFA_CACHEINFO) {
struct ifa_cacheinfo* cache_info =
reinterpret_cast<struct ifa_cacheinfo*>(RTA_DATA(route_attribute));
if (cache_info->cstamp != cache_info->tstamp)
return true;
}
route_attribute = RTA_NEXT(route_attribute, address_message_length);
}
return false;
}
} // namespace
int InitializeNetlinkSocket() {
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
PLOG(ERROR) << "Error creating netlink socket";
return -1;
}
if (!SetNonBlocking(sock)) {
PLOG(ERROR) << "Failed to set netlink socket to non-blocking mode.";
if (close(sock) != 0)
PLOG(ERROR) << "Failed to close socket";
return -1;
}
struct sockaddr_nl local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.nl_family = AF_NETLINK;
local_addr.nl_pid = getpid();
local_addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
RTMGRP_NOTIFY;
int ret = bind(sock, reinterpret_cast<struct sockaddr*>(&local_addr),
sizeof(local_addr));
if (ret < 0) {
PLOG(ERROR) << "Error binding netlink socket";
if (close(sock) != 0)
PLOG(ERROR) << "Failed to close socket";
return -1;
}
return sock;
}
bool HandleNetlinkMessage(char* buf, size_t len) {
const struct nlmsghdr* netlink_message_header =
reinterpret_cast<struct nlmsghdr*>(buf);
DCHECK(netlink_message_header);
for (; NLMSG_OK(netlink_message_header, len);
netlink_message_header = NLMSG_NEXT(netlink_message_header, len)) {
int netlink_message_type = netlink_message_header->nlmsg_type;
switch (netlink_message_type) {
case NLMSG_DONE:
NOTREACHED()
<< "This is a monitoring netlink socket. It should never be done.";
return false;
case NLMSG_ERROR:
LOG(ERROR) << "Unexpected netlink error.";
return false;
// During IP address changes, we will see all these messages. Only fire
// the notification when we get a new address or remove an address. We
// may still end up notifying observers more than strictly necessary, but
// if the primary interface goes down and back up, then this is necessary.
case RTM_NEWADDR:
if (IsIPv6Update(netlink_message_header) &&
IsDuplicateIPv6AddressUpdate(netlink_message_header))
return false;
return true;
case RTM_DELADDR:
return true;
case RTM_NEWLINK:
case RTM_DELLINK:
return false;
default:
LOG(DFATAL) << "Received unexpected netlink message type: "
<< netlink_message_type;
return false;
}
}
return false;
}