Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85fddd86 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Android Git Automerger
Browse files

am cbc32e9f: Merge "Support parsing RDNSS ND options from netlink."

* commit 'cbc32e9f':
  Support parsing RDNSS ND options from netlink.
parents 6231f7f0 cbc32e9f
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ public:
    const static int NlActionLinkUp;
    const static int NlActionAddressUpdated;
    const static int NlActionAddressRemoved;
    const static int NlActionRdnss;

    NetlinkEvent();
    virtual ~NetlinkEvent();
@@ -49,9 +50,10 @@ public:
    void dump();

 protected:
    bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
    bool parseBinaryNetlinkMessage(char *buffer, int size);
    bool parseAsciiNetlinkMessage(char *buffer, int size);
    bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
    bool parseNdUserOptMessage(struct nduseroptmsg *msg, int optsize);
};

#endif
+121 −4
Original line number Diff line number Diff line
@@ -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);
        }
    }