NetlinkEvent.cpp revision ec16b9d47cacb0d873ee0ff80c919f49215c0005
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/NetlinkListener.h>
23#include <sysutils/NetlinkEvent.h>
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <linux/rtnetlink.h>
28#include <linux/if.h>
29
30const int NetlinkEvent::NlActionUnknown = 0;
31const int NetlinkEvent::NlActionAdd = 1;
32const int NetlinkEvent::NlActionRemove = 2;
33const int NetlinkEvent::NlActionChange = 3;
34const int NetlinkEvent::NlActionLinkUp = 4;
35const int NetlinkEvent::NlActionLinkDown = 5;
36
37NetlinkEvent::NetlinkEvent() {
38    mAction = NlActionUnknown;
39    memset(mParams, 0, sizeof(mParams));
40    mPath = NULL;
41    mSubsystem = NULL;
42}
43
44NetlinkEvent::~NetlinkEvent() {
45    int i;
46    if (mPath)
47        free(mPath);
48    if (mSubsystem)
49        free(mSubsystem);
50    for (i = 0; i < NL_PARAMS_MAX; i++) {
51        if (!mParams[i])
52            break;
53        free(mParams[i]);
54    }
55}
56
57void NetlinkEvent::dump() {
58    int i;
59
60    for (i = 0; i < NL_PARAMS_MAX; i++) {
61        if (!mParams[i])
62            break;
63        SLOGD("NL param '%s'\n", mParams[i]);
64    }
65}
66
67/*
68 * Parse an binary message from a NETLINK_ROUTE netlink socket.
69 */
70bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
71    size_t sz = size;
72    struct nlmsghdr *nh = (struct nlmsghdr *) buffer;
73
74    while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) {
75        if (nh->nlmsg_type == RTM_NEWLINK) {
76            int len = nh->nlmsg_len - sizeof(*nh);
77            struct ifinfomsg *ifi;
78
79            if (sizeof(*ifi) <= (size_t) len) {
80                ifi = (ifinfomsg *)NLMSG_DATA(nh);
81
82                if ((ifi->ifi_flags & IFF_LOOPBACK) == 0) {
83                    struct rtattr *rta = (struct rtattr *)
84                      ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
85                    len = NLMSG_PAYLOAD(nh, sizeof(*ifi));
86
87                    while(RTA_OK(rta, len)) {
88                        switch(rta->rta_type) {
89                        case IFLA_IFNAME:
90                            char buffer[16 + IFNAMSIZ];
91                            snprintf(buffer, sizeof(buffer), "INTERFACE=%s",
92                                     (char *) RTA_DATA(rta));
93                            mParams[0] = strdup(buffer);
94                            mAction = (ifi->ifi_flags & IFF_LOWER_UP) ?
95                              NlActionLinkUp : NlActionLinkDown;
96                            mSubsystem = strdup("net");
97                            break;
98                        }
99
100                        rta = RTA_NEXT(rta, len);
101                    }
102                }
103            }
104        }
105
106        nh = NLMSG_NEXT(nh, size);
107    }
108
109    return true;
110}
111
112/* If the string between 'str' and 'end' begins with 'prefixlen' characters
113 * from the 'prefix' array, then return 'str + prefixlen', otherwise return
114 * NULL.
115 */
116static const char*
117has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
118{
119    if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
120        return str + prefixlen;
121    else
122        return NULL;
123}
124
125/* Same as strlen(x) for constant string literals ONLY */
126#define CONST_STRLEN(x)  (sizeof(x)-1)
127
128/* Convenience macro to call has_prefix with a constant string literal  */
129#define HAS_CONST_PREFIX(str,end,prefix)  has_prefix((str),(end),prefix,CONST_STRLEN(prefix))
130
131
132/*
133 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
134 * netlink socket.
135 */
136bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
137    char *s = buffer;
138    char *end;
139    int param_idx = 0;
140    int i;
141    int first = 1;
142
143    if (size == 0)
144        return false;
145
146    /* Ensure the buffer is zero-terminated, the code below depends on this */
147    buffer[size-1] = '\0';
148
149    end = s + size;
150    while (s < end) {
151        if (first) {
152            const char *p;
153            /* buffer is 0-terminated, no need to check p < end */
154            for (p = s; *p != '@'; p++) {
155                if (!*p) { /* no '@', should not happen */
156                    return false;
157                }
158            }
159            mPath = strdup(p+1);
160            first = 0;
161        } else {
162            const char* a;
163            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
164                if (!strcmp(a, "add"))
165                    mAction = NlActionAdd;
166                else if (!strcmp(a, "remove"))
167                    mAction = NlActionRemove;
168                else if (!strcmp(a, "change"))
169                    mAction = NlActionChange;
170            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
171                mSeq = atoi(a);
172            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
173                mSubsystem = strdup(a);
174            } else if (param_idx < NL_PARAMS_MAX) {
175                mParams[param_idx++] = strdup(s);
176            }
177        }
178        s += strlen(s) + 1;
179    }
180    return true;
181}
182
183bool NetlinkEvent::decode(char *buffer, int size, int format) {
184  if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
185    return parseBinaryNetlinkMessage(buffer, size);
186  } else {
187    return parseAsciiNetlinkMessage(buffer, size);
188  }
189}
190
191const char *NetlinkEvent::findParam(const char *paramName) {
192    size_t len = strlen(paramName);
193    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
194        const char *ptr = mParams[i] + len;
195        if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
196            return ++ptr;
197    }
198
199    SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
200    return NULL;
201}
202