| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // Netif helper - emits information about network interfaces as json. |
| // Here's an example of output from my system: |
| // { |
| // "eth0": { |
| // "flags": [ "up", "broadcast", "running", "multi", "lower-up" ], |
| // "ipv4": { |
| // "addrs": [ "172.31.197.126" ], |
| // "destination": "172.31.197.255", |
| // "mask": "255.255.254.0" |
| // }, |
| // "ipv6": { |
| // "addrs": [ "2620:0:1004:1:198:42c6:435c:aa09", |
| // "2620:0:1004:1:210:60ff:fe3b:c2d0", "fe80::210:60ff:fe3b:c2d0" ] |
| // }, |
| // "mac": "0010603BC2D0" |
| // }, |
| // "lo": { |
| // "flags": [ "up", "loopback", "running", "lower-up" ], |
| // "ipv4": { |
| // "addrs": [ "127.0.0.1" ], |
| // "destination": "127.0.0.1", |
| // "mask": "255.0.0.0" |
| // }, |
| // "ipv6": { |
| // "addrs": [ "::1" ] |
| // }, |
| // "mac": "000000000000" |
| // }, |
| // "wlan0": { |
| // "flags": [ "broadcast", "multi" ], |
| // "mac": "68A3C41B264C", |
| // "signal-strengths": { |
| // "A9F1BDF1DAB1NVT4F4F59": 62 |
| // } |
| // }, |
| // "wwan0": { |
| // "flags": [ "broadcast", "multi" ], |
| // "mac": "020010ABA636" |
| // } |
| // } |
| // The meanings of the individual flags are up to Linux's networking stack (and |
| // sometimes up to the individual cards' drivers); "up" indicates that the |
| // interface is up in the ifconfig sense. |
| |
| #include <arpa/inet.h> |
| #include <ifaddrs.h> |
| #include <linux/if.h> |
| #include <netdb.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <base/json/json_writer.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/values.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <debugd/src/dbus_utils.h> |
| |
| #include "shill/dbus_proxies/org.chromium.flimflam.Manager.h" |
| #include "shill/dbus_proxies/org.chromium.flimflam.Service.h" |
| |
| using base::DictionaryValue; |
| using base::ListValue; |
| using base::StringValue; |
| using base::Value; |
| |
| std::string getmac(int fd, const char *ifname) { |
| struct ifreq ifr; |
| int ret; |
| memset(&ifr, 0, sizeof(ifr)); |
| ifr.ifr_addr.sa_family = AF_PACKET; |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); |
| ret = ioctl(fd, SIOCGIFHWADDR, &ifr); |
| if (ret < 0) |
| return "<can't fetch>"; |
| return base::HexEncode(ifr.ifr_hwaddr.sa_data, 6); |
| } |
| |
| std::string sockaddr2str(struct sockaddr *sa) { |
| char *buf = new char[INET6_ADDRSTRLEN]; |
| void *addr; |
| // These need NOLINT because cpplint thinks we're taking the address of a |
| // cast, which we aren't - we're taking the address of a member after casting |
| // a pointer to a different type. |
| if (sa->sa_family == AF_INET) |
| addr = &((struct sockaddr_in *)sa)->sin_addr; // NOLINT |
| else if (sa->sa_family == AF_INET6) |
| addr = &((struct sockaddr_in6 *)sa)->sin6_addr; // NOLINT |
| else |
| return "unknown"; |
| return inet_ntop(sa->sa_family, addr, buf, INET6_ADDRSTRLEN); |
| } |
| |
| struct ifflag { |
| unsigned int bit; |
| const char *name; |
| } ifflags[] = { |
| { IFF_UP, "up" }, |
| { IFF_BROADCAST, "broadcast" }, |
| { IFF_DEBUG, "debug" }, |
| { IFF_LOOPBACK, "loopback" }, |
| { IFF_POINTOPOINT, "point-to-point" }, |
| { IFF_RUNNING, "running" }, |
| { IFF_NOARP, "noarp" }, |
| { IFF_PROMISC, "promisc" }, |
| { IFF_NOTRAILERS, "notrailers" }, |
| { IFF_ALLMULTI, "allmulti" }, |
| { IFF_MASTER, "master" }, |
| { IFF_SLAVE, "slave" }, |
| { IFF_MULTICAST, "multi" }, |
| { IFF_PORTSEL, "portsel" }, |
| { IFF_AUTOMEDIA, "automedia" }, |
| { IFF_DYNAMIC, "dynamic" }, |
| { IFF_LOWER_UP, "lower-up" }, |
| { IFF_DORMANT, "dormant" }, |
| { IFF_ECHO, "echo" } |
| }; |
| |
| ListValue *flags2list(unsigned int flags) { |
| ListValue *lv = new ListValue(); |
| for (unsigned int i = 0; i < arraysize(ifflags); ++i) |
| if (flags & ifflags[i].bit) |
| lv->Append(new StringValue(ifflags[i].name)); |
| if (lv->empty()) { |
| delete lv; |
| return NULL; |
| } |
| return lv; |
| } |
| |
| // Duplicated from </src/helpers/network_status.cc>. Need to figure out how to |
| // refactor these. |
| class ManagerProxy : public org::chromium::flimflam::Manager_proxy, |
| public DBus::ObjectProxy { |
| public: |
| ManagerProxy(DBus::Connection* connection, |
| const char* path, |
| const char* service) |
| : DBus::ObjectProxy(*connection, path, service) {} |
| ~ManagerProxy() override = default; |
| void PropertyChanged(const std::string&, const DBus::Variant&) override {} |
| void StateChanged(const std::string&) override {} |
| }; |
| |
| class ServiceProxy : public org::chromium::flimflam::Service_proxy, |
| public DBus::ObjectProxy { |
| public: |
| ServiceProxy(DBus::Connection* connection, |
| const char* path, |
| const char* service) |
| : DBus::ObjectProxy(*connection, path, service) {} |
| ~ServiceProxy() override = default; |
| void PropertyChanged(const std::string&, const DBus::Variant&) override {} |
| }; |
| |
| class NetInterface { |
| public: |
| NetInterface(int fd, const char *name); |
| ~NetInterface() = default; |
| |
| bool Init(); |
| void AddAddress(struct ifaddrs *ifa); |
| void AddSignalStrength(const std::string& name, int strength); |
| Value *ToValue(); |
| |
| private: |
| int fd_; |
| const char *name_; |
| DictionaryValue *ipv4_; |
| DictionaryValue *ipv6_; |
| ListValue *flags_; |
| std::string mac_; |
| DictionaryValue *signal_strengths_; |
| |
| void AddAddressTo(DictionaryValue *dv, struct sockaddr *sa); |
| }; |
| |
| NetInterface::NetInterface(int fd, const char *name) |
| : fd_(fd), |
| name_(name), |
| ipv4_(NULL), |
| ipv6_(NULL), |
| flags_(NULL), |
| signal_strengths_(NULL) {} |
| |
| bool NetInterface::Init() { |
| mac_ = getmac(fd_, name_); |
| return true; |
| } |
| |
| void NetInterface::AddSignalStrength(const std::string& name, int strength) { |
| if (!signal_strengths_) |
| signal_strengths_ = new DictionaryValue(); |
| signal_strengths_->SetInteger(name, strength); |
| } |
| |
| void NetInterface::AddAddressTo(DictionaryValue *dv, struct sockaddr *sa) { |
| if (!dv->HasKey("addrs")) |
| dv->Set("addrs", new ListValue()); |
| ListValue *lv; |
| dv->Get("addrs", reinterpret_cast<Value**>(&lv)); |
| lv->Append(new StringValue(sockaddr2str(sa))); |
| } |
| |
| void NetInterface::AddAddress(struct ifaddrs *ifa) { |
| if (!flags_) |
| flags_ = flags2list(ifa->ifa_flags); |
| if (!ifa->ifa_addr) |
| return; |
| if (ifa->ifa_addr->sa_family == AF_INET) { |
| // An IPv4 address. |
| if (!ipv4_) |
| ipv4_ = new DictionaryValue(); |
| AddAddressTo(ipv4_, ifa->ifa_addr); |
| if (!ipv4_->HasKey("mask")) |
| ipv4_->Set("mask", new StringValue(sockaddr2str(ifa->ifa_netmask))); |
| if (!ipv4_->HasKey("destination")) |
| ipv4_->Set("destination", |
| new StringValue(sockaddr2str(ifa->ifa_broadaddr))); |
| } else if (ifa->ifa_addr->sa_family == AF_INET6) { |
| // An IPv6 address. |
| if (!ipv6_) |
| ipv6_ = new DictionaryValue(); |
| AddAddressTo(ipv6_, ifa->ifa_addr); |
| } |
| } |
| |
| Value *NetInterface::ToValue() { |
| DictionaryValue *dv = new DictionaryValue(); |
| if (ipv4_) |
| dv->Set("ipv4", ipv4_); |
| if (ipv6_) |
| dv->Set("ipv6", ipv6_); |
| if (flags_) |
| dv->Set("flags", flags_); |
| if (signal_strengths_) |
| dv->Set("signal-strengths", signal_strengths_); |
| dv->Set("mac", new StringValue(mac_)); |
| return dv; |
| } |
| |
| std::string DevicePathToName(const std::string& path) { |
| const char *kPrefix = "/device/"; |
| if (path.find(kPrefix) == 0) |
| return path.substr(strlen(kPrefix)); |
| return "?"; |
| } |
| |
| void AddSignalStrengths(std::map<std::string, NetInterface*> *interfaces) { |
| DBus::BusDispatcher dispatcher; |
| DBus::default_dispatcher = &dispatcher; |
| DBus::Connection connection = DBus::Connection::SystemBus(); |
| ManagerProxy manager(&connection, |
| shill::kFlimflamServicePath, |
| shill::kFlimflamServiceName); |
| |
| std::map<std::string, DBus::Variant> props = manager.GetProperties(); |
| if (props.count("Services") != 1) |
| return; |
| DBus::Variant& devices = props["Services"]; |
| if (strcmp(devices.signature().c_str(), "ao")) |
| return; |
| std::vector<DBus::Path> paths = devices; |
| for (std::vector<DBus::Path>::iterator it = paths.begin(); |
| it != paths.end(); ++it) { |
| ServiceProxy service(&connection, it->c_str(), shill::kFlimflamServiceName); |
| std::map<std::string, DBus::Variant> props = service.GetProperties(); |
| if ( props.count("Strength") != 1 |
| || props.count("Name") != 1 |
| || props.count("Device") != 1) |
| continue; |
| uint8_t strength = props["Strength"]; |
| std::string name = props["Name"]; |
| DBus::Variant& varpath = props["Device"]; |
| DBus::Path devpath = varpath; |
| std::string devname = DevicePathToName(devpath); |
| if (interfaces->count(devname)) { |
| interfaces->find(devname)->second->AddSignalStrength(name, strength); |
| } |
| } |
| } |
| |
| int main() { |
| struct ifaddrs *ifaddrs; |
| int fd; |
| DictionaryValue result; |
| std::map<std::string, NetInterface*> interfaces; |
| |
| if (getifaddrs(&ifaddrs) == -1) { |
| perror("getifaddrs"); |
| exit(1); |
| } |
| |
| fd = socket(AF_INET, SOCK_STREAM, 0); |
| if (fd < 0) { |
| perror("socket"); |
| exit(1); |
| } |
| |
| for (struct ifaddrs *ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { |
| if (!interfaces.count(ifa->ifa_name)) { |
| interfaces[ifa->ifa_name] = new NetInterface(fd, ifa->ifa_name); |
| interfaces[ifa->ifa_name]->Init(); |
| } |
| interfaces[ifa->ifa_name]->AddAddress(ifa); |
| } |
| |
| AddSignalStrengths(&interfaces); |
| |
| for (std::map<std::string, NetInterface*>::iterator it = interfaces.begin(); |
| it != interfaces.end(); ++it) |
| result.Set(it->first, it->second->ToValue()); |
| |
| std::string json; |
| base::JSONWriter::WriteWithOptions(&result, |
| base::JSONWriter::OPTIONS_PRETTY_PRINT, |
| &json); |
| printf("%s\n", json.c_str()); |
| return 0; |
| } |