NetlinkEvent.cpp revision 80f63d4b2c45bd609cbda78bcd016baf0138a5d3
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/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>
39
40const int NetlinkEvent::NlActionUnknown = 0;
41const int NetlinkEvent::NlActionAdd = 1;
42const int NetlinkEvent::NlActionRemove = 2;
43const int NetlinkEvent::NlActionChange = 3;
44const int NetlinkEvent::NlActionLinkUp = 4;
45const int NetlinkEvent::NlActionLinkDown = 5;
46const int NetlinkEvent::NlActionAddressUpdated = 6;
47const int NetlinkEvent::NlActionAddressRemoved = 7;
48const int NetlinkEvent::NlActionRdnss = 8;
49
50NetlinkEvent::NetlinkEvent() {
51    mAction = NlActionUnknown;
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 * Parse a RTM_NEWADDR or RTM_DELADDR message.
82 */
83bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
84                                      int rtasize) {
85    struct rtattr *rta;
86    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
98    for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize);
99         rta = RTA_NEXT(rta, rtasize)) {
100        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 (%zu 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 (%zu 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.
138            mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated :
139                                              NlActionAddressRemoved;
140            mSubsystem = strdup("net");
141            asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
142                     ifaddr->ifa_prefixlen);
143            asprintf(&mParams[1], "INTERFACE=%s", ifname);
144            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 (%zu vs. %zu 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        }
166    }
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/*
177 * 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.
276 */
277bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
278    const struct nlmsghdr *nh;
279
280    for (nh = (struct nlmsghdr *) buffer;
281         NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
282         nh = NLMSG_NEXT(nh, size)) {
283
284        if (nh->nlmsg_type == RTM_NEWLINK) {
285            int len = nh->nlmsg_len - sizeof(*nh);
286            struct ifinfomsg *ifi;
287
288            if (sizeof(*ifi) > (size_t) len) {
289                SLOGE("Got a short RTM_NEWLINK message\n");
290                continue;
291            }
292
293            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;
311                    mSubsystem = strdup("net");
312                    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;
328            asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
329            asprintf(&mParams[1], "INTERFACE=%s", devname);
330            mSubsystem = strdup("qlog");
331            mAction = NlActionChange;
332
333        } 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            }
348
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
364        } else {
365                SLOGD("Unexpected netlink message. type=0x%x\n",
366                      nh->nlmsg_type);
367        }
368    }
369
370    return true;
371}
372
373/* 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
393/*
394 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
395 * netlink socket.
396 */
397bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
398    const char *s = buffer;
399    const char *end;
400    int param_idx = 0;
401    int first = 1;
402
403    if (size == 0)
404        return false;
405
406    /* Ensure the buffer is zero-terminated, the code below depends on this */
407    buffer[size-1] = '\0';
408
409    end = s + size;
410    while (s < end) {
411        if (first) {
412            const char *p;
413            /* buffer is 0-terminated, no need to check p < end */
414            for (p = s; *p != '@'; p++) {
415                if (!*p) { /* no '@', should not happen */
416                    return false;
417                }
418            }
419            mPath = strdup(p+1);
420            first = 0;
421        } else {
422            const char* a;
423            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
424                if (!strcmp(a, "add"))
425                    mAction = NlActionAdd;
426                else if (!strcmp(a, "remove"))
427                    mAction = NlActionRemove;
428                else if (!strcmp(a, "change"))
429                    mAction = NlActionChange;
430            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
431                mSeq = atoi(a);
432            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
433                mSubsystem = strdup(a);
434            } else if (param_idx < NL_PARAMS_MAX) {
435                mParams[param_idx++] = strdup(s);
436            }
437        }
438        s += strlen(s) + 1;
439    }
440    return true;
441}
442
443bool NetlinkEvent::decode(char *buffer, int size, int format) {
444    if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
445        return parseBinaryNetlinkMessage(buffer, size);
446    } else {
447        return parseAsciiNetlinkMessage(buffer, size);
448    }
449}
450
451const char *NetlinkEvent::findParam(const char *paramName) {
452    size_t len = strlen(paramName);
453    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
454        const char *ptr = mParams[i] + len;
455        if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
456            return ++ptr;
457    }
458
459    SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
460    return NULL;
461}
462