blob: 9f2606c4c969821da6b2bc382cd4cb87c9c94baa [file] [log] [blame]
San Mehat168415b2009-05-06 11:14:21 -07001/*
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>
San Mehat3d407292009-05-07 08:49:30 -070017#include <string.h>
San Mehat168415b2009-05-06 11:14:21 -070018
19#define LOG_TAG "NetlinkEvent"
20#include <cutils/log.h>
21
22#include <sysutils/NetlinkEvent.h>
23
Mike J. Chenec16b9d2011-06-23 14:55:28 -070024#include <sys/types.h>
25#include <sys/socket.h>
Lorenzo Colitti381f70f2013-08-02 05:58:37 +090026#include <netinet/in.h>
Lorenzo Colittic7eec832013-08-12 17:03:32 +090027#include <netinet/icmp6.h>
Lorenzo Colitti381f70f2013-08-02 05:58:37 +090028#include <arpa/inet.h>
29#include <net/if.h>
30
Mike J. Chenec16b9d2011-06-23 14:55:28 -070031#include <linux/if.h>
JP Abgralle6f80142011-07-14 16:46:32 -070032#include <linux/netfilter/nfnetlink.h>
33#include <linux/netfilter_ipv4/ipt_ULOG.h>
34/* From kernel's net/netfilter/xt_quota2.c */
35const int QLOG_NL_EVENT = 112;
36
37#include <linux/netlink.h>
38#include <linux/rtnetlink.h>
Mike J. Chenec16b9d2011-06-23 14:55:28 -070039
San Mehat168415b2009-05-06 11:14:21 -070040const int NetlinkEvent::NlActionUnknown = 0;
41const int NetlinkEvent::NlActionAdd = 1;
42const int NetlinkEvent::NlActionRemove = 2;
43const int NetlinkEvent::NlActionChange = 3;
Mike J. Chenec16b9d2011-06-23 14:55:28 -070044const int NetlinkEvent::NlActionLinkUp = 4;
45const int NetlinkEvent::NlActionLinkDown = 5;
Lorenzo Colitti526b8382013-09-03 00:25:14 +090046const int NetlinkEvent::NlActionAddressUpdated = 6;
47const int NetlinkEvent::NlActionAddressRemoved = 7;
Lorenzo Colittic7eec832013-08-12 17:03:32 +090048const int NetlinkEvent::NlActionRdnss = 8;
San Mehat168415b2009-05-06 11:14:21 -070049
50NetlinkEvent::NetlinkEvent() {
51 mAction = NlActionUnknown;
San Mehatebfe3db2009-10-10 17:35:13 -070052 memset(mParams, 0, sizeof(mParams));
53 mPath = NULL;
54 mSubsystem = NULL;
San Mehat168415b2009-05-06 11:14:21 -070055}
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
San Mehatd6744132009-12-24 07:17:09 -080070void NetlinkEvent::dump() {
71 int i;
72
73 for (i = 0; i < NL_PARAMS_MAX; i++) {
74 if (!mParams[i])
75 break;
San Mehat7e8529a2010-03-25 09:31:42 -070076 SLOGD("NL param '%s'\n", mParams[i]);
San Mehatd6744132009-12-24 07:17:09 -080077 }
78}
79
Mike J. Chenec16b9d2011-06-23 14:55:28 -070080/*
Lorenzo Colittic7eec832013-08-12 17:03:32 +090081 * Parse a RTM_NEWADDR or RTM_DELADDR message.
Lorenzo Colitti381f70f2013-08-02 05:58:37 +090082 */
83bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
84 int rtasize) {
Lorenzo Colitti96834562013-08-17 03:40:31 +090085 struct rtattr *rta;
Lorenzo Colitti381f70f2013-08-02 05:58:37 +090086 struct ifa_cacheinfo *cacheinfo = NULL;
87 char addrstr[INET6_ADDRSTRLEN] = "";
88
89 // Sanity check.
90 if (type != RTM_NEWADDR && type != RTM_DELADDR) {
91 SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
92 return false;
93 }
94
95 // For log messages.
96 const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR";
97
Lorenzo Colitti96834562013-08-17 03:40:31 +090098 for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize);
99 rta = RTA_NEXT(rta, rtasize)) {
Lorenzo Colitti381f70f2013-08-02 05:58:37 +0900100 if (rta->rta_type == IFA_ADDRESS) {
101 // Only look at the first address, because we only support notifying
102 // one change at a time.
103 if (*addrstr != '\0') {
104 SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", msgtype);
105 continue;
106 }
107
108 // Convert the IP address to a string.
109 if (ifaddr->ifa_family == AF_INET) {
110 struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta);
111 if (RTA_PAYLOAD(rta) < sizeof(*addr4)) {
112 SLOGE("Short IPv4 address (%d bytes) in %s",
113 RTA_PAYLOAD(rta), msgtype);
114 continue;
115 }
116 inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr));
117 } else if (ifaddr->ifa_family == AF_INET6) {
118 struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta);
119 if (RTA_PAYLOAD(rta) < sizeof(*addr6)) {
120 SLOGE("Short IPv6 address (%d bytes) in %s",
121 RTA_PAYLOAD(rta), msgtype);
122 continue;
123 }
124 inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr));
125 } else {
126 SLOGE("Unknown address family %d\n", ifaddr->ifa_family);
127 continue;
128 }
129
130 // Find the interface name.
131 char ifname[IFNAMSIZ + 1];
132 if (!if_indextoname(ifaddr->ifa_index, ifname)) {
133 SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
134 return false;
135 }
136
137 // Fill in interface information.
Lorenzo Colitti526b8382013-09-03 00:25:14 +0900138 mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated :
139 NlActionAddressRemoved;
140 mSubsystem = strdup("net");
Lorenzo Colitti381f70f2013-08-02 05:58:37 +0900141 asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
142 ifaddr->ifa_prefixlen);
Lorenzo Colitti526b8382013-09-03 00:25:14 +0900143 asprintf(&mParams[1], "INTERFACE=%s", ifname);
Lorenzo Colitti381f70f2013-08-02 05:58:37 +0900144 asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
145 asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
146 } else if (rta->rta_type == IFA_CACHEINFO) {
147 // Address lifetime information.
148 if (cacheinfo) {
149 // We only support one address.
150 SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype);
151 continue;
152 }
153
154 if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {
155 SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s",
156 RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype);
157 continue;
158 }
159
160 cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
161 asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
162 asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
163 asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
164 asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
165 }
Lorenzo Colitti381f70f2013-08-02 05:58:37 +0900166 }
167
168 if (addrstr[0] == '\0') {
169 SLOGE("No IFA_ADDRESS in %s\n", msgtype);
170 return false;
171 }
172
173 return true;
174}
175
176/*
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900177 * Parse a RTM_NEWNDUSEROPT message.
178 */
179bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
180 // Check the length is valid.
181 if (msg->nduseropt_opts_len > len) {
182 SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
183 msg->nduseropt_opts_len, len);
184 return false;
185 }
186 len = msg->nduseropt_opts_len;
187
188 // Check address family and packet type.
189 if (msg->nduseropt_family != AF_INET6) {
190 SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n",
191 msg->nduseropt_family);
192 return false;
193 }
194
195 if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
196 msg->nduseropt_icmp_code != 0) {
197 SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n",
198 msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);
199 return false;
200 }
201
202 // Find the interface name.
203 char ifname[IFNAMSIZ + 1];
204 if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
205 SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
206 msg->nduseropt_ifindex);
207 return false;
208 }
209
210 // The kernel sends a separate netlink message for each ND option in the RA.
211 // So only parse the first ND option in the message.
212 struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);
213
214 // The length is in multiples of 8 octets.
215 uint16_t optlen = opthdr->nd_opt_len;
216 if (optlen * 8 > len) {
217 SLOGE("Invalid option length %d > %d for ND option %d\n",
218 optlen * 8, len, opthdr->nd_opt_type);
219 return false;
220 }
221
222 if (opthdr->nd_opt_type == ND_OPT_RDNSS) {
223 // DNS Servers (RFC 6106).
224 // Each address takes up 2*8 octets, and the header takes up 8 octets.
225 // So for a valid option with one or more addresses, optlen must be
226 // odd and greater than 1.
227 if ((optlen < 3) || !(optlen & 0x1)) {
228 SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
229 return false;
230 }
231 int numaddrs = (optlen - 1) / 2;
232
233 // Find the lifetime.
234 struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
235 uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
236
237 // Construct "SERVERS=<comma-separated string of DNS addresses>".
238 // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
239 // the last address are followed by ','; the last is followed by '\0'.
240 static const char kServerTag[] = "SERVERS=";
241 static const int kTagLength = sizeof(kServerTag) - 1;
242 int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
243 char *buf = (char *) malloc(bufsize);
244 if (!buf) {
245 SLOGE("RDNSS option: out of memory\n");
246 return false;
247 }
248 strcpy(buf, kServerTag);
249 int pos = kTagLength;
250
251 struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
252 for (int i = 0; i < numaddrs; i++) {
253 if (i > 0) {
254 buf[pos++] = ',';
255 }
256 inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
257 pos += strlen(buf + pos);
258 }
259 buf[pos] = '\0';
260
261 mAction = NlActionRdnss;
262 mSubsystem = strdup("net");
263 asprintf(&mParams[0], "INTERFACE=%s", ifname);
264 asprintf(&mParams[1], "LIFETIME=%u", lifetime);
265 mParams[2] = buf;
266 } else {
267 SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
268 return false;
269 }
270
271 return true;
272}
273
274/*
275 * Parse a binary message from a NETLINK_ROUTE netlink socket.
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700276 */
277bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
Lorenzo Colitti96834562013-08-17 03:40:31 +0900278 const struct nlmsghdr *nh;
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700279
Lorenzo Colitti96834562013-08-17 03:40:31 +0900280 for (nh = (struct nlmsghdr *) buffer;
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900281 NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
Lorenzo Colitti96834562013-08-17 03:40:31 +0900282 nh = NLMSG_NEXT(nh, size)) {
JP Abgralle6f80142011-07-14 16:46:32 -0700283
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700284 if (nh->nlmsg_type == RTM_NEWLINK) {
285 int len = nh->nlmsg_len - sizeof(*nh);
286 struct ifinfomsg *ifi;
287
JP Abgralle6f80142011-07-14 16:46:32 -0700288 if (sizeof(*ifi) > (size_t) len) {
289 SLOGE("Got a short RTM_NEWLINK message\n");
290 continue;
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700291 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700292
JP Abgralle6f80142011-07-14 16:46:32 -0700293 ifi = (ifinfomsg *)NLMSG_DATA(nh);
294 if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
295 continue;
296 }
297
298 struct rtattr *rta = (struct rtattr *)
299 ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
300 len = NLMSG_PAYLOAD(nh, sizeof(*ifi));
301
302 while(RTA_OK(rta, len)) {
303 switch(rta->rta_type) {
304 case IFLA_IFNAME:
305 char buffer[16 + IFNAMSIZ];
306 snprintf(buffer, sizeof(buffer), "INTERFACE=%s",
307 (char *) RTA_DATA(rta));
308 mParams[0] = strdup(buffer);
309 mAction = (ifi->ifi_flags & IFF_LOWER_UP) ?
310 NlActionLinkUp : NlActionLinkDown;
Lorenzo Colitti526b8382013-09-03 00:25:14 +0900311 mSubsystem = strdup("net");
JP Abgralle6f80142011-07-14 16:46:32 -0700312 break;
313 }
314
315 rta = RTA_NEXT(rta, len);
316 }
317
318 } else if (nh->nlmsg_type == QLOG_NL_EVENT) {
319 char *devname;
320 ulog_packet_msg_t *pm;
321 size_t len = nh->nlmsg_len - sizeof(*nh);
322 if (sizeof(*pm) > len) {
323 SLOGE("Got a short QLOG message\n");
324 continue;
325 }
326 pm = (ulog_packet_msg_t *)NLMSG_DATA(nh);
327 devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
JP Abgralle6f80142011-07-14 16:46:32 -0700328 asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
329 asprintf(&mParams[1], "INTERFACE=%s", devname);
330 mSubsystem = strdup("qlog");
331 mAction = NlActionChange;
332
Lorenzo Colitti381f70f2013-08-02 05:58:37 +0900333 } else if (nh->nlmsg_type == RTM_NEWADDR ||
334 nh->nlmsg_type == RTM_DELADDR) {
335 int len = nh->nlmsg_len - sizeof(*nh);
336 struct ifaddrmsg *ifa;
337
338 if (sizeof(*ifa) > (size_t) len) {
339 SLOGE("Got a short RTM_xxxADDR message\n");
340 continue;
341 }
342
343 ifa = (ifaddrmsg *)NLMSG_DATA(nh);
344 size_t rtasize = IFA_PAYLOAD(nh);
345 if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) {
346 continue;
347 }
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900348
349 } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
350 int len = nh->nlmsg_len - sizeof(*nh);
351 struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh);
352
353 if (sizeof(*ndmsg) > (size_t) len) {
354 SLOGE("Got a short RTM_NEWNDUSEROPT message\n");
355 continue;
356 }
357
358 size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg));
359 if (!parseNdUserOptMessage(ndmsg, optsize)) {
360 continue;
361 }
362
363
JP Abgralle6f80142011-07-14 16:46:32 -0700364 } else {
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900365 SLOGD("Unexpected netlink message. type=0x%x\n",
366 nh->nlmsg_type);
JP Abgralle6f80142011-07-14 16:46:32 -0700367 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700368 }
369
370 return true;
371}
372
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100373/* If the string between 'str' and 'end' begins with 'prefixlen' characters
374 * from the 'prefix' array, then return 'str + prefixlen', otherwise return
375 * NULL.
376 */
377static const char*
378has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
379{
380 if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
381 return str + prefixlen;
382 else
383 return NULL;
384}
385
386/* Same as strlen(x) for constant string literals ONLY */
387#define CONST_STRLEN(x) (sizeof(x)-1)
388
389/* Convenience macro to call has_prefix with a constant string literal */
390#define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix))
391
392
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700393/*
394 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
395 * netlink socket.
396 */
397bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
Mike J. Chen17260b12011-06-23 15:00:30 -0700398 const char *s = buffer;
399 const char *end;
San Mehat168415b2009-05-06 11:14:21 -0700400 int param_idx = 0;
401 int i;
402 int first = 1;
403
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100404 if (size == 0)
405 return false;
406
407 /* Ensure the buffer is zero-terminated, the code below depends on this */
408 buffer[size-1] = '\0';
409
San Mehat168415b2009-05-06 11:14:21 -0700410 end = s + size;
411 while (s < end) {
412 if (first) {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100413 const char *p;
414 /* buffer is 0-terminated, no need to check p < end */
415 for (p = s; *p != '@'; p++) {
416 if (!*p) { /* no '@', should not happen */
417 return false;
418 }
419 }
420 mPath = strdup(p+1);
San Mehat168415b2009-05-06 11:14:21 -0700421 first = 0;
422 } else {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100423 const char* a;
424 if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
San Mehat168415b2009-05-06 11:14:21 -0700425 if (!strcmp(a, "add"))
426 mAction = NlActionAdd;
427 else if (!strcmp(a, "remove"))
428 mAction = NlActionRemove;
429 else if (!strcmp(a, "change"))
430 mAction = NlActionChange;
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100431 } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
432 mSeq = atoi(a);
433 } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
434 mSubsystem = strdup(a);
435 } else if (param_idx < NL_PARAMS_MAX) {
San Mehat168415b2009-05-06 11:14:21 -0700436 mParams[param_idx++] = strdup(s);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100437 }
San Mehat168415b2009-05-06 11:14:21 -0700438 }
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100439 s += strlen(s) + 1;
San Mehat168415b2009-05-06 11:14:21 -0700440 }
441 return true;
442}
443
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700444bool NetlinkEvent::decode(char *buffer, int size, int format) {
Mike J. Chen17260b12011-06-23 15:00:30 -0700445 if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
446 return parseBinaryNetlinkMessage(buffer, size);
447 } else {
448 return parseAsciiNetlinkMessage(buffer, size);
449 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700450}
451
San Mehat168415b2009-05-06 11:14:21 -0700452const char *NetlinkEvent::findParam(const char *paramName) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800453 size_t len = strlen(paramName);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100454 for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800455 const char *ptr = mParams[i] + len;
456 if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
457 return ++ptr;
San Mehat168415b2009-05-06 11:14:21 -0700458 }
459
San Mehat7e8529a2010-03-25 09:31:42 -0700460 SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
San Mehat168415b2009-05-06 11:14:21 -0700461 return NULL;
462}