1/* 2 * Copyright (C) 2012 Intel Corporation. All rights reserved. 3 * Copyright (C) 2013 Samsung Electronics. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "NetworkStateNotifier.h" 29 30#include "Logging.h" 31#include <Ecore.h> 32#include <Eeze.h> 33#include <Eeze_Net.h> 34#include <asm/types.h> 35#include <string.h> 36#include <sys/socket.h> 37#include <unistd.h> 38 39// Must come at the end so that sys/socket.h is included first. 40#include <linux/netlink.h> 41#include <linux/rtnetlink.h> 42 43static const char udevLoopBackInterfaceSysPath[] = "lo"; 44static const char udevOperstateAttribute[] = "operstate"; 45static const char udevOperstateUp[] = "up"; 46static const size_t bufferSize = 4096; 47 48namespace WebCore { 49 50void NetworkStateNotifier::updateState() 51{ 52 // Assume that we're offline until proven otherwise. 53 m_isOnLine = false; 54 55 LOG(Network, "Checking online state..."); 56 57 Eina_List* networkInterfaces = eeze_net_list(); 58 59 Eina_List* list; 60 void* data; 61 EINA_LIST_FOREACH(networkInterfaces, list, data) { 62 Eeze_Net* networkInterface = static_cast<Eeze_Net*>(data); 63 64 // Skip Loopback interface. 65 const char* syspath = eeze_net_syspath_get(networkInterface); 66 if (!syspath || !strcmp(syspath, udevLoopBackInterfaceSysPath)) 67 continue; 68 69 // Skip interfaces that are not up. 70 const char* state = eeze_net_attribute_get(networkInterface, udevOperstateAttribute); 71 LOG(Network, "Found network interface \"%s\" with state: \"%s\"", syspath, state); 72 if (!state || strcmp(state, udevOperstateUp)) 73 continue; 74 75 // Check if the interface has an IP address. 76 eeze_net_scan(networkInterface); 77 if (eeze_net_addr_get(networkInterface, EEZE_NET_ADDR_TYPE_IP) || eeze_net_addr_get(networkInterface, EEZE_NET_ADDR_TYPE_IP6)) { 78#if !LOG_DISABLED 79 const char* ipAddress = eeze_net_addr_get(networkInterface, EEZE_NET_ADDR_TYPE_IP); 80 if (!ipAddress) 81 ipAddress = eeze_net_addr_get(networkInterface, EEZE_NET_ADDR_TYPE_IP6); 82 LOG(Network, "Network interface at %s has the following IP address: %s", syspath, ipAddress); 83#endif 84 m_isOnLine = true; 85 break; 86 } 87 } 88 89 LOG(Network, "Detected online state is \"%s\"", m_isOnLine ? "online" : "offline"); 90 91 EINA_LIST_FREE(networkInterfaces, data) 92 eeze_net_free(static_cast<Eeze_Net*>(data)); 93} 94 95void NetworkStateNotifier::networkInterfaceChanged() 96{ 97 bool wasOnline = m_isOnLine; 98 updateState(); 99 100 if (wasOnline != m_isOnLine && m_networkStateChangedFunction) 101 m_networkStateChangedFunction(); 102} 103 104Eina_Bool NetworkStateNotifier::readSocketCallback(void* userData, Ecore_Fd_Handler* handler) 105{ 106 NetworkStateNotifier* notifier = static_cast<NetworkStateNotifier*>(userData); 107 108 int sock = ecore_main_fd_handler_fd_get(handler); 109 char buffer[bufferSize]; 110 111 bool detectedChange = false; 112 113 nlmsghdr* nlh = reinterpret_cast<nlmsghdr*>(buffer); 114 while (true) { 115 ssize_t length = recv(sock, nlh, bufferSize, MSG_DONTWAIT); 116 if (!length) { 117 LOG_ERROR("NETLINK socket was closed unexpectedly."); 118 notifier->m_fdHandler = 0; 119 return ECORE_CALLBACK_CANCEL; 120 } 121 if (length == -1) { 122 if (errno == EINTR) 123 continue; 124 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 125 break; 126 LOG_ERROR("recv on NETLINK socket failed."); 127 notifier->m_fdHandler = 0; 128 return ECORE_CALLBACK_CANCEL; 129 } 130 while ((NLMSG_OK(nlh, static_cast<unsigned>(length))) && (nlh->nlmsg_type != NLMSG_DONE)) { 131 if (nlh->nlmsg_type == NLMSG_ERROR) { 132 LOG_ERROR("Unexpected NETLINK error %d.", reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(nlh))->error); 133 notifier->m_fdHandler = 0; 134 return ECORE_CALLBACK_CANCEL; 135 } 136 if ((nlh->nlmsg_type == RTM_NEWADDR && !notifier->m_isOnLine) || (nlh->nlmsg_type == RTM_DELADDR && notifier->m_isOnLine)) 137 detectedChange = true; 138 nlh = NLMSG_NEXT(nlh, length); 139 } 140 } 141 142 if (detectedChange) 143 notifier->networkInterfaceChanged(); 144 145 return ECORE_CALLBACK_RENEW; 146} 147 148NetworkStateNotifier::~NetworkStateNotifier() 149{ 150 if (m_fdHandler) 151 ecore_main_fd_handler_del(m_fdHandler); 152 if (m_netlinkSocket != -1) { 153 int rv = 0; 154 do { 155 rv = close(m_netlinkSocket); 156 } while (rv == -1 && errno == EINTR); 157 } 158 eeze_shutdown(); 159} 160 161NetworkStateNotifier::NetworkStateNotifier() 162 : m_isOnLine(false) 163 , m_networkStateChangedFunction(0) 164 , m_netlinkSocket(-1) 165 , m_fdHandler(0) 166{ 167 if (eeze_init() < 0) { 168 LOG_ERROR("Failed to initialize eeze library."); 169 return; 170 } 171 172 updateState(); 173 174 // Watch for network address changes to keep online state up-to-date. 175 m_netlinkSocket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 176 if (m_netlinkSocket == -1) { 177 LOG_ERROR("Couldn't create NETLINK socket."); 178 return; 179 } 180 181 sockaddr_nl addr; 182 memset(&addr, 0, sizeof(addr)); 183 addr.nl_family = AF_NETLINK; 184 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; 185 186 if (bind(m_netlinkSocket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) { 187 LOG_ERROR("Couldn't bind to NETLINK socket."); 188 return; 189 } 190 191 m_fdHandler = ecore_main_fd_handler_add(m_netlinkSocket, ECORE_FD_READ, readSocketCallback, this, 0, 0); 192} 193 194} // namespace WebCore 195