blob: 34f20160eebdb2ce92ec329c554ab3f7638ee4f7 [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/*
The Android Open Source Project66ed50a2013-11-22 11:28:10 -0800177<<<<<<< HEAD
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900178 * Parse a RTM_NEWNDUSEROPT message.
179 */
180bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
181 // Check the length is valid.
182 if (msg->nduseropt_opts_len > len) {
183 SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
184 msg->nduseropt_opts_len, len);
185 return false;
186 }
187 len = msg->nduseropt_opts_len;
188
189 // Check address family and packet type.
190 if (msg->nduseropt_family != AF_INET6) {
191 SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n",
192 msg->nduseropt_family);
193 return false;
194 }
195
196 if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
197 msg->nduseropt_icmp_code != 0) {
198 SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n",
199 msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);
200 return false;
201 }
202
203 // Find the interface name.
204 char ifname[IFNAMSIZ + 1];
205 if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
206 SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
207 msg->nduseropt_ifindex);
208 return false;
209 }
210
211 // The kernel sends a separate netlink message for each ND option in the RA.
212 // So only parse the first ND option in the message.
213 struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);
214
215 // The length is in multiples of 8 octets.
216 uint16_t optlen = opthdr->nd_opt_len;
217 if (optlen * 8 > len) {
218 SLOGE("Invalid option length %d > %d for ND option %d\n",
219 optlen * 8, len, opthdr->nd_opt_type);
220 return false;
221 }
222
223 if (opthdr->nd_opt_type == ND_OPT_RDNSS) {
224 // DNS Servers (RFC 6106).
225 // Each address takes up 2*8 octets, and the header takes up 8 octets.
226 // So for a valid option with one or more addresses, optlen must be
227 // odd and greater than 1.
228 if ((optlen < 3) || !(optlen & 0x1)) {
229 SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
230 return false;
231 }
232 int numaddrs = (optlen - 1) / 2;
233
234 // Find the lifetime.
235 struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
236 uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
237
238 // Construct "SERVERS=<comma-separated string of DNS addresses>".
239 // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
240 // the last address are followed by ','; the last is followed by '\0'.
241 static const char kServerTag[] = "SERVERS=";
242 static const int kTagLength = sizeof(kServerTag) - 1;
243 int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
244 char *buf = (char *) malloc(bufsize);
245 if (!buf) {
246 SLOGE("RDNSS option: out of memory\n");
247 return false;
248 }
249 strcpy(buf, kServerTag);
250 int pos = kTagLength;
251
252 struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
253 for (int i = 0; i < numaddrs; i++) {
254 if (i > 0) {
255 buf[pos++] = ',';
256 }
257 inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
258 pos += strlen(buf + pos);
259 }
260 buf[pos] = '\0';
261
262 mAction = NlActionRdnss;
263 mSubsystem = strdup("net");
264 asprintf(&mParams[0], "INTERFACE=%s", ifname);
265 asprintf(&mParams[1], "LIFETIME=%u", lifetime);
266 mParams[2] = buf;
267 } else {
268 SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
269 return false;
270 }
271
272 return true;
273}
274
275/*
276 * Parse a binary message from a NETLINK_ROUTE netlink socket.
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700277 */
278bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
Lorenzo Colitti96834562013-08-17 03:40:31 +0900279 const struct nlmsghdr *nh;
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700280
Lorenzo Colitti96834562013-08-17 03:40:31 +0900281 for (nh = (struct nlmsghdr *) buffer;
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900282 NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
Lorenzo Colitti96834562013-08-17 03:40:31 +0900283 nh = NLMSG_NEXT(nh, size)) {
JP Abgralle6f80142011-07-14 16:46:32 -0700284
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700285 if (nh->nlmsg_type == RTM_NEWLINK) {
286 int len = nh->nlmsg_len - sizeof(*nh);
287 struct ifinfomsg *ifi;
288
JP Abgralle6f80142011-07-14 16:46:32 -0700289 if (sizeof(*ifi) > (size_t) len) {
290 SLOGE("Got a short RTM_NEWLINK message\n");
291 continue;
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700292 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700293
JP Abgralle6f80142011-07-14 16:46:32 -0700294 ifi = (ifinfomsg *)NLMSG_DATA(nh);
295 if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
296 continue;
297 }
298
299 struct rtattr *rta = (struct rtattr *)
300 ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
301 len = NLMSG_PAYLOAD(nh, sizeof(*ifi));
302
303 while(RTA_OK(rta, len)) {
304 switch(rta->rta_type) {
305 case IFLA_IFNAME:
306 char buffer[16 + IFNAMSIZ];
307 snprintf(buffer, sizeof(buffer), "INTERFACE=%s",
308 (char *) RTA_DATA(rta));
309 mParams[0] = strdup(buffer);
310 mAction = (ifi->ifi_flags & IFF_LOWER_UP) ?
311 NlActionLinkUp : NlActionLinkDown;
Lorenzo Colitti526b8382013-09-03 00:25:14 +0900312 mSubsystem = strdup("net");
JP Abgralle6f80142011-07-14 16:46:32 -0700313 break;
314 }
315
316 rta = RTA_NEXT(rta, len);
317 }
318
319 } else if (nh->nlmsg_type == QLOG_NL_EVENT) {
320 char *devname;
321 ulog_packet_msg_t *pm;
322 size_t len = nh->nlmsg_len - sizeof(*nh);
323 if (sizeof(*pm) > len) {
324 SLOGE("Got a short QLOG message\n");
325 continue;
326 }
327 pm = (ulog_packet_msg_t *)NLMSG_DATA(nh);
328 devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
JP Abgralle6f80142011-07-14 16:46:32 -0700329 asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
330 asprintf(&mParams[1], "INTERFACE=%s", devname);
331 mSubsystem = strdup("qlog");
332 mAction = NlActionChange;
333
Lorenzo Colitti381f70f2013-08-02 05:58:37 +0900334 } else if (nh->nlmsg_type == RTM_NEWADDR ||
335 nh->nlmsg_type == RTM_DELADDR) {
336 int len = nh->nlmsg_len - sizeof(*nh);
337 struct ifaddrmsg *ifa;
338
339 if (sizeof(*ifa) > (size_t) len) {
340 SLOGE("Got a short RTM_xxxADDR message\n");
341 continue;
342 }
343
344 ifa = (ifaddrmsg *)NLMSG_DATA(nh);
345 size_t rtasize = IFA_PAYLOAD(nh);
346 if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) {
347 continue;
348 }
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900349
350 } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
351 int len = nh->nlmsg_len - sizeof(*nh);
352 struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh);
353
354 if (sizeof(*ndmsg) > (size_t) len) {
355 SLOGE("Got a short RTM_NEWNDUSEROPT message\n");
356 continue;
357 }
358
359 size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg));
360 if (!parseNdUserOptMessage(ndmsg, optsize)) {
361 continue;
362 }
363
364
JP Abgralle6f80142011-07-14 16:46:32 -0700365 } else {
Lorenzo Colittic7eec832013-08-12 17:03:32 +0900366 SLOGD("Unexpected netlink message. type=0x%x\n",
367 nh->nlmsg_type);
JP Abgralle6f80142011-07-14 16:46:32 -0700368 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700369 }
370
371 return true;
372}
373
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100374/* If the string between 'str' and 'end' begins with 'prefixlen' characters
375 * from the 'prefix' array, then return 'str + prefixlen', otherwise return
376 * NULL.
377 */
378static const char*
379has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
380{
381 if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
382 return str + prefixlen;
383 else
384 return NULL;
385}
386
387/* Same as strlen(x) for constant string literals ONLY */
388#define CONST_STRLEN(x) (sizeof(x)-1)
389
390/* Convenience macro to call has_prefix with a constant string literal */
391#define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix))
392
393
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700394/*
395 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
396 * netlink socket.
397 */
398bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
Mike J. Chen17260b12011-06-23 15:00:30 -0700399 const char *s = buffer;
400 const char *end;
San Mehat168415b2009-05-06 11:14:21 -0700401 int param_idx = 0;
402 int i;
403 int first = 1;
404
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100405 if (size == 0)
406 return false;
407
408 /* Ensure the buffer is zero-terminated, the code below depends on this */
409 buffer[size-1] = '\0';
410
San Mehat168415b2009-05-06 11:14:21 -0700411 end = s + size;
412 while (s < end) {
413 if (first) {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100414 const char *p;
415 /* buffer is 0-terminated, no need to check p < end */
416 for (p = s; *p != '@'; p++) {
417 if (!*p) { /* no '@', should not happen */
418 return false;
419 }
420 }
421 mPath = strdup(p+1);
San Mehat168415b2009-05-06 11:14:21 -0700422 first = 0;
423 } else {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100424 const char* a;
425 if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
San Mehat168415b2009-05-06 11:14:21 -0700426 if (!strcmp(a, "add"))
427 mAction = NlActionAdd;
428 else if (!strcmp(a, "remove"))
429 mAction = NlActionRemove;
430 else if (!strcmp(a, "change"))
431 mAction = NlActionChange;
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100432 } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
433 mSeq = atoi(a);
434 } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
435 mSubsystem = strdup(a);
436 } else if (param_idx < NL_PARAMS_MAX) {
San Mehat168415b2009-05-06 11:14:21 -0700437 mParams[param_idx++] = strdup(s);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100438 }
San Mehat168415b2009-05-06 11:14:21 -0700439 }
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100440 s += strlen(s) + 1;
San Mehat168415b2009-05-06 11:14:21 -0700441 }
442 return true;
443}
444
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700445bool NetlinkEvent::decode(char *buffer, int size, int format) {
Mike J. Chen17260b12011-06-23 15:00:30 -0700446 if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
447 return parseBinaryNetlinkMessage(buffer, size);
448 } else {
449 return parseAsciiNetlinkMessage(buffer, size);
450 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700451}
452
San Mehat168415b2009-05-06 11:14:21 -0700453const char *NetlinkEvent::findParam(const char *paramName) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800454 size_t len = strlen(paramName);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100455 for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800456 const char *ptr = mParams[i] + len;
457 if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
458 return ++ptr;
San Mehat168415b2009-05-06 11:14:21 -0700459 }
460
San Mehat7e8529a2010-03-25 09:31:42 -0700461 SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
San Mehat168415b2009-05-06 11:14:21 -0700462 return NULL;
463}