分类: LINUX
2023-08-04 18:53:11
Int skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2)
l {BANNED}中国第一个参数必须是 AF_NETLINK 或 PF_NETLINK,在 Linux 中,它们俩实际为一个东西,它表示要使用netlink,
l 第二个参数必须是SOCK_RAW或SOCK_DGRAM, 因为netlink是一个面向数据报的服务;
l 第三个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型
struct sockaddr_nl local;
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_pid = getpid(); /*设置pid为自己的pid值,也可以自定义值,不能和现有的重复*/
local.nl_groups = 0;
bind(skfd, (struct sockaddr*)&local, sizeof(local));
用户空间调用send函数(如sendto、sendmsg等)向内核发送数据,使用同样的socket地址来描述内核,不过需要注意,由于对端是内核,nl_pid必须设置为0。
l 如果该消息是发送至内核的,那么nl_pid和nl_groups都置为0.
l 如果消息是发送给另一个进程的单播消息,nl_pid是另外一个进程的pid值而nl_groups为零。
struct sockaddr_nl daddr;
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
//初始化消息头
struct nlmsghdr nlh;
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
//设置消息内容
memcpy(NLMSG_DATA(nlh),msg,strlen(msg));
int ret = sendto(skfd,nlh,nlh->nlmsg_len,0,(struct sockaddr *)&daddr,sizeof(struct sockaddr_nl));
ret大于0 ,表示发送的实际长度
ret小于0 ,表示发送失败,错误通过errno获取
typedef struct _user_msg_info
{
struct nlmsghdr hdr;
char msg[MSG_LEN];
} user_msg_info;
user_msg_info u_info;
memset(&u_info, 0, sizeof(u_info));
len = sizeof(struct sockaddr_nl);
//接收消息
Int ret = recvfrom(skfd,&u_info,sizeof(user_msg_info),0,(struct sockaddr *)&daddr,&len);
ret大于0 ,表示接收的实际长度
ret小于0 ,表示接收失败,错误原因通过errno获取
用close函数关闭打开的netlink socket
close(skfd);
通过netlink_kernel_create创建一个netlink套接字,同时,注册一个回调函数,用于接收处理用户空间的消息。
struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
参数说明:
l net:是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用 init_net这个全局变量。
l unit:表示netlink协议类型,如NETLINK_TEST、NETLINK_SELINUX。
l cfg:存放的是netlink内核配置参数,参数都是可选的,不需要的可以不赋值
struct netlink_kernel_cfg {
unsigned int groups; //用户指定关联的多播组
unsigned int flags; //指定socket标志
void (*input)(struct sk_buff *skb); //指定接收处理回调函数
struct mutex *cb_mutex; //互斥量保护回调函数
int (*bind)(struct net *net, int group); //用于绑定到socket
void (*unbind)(struct net *net, int group); //用于解绑socket
bool (*compare)(struct net *net, struct sock *sk); //用于比较socket
};
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)
参数说明:
l ssk:为函数 netlink_kernel_create()返回的socket。
l skb:存放消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,宏NETLINK_CB(skb)就用于方便设置该控制块。
l pid:为接收此消息进程的pid,即目标地址,如果目标为组或内核,它设置为 0。
l nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,内核设置超时时间为0;而如果为0,该函数在没有接收缓存可利用定时睡眠,内核设置了大于0的固定超时时间。
返回值:
1、大于0表示发送成功,返回值是发送数据实际长度(包括消息头)。
2、小于0 表示发送失败,根据errno可以获取错误原因,如果是异步的需要对EAGAIN进行重发处理。
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)
前面的三个参数与 netlink_unicast相同
l 参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。
l 参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。
接收消息应该是由内核接收到消息后,回调netlink_kernel_create 创建时注册的接收函数
void (*input)(struct sk_buff *skb); //指定接收处理回调函数