blob: 65cfb4e841f8e28deb9ad3caaa165f044c02d46d [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
Mike J. Chenec16b9d2011-06-23 14:55:28 -070022#include <sysutils/NetlinkListener.h>
San Mehat168415b2009-05-06 11:14:21 -070023#include <sysutils/NetlinkEvent.h>
24
Mike J. Chenec16b9d2011-06-23 14:55:28 -070025#include <sys/types.h>
26#include <sys/socket.h>
27#include <linux/rtnetlink.h>
28#include <linux/if.h>
29
San Mehat168415b2009-05-06 11:14:21 -070030const int NetlinkEvent::NlActionUnknown = 0;
31const int NetlinkEvent::NlActionAdd = 1;
32const int NetlinkEvent::NlActionRemove = 2;
33const int NetlinkEvent::NlActionChange = 3;
Mike J. Chenec16b9d2011-06-23 14:55:28 -070034const int NetlinkEvent::NlActionLinkUp = 4;
35const int NetlinkEvent::NlActionLinkDown = 5;
San Mehat168415b2009-05-06 11:14:21 -070036
37NetlinkEvent::NetlinkEvent() {
38 mAction = NlActionUnknown;
San Mehatebfe3db2009-10-10 17:35:13 -070039 memset(mParams, 0, sizeof(mParams));
40 mPath = NULL;
41 mSubsystem = NULL;
San Mehat168415b2009-05-06 11:14:21 -070042}
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
San Mehatd6744132009-12-24 07:17:09 -080057void NetlinkEvent::dump() {
58 int i;
59
60 for (i = 0; i < NL_PARAMS_MAX; i++) {
61 if (!mParams[i])
62 break;
San Mehat7e8529a2010-03-25 09:31:42 -070063 SLOGD("NL param '%s'\n", mParams[i]);
San Mehatd6744132009-12-24 07:17:09 -080064 }
65}
66
Mike J. Chenec16b9d2011-06-23 14:55:28 -070067/*
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
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100112/* 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
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700132/*
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;
San Mehat168415b2009-05-06 11:14:21 -0700139 int param_idx = 0;
140 int i;
141 int first = 1;
142
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100143 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
San Mehat168415b2009-05-06 11:14:21 -0700149 end = s + size;
150 while (s < end) {
151 if (first) {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100152 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);
San Mehat168415b2009-05-06 11:14:21 -0700160 first = 0;
161 } else {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100162 const char* a;
163 if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
San Mehat168415b2009-05-06 11:14:21 -0700164 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;
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100170 } 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) {
San Mehat168415b2009-05-06 11:14:21 -0700175 mParams[param_idx++] = strdup(s);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100176 }
San Mehat168415b2009-05-06 11:14:21 -0700177 }
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100178 s += strlen(s) + 1;
San Mehat168415b2009-05-06 11:14:21 -0700179 }
180 return true;
181}
182
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700183bool 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
San Mehat168415b2009-05-06 11:14:21 -0700191const char *NetlinkEvent::findParam(const char *paramName) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800192 size_t len = strlen(paramName);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100193 for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800194 const char *ptr = mParams[i] + len;
195 if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
196 return ++ptr;
San Mehat168415b2009-05-06 11:14:21 -0700197 }
198
San Mehat7e8529a2010-03-25 09:31:42 -0700199 SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
San Mehat168415b2009-05-06 11:14:21 -0700200 return NULL;
201}