Android netd工作原理详解

NETD是Android一个专门管理网络链接, 路由以及iptables的系统Daemon进程, 其在系统启动时加载 :

1
2
3
4
5
6
7

service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
socket fwmarkd stream 0660 root inet

启动netd时, 会创建四个socket,用于其他进程与netd进行通信:

总的说来, netd进程在Android中间层服务ConnectivityManagerSerivce以及内核之间建立了一个沟通的桥梁:

  • 对Java层系统服务ConnectivityManagerService,netd通过socket接口为其提供了控制指令的通道
  • 对内核, netd通过netlink socket与内核进行数据/指令的收发, 并将来自内核的消息发送给上层

接下来, 本文从三个方面来看下netd的具体工作原理与实现.

  • netd的初始化与启动
  • ConnectivityManagerSerivcenetd的交互
  • netd与内核的交互

关于netlink socket可以参考: http://qos.ittc.ku.edu/netlink/html/node4.html

netd的启动与初始化

netd进程启动时, 主要处理做以下事情:

  • 创建一个NetlinkManager, 用于管理与内核通信的netlink连接
  • 初始化网络控制类, 如路由控制RouteController, 带宽控制BandwidthController
  • 启动各类事件监听类: DnsProxyListener监听DNS代理; CommandListener监听来自NetworkManagement的指令
  • 启动NetdHwService, 为HAL层提供接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103


int main() {
using android::net::gCtls;
Stopwatch s;

ALOGI("Netd 1.0 starting");

blockSigpipe();


NetlinkManager *nm = NetlinkManager::Instance();
if (nm == nullptr) {
ALOGE("Unable to create NetlinkManager");
exit(1);
};

gCtls = new android::net::Controllers();
gCtls->init();

CommandListener cl;
nm->setBroadcaster((SocketListener *) &cl);

if (nm->start()) {
ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}

std::unique_ptr<NFLogListener> logListener;
{
auto result = makeNFLogListener();
if (!isOk(result)) {
ALOGE("Unable to create NFLogListener: %s", toString(result).c_str());
exit(1);
}
logListener = std::move(result.value());
auto status = gCtls->wakeupCtrl.init(logListener.get());
if (!isOk(result)) {
ALOGE("Unable to init WakeupController: %s", toString(result).c_str());
// We can still continue without wakeup packet logging.
}
}

// Set local DNS mode, to prevent bionic from proxying
// back to this service, recursively.
setenv("ANDROID_DNS_MODE", "local", 1);
DnsProxyListener dpl(&gCtls->netCtrl, &gCtls->eventReporter);
if (dpl.startListener()) {
ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
exit(1);
}

MDnsSdListener mdnsl;
if (mdnsl.startListener()) {
ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
exit(1);
}

FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter);
if (fwmarkServer.startListener()) {
ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));
exit(1);
}

Stopwatch subTime;
status_t ret;
if ((ret = NetdNativeService::start()) != android::OK) {
ALOGE("Unable to start NetdNativeService: %d", ret);
exit(1);
}
ALOGI("Registering NetdNativeService: %.1fms", subTime.getTimeAndReset());

/*
* Now that we're up, we can respond to commands. Starting the listener also tells
* NetworkManagementService that we are up and that our binder interface is ready.
*/
if (cl.startListener()) {
ALOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
ALOGI("Starting CommandListener: %.1fms", subTime.getTimeAndReset());

write_pid_file();

// Now that netd is ready to process commands, advertise service
// availability for HAL clients.
NetdHwService mHwSvc;
if ((ret = mHwSvc.start()) != android::OK) {
ALOGE("Unable to start NetdHwService: %d", ret);
exit(1);
}
ALOGI("Registering NetdHwService: %.1fms", subTime.getTimeAndReset());

ALOGI("Netd started in %dms", static_cast<int>(s.timeTaken()));

IPCThreadState::self()->joinThreadPool();

ALOGI("Netd exiting");

remove_pid_file();

exit(0);
}

CommandListener用于接收处理来自上层NetworkManagementService指令, 在netd启动时, 会监听netd这个socket, 并允许最多4个客户端请求的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

int SocketListener::startListener() {
return startListener(4);
}

int SocketListener::startListener(int backlog) {

if (!mSocketName && mSock == -1) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
return -1;
} else if (mSocketName) {
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",
mSocketName, strerror(errno));
return -1;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
fcntl(mSock, F_SETFD, FD_CLOEXEC);
}

if (mListen && listen(mSock, backlog) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));

if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}

if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}

return 0;
}

这里创建了一个专门的线程用于处理来自上层客户端的命令请求:

  • 检查控制pipe的数据,是否有必要停止监听;
  • 监听来自客户端的请求, 如果有, 则新建一个SocketClient保存下来
  • 从已有的SocketClient中检查是否有可用的数据, 如果有则通过onDataAvailable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);

me->runListener();
pthread_exit(NULL);
return NULL;
}

void SocketListener::runListener() {

SocketClientCollection pendingList;

while(1) {
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = -1;

FD_ZERO(&read_fds);

if (mListen) {
max = mSock;
FD_SET(mSock, &read_fds);
}

FD_SET(mCtrlPipe[0], &read_fds);
if (mCtrlPipe[0] > max)
max = mCtrlPipe[0];

pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
// NB: calling out to an other object with mClientsLock held (safe)
int fd = (*it)->getSocket();
FD_SET(fd, &read_fds);
if (fd > max) {
max = fd;
}
}
pthread_mutex_unlock(&mClientsLock);
SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
if (errno == EINTR)
continue;
SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
sleep(1);
continue;
} else if (!rc)
continue;

if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
if (c == CtrlPipe_Shutdown) {
break;
}
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) {
int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
sleep(1);
continue;
}
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c, true, mUseCmdNum));
pthread_mutex_unlock(&mClientsLock);
}

/* Add all active clients to the pending list first */
pendingList.clear();
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
SocketClient* c = *it;
// NB: calling out to an other object with mClientsLock held (safe)
int fd = c->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);

/* Process the pending list, since it is owned by the thread,
* there is no need to lock it */
while (!pendingList.empty()) {
/* Pop the first item from the list */
it = pendingList.begin();
SocketClient* c = *it;
pendingList.erase(it);
/* Process it, if false is returned, remove from list */
if (!onDataAvailable(c)) {
release(c, false);
}
c->decRef();
}
}
}

netd启动完成后, 就可以处理来自中间层的指令请求以及与内核进行交互了.

netd与NetworkManagerService的交互

SystemServer进程启动时, 创建NetworkManagementService(以下简称(NMS)), 此时NMS会主动与netd建立socket链接:

1
2
3
4
5
6
7
8
9
10
11
12

// SystemServer.java
if (!disableNetwork) {
traceBeginAndSlog("StartNetworkManagementService");
try {
networkManagement = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
} catch (Throwable e) {
reportWtf("starting NetworkManagement Service", e);
}
traceEnd();
}

创建NMS时, 启动一个新的线程用于与netd通信,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45


static NetworkManagementService create(Context context, String socket)
throws InterruptedException {
final NetworkManagementService service = new NetworkManagementService(context, socket);
final CountDownLatch connectedSignal = service.mConnectedSignal;
if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
service.mThread.start();
if (DBG) Slog.d(TAG, "Awaiting socket connection");
connectedSignal.await();
service.connectNativeNetdService();

return service;
}


private NetworkManagementService(Context context, String socket) {
mContext = context;

// make sure this is on the same looper as our NativeDaemonConnector for sync purposes
mFgHandler = new Handler(FgThread.get().getLooper());

// Don't need this wake lock, since we now have a time stamp for when
// the network actually went inactive. (It might be nice to still do this,
// but I don't want to do it through the power manager because that pollutes the
// battery stats history with pointless noise.)
//PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = null; //pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG);

mConnector = new NativeDaemonConnector(
new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl,
FgThread.get().getLooper());
mThread = new Thread(mConnector, NETD_TAG);

mDaemonHandler = new Handler(FgThread.get().getLooper());

// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);

LocalServices.addService(NetworkManagementInternal.class, new LocalService());

synchronized (mTetheringStatsProviders) {
mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd");
}
}

NMS通过NativeDaemonConnectornetd建立socket通信, NativeDaemonConnector主要做两个事情:

  • netd建立一个数据链接
  • 不断读取socket中的数据流: 一种是netd主动上报的命令, 一种是NMS发送给netd后的指令的响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119


@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);

while (true) {
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}


private void listenToSocket() throws IOException {
LocalSocket socket = null;

try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();

socket.connect(address);

InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}

mCallbacks.onDaemonConnected();

FileDescriptor[] fdList = null;
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;

while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
fdList = socket.getAncillaryFileDescriptors();

// Add our starting point to the count and reset the start.
count += start;
start = 0;

for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
// Note - do not log this raw message since it may contain
// sensitive data
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);

boolean releaseWl = false;
try {
final NativeDaemonEvent event =
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);

log("RCV <- {" + event + "}");

if (event.isClassUnsolicited()) {
Message msg = mCallbackHandler.obtainMessage(
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}

start = i + 1;
}
}

// We should end at the amount we read. If not, compact then
// buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
start = remaining;
} else {
start = 0;
}
}
} catch (IOException ex) {
loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
}

try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
loge("Failed closing socket: " + ex);
}
}
}

socket链接建立完成之后, NMS与netd可以相互通信, 发送指令与数据了. NMS通过NativeDaemonConnector执行相应的指令, 比如NMS设置网络接口的配置(打开/关闭网口):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

@Override
public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
LinkAddress linkAddr = cfg.getLinkAddress();
if (linkAddr == null || linkAddr.getAddress() == null) {
throw new IllegalStateException("Null LinkAddress given");
}

final Command cmd = new Command("interface", "setcfg", iface,
linkAddr.getAddress().getHostAddress(),
linkAddr.getPrefixLength());
for (String flag : cfg.getFlags()) {
cmd.appendArg(flag);
}

try {
mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}

NativeDaemonConnector会将每个指令都指定一个唯一的序列, 并将其响应放到一个阻塞队列, 等待netd返回指令的结果, 如果超过指定的超时时间, 则抛出一个超时的异常.

在第一部分时, 讲到SocketListener拿到上层发过来的指令后, 会将其分发给对应的指令类进行处理(看SocketListener的子类FrameworkListener):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[CMD_BUF_SIZE];
int len;

len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
if (len < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return false;
} else if (!len) {
return false;
} else if (buffer[len-1] != '\0') {
SLOGW("String is not zero-terminated");
android_errorWriteLog(0x534e4554, "29831647");
c->sendMsg(500, "Command too large for buffer", false);
mSkipToNextNullByte = true;
return true;
}

int offset = 0;
int i;

for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
/* IMPORTANT: dispatchCommand() expects a zero-terminated string */
if (mSkipToNextNullByte) {
mSkipToNextNullByte = false;
} else {
dispatchCommand(c, buffer + offset);
}
offset = i + 1;
}
}

mSkipToNextNullByte = false;
return true;
}

函数dispatchCommmand会检查所有指令处理类, 匹配相应的类去执行指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
FrameworkCommandCollection::iterator i;
int argc = 0;
char *argv[FrameworkListener::CMD_ARGS_MAX];
char tmp[CMD_BUF_SIZE];
char *p = data;
char *q = tmp;
char *qlimit = tmp + sizeof(tmp) - 1;
bool esc = false;
bool quote = false;
bool haveCmdNum = !mWithSeq;

memset(argv, 0, sizeof(argv));
memset(tmp, 0, sizeof(tmp));
...
*q = '\0';
if (argc >= CMD_ARGS_MAX)
goto overflow;
argv[argc++] = strdup(tmp);

if (quote) {
cli->sendMsg(500, "Unclosed quotes error", false);
goto out;
}

if (errorRate && (++mCommandCount % errorRate == 0)) {
/* ignore this command - let the timeout handler handle it */
SLOGE("Faking a timeout");
goto out;
}

for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;

if (!strcmp(argv[0], c->getCommand())) {
if (c->runCommand(cli, argc, argv)) {
SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
}
goto out;
}
}
cli->sendMsg(500, "Command not recognized", false);
out:
int j;
for (j = 0; j < argc; j++)
free(argv[j]);
return;

overflow:
cli->sendMsg(500, "Command too long", false);
goto out;
}

比如这里执行的是, 网络接口相关的设置, 因此会调用InterfaceCmd来执行该指令.

NETD与内核进行交互

NETD通过netlink事件与内核进行消息的交换.在第一部分时看到, netd启动时, 会配置socket与内核进行通信:

  • 一个netlink事件是NETLINK_KOBJECT_UEVENT: 用于内核向netd发生消息, 如网口的状态变化;
  • 一个netlink事件是NETLINK_ROUTE:用于接收路由信息, 如路由表的更新与删除;
  • 一个netlink事件是NETLINK_NFLOG:用于接收数据流量使用配额的消息, 如数据使用超限;
  • 一个netlink事件是NETLINK_NETFILTER用于接收包过滤(netfilter)的消息;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

int NetlinkManager::start() {
if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL) {
return -1;
}

if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_IFADDR |
RTMGRP_IPV6_ROUTE |
(1 << (RTNLGRP_ND_USEROPT - 1)),
NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
return -1;
}

if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
ALOGW("Unable to open qlog quota socket, check if xt_quota2 can send via UeventHandler");
// TODO: return -1 once the emulator gets a new kernel.
}

if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {
ALOGE("Unable to open strict socket");
// TODO: return -1 once the emulator gets a new kernel.
}

return 0;
}

每一个netlink的socket都会新建一个NetlinkHandler, 用于处理内核的消息, 并将该消息广播给上层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
const char *subsys = evt->getSubsystem();
if (!subsys) {
ALOGW("No subsystem found in netlink event");
return;
}

if (!strcmp(subsys, "net")) {
NetlinkEvent::Action action = evt->getAction();
const char *iface = evt->findParam("INTERFACE");

if (action == NetlinkEvent::Action::kAdd) {
notifyInterfaceAdded(iface);
} else if (action == NetlinkEvent::Action::kRemove) {
notifyInterfaceRemoved(iface);
} else if (action == NetlinkEvent::Action::kChange) {
evt->dump();
notifyInterfaceChanged("nana", true);
} else if (action == NetlinkEvent::Action::kLinkUp) {
notifyInterfaceLinkChanged(iface, true);
} else if (action == NetlinkEvent::Action::kLinkDown) {
notifyInterfaceLinkChanged(iface, false);
} else if (action == NetlinkEvent::Action::kAddressUpdated ||
action == NetlinkEvent::Action::kAddressRemoved) {
const char *address = evt->findParam("ADDRESS");
const char *flags = evt->findParam("FLAGS");
const char *scope = evt->findParam("SCOPE");
if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
// Note: if this interface was deleted, iface is "" and we don't notify.
SockDiag sd;
if (sd.open()) {
char addrstr[INET6_ADDRSTRLEN];
strncpy(addrstr, address, sizeof(addrstr));
char *slash = strchr(addrstr, '/');
if (slash) {
*slash = '\0';
}

int ret = sd.destroySockets(addrstr);
if (ret < 0) {
ALOGE("Error destroying sockets: %s", strerror(ret));
}
} else {
ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno));
}
}
if (iface && iface[0] && address && flags && scope) {
notifyAddressChanged(action, address, iface, flags, scope);
}
} else if (action == NetlinkEvent::Action::kRdnss) {
const char *lifetime = evt->findParam("LIFETIME");
const char *servers = evt->findParam("SERVERS");
if (lifetime && servers) {
notifyInterfaceDnsServers(iface, lifetime, servers);
}
} else if (action == NetlinkEvent::Action::kRouteUpdated ||
action == NetlinkEvent::Action::kRouteRemoved) {
const char *route = evt->findParam("ROUTE");
const char *gateway = evt->findParam("GATEWAY");
const char *iface = evt->findParam("INTERFACE");
if (route && (gateway || iface)) {
notifyRouteChange(action, route, gateway, iface);
}
}

} else if (!strcmp(subsys, "qlog") || !strcmp(subsys, "xt_quota2")) {
const char *alertName = evt->findParam("ALERT_NAME");
const char *iface = evt->findParam("INTERFACE");
notifyQuotaLimitReached(alertName, iface);

} else if (!strcmp(subsys, "strict")) {
const char *uid = evt->findParam("UID");
const char *hex = evt->findParam("HEX");
notifyStrictCleartext(uid, hex);

} else if (!strcmp(subsys, "xt_idletimer")) {
const char *label = evt->findParam("INTERFACE");
const char *state = evt->findParam("STATE");
const char *timestamp = evt->findParam("TIME_NS");
const char *uid = evt->findParam("UID");
if (state)
notifyInterfaceClassActivity(label, !strcmp("active", state),
timestamp, uid);
}
}

参考资料