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
17#include <stdarg.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <errno.h>
22
23#define LOG_TAG "Netd"
24
25#include <cutils/log.h>
26
27#include <netutils/ifc.h>
28#include <sysutils/NetlinkEvent.h>
29#include "NetlinkHandler.h"
30#include "NetlinkManager.h"
31#include "ResponseCode.h"
32#include "SockDiag.h"
33
34static const char *kUpdated = "updated";
35static const char *kRemoved = "removed";
36
37namespace android {
38namespace net {
39
40NetlinkHandler::NetlinkHandler(NetlinkManager *nm, int listenerSocket,
41                               int format) :
42                        NetlinkListener(listenerSocket, format) {
43    mNm = nm;
44}
45
46NetlinkHandler::~NetlinkHandler() {
47}
48
49int NetlinkHandler::start() {
50    return this->startListener();
51}
52
53int NetlinkHandler::stop() {
54    return this->stopListener();
55}
56
57void NetlinkHandler::onEvent(NetlinkEvent *evt) {
58    const char *subsys = evt->getSubsystem();
59    if (!subsys) {
60        ALOGW("No subsystem found in netlink event");
61        return;
62    }
63
64    if (!strcmp(subsys, "net")) {
65        NetlinkEvent::Action action = evt->getAction();
66        const char *iface = evt->findParam("INTERFACE");
67
68        if (action == NetlinkEvent::Action::kAdd) {
69            notifyInterfaceAdded(iface);
70        } else if (action == NetlinkEvent::Action::kRemove) {
71            notifyInterfaceRemoved(iface);
72        } else if (action == NetlinkEvent::Action::kChange) {
73            evt->dump();
74            notifyInterfaceChanged("nana", true);
75        } else if (action == NetlinkEvent::Action::kLinkUp) {
76            notifyInterfaceLinkChanged(iface, true);
77        } else if (action == NetlinkEvent::Action::kLinkDown) {
78            notifyInterfaceLinkChanged(iface, false);
79        } else if (action == NetlinkEvent::Action::kAddressUpdated ||
80                   action == NetlinkEvent::Action::kAddressRemoved) {
81            const char *address = evt->findParam("ADDRESS");
82            const char *flags = evt->findParam("FLAGS");
83            const char *scope = evt->findParam("SCOPE");
84            if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
85                // Note: if this interface was deleted, iface is "" and we don't notify.
86                SockDiag sd;
87                if (sd.open()) {
88                    char addrstr[INET6_ADDRSTRLEN];
89                    strncpy(addrstr, address, sizeof(addrstr));
90                    char *slash = strchr(addrstr, '/');
91                    if (slash) {
92                        *slash = '\0';
93                    }
94
95                    int ret = sd.destroySockets(addrstr);
96                    if (ret < 0) {
97                        ALOGE("Error destroying sockets: %s", strerror(ret));
98                    }
99                } else {
100                    ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno));
101                }
102            }
103            if (iface && iface[0] && address && flags && scope) {
104                notifyAddressChanged(action, address, iface, flags, scope);
105            }
106        } else if (action == NetlinkEvent::Action::kRdnss) {
107            const char *lifetime = evt->findParam("LIFETIME");
108            const char *servers = evt->findParam("SERVERS");
109            if (lifetime && servers) {
110                notifyInterfaceDnsServers(iface, lifetime, servers);
111            }
112        } else if (action == NetlinkEvent::Action::kRouteUpdated ||
113                   action == NetlinkEvent::Action::kRouteRemoved) {
114            const char *route = evt->findParam("ROUTE");
115            const char *gateway = evt->findParam("GATEWAY");
116            const char *iface = evt->findParam("INTERFACE");
117            if (route && (gateway || iface)) {
118                notifyRouteChange(action, route, gateway, iface);
119            }
120        }
121
122    } else if (!strcmp(subsys, "qlog") || !strcmp(subsys, "xt_quota2")) {
123        const char *alertName = evt->findParam("ALERT_NAME");
124        const char *iface = evt->findParam("INTERFACE");
125        notifyQuotaLimitReached(alertName, iface);
126
127    } else if (!strcmp(subsys, "strict")) {
128        const char *uid = evt->findParam("UID");
129        const char *hex = evt->findParam("HEX");
130        notifyStrictCleartext(uid, hex);
131
132    } else if (!strcmp(subsys, "xt_idletimer")) {
133        const char *label = evt->findParam("INTERFACE");
134        const char *state = evt->findParam("STATE");
135        const char *timestamp = evt->findParam("TIME_NS");
136        const char *uid = evt->findParam("UID");
137        if (state)
138            notifyInterfaceClassActivity(label, !strcmp("active", state),
139                                         timestamp, uid);
140
141#if !LOG_NDEBUG
142    } else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {
143        /* It is not a VSYNC or a backlight event */
144        ALOGV("unexpected event from subsystem %s", subsys);
145#endif
146    }
147}
148
149void NetlinkHandler::notify(int code, const char *format, ...) {
150    char *msg;
151    va_list args;
152    va_start(args, format);
153    if (vasprintf(&msg, format, args) >= 0) {
154        mNm->getBroadcaster()->sendBroadcast(code, msg, false);
155        free(msg);
156    } else {
157        SLOGE("Failed to send notification: vasprintf: %s", strerror(errno));
158    }
159    va_end(args);
160}
161
162void NetlinkHandler::notifyInterfaceAdded(const char *name) {
163    notify(ResponseCode::InterfaceChange, "Iface added %s", name);
164}
165
166void NetlinkHandler::notifyInterfaceRemoved(const char *name) {
167    notify(ResponseCode::InterfaceChange, "Iface removed %s", name);
168}
169
170void NetlinkHandler::notifyInterfaceChanged(const char *name, bool isUp) {
171    notify(ResponseCode::InterfaceChange,
172           "Iface changed %s %s", name, (isUp ? "up" : "down"));
173}
174
175void NetlinkHandler::notifyInterfaceLinkChanged(const char *name, bool isUp) {
176    notify(ResponseCode::InterfaceChange,
177           "Iface linkstate %s %s", name, (isUp ? "up" : "down"));
178}
179
180void NetlinkHandler::notifyQuotaLimitReached(const char *name, const char *iface) {
181    notify(ResponseCode::BandwidthControl, "limit alert %s %s", name, iface);
182}
183
184void NetlinkHandler::notifyInterfaceClassActivity(const char *name,
185                                                  bool isActive,
186                                                  const char *timestamp,
187                                                  const char *uid) {
188    if (timestamp == NULL)
189        notify(ResponseCode::InterfaceClassActivity,
190           "IfaceClass %s %s", isActive ? "active" : "idle", name);
191    else if (uid != NULL && isActive)
192        notify(ResponseCode::InterfaceClassActivity,
193           "IfaceClass active %s %s %s", name, timestamp, uid);
194    else
195        notify(ResponseCode::InterfaceClassActivity,
196           "IfaceClass %s %s %s", isActive ? "active" : "idle", name, timestamp);
197}
198
199void NetlinkHandler::notifyAddressChanged(NetlinkEvent::Action action, const char *addr,
200                                          const char *iface, const char *flags,
201                                          const char *scope) {
202    notify(ResponseCode::InterfaceAddressChange,
203           "Address %s %s %s %s %s",
204           (action == NetlinkEvent::Action::kAddressUpdated) ? kUpdated : kRemoved,
205           addr, iface, flags, scope);
206}
207
208void NetlinkHandler::notifyInterfaceDnsServers(const char *iface,
209                                               const char *lifetime,
210                                               const char *servers) {
211    notify(ResponseCode::InterfaceDnsInfo, "DnsInfo servers %s %s %s",
212           iface, lifetime, servers);
213}
214
215void NetlinkHandler::notifyRouteChange(NetlinkEvent::Action action, const char *route,
216                                       const char *gateway, const char *iface) {
217    notify(ResponseCode::RouteChange,
218           "Route %s %s%s%s%s%s",
219           (action == NetlinkEvent::Action::kRouteUpdated) ? kUpdated : kRemoved,
220           route,
221           (gateway && *gateway) ? " via " : "",
222           gateway,
223           (iface && *iface) ? " dev " : "",
224           iface);
225}
226
227void NetlinkHandler::notifyStrictCleartext(const char* uid, const char* hex) {
228    notify(ResponseCode::StrictCleartext, "%s %s", uid, hex);
229}
230
231}  // namespace net
232}  // namespace android
233