1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Linux驱动-Netlink通信

Linux驱动-Netlink通信

时间:2020-02-21 07:05:25

相关推荐

Linux驱动-Netlink通信

什么是Netlink通信机制?

Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。Netlink 是一种特殊的 socket,它是 Linux 所特有的。

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

Netlink通信机制有哪些特点?

使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。

//netlink收发是以消息为单位的,每次收发可以包含一个或多个消息(msg)------------------------------------------------------------------------| 单次sendto或者recvfrom 数据部分 | ------------------------------------------------------------------------| msg0 |msg1| msgn |------------------------------------------------------------------------| nlmsghdr | data(携带的数据) | nlmsghdr | data(携带的数据) | ....|------------------------------------------------------------------------

netlink常用的数据结构以及接口

用户态常用结构体:

struct sockaddr_nl 结构体:

/*套接字结构体*/struct sockaddr_nl {__kernel_sa_family_t nl_family; /* AF_NETLINK (跟AF_INET对应)*/unsigned short nl_pad;/* zero */__u32 nl_pid;/* port ID (通信端口号)*/__u32 nl_groups; /* multicast groups mask */};

struct nlmsghdr结构体:

/*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 */};nlmsg_len :指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小nlmsg_type :用于应用内部定义消息的类型,它对 netlink 内核实现是透明的,因此大部分情况下设置为 0/* Type values */#define NLMSG_NOOP0x1 /* Nothing.*/#define NLMSG_ERROR0x2 /* Error */#define NLMSG_DONE0x3 /* End of a dump */#define NLMSG_OVERRUN 0x4 /* Data lost */#define NLMSG_MIN_TYPE0x10 /* < 0x10: reserved control messages *//*NLMSG_NOOP:不执行任何动作,必须将该消息丢弃;NLMSG_ERROR:消息发生错误;NLMSG_DONE:标识分组消息的末尾;NLMSG_OVERRUN:缓冲区溢出,表示某些消息已经丢失。NLMSG_MIN_TYPEK:预留 */nlmsg_flags :用于设置消息标志/* Flags values */#define NLM_F_REQUEST 1 /* It is request message. */#define NLM_F_MULTI2 /* Multipart message, terminated by NLMSG_DONE */#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */#define NLM_F_ECHO8 /* Echo this request */#define NLM_F_DUMP_INTR16 /* Dump was inconsistent due to sequence change *//* Modifiers to GET request */#define NLM_F_ROOT 0x100 /* specify tree root */#define NLM_F_MATCH 0x200 /* return all matching */#define NLM_F_ATOMIC 0x400 /* atomic GET */#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)/* Modifiers to NEW request */#define NLM_F_REPLACE 0x100 /* Override existing */#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */#define NLM_F_CREATE 0x400 /* Create, if it does not exist */#define NLM_F_APPEND 0x800 /* Add to end of list */nlmsg_seq: 消息序列号,用以将消息排队,有些类似TCP协议中的序号(不完全一样),但是netlink的这个字段是可选的,不强制使用;nlmsg_pid:发送端口的ID号,对于内核来说该值就是0,对于用户进程来说就是其socket所绑定的ID号

用户态常用接口:

应用层使用接口是标准的 socket API,与UDP通信类似

创建套接字:

int socket(int domain, int type, int protocol)domain :使用netlink方式通信时配置为 AF_NETLINKtype :使用netlink方式通信时配置为 SOCK_RAWprotocol:自定义的通信协议

绑定套接字:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)addr :传参时要将转入的struct sockaddr_nl结构体指针变量强转为struct sockaddr *

发送数据:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen)

接收数据:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen)

内核常用结构体:

struct sock 结构体

套接字结构体

struct netlink_kernel_cfg 结构体

/* optional Netlink kernel configuration parameters */struct netlink_kernel_cfg {unsigned int groups;unsigned int flags;void (*input)(struct sk_buff skb); / *input 回调函数 */一struct mutex *cb_mutex;void (*bind)(int group);bool (*compare)(struct net *net, struct sock *sk);};

struct sk_buf 结构体

套接字缓存,作为网络数据包的存放地点,使得协议栈中每个层都可以对数据进行操作,从而实现了数据包自底向上的传递

struct sk_buff{struct sk_buff *next;struct sk_buff *prev;struct sock *sock ; //struct sock是socket在网络层的表示,其中存放了网络层的信息unsigned int len; //表示当前协议数据包的长度。它包括主缓冲区中的数据长度(data指针指向它)和分片中的数据长度。unsigned int data_len; //和len不同,data_len只计算分片中数据的长度__u16 mac_len ; //数路链路层的头长度__u16 hdr_len ; //writable header length of cloned skbunsigned int truesize ; //socket buffer(套接字缓存区的大小)atomic_t users ; //对当前的struct sk_buff结构体的引用次数;__u32 priority ; //这个struct sk_buff结构体的优先级sk_buff_data_t transport_header ; //传输层头部的偏移量sk_buff_data_t network_header ; //网络层头部的偏移量sk_buff_data_t mac_header ; //数据链路层头部的偏移量char *data ; //socket buffer中数据的起始位置;sk_buff_data_t tail ; //socket buffer中数据的结束位置;char *head ; //socket buffer缓存区的起始位置;sk_buffer_data_t end ; //socket buffer缓存区的终止位置;struct net_device *dev; //将要发送struct sk_buff结构体的网络设备或struct sk_buff的接收网络设备int iif; //网络设备的接口索引号; struct timeval tstamp ; //用于存放接受的数据包的到达时间;__u8 local_df : 1 , //allow local fragmentaion;cloned : 1 , // head may be cloned;__u8 pkt_type : 3 , //数据包的类型;fclone : 2, // struct sk_buff clone status }

内核常用接口:

netlink_kernel_create

netlink_kernel_create内核函数用于创建 内核socket用用户态通信

static inline struct sock *netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)net: net指向所在的网络命名空间, 一般默认传入的是&init_net(不需要定义); 定义在net_namespace.c(extern struct net init_net);unit:netlink协议类型,对应用户态创建套接字时的protocol参数,两者需保持一致cfg: cfg存放的是netlink内核配置参数

单播netlink_unicast()

int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);ssk: netlink socket skb: skb buff 指针portid: 通信的端口号,对应用态的端口号nonblock:表示该函数是否为非阻塞,如果为1(MSG_DONTWAIT),该函数将在没有接收缓存可利用时立即返回,而如果为0(MSG_WAITALL),该函数在没有接收缓存可利用 定时睡眠

多播netlink_broadcast()

int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,__u32 group, gfp_t allocation);ssk: 同上(对应netlink_kernel_create 返回值)、skb: 内核skb buffportid: 通信的端口号,对应用态的端口号group: 是所有目标多播组对应掩码的"OR"操作的合值。allocation: 指定内核内存分配方式,通常GFP_ATOMIC用于中断上下文,而GFP_KERNEL用于其他场合。这个参数的存在是因为该API可能需要分配一个或多个缓冲区来对多播消息进行clone

nlmsg_hdr()

获取netlink消息的头部指针

nlmsg_new()

分配一个新的netlink消息

struct sk_buff *nlmsg_new(size_t payload, gfp_t flags)payload : 分配的大小flags:进程上下文,可以睡眠:GFP_KERNEL进程上下文,不可以睡眠:GFP_ATOMIC中断处理程序:GFP_ATOMIC软中断:GFP_ATOMICTasklet:GFP_ATOMIC用于DMA的内存,可以睡眠:GFP_DMA | GFP_KERNEL用于DMA的内存,不可以睡眠:GFP_DMA |GFP_ATOMIC

nlmsg_put()

向skb缓冲区中获取消息头空间并且初始化netlink消息头,入参中的第5个参数为netlink消息头的总空间

struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,int type, int payload, int flags)portid:与 netlink消息头 中的 nlmsg_pid 对应seq:与 netlink消息头 中的 nlmsg_seq 对应type:与 netlink消息头 中的 nlmsg_type 对应payload:与 netlink消息头 中的 nlmsg_len 对应flags:与 netlink消息头 中的 nlmsg_flags 对应

用户态与内核对数据处理的宏函数:

1、NLMSG_LENGTH(len)根据消息数据部分长度,返回消息长度,即头长度+len,但是这个长度不是对齐的(消息头和整个消息都是4字节对其的;len:消息数据部分长度2、NLMSG_SPACE(len) 根据消息数据部分长度,返回消息实际占用空间,也就是在NLMSG_LENGTH(len)基础上进行4字节对齐。len:消息数据部分长度-----------------------------------------------------------| nlmsghdr | data(携带的数据) |-----------------------------------------------------------| |<----- len --->| || <------- NLMSG_LENGTH(len) ---->|<--4字节对齐-->|| <------- NLMSG_SPACE(len) ---->|3、NLMSG_DATA(nlh)根据消息内存地址,返回数据部分地址.nlh:消息头地址,消息头地址和消息地址是一样。 -----------------------------------------------------| nlmsghdr | data(携带的数据) |-----------------------------------------------------| || |--->这里就是nlh地址 ||---> 这里就是返回值,也就是数据部分地址(与TCP/IP协议很像吧, 先是数据头再是数据)4、NLMSG_NEXT(nlh,len)根据当前消息地址,返回下一个消息地址nlh: 当前消息地址len:剩余消息部分长度,比如当前recvfrom收到100字节数据,包含4个长度为25字节的消息,存储在buf中:5、NLMSG_OK(nlh,len)检查nlh地址是否是一条完整的消息nlh: 通过recvfrom收到数据的缓冲区地址len: 收到数据的长度6、NLMSG_PAYLOAD(nlh,len)消息数据部分剩余长度nlh: 消息的首地址len: 数据部分已经使用的长度|<------------------100字节-------------------------->| -----------------------------------------------------| nlmsghdr | data(携带的数据) |-----------------------------------------------------| |<--------len------|<----+---->||| ||--> nlh +---> 这里就是返回值的含义

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。