OLD | NEW |
| (Empty) |
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 } | |
OLD | NEW |