diff options
Diffstat (limited to 'libsysutils/src/NetlinkEvent.cpp')
-rw-r--r-- | libsysutils/src/NetlinkEvent.cpp | 125 |
1 files changed, 121 insertions, 4 deletions
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 4beebb7..aae2ae7 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -23,6 +23,10 @@ #include <sys/types.h> #include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> + #include <linux/if.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter_ipv4/ipt_ULOG.h> @@ -38,6 +42,8 @@ const int NetlinkEvent::NlActionRemove = 2; const int NetlinkEvent::NlActionChange = 3; const int NetlinkEvent::NlActionLinkUp = 4; const int NetlinkEvent::NlActionLinkDown = 5; +const int NetlinkEvent::NlActionAddressUpdated = 6; +const int NetlinkEvent::NlActionAddressRemoved = 7; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; @@ -70,13 +76,110 @@ void NetlinkEvent::dump() { } /* + * Decode a RTM_NEWADDR or RTM_DELADDR message. + */ +bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, + int rtasize) { + struct rtattr *rta; + struct ifa_cacheinfo *cacheinfo = NULL; + char addrstr[INET6_ADDRSTRLEN] = ""; + + // Sanity check. + if (type != RTM_NEWADDR && type != RTM_DELADDR) { + SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type); + return false; + } + + // For log messages. + const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR"; + + for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize); + rta = RTA_NEXT(rta, rtasize)) { + if (rta->rta_type == IFA_ADDRESS) { + // Only look at the first address, because we only support notifying + // one change at a time. + if (*addrstr != '\0') { + SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", msgtype); + continue; + } + + // Convert the IP address to a string. + if (ifaddr->ifa_family == AF_INET) { + struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta); + if (RTA_PAYLOAD(rta) < sizeof(*addr4)) { + SLOGE("Short IPv4 address (%d bytes) in %s", + RTA_PAYLOAD(rta), msgtype); + continue; + } + inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr)); + } else if (ifaddr->ifa_family == AF_INET6) { + struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta); + if (RTA_PAYLOAD(rta) < sizeof(*addr6)) { + SLOGE("Short IPv6 address (%d bytes) in %s", + RTA_PAYLOAD(rta), msgtype); + continue; + } + inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr)); + } else { + SLOGE("Unknown address family %d\n", ifaddr->ifa_family); + continue; + } + + // Find the interface name. + char ifname[IFNAMSIZ + 1]; + if (!if_indextoname(ifaddr->ifa_index, ifname)) { + SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype); + return false; + } + + // Fill in interface information. + mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : + NlActionAddressRemoved; + mSubsystem = strdup("net"); + asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, + ifaddr->ifa_prefixlen); + asprintf(&mParams[1], "INTERFACE=%s", ifname); + asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags); + asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope); + } else if (rta->rta_type == IFA_CACHEINFO) { + // Address lifetime information. + if (cacheinfo) { + // We only support one address. + SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype); + continue; + } + + if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) { + SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s", + RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype); + continue; + } + + cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta); + asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered); + asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid); + asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp); + asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp); + } + } + + if (addrstr[0] == '\0') { + SLOGE("No IFA_ADDRESS in %s\n", msgtype); + return false; + } + + return true; +} + +/* * Parse an binary message from a NETLINK_ROUTE netlink socket. */ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { - size_t sz = size; - const struct nlmsghdr *nh = (struct nlmsghdr *) buffer; + const struct nlmsghdr *nh; - while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) { + for (nh = (struct nlmsghdr *) buffer; + NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE); + nh = NLMSG_NEXT(nh, size)) { if (nh->nlmsg_type == RTM_NEWLINK) { int len = nh->nlmsg_len - sizeof(*nh); @@ -127,10 +230,24 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { mSubsystem = strdup("qlog"); mAction = NlActionChange; + } else if (nh->nlmsg_type == RTM_NEWADDR || + nh->nlmsg_type == RTM_DELADDR) { + int len = nh->nlmsg_len - sizeof(*nh); + struct ifaddrmsg *ifa; + + if (sizeof(*ifa) > (size_t) len) { + SLOGE("Got a short RTM_xxxADDR message\n"); + continue; + } + + ifa = (ifaddrmsg *)NLMSG_DATA(nh); + size_t rtasize = IFA_PAYLOAD(nh); + if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) { + continue; + } } else { SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); } - nh = NLMSG_NEXT(nh, size); } return true; |