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