blob: 643971134ed6bee5344d8de0c7430c8cbe0d0303 [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>
Mike J. Chenec16b9d2011-06-23 14:55:28 -070026#include <linux/if.h>
JP Abgralle6f80142011-07-14 16:46:32 -070027#include <linux/netfilter/nfnetlink.h>
Ashish Sharma731d8312012-04-10 18:59:16 -070028#include <linux/netfilter/xt_IDLETIMER.h>
JP Abgralle6f80142011-07-14 16:46:32 -070029#include <linux/netfilter_ipv4/ipt_ULOG.h>
30/* From kernel's net/netfilter/xt_quota2.c */
31const int QLOG_NL_EVENT = 112;
32
33#include <linux/netlink.h>
34#include <linux/rtnetlink.h>
Mike J. Chenec16b9d2011-06-23 14:55:28 -070035
San Mehat168415b2009-05-06 11:14:21 -070036const int NetlinkEvent::NlActionUnknown = 0;
37const int NetlinkEvent::NlActionAdd = 1;
38const int NetlinkEvent::NlActionRemove = 2;
39const int NetlinkEvent::NlActionChange = 3;
Mike J. Chenec16b9d2011-06-23 14:55:28 -070040const int NetlinkEvent::NlActionLinkUp = 4;
41const int NetlinkEvent::NlActionLinkDown = 5;
Ashish Sharma731d8312012-04-10 18:59:16 -070042const int NetlinkEvent::NlActionIfaceActive = 6;
43const int NetlinkEvent::NlActionIfaceIdle = 7;
San Mehat168415b2009-05-06 11:14:21 -070044
45NetlinkEvent::NetlinkEvent() {
46 mAction = NlActionUnknown;
San Mehatebfe3db2009-10-10 17:35:13 -070047 memset(mParams, 0, sizeof(mParams));
48 mPath = NULL;
49 mSubsystem = NULL;
San Mehat168415b2009-05-06 11:14:21 -070050}
51
52NetlinkEvent::~NetlinkEvent() {
53 int i;
54 if (mPath)
55 free(mPath);
56 if (mSubsystem)
57 free(mSubsystem);
58 for (i = 0; i < NL_PARAMS_MAX; i++) {
59 if (!mParams[i])
60 break;
61 free(mParams[i]);
62 }
63}
64
San Mehatd6744132009-12-24 07:17:09 -080065void NetlinkEvent::dump() {
66 int i;
67
68 for (i = 0; i < NL_PARAMS_MAX; i++) {
69 if (!mParams[i])
70 break;
San Mehat7e8529a2010-03-25 09:31:42 -070071 SLOGD("NL param '%s'\n", mParams[i]);
San Mehatd6744132009-12-24 07:17:09 -080072 }
73}
74
Mike J. Chenec16b9d2011-06-23 14:55:28 -070075/*
Ashish Sharma731d8312012-04-10 18:59:16 -070076 * Parse an binary message from a NETLINK_ROUTE netlink socket
77 * and IDLETIMER netlink socket.
Mike J. Chenec16b9d2011-06-23 14:55:28 -070078 */
79bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
80 size_t sz = size;
Mike J. Chen17260b12011-06-23 15:00:30 -070081 const struct nlmsghdr *nh = (struct nlmsghdr *) buffer;
Mike J. Chenec16b9d2011-06-23 14:55:28 -070082
83 while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) {
JP Abgralle6f80142011-07-14 16:46:32 -070084
Mike J. Chenec16b9d2011-06-23 14:55:28 -070085 if (nh->nlmsg_type == RTM_NEWLINK) {
86 int len = nh->nlmsg_len - sizeof(*nh);
87 struct ifinfomsg *ifi;
88
JP Abgralle6f80142011-07-14 16:46:32 -070089 if (sizeof(*ifi) > (size_t) len) {
90 SLOGE("Got a short RTM_NEWLINK message\n");
91 continue;
Mike J. Chenec16b9d2011-06-23 14:55:28 -070092 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -070093
JP Abgralle6f80142011-07-14 16:46:32 -070094 ifi = (ifinfomsg *)NLMSG_DATA(nh);
95 if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
96 continue;
97 }
98
99 struct rtattr *rta = (struct rtattr *)
100 ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
101 len = NLMSG_PAYLOAD(nh, sizeof(*ifi));
102
103 while(RTA_OK(rta, len)) {
104 switch(rta->rta_type) {
105 case IFLA_IFNAME:
106 char buffer[16 + IFNAMSIZ];
107 snprintf(buffer, sizeof(buffer), "INTERFACE=%s",
108 (char *) RTA_DATA(rta));
109 mParams[0] = strdup(buffer);
110 mAction = (ifi->ifi_flags & IFF_LOWER_UP) ?
111 NlActionLinkUp : NlActionLinkDown;
112 mSubsystem = strdup("net");
113 break;
114 }
115
116 rta = RTA_NEXT(rta, len);
117 }
118
119 } else if (nh->nlmsg_type == QLOG_NL_EVENT) {
120 char *devname;
121 ulog_packet_msg_t *pm;
122 size_t len = nh->nlmsg_len - sizeof(*nh);
123 if (sizeof(*pm) > len) {
124 SLOGE("Got a short QLOG message\n");
125 continue;
126 }
127 pm = (ulog_packet_msg_t *)NLMSG_DATA(nh);
128 devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
JP Abgralle6f80142011-07-14 16:46:32 -0700129 asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
130 asprintf(&mParams[1], "INTERFACE=%s", devname);
131 mSubsystem = strdup("qlog");
132 mAction = NlActionChange;
133
Ashish Sharma731d8312012-04-10 18:59:16 -0700134 } else if (nh->nlmsg_type == NL_EVENT_TYPE_ACTIVE
135 || nh->nlmsg_type == NL_EVENT_TYPE_INACTIVE) {
136 char *ifacename;
137 ifacename = (char *)NLMSG_DATA(nh);
138 asprintf(&mParams[0], "INTERFACE=%s", ifacename);
139 mSubsystem = strdup("idletimer");
140 mAction = (nh->nlmsg_type == NL_EVENT_TYPE_ACTIVE) ?
141 NlActionIfaceActive : NlActionIfaceIdle;
JP Abgralle6f80142011-07-14 16:46:32 -0700142 } else {
143 SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type);
144 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700145 nh = NLMSG_NEXT(nh, size);
146 }
147
148 return true;
149}
150
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100151/* If the string between 'str' and 'end' begins with 'prefixlen' characters
152 * from the 'prefix' array, then return 'str + prefixlen', otherwise return
153 * NULL.
154 */
155static const char*
156has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
157{
158 if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
159 return str + prefixlen;
160 else
161 return NULL;
162}
163
164/* Same as strlen(x) for constant string literals ONLY */
165#define CONST_STRLEN(x) (sizeof(x)-1)
166
167/* Convenience macro to call has_prefix with a constant string literal */
168#define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix))
169
170
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700171/*
172 * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
173 * netlink socket.
174 */
175bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
Mike J. Chen17260b12011-06-23 15:00:30 -0700176 const char *s = buffer;
177 const char *end;
San Mehat168415b2009-05-06 11:14:21 -0700178 int param_idx = 0;
179 int i;
180 int first = 1;
181
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100182 if (size == 0)
183 return false;
184
185 /* Ensure the buffer is zero-terminated, the code below depends on this */
186 buffer[size-1] = '\0';
187
San Mehat168415b2009-05-06 11:14:21 -0700188 end = s + size;
189 while (s < end) {
190 if (first) {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100191 const char *p;
192 /* buffer is 0-terminated, no need to check p < end */
193 for (p = s; *p != '@'; p++) {
194 if (!*p) { /* no '@', should not happen */
195 return false;
196 }
197 }
198 mPath = strdup(p+1);
San Mehat168415b2009-05-06 11:14:21 -0700199 first = 0;
200 } else {
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100201 const char* a;
202 if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
San Mehat168415b2009-05-06 11:14:21 -0700203 if (!strcmp(a, "add"))
204 mAction = NlActionAdd;
205 else if (!strcmp(a, "remove"))
206 mAction = NlActionRemove;
207 else if (!strcmp(a, "change"))
208 mAction = NlActionChange;
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100209 } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
210 mSeq = atoi(a);
211 } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
212 mSubsystem = strdup(a);
213 } else if (param_idx < NL_PARAMS_MAX) {
San Mehat168415b2009-05-06 11:14:21 -0700214 mParams[param_idx++] = strdup(s);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100215 }
San Mehat168415b2009-05-06 11:14:21 -0700216 }
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100217 s += strlen(s) + 1;
San Mehat168415b2009-05-06 11:14:21 -0700218 }
219 return true;
220}
221
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700222bool NetlinkEvent::decode(char *buffer, int size, int format) {
Mike J. Chen17260b12011-06-23 15:00:30 -0700223 if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
224 return parseBinaryNetlinkMessage(buffer, size);
225 } else {
226 return parseAsciiNetlinkMessage(buffer, size);
227 }
Mike J. Chenec16b9d2011-06-23 14:55:28 -0700228}
229
San Mehat168415b2009-05-06 11:14:21 -0700230const char *NetlinkEvent::findParam(const char *paramName) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800231 size_t len = strlen(paramName);
David 'Digit' Turner3311eea2011-01-17 01:59:22 +0100232 for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
Chih-Wei Huang80ec37a2010-07-14 14:00:41 +0800233 const char *ptr = mParams[i] + len;
234 if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
235 return ++ptr;
San Mehat168415b2009-05-06 11:14:21 -0700236 }
237
San Mehat7e8529a2010-03-25 09:31:42 -0700238 SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
San Mehat168415b2009-05-06 11:14:21 -0700239 return NULL;
240}