NetlinkEvent.cpp revision 66ed50af6870210ce013a5588a688434a5d48ee9
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 (%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.
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 (%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        }
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<<<<<<< HEAD
178 * 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.
277 */
278bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
279    const struct nlmsghdr *nh;
280
281    for (nh = (struct nlmsghdr *) buffer;
282         NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
283         nh = NLMSG_NEXT(nh, size)) {
284
285        if (nh->nlmsg_type == RTM_NEWLINK) {
286            int len = nh->nlmsg_len - sizeof(*nh);
287            struct ifinfomsg *ifi;
288
289            if (sizeof(*ifi) > (size_t) len) {
290                SLOGE("Got a short RTM_NEWLINK message\n");
291                continue;
292            }
293
294            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;
312                    mSubsystem = strdup("net");
313                    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;
329            asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
330            asprintf(&mParams[1], "INTERFACE=%s", devname);
331            mSubsystem = strdup("qlog");
332            mAction = NlActionChange;
333
334        } 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            }
349
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
365        } else {
366                SLOGD("Unexpected netlink message. type=0x%x\n",
367                      nh->nlmsg_type);
368        }
369    }
370
371    return true;
372}
373
374/* 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
394/*
395 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
396 * netlink socket.
397 */
398bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
399    const char *s = buffer;
400    const char *end;
401    int param_idx = 0;
402    int i;
403    int first = 1;
404
405    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
411    end = s + size;
412    while (s < end) {
413        if (first) {
414            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);
422            first = 0;
423        } else {
424            const char* a;
425            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
426                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;
432            } 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) {
437                mParams[param_idx++] = strdup(s);
438            }
439        }
440        s += strlen(s) + 1;
441    }
442    return true;
443}
444
445bool NetlinkEvent::decode(char *buffer, int size, int format) {
446    if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
447        return parseBinaryNetlinkMessage(buffer, size);
448    } else {
449        return parseAsciiNetlinkMessage(buffer, size);
450    }
451}
452
453const char *NetlinkEvent::findParam(const char *paramName) {
454    size_t len = strlen(paramName);
455    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
456        const char *ptr = mParams[i] + len;
457        if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
458            return ++ptr;
459    }
460
461    SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
462    return NULL;
463}
464