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