netlink 套接字传递参数
Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态 socket 协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),socket 监视(NETLINK_INET_DIAG),netfilter 日志(NETLINK_NFLOG),ipsec 安全策略(NETLINK_XFRM),SELinux 事件通知(NETLINK_SELINUX),iSCSI 子系统(NETLINK_ISCSI),进程审计(NETLINK_AUDIT),转发信息表查询(NETLINK_FIB_LOOKUP),netlink connector(NETLINK_CONNECTOR),netfilter 子系统(NETLINK_NETFILTER),IPv6 防火墙(NETLINK_IP6_FW),DECnet 路由信息(NETLINK_DNRTMSG),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT),通用 netlink(NETLINK_GENERIC)。
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 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。
4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对内核事件感兴趣的应用都能收到该子系统发送的内核事件,在后面的文章中将介绍这一机制的使用。
5.内核可以使用 netlink 首先发起会话,但系统调用和 ioctl 只能由用户应用发起调用。
6.netlink 使用标准的 socket API,因此很容易使用,但系统调用和 ioctl则需要专门的培训才能使用。
用户空间的程序设计
(1)创建套接字。
函数netlink_kernel_create创建netlink类型的socket,与输入句柄连接,并将sock插入nl_ table链表中,函数简要说明如下:
struct sock3 netlink_kernel_create ( int unit, void ( 3 input)
( struct sock 3 sk, int len) )
/*参数unit表示netlink协议类型,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink sock2et时,该input函数指针就会被引用。函数指针input的参数sk
实际上就是函数netlink_kernel_create返回的struct sock指针,sock实际是socket的一个内核表示数据结构,用户空间应用创建的socket在内核中也会有一个struct sock结构来表示。*/
{ ⋯⋯
if ( sock_create_ lite ( PF_NETL INK, SOCK_DGRAM, unit,
&sock) ) return NULL;
/ /创建socket结构类型节点sock
if ( netlink_create ( sock, unit) < 0) {
sock_release ( sock) ;
return NULL;
}
sk = sock - > sk; / /给sock加上netlink_op s,分配并初始化
sk
sk - > sk_data_ready = netlink_data_ready; / /函数netlink_data_ready接收数据,唤醒接收等待队列
if ( input) nlk_sk ( sk) - > data_ready = input; / /赋值输入
数据函数句柄
netlink_insert ( sk, 0) ; / / sk加入nl_table链表中
return sk;
}
(2)程序设计。
用户空间的main ( )简要说明如下。
int main ( void) { skfd = socket ( PF_NETL INK, SOCK_RAW,NL_ IMP2) ; /*netlink对应的协议簇是AF_NETL INK,第2个参数必须是SOCK_RAW或SOCK_DGRAM, 第3个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型。*/ if ( bind ( skfd, ( struct sockaddr3 ) &local, sizeof ( local) ) != 0) return - 1; /* bind函数需要绑定协议地址, netlink的socket地址使 用struct sockaddr_nl结构描述*/ signal ( SIGINT, sig_ int) ; //设置SIGINT信号为sig_int函数 … sendto ( skfd, &message, message. hdr. nlmsg _ len, 0, ( structsockaddr3 ) &kpeer, sizeof ( kpeer) ) ; //通过socket向内核模块发消息IMP2_U_P ID,下面循环是 从内核接收消息 while (1) { kpeerlen = sizeof ( struct sockaddr_nl) ; rcvlen = recvfrom ( skfd, &info, sizeof ( struct u_packet_info) ,0, ( struct sockaddr3 ) &kpeer, &kpeerlen) ; //从socket中接收消息 } return 0; }
内核空间程序设计
(1)接收用户空间数据程序。
数据接收程序,用户进程所发送的实际数据保存在sk指向
的sock中。
static void kernel_receive ( struct sock 3 sk, int len) //接收用户空间数据,运行在软件中断环境。 { do { while ( ( skb = skb _ dequeue ( &sk - > receive _queue) ) ! =NULL) //从队列取数据 { kfree_skb ( skb) ; } } while ( nlfd && nlfd - > receive_queue. qlen) ; }
(2)用户进程发送数据。
static int send_to_user ( strcut packet_info 3 info) /* 发送数 据到用户空间*/ { ⋯⋯ / /开辟一个新的套接字缓存 skb = alloc_skb ( size, GFP_ATOM IC) ; old_tail = skb - > tail; //填写数据报相关信息 nlh =NLMSG_PUT ( skb, 0, 0, IMP2 _K_MSG, size - sizeof(3 nlh)); packet =NLMSG_DATA ( nlh) ; /* 跳过消息首部,指向数据区*/ memset (packet, 0, sizeof ( struct packet_info) ) ; /* 初始化数据区* / //传输到用户空间的数据 packet - > src = info - > src; packet - > dest = info - > dest; /* 计算经过字节对其后的数据实际长度* / nlh - > nlmsg_len = skb - > tail - old_tail; NETL INK_CB ( skb). dst_group s = 0; /* 设置控制字段*/ read_lock_bh (&user_p roc. lock) ; //利用socket向用户空间广播消息 ret = netlink_unicast ( nlfd, skb, user_p roc. p id,MSG_DONT2WA IT) ; //发送数据 read_unlock_bh (&user_p roc. lock) ; return ret; //若发送失败,则取消套接字缓存 Nlmsg_failure: if (skb) kfree_skb (skb) ; return - 1; }