| // 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; |
| } |