[Android][Linux][Netlink]内核态和用户态使用netlink方法总结

一、Netlink概述

二、用户态如何使用netlink

三、内核态如何使用netlink

四、内核态和用户态之间使用netlink实例(NETD

五、内核态和用户态之间使用netlink实例(SYN FIREWALL


一、Netlink概述

(本段摘抄自网络)

Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 netlink

 

Netlink 相对于系统调用,ioctl 以及 /proc 文件系统而言具有以下优点:

 

1,为了使用 netlink,用户仅需要在 include/linux/netlink.h 中增加一个新类型的 netlink 协议定义即可, 如 #define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换。但系统调用需要增加新的系统调用,ioctl 则需要增加设备或文件, 那需要不少代码,proc 文件系统则需要在 /proc 下添加新的文件或目录,那将使本来就混乱的 /proc 更加混乱。

 

2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接 收队列,而不需要等待接收者收到消息,但系统调用与 ioctl 则是同步通信机制,如果传递的数据太长,将影响调度粒度。

 

3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。

 

4netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对内核事件感兴趣的应用都能收到该子系统发送的内核事件。

 

5.内核可以使用 netlink 首先发起会话,但系统调用和 ioctl 只能由用户应用发起调用。

 

6.netlink 使用标准的 socket API,因此很容易使用,但系统调用和 ioctl则需要专门的培训才能使用。

 

使用netlink进行应用与内核通信的应用很多

#define NETLINK_ROUTE	 0	/* Routing/device hook	 */
#define NETLINK_UNUSED	 1	/* Unused number	 */
#define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
#define NETLINK_FIREWALL	3	/* Firewalling hook	 */
#define NETLINK_SOCK_DIAG	4	/* socket monitoring	 */
#define NETLINK_NFLOG	 5	/* netfilter/iptables ULOG */
#define NETLINK_XFRM	 6	/* ipsec */
#define NETLINK_SELINUX	 7	/* SELinux event notifications */
#define NETLINK_ISCSI	 8	/* Open-iSCSI */
#define NETLINK_AUDIT	 9	/* auditing */
#define NETLINK_FIB_LOOKUP	10
#define NETLINK_CONNECTOR	11
#define NETLINK_NETFILTER	12	/* netfilter subsystem */
#define NETLINK_IP6_FW	 13
#define NETLINK_DNRTMSG	 14	/* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */
#define NETLINK_GENERIC	 16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT	18	/* SCSI Transports */
#define NETLINK_ECRYPTFS	19
#define NETLINK_RDMA	 20
#define NETLINK_CRYPTO	 21	/* Crypto layer */

 

其中,比较重点的是NETLINK_KOBJECT_UEVENT,即kernel uevent事件,内核设备驱动在某个时刻触发的某个事件(比如up/down)会通过netlink通知给用户空间。


二、用户态如何使用netlink

用户态应用使用标准的socket APIs, socket(), bind(), sendmsg(), recvmsg()close()就能很容易地使用netlink socket

使用netlink步骤如下:

1.包含头文件linux/netlink.h以及sys/socket.h

2.创建一个netlink socket

socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);

第一个参数必须是AF_NETLINKPF_NETLINK,表示要使用netlink

第二个参数必须是SOCK_RAWSOCK_DGRAM, 

第三个参数指定netlink协议类型,即第一节讲的义协议类型,如果是要监控内核设备驱动,直接用现成的NETLINK_KOBJECT_UEVENT即可。NETLINK_GENERIC是一个通用的协议类型,它是专门为用户使用的,因此,用户可以直接使用它,而不必再添加新的协议类型。


3.设置socket的选项(可选)

setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

4.使用bind() 创建的netlink socketnetlink socket地址绑定在一起

bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr));


其中,nladdr是sockaddr_nl结构体:

struct sockaddr_nl
{
  sa_family_t    nl_family;
  unsigned short 	nl_pad;
  __u32          nl_pid;
  __u32          nl_groups;
};

字段nl_family必须设置为AF_NETLINK或着PF_NETLINK,字段nl_pad当前没有使用,因此要总是设置为0,字段nl_pid为接收或发送消息的进程的ID,如果希望内核处理消息或多播消息,就把该字段设置为0,否则设置为处理消息的进程ID。字段nl_groups用于指定多播组,bind函数用于把调用进程加入到该字段指定的多播组,如果设置为0,表示调用者不加入任何多播组。

struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;


5.创建一个listener监听该socket,重写listeneronEvent函数,来完成对监听到的数据进行筛选/分类/处理。

mHandler = new NetlinkHandler(mSock);


三、内核态如何使用netlink

内核模块使用netlink,不同于用户态应用对netlink的使用,需要专门的netlink API。

使用netlink步骤如下:

1.包含头文件linux/netlink.h

2.创建一个netlink socket。建立OK后,建立了同样协议类型netlink的用户态通过sendmsg()即可向内核态发送消息指令了。其中第四个参数为接收用户态消息的函数指针,为null说明为单向的,即内核不处理用户态发来的消息,只是向用户态通知内核事件。

ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
  1, NULL, NULL, THIS_MODULE);

PS:由于NETLINK_KOBJECT_UEVENT是表示的内核设备驱动事件,所以新增的设备驱动无需建立netlink socket,建立了NETLINK_KOBJECT_UEVENT类型netlink用户态都可以收到新增设备驱动的消息事件。只有新增的内核模块,才需要建立netlink socket


3.向用户态上报消息

int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);
void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation);

其中,参数sk为函数netlink_kernel_create()返回的socket,参数skb存放消息,参数pid为接收消息进程的pid,参数group为接收消息的多播组


四、内核态和用户态之间使用netlink实例(NETD

1.内核态部分

由于netd监听的是已有设备驱动的消息事件,所以内核态无需修改。

 

2.用户态部分

2.1netd中新增NetlinkManager类,用以建立netlink socket

int NetlinkManager::start() {
    if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
        return -1;
    }
    ...
}
 
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) {
    ...}
 
    if (setsockopt(*sock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
    ...}
 
    if (setsockopt(*sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
    ...}
 
    if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
    ...}
 
    NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
    if (handler->start()) {
    ...}
    return handler;
}
 
int NetlinkManager::stop() {
    ...
    if (mUeventHandler->stop()) {
    ...}
 
    delete mUeventHandler;
    mUeventHandler = NULL;
 
    close(mUeventSock);
    mUeventSock = -1;
...
}


2.2netd中新增NetlinkHandler类,用以筛选/分类/处理netlink消息

int NetlinkHandler::start() {
    return this->startListener();
}
 
int NetlinkHandler::stop() {
    return this->stopListener();
}
 
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
const char *subsys = evt->getSubsystem();
 	 ...
if (!strcmp(subsys, "net")) {
        int action = evt->getAction();
        const char *iface = evt->findParam("INTERFACE");
 
        if (action == evt->NlActionAdd) {
            notifyInterfaceAdded(iface);
        } else if (action == evt->NlActionRemove) {
            notifyInterfaceRemoved(iface);
        } else if (action == evt->NlActionChange) {
            evt->dump();
            notifyInterfaceChanged("nana", true);
        } else if (action == evt->NlActionLinkUp) {
            notifyInterfaceLinkChanged(iface, true);
        } else if (action == evt->NlActionLinkDown) {
            notifyInterfaceLinkChanged(iface, false);
        } else if (action == evt->NlActionAddressUpdated ||
                   action == evt->NlActionAddressRemoved) {
            const char *address = evt->findParam("ADDRESS");
            const char *flags = evt->findParam("FLAGS");
            const char *scope = evt->findParam("SCOPE");
            if (iface && flags && scope) {
                notifyAddressChanged(action, address, iface, flags, scope);
            ...}
        ...}
    ...}
}

 

2.3netd.main()start NetlinkManager,即完成对netlink的使用,监听到NETLINK_KOBJECT_UEVENT,即kernel uevent事件,内核设备驱动(比如rmnet0在某个时刻触发的某个事件(比如up/down)会通过netlink通知给netd

int main() {
    NetlinkManager *nm;
    if (!(nm = NetlinkManager::Instance())) {
    ...};
    cl = new CommandListener(rangeMap);
    nm->setBroadcaster((SocketListener *) cl);
    if (nm->start()) {
 	 ...}
...}


五、内核态和用户态之间使用netlink实例(SYN FIREWALL

1.内核态

1.1 建立netlink socket

新增的设备驱动无需建立netlink socket,建立了NETLINK_KOBJECT_UEVENT类型netlink用户态都可以收到新增设备驱动的消息事件。只有新增的内核模块,才需要建立netlink socket

此处只是新增了一个switch device,而非新增module,故无需新建netlink socket

ret = switch_dev_register(&ipv4_sdev);

 

1.2 向用户态上报state change事件

switch_set_state(&ipv4_sdev, 0x10000|256*port1+port2);

调用关系如下:

switch_set_state()  -->  kobject_uevent_env()  -->  netlink_broadcast_filtered()

 

2.用户态

框架同netd部分,不再赘述。

你可能感兴趣的:([Android][Linux][Netlink]内核态和用户态使用netlink方法总结)