1.文章介绍
Netd模块是Android中专门负责网络管理和控制的后台守护进程,本篇文章主要分析Netd的工作流程,对Netd有一个在框架层次上的理解。
2.干货
1.Netd模块源码位置
源码位置根目录/system/netd/
2.入口函数
/system/netd/main.cpp
int main() {
CommandListener *cl;
NetlinkManager *nm;
DnsProxyListener *dpl;
MDnsSdListener *mdnsl;
blockSigpipe();
if (!(nm = NetlinkManager::Instance())) {
ALOGE("Unable to create NetlinkManager");
exit(1);
};
UidMarkMap *rangeMap = new UidMarkMap();
cl = new CommandListener(rangeMap);
nm->setBroadcaster((SocketListener *) cl);
if (nm->start()) {
ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
setenv("ANDROID_DNS_MODE", "local", 1);
dpl = new DnsProxyListener(rangeMap);
if (dpl->startListener()) {
ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
exit(1);
}
mdnsl = new MDnsSdListener();
if (mdnsl->startListener()) {
ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
exit(1);
}
/*
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) {
ALOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
ALOGI("Netd exiting");
exit(0);
}
可以看出以下类的重要性:
NetlinkManager *nm;
CommandListener *cl;
DnsProxyListener、MDnsSdListener涉及到Android DNS模块的处理,本文暂不作分析。
1.NetlinkManager分析
首先了解下NetLink是什么,在linux下NetLink是一个异步通信机制的socket,区别于系统调用和ioctl的同步调用机制。
可以看到/system/netd/NetlinkManager .cpp这种懒汉单实例模式:
NetlinkManager *NetlinkManager::sInstance = NULL;
NetlinkManager *NetlinkManager::Instance() {
if (!sInstance)
sInstance = new NetlinkManager();
return sInstance;
}
NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,
int groups, int format) {
struct sockaddr_nl nladdr;
int sz = 64 * 1024;
int on = 1;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid();
nladdr.nl_groups = groups;
if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
ALOGE("Unable to create netlink socket: %s", strerror(errno));
return NULL;
}
if (setsockopt(*sock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
ALOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
close(*sock);
return NULL;
}
if (setsockopt(*sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
close(*sock);
return NULL;
}
if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
ALOGE("Unable to bind netlink socket: %s", strerror(errno));
close(*sock);
return NULL;
}
NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
if (handler->start()) {
ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
close(*sock);
return NULL;
}
return handler;
}
int NetlinkManager::start() {
if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
return -1;
}
if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_IFADDR,
NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
return -1;
}
if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
ALOGE("Unable to open quota2 logging socket");
// TODO: return -1 once the emulator gets a new kernel.
}
return 0;
}
NetlinkManager主要是向kernel注册三个用于接收UEvent事件的socket
1.NETLINK_KOBJECT_UEVENT
一般是/sys/class/net模块下的加载和卸载消息
2.RTMGRP_LINK、RTMGRP_IPV4_IFADDR、RTMGRP_IPV6_IFADDR
一般是网络链路接通或断开时的消息
3.NETLINK_NFLOG
一般和带宽控制有关
从上面源码中可以看到三个UEvent事件分别对应了mUeventHandler 、mRouteHandler 、mQuotaHandler,而这三个事件在Netd模块都声明为NetlinkHandler。
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
const char *subsys = evt->getSubsystem();
if (!subsys) {
ALOGW("No subsystem found in netlink event");
return;
}
if (!strcmp(subsys, "net")) {
int action = evt->getAction();
const char *iface = evt->findParam("INTERFACE");
if (action == evt->NlActionAdd) {
notifyInterfaceAdded(iface);
}
...
}
}
NetlinkHandler收到的socket消息就是通过onEvent回调处理的。
例如:
void NetlinkHandler::notifyInterfaceAdded(const char *name) {
char msg[255];
snprintf(msg, sizeof(msg), "Iface added %s", name);
mNm->getBroadcaster()->sendBroadcast(ResponseCode::InterfaceChange,
msg, false);
}
这里间接调用了SocketListener发送一个消息。
源码位置:\system\core\libsysutils\src:
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
mListen = listen;
mSocketName = socketName;
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();
}
int SocketListener::startListener() {
...
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
return 0;
}
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast(obj);
me->runListener();
pthread_exit(NULL);
return NULL;
}
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
pthread_mutex_lock(&mClientsLock);
SocketClientCollection::iterator i;
for (i = mClients->begin(); i != mClients->end(); ++i) {
// broadcasts are unsolicited and should not include a cmd number
if ((*i)->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
}
}
pthread_mutex_unlock(&mClientsLock);
}
...
每一个SockectListener都会单独创建一个线程用于接收socket消息。当Kernel发送UEvent消息后,就可以通过子类onDataAvailable函数回调回去处理。
void SocketListener::runListener() {
SocketClientCollection *pendingList = new SocketClientCollection();
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);
}
...
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
...
}
if (!onDataAvailable(c) && mListen) {
...
}
}
比如NetlinkListener是SocketListener的派生,在接收到Kernel的Uevent消息后,先通过NetlinkEvent解析消息,然后通过onEvent接口回调处理,在NetlinkHandler中最终江通过Broadcaster转发出去。
NetlinkListener::NetlinkListener(int socket) :
SocketListener(socket, false) {
mFormat = NETLINK_FORMAT_ASCII;
}
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
socket, mBuffer, sizeof(mBuffer), &uid));
if (count < 0) {
if (uid > 0)
LOG_EVENT_INT(65537, uid);
SLOGE("recvmsg failed (%s)", strerror(errno));
return false;
}
NetlinkEvent *evt = new NetlinkEvent();
if (!evt->decode(mBuffer, count, mFormat)) {
SLOGE("Error decoding NetlinkEvent");
} else {
onEvent(evt);
}
delete evt;
return true;
}
从SocketListener::sendBroadcast方法中可以看到,消息是通过FrameworkClient转发的:
FrameworkClient::FrameworkClient(int socket) {
mSocket = socket;
pthread_mutex_init(&mWriteMutex, NULL);
}
int FrameworkClient::sendMsg(const char *msg) {
int ret;
if (mSocket < 0) {
errno = EHOSTUNREACH;
return -1;
}
pthread_mutex_lock(&mWriteMutex);
ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
if (ret < 0) {
SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
}
pthread_mutex_unlock(&mWriteMutex);
return 0;
}
int FrameworkClient::sendMsg(const char *msg, const char *data) {
size_t bufflen = strlen(msg) + strlen(data) + 1;
char *buffer = (char *) alloca(bufflen);
if (!buffer) {
errno = -ENOMEM;
return -1;
}
snprintf(buffer, bufflen, "%s%s", msg, data);
return sendMsg(buffer);
}
而在Android Framework层的NetworkManagementService实现中,可以看到通过NativeDaemonConnector对netd的sockect监听,当FrameworkClient转发消息到socket时,NativeDaemonConnector就会取出消息然后通过java层的Observer转发出去,进而实现底层消息的上报:
private static final String NETD_SOCKET_NAME = "netd";
public static NetworkManagementService create(Context context) throws InterruptedException {
return create(context, NETD_SOCKET_NAME);
}
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();
if (DBG) Slog.d(TAG, "Connected");
return service;
}
private NetworkManagementService(Context context, String socket) {
mContext = context;
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
return;
}
mConnector = new NativeDaemonConnector(
new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
mThread = new Thread(mConnector, NETD_TAG);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
}
private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
...
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
switch (code) {
case NetdResponseCode.InterfaceChange:
...
case NetdResponseCode.InterfaceClassActivity:
/*
* An network interface class state changed (active/idle)
* Format: "NNN IfaceClass
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
int responseQueueSize, String logTag, int maxLogSize) {
mCallbacks = callbacks;
mSocket = socket;
mResponseQueue = new ResponseQueue(responseQueueSize);
mSequenceNumber = new AtomicInteger(0);
TAG = logTag != null ? logTag : "NativeDaemonConnector";
mLocalLog = new LocalLog(maxLogSize);
}
@Override
public void run() {
mCallbackHandler = new Handler(FgThread.get().getLooper(), 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();
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
while (true) {
...
mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
event.getCode(), event.getRawEvent()));
...
}
}
Android Framework层核心类ConnectivityService中,事件的广播:
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
NetworkFactory netFactory) {
...
try {
mNetd.registerObserver(mTethering);
mNetd.registerObserver(mDataActivityObserver);
mNetd.registerObserver(mClat);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
}
private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
@Override
public void interfaceClassDataActivityChanged(String label, boolean active) {
int deviceType = Integer.parseInt(label);
sendDataActivityBroadcast(deviceType, active);
}
};
private void sendDataActivityBroadcast(int deviceType, boolean active) {
Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
final long ident = Binder.clearCallingIdentity();
try {
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
@Override
public void interfaceStatusChanged(String iface, boolean up) {
// default no-op
}
@Override
public void interfaceRemoved(String iface) {
// default no-op
}
@Override
public void addressUpdated(String address, String iface, int flags, int scope) {
// default no-op
}
@Override
public void addressRemoved(String address, String iface, int flags, int scope) {
// default no-op
}
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
// default no-op
}
@Override
public void interfaceAdded(String iface) {
// default no-op
}
@Override
public void interfaceClassDataActivityChanged(String label, boolean active) {
// default no-op
}
@Override
public void limitReached(String limitName, String iface) {
// default no-op
}
}
分析到这一步,已经可以看到sendOrderedBroadcastAsUser这个接口,同学你应该很开心了吧!其他如interfaceAdded、interfaceRemoved、interfaceStatusChanged、interfaceLinkStateChanged等接口的事件上报亦可以按以上思路在源码中找到对应的实现。
以下是参考了深入理解Andoriod卷画的NetLinkManager工作流程图:
2.CommandListener
主要作用是把从Framework层NetworkManageService接收的指令转交到对应的指令对象去处理。
CommandListener::CommandListener(UidMarkMap *map) :
FrameworkListener("netd", true) {
registerCmd(new InterfaceCmd());
registerCmd(new IpFwdCmd());
registerCmd(new TetherCmd());
registerCmd(new NatCmd());
registerCmd(new ListTtysCmd());
registerCmd(new PppdCmd());
registerCmd(new SoftapCmd());
registerCmd(new BandwidthControlCmd());
registerCmd(new IdletimerControlCmd());
registerCmd(new ResolverCmd());
registerCmd(new FirewallCmd());
registerCmd(new ClatdCmd());
registerCmd(new SambaControlCmd());
registerCmd(new TraceRouteControlCmd());
registerCmd(new BridgeControlCmd());
if (!sSecondaryTableCtrl)
sSecondaryTableCtrl = new SecondaryTableController(map);
if (!sTetherCtrl)
sTetherCtrl = new TetherController();
if (!sNatCtrl)
sNatCtrl = new NatController(sSecondaryTableCtrl);
if (!sPppCtrl)
sPppCtrl = new PppController();
if (!sSoftapCtrl)
sSoftapCtrl = new SoftapController();
if (!sBandwidthCtrl)
sBandwidthCtrl = new BandwidthController();
if (!sIdletimerCtrl)
sIdletimerCtrl = new IdletimerController();
if (!sResolverCtrl)
sResolverCtrl = new ResolverController();
if (!sFirewallCtrl)
sFirewallCtrl = new FirewallController();
if (!sInterfaceCtrl)
sInterfaceCtrl = new InterfaceController();
if (!sClatdCtrl)
sClatdCtrl = new ClatdController();
if (!sSambaCtrl)
sSambaCtrl = new SambaController();
if (!sTraceRouteCtrl)
sTraceRouteCtrl = new TraceRouteController();
if (!sBridgeCtrl)
sBridgeCtrl = new BridgeController();
// Create chains for children modules
createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT);
createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD);
createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT);
createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING);
createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING);
createChildChains(V4V6, "mangle", "OUTPUT", MANGLE_OUTPUT);
createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING);
createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING);
// Let each module setup their child chains
setupOemIptablesHook();
sFirewallCtrl->setupIptablesHooks();
/* Does DROPs in FORWARD by default */
sNatCtrl->setupIptablesHooks();
/*
* Does REJECT in INPUT, OUTPUT. Does counting also.
* No DROP/REJECT allowed later in netfilter-flow hook order.
*/
sBandwidthCtrl->setupIptablesHooks();
/*
* Counts in nat: PREROUTING, POSTROUTING.
* No DROP/REJECT allowed later in netfilter-flow hook order.
*/
sIdletimerCtrl->setupIptablesHooks();
sBandwidthCtrl->enableBandwidthControl(false);
sSecondaryTableCtrl->setupIptablesHooks();
}
CommandListener是从FrameworkListener派生,在FrameworkListener内部有一个数组mCommands来存储注册到FrameworkListener中的命令处理对象。
FrameworkListener从SocketListener派生,在第一节分析SocketListener时就已经说过每一个SocketListener就是一个单独的线程用于接收sockect消息,当接收到sockect端消息时,就会回调onDataAvailable接口:
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);
}
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
mCommands->push_back(cmd);
}
bool FrameworkListener::onDataAvailable(SocketClient *c) {
...
dispatchCommand(c, buffer + offset);
...
}
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
...
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;
}
...
}
从CommandListener源码中可以看到类似InterfaceCmd、IpFwdCmd等和网络相关的Command类,这些类都是从NetdCommand派生的:
CommandListener::InterfaceCmd::InterfaceCmd() :
NetdCommand("interface") {
}
int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
return 0;
}
if (!strcmp(argv[1], "list")) {
...
}
CommandListener::IpFwdCmd::IpFwdCmd() :
NetdCommand("ipfwd") {
}
int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
int rc = 0;
char value[PROPERTY_VALUE_MAX];
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
return 0;
}
if (!strcmp(argv[1], "status")) {
...
}
...
而NetdCommand又是从FrameworkCommand派生的:
NetdCommand::NetdCommand(const char *cmd) :
FrameworkCommand(cmd) {
}
class FrameworkCommand {
private:
const char *mCommand;
public:
FrameworkCommand(const char *cmd);
virtual ~FrameworkCommand() { }
virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
const char *getCommand() { return mCommand; }
};
#include
FrameworkCommand::FrameworkCommand(const char *cmd) {
mCommand = cmd;
}
int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) {
SLOGW("Command %s has no run handler!", getCommand());
errno = ENOSYS;
return -1;
}
因此,当Framework中有类似interface、ipfwd、softap等sockect指令时,在netd模块中就会通过sockect的select查选到(可以看之前对SockectListener的分析),进而回调到FrameworkListener的OnDataAvialable接口,由于是派生的NetdCommand类,进而调用对应的runCommand接口,实现对指令的特殊处理。
例如通过Android的系统设置打开AP热点(打开AP在Framework层的流程和源码本文暂不做分析),SoftapController.cpp:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_TAG "SoftapController"
#include
#include
#include
#include "wifi.h"
#include "ResponseCode.h"
#include "SoftapController.h"
static const char HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf";
static const char HOSTAPD_BIN_FILE[] = "/system/bin/hostapd";
SoftapController::SoftapController() {
mPid = 0;
mSock = socket(AF_INET, SOCK_DGRAM, 0);
if (mSock < 0)
ALOGE("Failed to open socket");
memset(mIface, 0, sizeof(mIface));
}
SoftapController::~SoftapController() {
}
int SoftapController::startSoftap() {
pid_t pid = 1;
int wifi_device = wifi_get_device_id();
if (WIFI_RALINK_RT3070 == wifi_device || WIFI_RALINK_RT5370 == wifi_device
|| WIFI_RALINK_RT5372 == wifi_device || WIFI_RALINK_RT5572 == wifi_device
|| WIFI_RALINK_MT7601U == wifi_device)
return 0;
if (mPid) {
ALOGE("SoftAP is already running");
return ResponseCode::SoftapStatusResult;
}
if ((pid = fork()) < 0) {
ALOGE("fork failed (%s)", strerror(errno));
return ResponseCode::ServiceStartFailed;
}
if (!pid) {
ensure_entropy_file_exists();
if (execl(HOSTAPD_BIN_FILE, HOSTAPD_BIN_FILE,
"-e", WIFI_ENTROPY_FILE,
HOSTAPD_CONF_FILE, (char *) NULL)) {
ALOGE("execl failed (%s)", strerror(errno));
}
ALOGE("SoftAP failed to start");
return ResponseCode::ServiceStartFailed;
} else {
mPid = pid;
ALOGD("SoftAP started successfully");
usleep(AP_BSS_START_DELAY);
}
return ResponseCode::SoftapStatusResult;
}
int SoftapController::stopSoftap() {
int wifi_device = wifi_get_device_id();
if (WIFI_RALINK_RT3070 == wifi_device || WIFI_RALINK_RT5370 == wifi_device
|| WIFI_RALINK_RT5372 == wifi_device || WIFI_RALINK_RT5572 == wifi_device
|| WIFI_RALINK_MT7601U == wifi_device) {
struct ifreq ifr;
int s;
/* configure WiFi interface down */
memset(&ifr, 0, sizeof(struct ifreq));
strcpy(ifr.ifr_name, mIface);
if((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
if(ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
ifr.ifr_flags = (ifr.ifr_flags & (~IFF_UP));
ioctl(s, SIOCSIFFLAGS, &ifr);
}
close(s);
}
usleep(200000);
return 0;
}
if (mPid == 0) {
ALOGE("SoftAP is not running");
return ResponseCode::SoftapStatusResult;
}
ALOGD("Stopping the SoftAP service...");
kill(mPid, SIGTERM);
waitpid(mPid, NULL, 0);
mPid = 0;
ALOGD("SoftAP stopped successfully");
usleep(AP_BSS_STOP_DELAY);
return ResponseCode::SoftapStatusResult;
}
bool SoftapController::isSoftapStarted() {
return (mPid != 0);
}
/* configure softap by sending private ioctls to driver directly */
int SoftapController::ap_config_with_iwpriv_cmd(int s, char *ifname, char **argv)
{
char tBuf[4096];
struct iwreq wrq;
struct iw_priv_args *priv_ptr;
int i, j;
int cmd = 0, sub_cmd = 0;
int device_id;
char mBuf[256];
int hidden_ssid = 0;
device_id = wifi_get_device_id();
/* get all private commands that driver supported */
strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name));
wrq.u.data.pointer = tBuf;
wrq.u.data.length = sizeof(tBuf) / sizeof(struct iw_priv_args);
wrq.u.data.flags = 0;
if (ioctl(s, SIOCGIWPRIV, &wrq) < 0) {
return -1;
}
/* if driver don't support 'set' command, return failure */
priv_ptr = (struct iw_priv_args *)wrq.u.data.pointer;
for(i = 0; i < wrq.u.data.length; i++) {
if (strcmp(priv_ptr[i].name, "set") == 0) {
cmd = priv_ptr[i].cmd;
break;
}
}
if (i == wrq.u.data.length) {
return -1;
}
/* get the 'set' command's ID */
if (cmd < SIOCDEVPRIVATE) {
for(j = 0; j < i; j++) {
if ((priv_ptr[j].set_args == priv_ptr[i].set_args)
&& (priv_ptr[j].get_args == priv_ptr[i].get_args)
&& (priv_ptr[j].name[0] == '\0'))
break;
}
if (j == i) {
return -1;
}
sub_cmd = cmd;
cmd = priv_ptr[j].cmd;
}
/* configure AP, order should be as follow
* 1. Channel
* 2. AuthMode
* 3. EncrypType
* for WPAPSK/WPA2PSK:
* 4.Hidden/Broadcast SSID
* 5. SSID (must after AuthMode and before Password)
* 6. Password
*/
strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name));
wrq.u.data.pointer = mBuf;
wrq.u.data.flags = sub_cmd;
/* configure Channel */
sprintf(mBuf, "Channel=%s", argv[2]);
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
/* configure AuthMode */
if(!strcmp(argv[3], "wpa-psk"))
sprintf(mBuf, "AuthMode=WPAPSK");
else if (!strcmp(argv[3], "wpa2-psk"))
sprintf(mBuf, "AuthMode=WPA2PSK");
else
sprintf(mBuf, "AuthMode=OPEN");
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
/* configure EncrypType */
if (!strcmp(argv[3], "wpa-psk"))
sprintf(mBuf, "EncrypType=AES");
else if (!strcmp(argv[3], "wpa2-psk"))
sprintf(mBuf, "EncrypType=AES");
else
sprintf(mBuf, "EncrypType=NONE");
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
/* configure hide SSID */
if (!strcmp(argv[1], "hidden"))
hidden_ssid = 1;
sprintf(mBuf, "HideSSID=%d", hidden_ssid);
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
/* configure SSID */
sprintf(mBuf, "SSID=%s", argv[0]);
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
/* configure password of WPAPSK/WPA2PSK */
if (strcmp(argv[3], "open")) {
sprintf(mBuf, "WPAPSK=%s", argv[4]);
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
if (device_id == WIFI_RALINK_MT7601U) {
/* for MT7601U, configure SSID again */
sprintf(mBuf, "SSID=%s", argv[0]);
wrq.u.data.length = strlen(mBuf) + 1;
if(ioctl(s, cmd, &wrq) < 0)
return -1;
}
}
return 0;
}
/*
* Arguments:
* argv[2] - wlan interface
* argv[3] - SSID
* argv[4] - Broadcast/Hidden
* argv[5] - Channel
* argv[6] - Security
* argv[7] - Key
*/
int SoftapController::setSoftap(int argc, char *argv[]) {
char psk_str[2*SHA256_DIGEST_LENGTH+1];
int ret = ResponseCode::SoftapStatusResult;
int i = 0;
int fd;
int hidden = 0;
int channel = AP_CHANNEL_DEFAULT;
int wifi_device;
char hw_mode;
char ht40_capab[32];
char *wbuf = NULL;
char *fbuf = NULL;
if (argc < 5) {
ALOGE("Softap set is missing arguments. Please use:");
ALOGE("softap ");
return ResponseCode::CommandSyntaxError;
}
wifi_device = wifi_get_device_id();
if (!strcasecmp(argv[4], "hidden")) {
if (WIFI_ATHEROS_QCA1021X == wifi_device || WIFI_ATHEROS_QCA1021G == wifi_device
|| WIFI_ATHEROS_AR9374 == wifi_device)
hidden = 2;
else
hidden = 1;
}
if (argc >= 5) {
channel = atoi(argv[5]);
if (channel <= 0)
channel = AP_CHANNEL_DEFAULT;
}
memset(ht40_capab, 0, sizeof(ht40_capab));
if (channel >= 36) {
int ht40plus[] = {36, 44, 52, 60, 100, 108, 116, 124,
132, 149, 157};
int ht40minus[] = {40, 48, 56, 64, 104, 112, 120, 128,
136, 153, 161};
hw_mode = 'a';
for (i = 0; i < sizeof(ht40plus)/sizeof(ht40plus[0]); i++)
if (channel == ht40plus[i]) {
strcpy(ht40_capab, "[SHORT-GI-40][HT40+]");
break;
}
for (i = 0; i < sizeof(ht40minus)/sizeof(ht40minus[0]); i++)
if (channel == ht40minus[i]) {
strcpy(ht40_capab, "[SHORT-GI-40][HT40-]");
break;
}
} else {
hw_mode = 'g';
if (channel > 7)
strcpy(ht40_capab, "[SHORT-GI-40][HT40-]");
else
strcpy(ht40_capab, "[SHORT-GI-40][HT40+]");
}
char *ssid, *iface;
if (mSock < 0) {
ALOGE("Softap set - failed to open socket");
return -1;
}
strncpy(mIface, argv[2], sizeof(mIface));
iface = argv[2];
if (WIFI_RALINK_RT3070 == wifi_device || WIFI_RALINK_RT5370 == wifi_device
|| WIFI_RALINK_RT5372 == wifi_device || WIFI_RALINK_RT5572 == wifi_device
|| WIFI_RALINK_MT7601U == wifi_device) {
struct ifreq ifr;
int s;
/* RT3070/5370/5372/MT7601U don't use hostapd, driver reads RT2870AP.dat
* and configures WiFi to AP mode while intialize. After initialization
* complete, configure WiFi interface up will startup AP. Then
* reconfigure AP by private commands.
*/
memset(&ifr, 0, sizeof(struct ifreq));
strcpy(ifr.ifr_name, iface);
if((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
if(ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
ifr.ifr_flags = (ifr.ifr_flags | IFF_UP);
if (ioctl(s, SIOCSIFFLAGS, &ifr) >= 0) {
ret = ap_config_with_iwpriv_cmd(s, iface, argv + 3);
}
}
close(s);
}
return 0;
}
if (argc < 4) {
ALOGE("Softap set - missing arguments");
return -1;
}
if (argc > 3) {
ssid = argv[3];
} else {
ssid = (char *)"AndroidAP";
}
asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
"/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n"
"hw_mode=%c\nht_capab=[SHORT-GI-20]%s\nignore_broadcast_ssid=%d\n",
argv[2], argv[3], channel, hw_mode, ht40_capab, hidden);
if (argc > 7) {
if (!strcmp(argv[6], "wpa-psk")) {
generatePsk(argv[3], argv[7], psk_str);
asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
} else if (!strcmp(argv[6], "wpa2-psk")) {
generatePsk(argv[3], argv[7], psk_str);
asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
} else if (!strcmp(argv[6], "open")) {
asprintf(&fbuf, "%s", wbuf);
}
} else if (argc > 6) {
if (!strcmp(argv[6], "open")) {
asprintf(&fbuf, "%s", wbuf);
}
} else {
asprintf(&fbuf, "%s", wbuf);
}
fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
if (fd < 0) {
ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
free(wbuf);
free(fbuf);
return ResponseCode::OperationFailed;
}
if (write(fd, fbuf, strlen(fbuf)) < 0) {
ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
ret = ResponseCode::OperationFailed;
}
free(wbuf);
free(fbuf);
/* Note: apparently open can fail to set permissions correctly at times */
if (fchmod(fd, 0660) < 0) {
ALOGE("Error changing permissions of %s to 0660: %s",
HOSTAPD_CONF_FILE, strerror(errno));
close(fd);
unlink(HOSTAPD_CONF_FILE);
return ResponseCode::OperationFailed;
}
if (fchown(fd, AID_SYSTEM, AID_WIFI) < 0) {
ALOGE("Error changing group ownership of %s to %d: %s",
HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno));
close(fd);
unlink(HOSTAPD_CONF_FILE);
return ResponseCode::OperationFailed;
}
close(fd);
return ret;
}
void SoftapController::generatePsk(char *ssid, char *passphrase, char *psk_str) {
unsigned char psk[SHA256_DIGEST_LENGTH];
int j;
// Use the PKCS#5 PBKDF2 with 4096 iterations
PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase),
reinterpret_cast(ssid), strlen(ssid),
4096, SHA256_DIGEST_LENGTH, psk);
for (j=0; j < SHA256_DIGEST_LENGTH; j++) {
sprintf(&psk_str[j*2], "%02x", psk[j]);
}
}
首先可以看看SoftapController的startSoftap、stopSoftap、setSoftap接口,然后就可以联想到CommandListener源码中对Softap的处理,当接收到Framework层的set指令时调用的SoftapController的setSoftap,配置好AP参数后,Framework层再调用startap指令,就可以成功开启AP热点了。
int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
int rc = ResponseCode::SoftapStatusResult;
int flag = 0;
char *retbuf = NULL;
if (sSoftapCtrl == NULL) {
cli->sendMsg(ResponseCode::ServiceStartFailed, "SoftAP is not available", false);
return -1;
}
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Missing argument in a SoftAP command", false);
return 0;
}
if (!strcmp(argv[1], "startap")) {
rc = sSoftapCtrl->startSoftap();
} else if (!strcmp(argv[1], "stopap")) {
rc = sSoftapCtrl->stopSoftap();
} else if (!strcmp(argv[1], "fwreload")) {
rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
} else if (!strcmp(argv[1], "status")) {
asprintf(&retbuf, "Softap service %s running",
(sSoftapCtrl->isSoftapStarted() ? "is" : "is not"));
cli->sendMsg(rc, retbuf, false);
free(retbuf);
return 0;
} else if (!strcmp(argv[1], "set")) {
rc = sSoftapCtrl->setSoftap(argc, argv);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false);
return 0;
}
if (rc >= 400 && rc < 600)
cli->sendMsg(rc, "SoftAP command has failed", false);
else
cli->sendMsg(rc, "Ok", false);
return 0;
}
源码中在setSoftap时分为
1.使用ioctls接口配置驱动( RT3070/5370/5372/MT7601U don't use hostapd, driver reads RT2870AP.dat)
2.通过hostapd方式配置AP,即把AP配置写入/data/misc/wifi/hostapd.conf,在执行/system/bin/hostapd时读取配置
以下是参考了深入理解Andoriod卷画的CommandListener工作流程图:
3.结束语
Netd进程本身的代码不多,但是涉及的面比较广,本篇文章先只介绍一个骨架,并没有深入骨髓,文章会继续更新,谢谢关注!