赞
踩
1、创建一个基于自定义端口NETLINK_TEST的socket。
#define NETLINK_TEST 99
skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); //Create a socket using user defined protocol NETLINK_TEST.
if (skfd == -1) {
perror("create socket error\n");
return -1;
}
2、初始化源地址结构struct sockaddr_nl用于数据传输
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
struct sockaddr_nl saddr;
//Source address.
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK; //AF_NETLINK
saddr.nl_pid = USER_PORT; //netlink portid, same as kernel.
saddr.nl_groups = 0;
该结构用于构建netlink的通信地址,类似于socket编程中的sockaddr_in类似,nl_pid用来表示通信端口,nl_groups用来表示通信组,注意这里为希望加入多播组的号的掩码,也就是最多支持32个组。
3**、初始化源地址和建立套接字文件描述符成功后,需要对套接字进行地址和端口的绑定,才能进行数据的接收和发送操作。**
//bind to skfd with saddr.
if (bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) {
perror("bind() error\n");
close(skfd);
return -1;
}
4、构造destination addr,即初始化结构为struct sockaddr_nl类型目的地址。
//Destination address.
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
5、构造Netlink的消息头,用struct nlmsghdr来描述,Netlink报文的数据区由消息头和消息体构成,消息体接在消息头之后。
struct nlmsghdr {
__u32 nlmsg_len; /* Length of message including header */
__u16 nlmsg_type; /* Message content */
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process port ID */
};
以下的netlink常用的宏
#define NLMSG_ALIGNTO 4U //用于得到不小于len且字节对齐的最小数值 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) //netlink头部长度 #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) //计算消息数据len的真实消息长度,消息体+消息头 #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) //返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) //用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) //用于得到下一个消息的首地址,同时len变为剩余消息的长度 #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) //判断消息是否>len #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len <= (len)) //用于返回payload的长度 #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
//为消息头和消息体分配内存并初始化Netlink消息头
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = saddr.nl_pid; //self port
6、netlink报文消息体初始化,也就是我们需要传输的数据。
memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
7、发送数据和接收数据
通过skfd向内核发送数据。
//包含头文件
#include <sys/types.h>
#include <sys/socket.h>
//函数定义
int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);
**函数说明:**sendto() 用来将数据由指定的socket 传给对方主机. 参数s 为已建好连线的socket, 如果利用UDP协议则不需经过连线操作. 参数msg 指向欲连线的数据内容, 参数flags 一般设0, 详细描述请参考send(). 参数to 用来指定欲传送的网络地址, 结构sockaddr 请参考bind(). 参数tolen 为sockaddr 的结果长度.
返回值:成功则返回实际传送出去的字符数, 失败返回-1, 错误原因存于errno 中.
错误代码:
1、EBADF 参数s 非法的socket 处理代码.
2、EFAULT 参数中有一指针指向无法存取的内存空间.
3、WNOTSOCK canshu s 为一文件描述词, 非socket.
4、EINTR 被信号所中断.
5、EAGAIN 此动作会令进程阻断, 但参数s 的soket 为补课阻断的.
6、ENOBUFS 系统的缓冲内存不足.
7、EINVAL 传给系统调用的参数不正确.
ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
if (!ret) {
perror("sendto error\n");
close(skfd);
exit(-1);
}
接收来自内核的数据:
//包含头文件
#include <sys/types.h>
#include <sys/socket.h>
//函数定义
int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from,int *fromlen);
**函数说明:**recv()用来接收远程主机经指定的socket 传来的数据, 并把数据存到由参数buf 指向的内存空间, 参数len 为可接收数据的最大长度. 参数flags 一般设0, 其他数值定义请参考recv(). 参数from 用来指定欲传送的网络地址, 结构sockaddr 请参考bind(). 参数fromlen 为sockaddr 的结构长度.
返回值:成功则返回接收到的字符数, 失败则返回-1, 错误原因存于errno 中.
错误代码:
EBADF 参数s 非合法的socket 处理代码
EFAULT 参数中有一指针指向无法存取的内存空间.
ENOTSOCK 参数s 为一文件描述词, 非socket.
EINTR 被信号所中断.
EAGAIN 此动作会令进程阻断, 但参数s 的socket 为不可阻断.
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确.
//消息头+消息体
typedef struct _user_msg_info {
struct nlmsghdr hdr;
char msg[MSG_LEN];
} user_msg_info;
//Receive netlink message from kernel.
memset(&u_info, 0, sizeof(u_info));
len = sizeof(struct sockaddr_nl);
ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&daddr, &len);
if (!ret) {
perror("recv form kernel error\n");
close(skfd);
exit(-1);
}
1、在驱动初始化阶段初始化一个socket用于与用户态数据交互。
struct netlink_kernel_cfg { unsigned int groups; unsigned int flags; void (*input)(struct sk_buff *skb);-----------------------------------input回调函数 struct mutex *cb_mutex; int (*bind)(struct net *net, int group); void (*unbind)(struct net *net, int group); bool (*compare)(struct net *net, struct sock *sk); }; //内核接收数据的回调函数 struct netlink_kernel_cfg cfg = { .input = netlink_rcv_msg, /* set recv callback */ }; struct socket *nlsk = NULL; /* Create netlink socket */ nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg); if (nlsk == NULL) { printk("netlink_kernel_create error !\n"); return -1; }
2、回调函数处理用户态发送过来的数据。
skb->len;
@len: Length of actual data
//接收函数的参数为struct sk_buffer *skb;
nlh = nlmsg_hdr(skb); //Get nlmsghdr from sk_buff.
umsg = NLMSG_DATA(nlh); //Get payload from nlmsghdr.
3、向用户态发送数据,创建一个新的sk_buffer -> nlmsg_put()设置sk_buffer nltmsghdr -> 填充paylaod -> 往端口发送数据。
int send_usrmsg(char *pbuf, uint16_t len) { struct sk_buff *nl_skb; struct nlmsghdr *nlh; int ret; //Create sk_buff using nlmsg_new(). nl_skb = nlmsg_new(len, GFP_ATOMIC); if(!nl_skb) { printk("netlink alloc failure\n"); return -1; } //Set up nlmsghdr. nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0); if(nlh == NULL) { printk("nlmsg_put failaure \n"); nlmsg_free(nl_skb); //If nlmsg_put() failed, nlmsg_free() will free sk_buff. return -1; } //Copy pbuf to nlmsghdr payload. memcpy(nlmsg_data(nlh), pbuf, len); ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT); return ret; }
内核的发送端需要构造sk_buffer。
参考文档:
netlink:https://www.cnblogs.com/arnoldlu/p/9532254.html
netlink详细文档:https://blog.csdn.net/stone8761/article/details/72780863
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。