1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <stdlib.h>
17#include <string.h>
18
19#define LOG_TAG "NetlinkEvent"
20#include <cutils/log.h>
21
22#include <sysutils/NetlinkEvent.h>
23
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <netinet/icmp6.h>
28#include <arpa/inet.h>
29#include <net/if.h>
30
31#include <linux/if.h>
32#include <linux/if_addr.h>
33#include <linux/if_link.h>
34#include <linux/netfilter/nfnetlink.h>
35#include <linux/netfilter/nfnetlink_log.h>
36#include <linux/netfilter_ipv4/ipt_ULOG.h>
37
38/* From kernel's net/netfilter/xt_quota2.c */
39const int LOCAL_QLOG_NL_EVENT = 112;
40const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
41
42#include <linux/netlink.h>
43#include <linux/rtnetlink.h>
44
45#include <netlink/attr.h>
46#include <netlink/genl/genl.h>
47#include <netlink/handlers.h>
48#include <netlink/msg.h>
49
50NetlinkEvent::NetlinkEvent() {
51    mAction = Action::kUnknown;
52    memset(mParams, 0, sizeof(mParams));
53    mPath = NULL;
54    mSubsystem = NULL;
55}
56
57NetlinkEvent::~NetlinkEvent() {
58    int i;
59    if (mPath)
60        free(mPath);
61    if (mSubsystem)
62        free(mSubsystem);
63    for (i = 0; i < NL_PARAMS_MAX; i++) {
64        if (!mParams[i])
65            break;
66        free(mParams[i]);
67    }
68}
69
70void NetlinkEvent::dump() {
71    int i;
72
73    for (i = 0; i < NL_PARAMS_MAX; i++) {
74        if (!mParams[i])
75            break;
76        SLOGD("NL param '%s'\n", mParams[i]);
77    }
78}
79
80/*
81 * Returns the message name for a message in the NETLINK_ROUTE family, or NULL
82 * if parsing that message is not supported.
83 */
84static const char *rtMessageName(int type) {
85#define NL_EVENT_RTM_NAME(rtm) case rtm: return #rtm;
86    switch (type) {
87        NL_EVENT_RTM_NAME(RTM_NEWLINK);
88        NL_EVENT_RTM_NAME(RTM_DELLINK);
89        NL_EVENT_RTM_NAME(RTM_NEWADDR);
90        NL_EVENT_RTM_NAME(RTM_DELADDR);
91        NL_EVENT_RTM_NAME(RTM_NEWROUTE);
92        NL_EVENT_RTM_NAME(RTM_DELROUTE);
93        NL_EVENT_RTM_NAME(RTM_NEWNDUSEROPT);
94        NL_EVENT_RTM_NAME(LOCAL_QLOG_NL_EVENT);
95        NL_EVENT_RTM_NAME(LOCAL_NFLOG_PACKET);
96        default:
97            return NULL;
98    }
99#undef NL_EVENT_RTM_NAME
100}
101
102/*
103 * Checks that a binary NETLINK_ROUTE message is long enough for a payload of
104 * size bytes.
105 */
106static bool checkRtNetlinkLength(const struct nlmsghdr *nh, size_t size) {
107    if (nh->nlmsg_len < NLMSG_LENGTH(size)) {
108        SLOGE("Got a short %s message\n", rtMessageName(nh->nlmsg_type));
109        return false;
110    }
111    return true;
112}
113
114/*
115 * Utility function to log errors.
116 */
117static bool maybeLogDuplicateAttribute(bool isDup,
118                                       const char *attributeName,
119                                       const char *messageName) {
120    if (isDup) {
121        SLOGE("Multiple %s attributes in %s, ignoring\n", attributeName, messageName);
122        return true;
123    }
124    return false;
125}
126
127/*
128 * Parse a RTM_NEWLINK message.
129 */
130bool NetlinkEvent::parseIfInfoMessage(const struct nlmsghdr *nh) {
131    struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(nh);
132    if (!checkRtNetlinkLength(nh, sizeof(*ifi)))
133        return false;
134
135    if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
136        return false;
137    }
138
139    int len = IFLA_PAYLOAD(nh);
140    struct rtattr *rta;
141    for (rta = IFLA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
142        switch(rta->rta_type) {
143            case IFLA_IFNAME:
144                asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta));
145                mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp :
146                                                            Action::kLinkDown;
147                mSubsystem = strdup("net");
148                return true;
149        }
150    }
151
152    return false;
153}
154
155/*
156 * Parse a RTM_NEWADDR or RTM_DELADDR message.
157 */
158bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
159    struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
160    struct ifa_cacheinfo *cacheinfo = NULL;
161    char addrstr[INET6_ADDRSTRLEN] = "";
162    char ifname[IFNAMSIZ];
163
164    if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
165        return false;
166
167    // Sanity check.
168    int type = nh->nlmsg_type;
169    if (type != RTM_NEWADDR && type != RTM_DELADDR) {
170        SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
171        return false;
172    }
173
174    // For log messages.
175    const char *msgtype = rtMessageName(type);
176
177    struct rtattr *rta;
178    int len = IFA_PAYLOAD(nh);
179    for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
180        if (rta->rta_type == IFA_ADDRESS) {
181            // Only look at the first address, because we only support notifying
182            // one change at a time.
183            if (maybeLogDuplicateAttribute(*addrstr != '\0', "IFA_ADDRESS", msgtype))
184                continue;
185
186            // Convert the IP address to a string.
187            if (ifaddr->ifa_family == AF_INET) {
188                struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta);
189                if (RTA_PAYLOAD(rta) < sizeof(*addr4)) {
190                    SLOGE("Short IPv4 address (%zu bytes) in %s",
191                          RTA_PAYLOAD(rta), msgtype);
192                    continue;
193                }
194                inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr));
195            } else if (ifaddr->ifa_family == AF_INET6) {
196                struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta);
197                if (RTA_PAYLOAD(rta) < sizeof(*addr6)) {
198                    SLOGE("Short IPv6 address (%zu bytes) in %s",
199                          RTA_PAYLOAD(rta), msgtype);
200                    continue;
201                }
202                inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr));
203            } else {
204                SLOGE("Unknown address family %d\n", ifaddr->ifa_family);
205                continue;
206            }
207
208            // Find the interface name.
209            if (!if_indextoname(ifaddr->ifa_index, ifname)) {
210                SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
211                return false;
212            }
213
214        } else if (rta->rta_type == IFA_CACHEINFO) {
215            // Address lifetime information.
216            if (maybeLogDuplicateAttribute(cacheinfo, "IFA_CACHEINFO", msgtype))
217                continue;
218
219            if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {
220                SLOGE("Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s",
221                      RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype);
222                continue;
223            }
224
225            cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
226        }
227    }
228
229    if (addrstr[0] == '\0') {
230        SLOGE("No IFA_ADDRESS in %s\n", msgtype);
231        return false;
232    }
233
234    // Fill in netlink event information.
235    mAction = (type == RTM_NEWADDR) ? Action::kAddressUpdated :
236                                      Action::kAddressRemoved;
237    mSubsystem = strdup("net");
238    asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
239             ifaddr->ifa_prefixlen);
240    asprintf(&mParams[1], "INTERFACE=%s", ifname);
241    asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
242    asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
243
244    if (cacheinfo) {
245        asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
246        asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
247        asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
248        asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
249    }
250
251    return true;
252}
253
254/*
255 * Parse a QLOG_NL_EVENT message.
256 */
257bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {
258    const char *devname;
259    ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh);
260    if (!checkRtNetlinkLength(nh, sizeof(*pm)))
261        return false;
262
263    devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
264    asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
265    asprintf(&mParams[1], "INTERFACE=%s", devname);
266    mSubsystem = strdup("qlog");
267    mAction = Action::kChange;
268    return true;
269}
270
271/*
272 * Parse a LOCAL_NFLOG_PACKET message.
273 */
274bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {
275    int uid = -1;
276    int len = 0;
277    char* raw = NULL;
278
279    struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
280    if (uid_attr) {
281        uid = ntohl(nla_get_u32(uid_attr));
282    }
283
284    struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
285    if (payload) {
286        /* First 256 bytes is plenty */
287        len = nla_len(payload);
288        if (len > 256) len = 256;
289        raw = (char*) nla_data(payload);
290    }
291
292    char* hex = (char*) calloc(1, 5 + (len * 2));
293    strcpy(hex, "HEX=");
294    for (int i = 0; i < len; i++) {
295        hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
296        hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
297    }
298
299    asprintf(&mParams[0], "UID=%d", uid);
300    mParams[1] = hex;
301    mSubsystem = strdup("strict");
302    mAction = Action::kChange;
303    return true;
304}
305
306/*
307 * Parse a RTM_NEWROUTE or RTM_DELROUTE message.
308 */
309bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {
310    uint8_t type = nh->nlmsg_type;
311    const char *msgname = rtMessageName(type);
312
313    // Sanity check.
314    if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
315        SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
316        return false;
317    }
318
319    struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA(nh);
320    if (!checkRtNetlinkLength(nh, sizeof(*rtm)))
321        return false;
322
323    if (// Ignore static routes we've set up ourselves.
324        (rtm->rtm_protocol != RTPROT_KERNEL &&
325         rtm->rtm_protocol != RTPROT_RA) ||
326        // We're only interested in global unicast routes.
327        (rtm->rtm_scope != RT_SCOPE_UNIVERSE) ||
328        (rtm->rtm_type != RTN_UNICAST) ||
329        // We don't support source routing.
330        (rtm->rtm_src_len != 0) ||
331        // Cloned routes aren't real routes.
332        (rtm->rtm_flags & RTM_F_CLONED)) {
333        return false;
334    }
335
336    int family = rtm->rtm_family;
337    int prefixLength = rtm->rtm_dst_len;
338
339    // Currently we only support: destination, (one) next hop, ifindex.
340    char dst[INET6_ADDRSTRLEN] = "";
341    char gw[INET6_ADDRSTRLEN] = "";
342    char dev[IFNAMSIZ] = "";
343
344    size_t len = RTM_PAYLOAD(nh);
345    struct rtattr *rta;
346    for (rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
347        switch (rta->rta_type) {
348            case RTA_DST:
349                if (maybeLogDuplicateAttribute(*dst, "RTA_DST", msgname))
350                    continue;
351                if (!inet_ntop(family, RTA_DATA(rta), dst, sizeof(dst)))
352                    return false;
353                continue;
354            case RTA_GATEWAY:
355                if (maybeLogDuplicateAttribute(*gw, "RTA_GATEWAY", msgname))
356                    continue;
357                if (!inet_ntop(family, RTA_DATA(rta), gw, sizeof(gw)))
358                    return false;
359                continue;
360            case RTA_OIF:
361                if (maybeLogDuplicateAttribute(*dev, "RTA_OIF", msgname))
362                    continue;
363                if (!if_indextoname(* (int *) RTA_DATA(rta), dev))
364                    return false;
365            default:
366                continue;
367        }
368    }
369
370   // If there's no RTA_DST attribute, then:
371   // - If the prefix length is zero, it's the default route.
372   // - If the prefix length is nonzero, there's something we don't understand.
373   //   Ignore the event.
374   if (!*dst && !prefixLength) {
375        if (family == AF_INET) {
376            strncpy(dst, "0.0.0.0", sizeof(dst));
377        } else if (family == AF_INET6) {
378            strncpy(dst, "::", sizeof(dst));
379        }
380    }
381
382    // A useful route must have a destination and at least either a gateway or
383    // an interface.
384    if (!*dst || (!*gw && !*dev))
385        return false;
386
387    // Fill in netlink event information.
388    mAction = (type == RTM_NEWROUTE) ? Action::kRouteUpdated :
389                                       Action::kRouteRemoved;
390    mSubsystem = strdup("net");
391    asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength);
392    asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : "");
393    asprintf(&mParams[2], "INTERFACE=%s", (*dev) ? dev : "");
394
395    return true;
396}
397
398/*
399 * Parse a RTM_NEWNDUSEROPT message.
400 */
401bool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) {
402    struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(nh);
403    if (!checkRtNetlinkLength(nh, sizeof(*msg)))
404        return false;
405
406    // Check the length is valid.
407    int len = NLMSG_PAYLOAD(nh, sizeof(*msg));
408    if (msg->nduseropt_opts_len > len) {
409        SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
410              msg->nduseropt_opts_len, len);
411        return false;
412    }
413    len = msg->nduseropt_opts_len;
414
415    // Check address family and packet type.
416    if (msg->nduseropt_family != AF_INET6) {
417        SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n",
418              msg->nduseropt_family);
419        return false;
420    }
421
422    if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
423        msg->nduseropt_icmp_code != 0) {
424        SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n",
425              msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);
426        return false;
427    }
428
429    // Find the interface name.
430    char ifname[IFNAMSIZ];
431    if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
432        SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
433              msg->nduseropt_ifindex);
434        return false;
435    }
436
437    // The kernel sends a separate netlink message for each ND option in the RA.
438    // So only parse the first ND option in the message.
439    struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);
440
441    // The length is in multiples of 8 octets.
442    uint16_t optlen = opthdr->nd_opt_len;
443    if (optlen * 8 > len) {
444        SLOGE("Invalid option length %d > %d for ND option %d\n",
445              optlen * 8, len, opthdr->nd_opt_type);
446        return false;
447    }
448
449    if (opthdr->nd_opt_type == ND_OPT_RDNSS) {
450        // DNS Servers (RFC 6106).
451        // Each address takes up 2*8 octets, and the header takes up 8 octets.
452        // So for a valid option with one or more addresses, optlen must be
453        // odd and greater than 1.
454        if ((optlen < 3) || !(optlen & 0x1)) {
455            SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
456            return false;
457        }
458        const int numaddrs = (optlen - 1) / 2;
459
460        // Find the lifetime.
461        struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
462        const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
463
464        // Construct "SERVERS=<comma-separated string of DNS addresses>".
465        static const char kServerTag[] = "SERVERS=";
466        static const size_t kTagLength = strlen(kServerTag);
467        // Reserve sufficient space for an IPv6 link-local address: all but the
468        // last address are followed by ','; the last is followed by '\0'.
469        static const size_t kMaxSingleAddressLength =
470                INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
471        const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength;
472        char *buf = (char *) malloc(bufsize);
473        if (!buf) {
474            SLOGE("RDNSS option: out of memory\n");
475            return false;
476        }
477        strcpy(buf, kServerTag);
478        size_t pos = kTagLength;
479
480        struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
481        for (int i = 0; i < numaddrs; i++) {
482            if (i > 0) {
483                buf[pos++] = ',';
484            }
485            inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
486            pos += strlen(buf + pos);
487            if (IN6_IS_ADDR_LINKLOCAL(addrs + i)) {
488                buf[pos++] = '%';
489                pos += strlcpy(buf + pos, ifname, bufsize - pos);
490            }
491        }
492        buf[pos] = '\0';
493
494        mAction = Action::kRdnss;
495        mSubsystem = strdup("net");
496        asprintf(&mParams[0], "INTERFACE=%s", ifname);
497        asprintf(&mParams[1], "LIFETIME=%u", lifetime);
498        mParams[2] = buf;
499    } else {
500        SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
501        return false;
502    }
503
504    return true;
505}
506
507/*
508 * Parse a binary message from a NETLINK_ROUTE netlink socket.
509 *
510 * Note that this function can only parse one message, because the message's
511 * content has to be stored in the class's member variables (mAction,
512 * mSubsystem, etc.). Invalid or unrecognized messages are skipped, but if
513 * there are multiple valid messages in the buffer, only the first one will be
514 * returned.
515 *
516 * TODO: consider only ever looking at the first message.
517 */
518bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
519    struct nlmsghdr *nh;
520
521    for (nh = (struct nlmsghdr *) buffer;
522         NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
523         nh = NLMSG_NEXT(nh, size)) {
524
525        if (!rtMessageName(nh->nlmsg_type)) {
526            SLOGD("Unexpected netlink message type %d\n", nh->nlmsg_type);
527            continue;
528        }
529
530        if (nh->nlmsg_type == RTM_NEWLINK) {
531            if (parseIfInfoMessage(nh))
532                return true;
533
534        } else if (nh->nlmsg_type == LOCAL_QLOG_NL_EVENT) {
535            if (parseUlogPacketMessage(nh))
536                return true;
537
538        } else if (nh->nlmsg_type == RTM_NEWADDR ||
539                   nh->nlmsg_type == RTM_DELADDR) {
540            if (parseIfAddrMessage(nh))
541                return true;
542
543        } else if (nh->nlmsg_type == RTM_NEWROUTE ||
544                   nh->nlmsg_type == RTM_DELROUTE) {
545            if (parseRtMessage(nh))
546                return true;
547
548        } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
549            if (parseNdUserOptMessage(nh))
550                return true;
551
552        } else if (nh->nlmsg_type == LOCAL_NFLOG_PACKET) {
553            if (parseNfPacketMessage(nh))
554                return true;
555
556        }
557    }
558
559    return false;
560}
561
562/* If the string between 'str' and 'end' begins with 'prefixlen' characters
563 * from the 'prefix' array, then return 'str + prefixlen', otherwise return
564 * NULL.
565 */
566static const char*
567has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
568{
569    if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
570        return str + prefixlen;
571    else
572        return NULL;
573}
574
575/* Same as strlen(x) for constant string literals ONLY */
576#define CONST_STRLEN(x)  (sizeof(x)-1)
577
578/* Convenience macro to call has_prefix with a constant string literal  */
579#define HAS_CONST_PREFIX(str,end,prefix)  has_prefix((str),(end),prefix,CONST_STRLEN(prefix))
580
581
582/*
583 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
584 * netlink socket.
585 */
586bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
587    const char *s = buffer;
588    const char *end;
589    int param_idx = 0;
590    int first = 1;
591
592    if (size == 0)
593        return false;
594
595    /* Ensure the buffer is zero-terminated, the code below depends on this */
596    buffer[size-1] = '\0';
597
598    end = s + size;
599    while (s < end) {
600        if (first) {
601            const char *p;
602            /* buffer is 0-terminated, no need to check p < end */
603            for (p = s; *p != '@'; p++) {
604                if (!*p) { /* no '@', should not happen */
605                    return false;
606                }
607            }
608            mPath = strdup(p+1);
609            first = 0;
610        } else {
611            const char* a;
612            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
613                if (!strcmp(a, "add"))
614                    mAction = Action::kAdd;
615                else if (!strcmp(a, "remove"))
616                    mAction = Action::kRemove;
617                else if (!strcmp(a, "change"))
618                    mAction = Action::kChange;
619            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
620                mSeq = atoi(a);
621            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
622                mSubsystem = strdup(a);
623            } else if (param_idx < NL_PARAMS_MAX) {
624                mParams[param_idx++] = strdup(s);
625            }
626        }
627        s += strlen(s) + 1;
628    }
629    return true;
630}
631
632bool NetlinkEvent::decode(char *buffer, int size, int format) {
633    if (format == NetlinkListener::NETLINK_FORMAT_BINARY
634            || format == NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST) {
635        return parseBinaryNetlinkMessage(buffer, size);
636    } else {
637        return parseAsciiNetlinkMessage(buffer, size);
638    }
639}
640
641const char *NetlinkEvent::findParam(const char *paramName) {
642    size_t len = strlen(paramName);
643    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
644        const char *ptr = mParams[i] + len;
645        if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
646            return ++ptr;
647    }
648
649    SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
650    return NULL;
651}
652