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 <arpa/inet.h> 28 #include <net/if.h> 29 30 #include <linux/if.h> 31 #include <linux/netfilter/nfnetlink.h> 32 #include <linux/netfilter_ipv4/ipt_ULOG.h> 33 /* From kernel's net/netfilter/xt_quota2.c */ 34 const int QLOG_NL_EVENT = 112; 35 36 #include <linux/netlink.h> 37 #include <linux/rtnetlink.h> 38 39 const int NetlinkEvent::NlActionUnknown = 0; 40 const int NetlinkEvent::NlActionAdd = 1; 41 const int NetlinkEvent::NlActionRemove = 2; 42 const int NetlinkEvent::NlActionChange = 3; 43 const int NetlinkEvent::NlActionLinkUp = 4; 44 const int NetlinkEvent::NlActionLinkDown = 5; 45 const int NetlinkEvent::NlActionAddressUpdated = 6; 46 const int NetlinkEvent::NlActionAddressRemoved = 7; 47 48 NetlinkEvent::NetlinkEvent() { 49 mAction = NlActionUnknown; 50 memset(mParams, 0, sizeof(mParams)); 51 mPath = NULL; 52 mSubsystem = NULL; 53 } 54 55 NetlinkEvent::~NetlinkEvent() { 56 int i; 57 if (mPath) 58 free(mPath); 59 if (mSubsystem) 60 free(mSubsystem); 61 for (i = 0; i < NL_PARAMS_MAX; i++) { 62 if (!mParams[i]) 63 break; 64 free(mParams[i]); 65 } 66 } 67 68 void NetlinkEvent::dump() { 69 int i; 70 71 for (i = 0; i < NL_PARAMS_MAX; i++) { 72 if (!mParams[i]) 73 break; 74 SLOGD("NL param '%s'\n", mParams[i]); 75 } 76 } 77 78 /* 79 * Decode a RTM_NEWADDR or RTM_DELADDR message. 80 */ 81 bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, 82 int rtasize) { 83 struct rtattr *rta; 84 struct ifa_cacheinfo *cacheinfo = NULL; 85 char addrstr[INET6_ADDRSTRLEN] = ""; 86 87 // Sanity check. 88 if (type != RTM_NEWADDR && type != RTM_DELADDR) { 89 SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type); 90 return false; 91 } 92 93 // For log messages. 94 const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR"; 95 96 for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize); 97 rta = RTA_NEXT(rta, rtasize)) { 98 if (rta->rta_type == IFA_ADDRESS) { 99 // Only look at the first address, because we only support notifying 100 // one change at a time. 101 if (*addrstr != '\0') { 102 SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", msgtype); 103 continue; 104 } 105 106 // Convert the IP address to a string. 107 if (ifaddr->ifa_family == AF_INET) { 108 struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta); 109 if (RTA_PAYLOAD(rta) < sizeof(*addr4)) { 110 SLOGE("Short IPv4 address (%d bytes) in %s", 111 RTA_PAYLOAD(rta), msgtype); 112 continue; 113 } 114 inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr)); 115 } else if (ifaddr->ifa_family == AF_INET6) { 116 struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta); 117 if (RTA_PAYLOAD(rta) < sizeof(*addr6)) { 118 SLOGE("Short IPv6 address (%d bytes) in %s", 119 RTA_PAYLOAD(rta), msgtype); 120 continue; 121 } 122 inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr)); 123 } else { 124 SLOGE("Unknown address family %d\n", ifaddr->ifa_family); 125 continue; 126 } 127 128 // Find the interface name. 129 char ifname[IFNAMSIZ + 1]; 130 if (!if_indextoname(ifaddr->ifa_index, ifname)) { 131 SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype); 132 return false; 133 } 134 135 // Fill in interface information. 136 mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : 137 NlActionAddressRemoved; 138 mSubsystem = strdup("net"); 139 asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, 140 ifaddr->ifa_prefixlen); 141 asprintf(&mParams[1], "INTERFACE=%s", ifname); 142 asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags); 143 asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope); 144 } else if (rta->rta_type == IFA_CACHEINFO) { 145 // Address lifetime information. 146 if (cacheinfo) { 147 // We only support one address. 148 SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype); 149 continue; 150 } 151 152 if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) { 153 SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s", 154 RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype); 155 continue; 156 } 157 158 cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta); 159 asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered); 160 asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid); 161 asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp); 162 asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp); 163 } 164 } 165 166 if (addrstr[0] == '\0') { 167 SLOGE("No IFA_ADDRESS in %s\n", msgtype); 168 return false; 169 } 170 171 return true; 172 } 173 174 /* 175 * Parse an binary message from a NETLINK_ROUTE netlink socket. 176 */ 177 bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { 178 const struct nlmsghdr *nh; 179 180 for (nh = (struct nlmsghdr *) buffer; 181 NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE); 182 nh = NLMSG_NEXT(nh, size)) { 183 184 if (nh->nlmsg_type == RTM_NEWLINK) { 185 int len = nh->nlmsg_len - sizeof(*nh); 186 struct ifinfomsg *ifi; 187 188 if (sizeof(*ifi) > (size_t) len) { 189 SLOGE("Got a short RTM_NEWLINK message\n"); 190 continue; 191 } 192 193 ifi = (ifinfomsg *)NLMSG_DATA(nh); 194 if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) { 195 continue; 196 } 197 198 struct rtattr *rta = (struct rtattr *) 199 ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 200 len = NLMSG_PAYLOAD(nh, sizeof(*ifi)); 201 202 while(RTA_OK(rta, len)) { 203 switch(rta->rta_type) { 204 case IFLA_IFNAME: 205 char buffer[16 + IFNAMSIZ]; 206 snprintf(buffer, sizeof(buffer), "INTERFACE=%s", 207 (char *) RTA_DATA(rta)); 208 mParams[0] = strdup(buffer); 209 mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? 210 NlActionLinkUp : NlActionLinkDown; 211 mSubsystem = strdup("net"); 212 break; 213 } 214 215 rta = RTA_NEXT(rta, len); 216 } 217 218 } else if (nh->nlmsg_type == QLOG_NL_EVENT) { 219 char *devname; 220 ulog_packet_msg_t *pm; 221 size_t len = nh->nlmsg_len - sizeof(*nh); 222 if (sizeof(*pm) > len) { 223 SLOGE("Got a short QLOG message\n"); 224 continue; 225 } 226 pm = (ulog_packet_msg_t *)NLMSG_DATA(nh); 227 devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name; 228 asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); 229 asprintf(&mParams[1], "INTERFACE=%s", devname); 230 mSubsystem = strdup("qlog"); 231 mAction = NlActionChange; 232 233 } else if (nh->nlmsg_type == RTM_NEWADDR || 234 nh->nlmsg_type == RTM_DELADDR) { 235 int len = nh->nlmsg_len - sizeof(*nh); 236 struct ifaddrmsg *ifa; 237 238 if (sizeof(*ifa) > (size_t) len) { 239 SLOGE("Got a short RTM_xxxADDR message\n"); 240 continue; 241 } 242 243 ifa = (ifaddrmsg *)NLMSG_DATA(nh); 244 size_t rtasize = IFA_PAYLOAD(nh); 245 if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) { 246 continue; 247 } 248 } else { 249 SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); 250 } 251 } 252 253 return true; 254 } 255 256 /* If the string between 'str' and 'end' begins with 'prefixlen' characters 257 * from the 'prefix' array, then return 'str + prefixlen', otherwise return 258 * NULL. 259 */ 260 static const char* 261 has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen) 262 { 263 if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen)) 264 return str + prefixlen; 265 else 266 return NULL; 267 } 268 269 /* Same as strlen(x) for constant string literals ONLY */ 270 #define CONST_STRLEN(x) (sizeof(x)-1) 271 272 /* Convenience macro to call has_prefix with a constant string literal */ 273 #define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix)) 274 275 276 /* 277 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT 278 * netlink socket. 279 */ 280 bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) { 281 const char *s = buffer; 282 const char *end; 283 int param_idx = 0; 284 int i; 285 int first = 1; 286 287 if (size == 0) 288 return false; 289 290 /* Ensure the buffer is zero-terminated, the code below depends on this */ 291 buffer[size-1] = '\0'; 292 293 end = s + size; 294 while (s < end) { 295 if (first) { 296 const char *p; 297 /* buffer is 0-terminated, no need to check p < end */ 298 for (p = s; *p != '@'; p++) { 299 if (!*p) { /* no '@', should not happen */ 300 return false; 301 } 302 } 303 mPath = strdup(p+1); 304 first = 0; 305 } else { 306 const char* a; 307 if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) { 308 if (!strcmp(a, "add")) 309 mAction = NlActionAdd; 310 else if (!strcmp(a, "remove")) 311 mAction = NlActionRemove; 312 else if (!strcmp(a, "change")) 313 mAction = NlActionChange; 314 } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) { 315 mSeq = atoi(a); 316 } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) { 317 mSubsystem = strdup(a); 318 } else if (param_idx < NL_PARAMS_MAX) { 319 mParams[param_idx++] = strdup(s); 320 } 321 } 322 s += strlen(s) + 1; 323 } 324 return true; 325 } 326 327 bool NetlinkEvent::decode(char *buffer, int size, int format) { 328 if (format == NetlinkListener::NETLINK_FORMAT_BINARY) { 329 return parseBinaryNetlinkMessage(buffer, size); 330 } else { 331 return parseAsciiNetlinkMessage(buffer, size); 332 } 333 } 334 335 const char *NetlinkEvent::findParam(const char *paramName) { 336 size_t len = strlen(paramName); 337 for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) { 338 const char *ptr = mParams[i] + len; 339 if (!strncmp(mParams[i], paramName, len) && *ptr == '=') 340 return ++ptr; 341 } 342 343 SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName); 344 return NULL; 345 } 346