summaryrefslogtreecommitdiffstats
path: root/libsysutils/src
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2013-08-12 17:03:32 +0900
committerLorenzo Colitti <lorenzo@google.com>2013-11-20 10:31:19 +0900
commitc7eec83f08329a2a0008ba939c07dd1dc7d9b7ff (patch)
treed56ce7475a6ff93463709ac01bb19c4fdf5e5e40 /libsysutils/src
parent526b838c9d647e8767fac957c53133153c3cf909 (diff)
downloadsystem_core-c7eec83f08329a2a0008ba939c07dd1dc7d9b7ff.zip
system_core-c7eec83f08329a2a0008ba939c07dd1dc7d9b7ff.tar.gz
system_core-c7eec83f08329a2a0008ba939c07dd1dc7d9b7ff.tar.bz2
Support parsing RDNSS ND options from netlink.
The RDNSS options (RFC 6106) used to configure DNS servers via router advertisements are passed from the kernel to userspace via RTM_NEWNDUSEROPT netlink messages. Add code to NetlinkEvent to parse them. Also fix a compiler warning and a couple of style issues. [Cherry-pick of b185e90dcc6ac111bff908edcc6d89fd6b37dc11] Bug: 9180552 Change-Id: I6c532c8f0ceef3afdc977a431a036df398013e1a
Diffstat (limited to 'libsysutils/src')
-rw-r--r--libsysutils/src/NetlinkEvent.cpp125
1 files changed, 121 insertions, 4 deletions
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index aae2ae7..9f2606c 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <net/if.h>
@@ -44,6 +45,7 @@ const int NetlinkEvent::NlActionLinkUp = 4;
const int NetlinkEvent::NlActionLinkDown = 5;
const int NetlinkEvent::NlActionAddressUpdated = 6;
const int NetlinkEvent::NlActionAddressRemoved = 7;
+const int NetlinkEvent::NlActionRdnss = 8;
NetlinkEvent::NetlinkEvent() {
mAction = NlActionUnknown;
@@ -76,7 +78,7 @@ void NetlinkEvent::dump() {
}
/*
- * Decode a RTM_NEWADDR or RTM_DELADDR message.
+ * Parse a RTM_NEWADDR or RTM_DELADDR message.
*/
bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
int rtasize) {
@@ -172,13 +174,111 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
}
/*
- * Parse an binary message from a NETLINK_ROUTE netlink socket.
+ * Parse a RTM_NEWNDUSEROPT message.
+ */
+bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
+ // Check the length is valid.
+ if (msg->nduseropt_opts_len > len) {
+ SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
+ msg->nduseropt_opts_len, len);
+ return false;
+ }
+ len = msg->nduseropt_opts_len;
+
+ // Check address family and packet type.
+ if (msg->nduseropt_family != AF_INET6) {
+ SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n",
+ msg->nduseropt_family);
+ return false;
+ }
+
+ if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+ msg->nduseropt_icmp_code != 0) {
+ SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n",
+ msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);
+ return false;
+ }
+
+ // Find the interface name.
+ char ifname[IFNAMSIZ + 1];
+ if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
+ SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
+ msg->nduseropt_ifindex);
+ return false;
+ }
+
+ // The kernel sends a separate netlink message for each ND option in the RA.
+ // So only parse the first ND option in the message.
+ struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);
+
+ // The length is in multiples of 8 octets.
+ uint16_t optlen = opthdr->nd_opt_len;
+ if (optlen * 8 > len) {
+ SLOGE("Invalid option length %d > %d for ND option %d\n",
+ optlen * 8, len, opthdr->nd_opt_type);
+ return false;
+ }
+
+ if (opthdr->nd_opt_type == ND_OPT_RDNSS) {
+ // DNS Servers (RFC 6106).
+ // Each address takes up 2*8 octets, and the header takes up 8 octets.
+ // So for a valid option with one or more addresses, optlen must be
+ // odd and greater than 1.
+ if ((optlen < 3) || !(optlen & 0x1)) {
+ SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
+ return false;
+ }
+ int numaddrs = (optlen - 1) / 2;
+
+ // Find the lifetime.
+ struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
+ uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
+
+ // Construct "SERVERS=<comma-separated string of DNS addresses>".
+ // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
+ // the last address are followed by ','; the last is followed by '\0'.
+ static const char kServerTag[] = "SERVERS=";
+ static const int kTagLength = sizeof(kServerTag) - 1;
+ int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
+ char *buf = (char *) malloc(bufsize);
+ if (!buf) {
+ SLOGE("RDNSS option: out of memory\n");
+ return false;
+ }
+ strcpy(buf, kServerTag);
+ int pos = kTagLength;
+
+ struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+ for (int i = 0; i < numaddrs; i++) {
+ if (i > 0) {
+ buf[pos++] = ',';
+ }
+ inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
+ pos += strlen(buf + pos);
+ }
+ buf[pos] = '\0';
+
+ mAction = NlActionRdnss;
+ mSubsystem = strdup("net");
+ asprintf(&mParams[0], "INTERFACE=%s", ifname);
+ asprintf(&mParams[1], "LIFETIME=%u", lifetime);
+ mParams[2] = buf;
+ } else {
+ SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Parse a binary message from a NETLINK_ROUTE netlink socket.
*/
bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
const struct nlmsghdr *nh;
for (nh = (struct nlmsghdr *) buffer;
- NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE);
+ NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
nh = NLMSG_NEXT(nh, size)) {
if (nh->nlmsg_type == RTM_NEWLINK) {
@@ -245,8 +345,25 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) {
continue;
}
+
+ } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
+ int len = nh->nlmsg_len - sizeof(*nh);
+ struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh);
+
+ if (sizeof(*ndmsg) > (size_t) len) {
+ SLOGE("Got a short RTM_NEWNDUSEROPT message\n");
+ continue;
+ }
+
+ size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg));
+ if (!parseNdUserOptMessage(ndmsg, optsize)) {
+ continue;
+ }
+
+
} else {
- SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type);
+ SLOGD("Unexpected netlink message. type=0x%x\n",
+ nh->nlmsg_type);
}
}