理论:
http://blog.youkuaiyun.com/unbutun/article/details/3394061
进一步深入:
http://edsionte.com/techblog/archives/4134
http://edsionte.com/techblog/archives/4140
http://edsionte.com/techblog/archives/4134
实践:
http://bbs.chinaunix.net/thread-3766684-1-1.html
附录代码:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <string.h>
- #include <asm/types.h>
- #include <sys/socket.h>
- #include <linux/netlink.h>
- #include <linux/inet_diag.h>
- #include <netinet/tcp.h>
- #define NETLINK_TEST 18
- #define MAX_PAYLOAD 1024
- struct req {
- struct nlmsghdr nlh;
- char [MAX_PAYLOAD];
- };
- void eprint(int err_no, char *str, int line)
- {
- printf("Error %d in line %d:%s() with %s\n", err_no, line, str, strerror(errno));
- }
- int main()
- {
- int sock_fd;
- sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
- if (sock_fd < 0) {
- eprint(errno, "socket", __LINE__);
- return errno;
- }
- //将本地套接字与源地址绑定
- struct sockaddr_nl src_addr;
- memset(&src_addr, 0, sizeof(src_addr));
- src_addr.nl_family = AF_NETLINK;
- src_addr.nl_pid = getpid(); //nl_pid字段指明发送消息一方的pid
- src_addr.nl_groups = 0; //nl_groups表示多播组的掩码 这里我们并没有涉及多播,因此默认为0
- if (bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
- eprint(errno, "bind", __LINE__);
- return errno;
- }
- //绑定了套接字之后,用户进程最终发送的是msghdr结构的消息,因此必须对这个结构进行初始化,
- //而msghdr结构又与
- //初始化msghdr结构 sockaddr_nl,iovec和nlmsghdr三个结构相关,因此必须依次对这些数据结构进行初始化
- /*
- struct msghdr {
- void * msg_name; // Socket name
- int msg_namelen; //Length of name
- struct iovec * msg_iov; // Data blocks
- __kernel_size_t msg_iovlen; // Number of blocks
- void * msg_control; // Per protocol magic (eg BSD file descriptor passing)
- __kernel_size_t msg_controllen; // Length of cmsg list
- unsigned msg_flags;
- };*/
- /*
- 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 dest_addr;
- memset(&dest_addr, 0, sizeof(dest_addr));
- dest_addr.nl_family = AF_NETLINK;
- dest_addr.nl_pid = 0; //即nl_pid必须为0,表示接收方为内核。
- dest_addr.nl_groups = 0;
- //req类型的数据报进行初始化,即依次对其封装的两个数据结构初始化:
- struct req r;//自定义协议数据结构 使用netlink进行用户进程和内核的数据交互时 用到
- r.nlh.nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); /*这里的nlmsg_len为为sizeof(structnlmsghdr)+MAX_PAYLOAD的总和。宏NLMSG_SPACE会自动将两者的长度相加
- #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
- #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) */
- r.nlh.nlmsg_pid = getpid();
- r.nlh.nlmsg_flags = 0;
- memset(r.buf, 0, MAX_PAYLOAD);
- strcpy(NLMSG_DATA(&(r.nlh)), "hello, I am edsionte!"); //#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
- //接下来对缓冲区向量iov进行初始化,让iov_base字段指向数据报结构,而iov_len为数据报长度。
- struct iovec iov;
- iov.iov_base = (void *)&r;
- iov.iov_len = sizeof(r);
- //一切就绪后,将目的套接字地址与当前要发送的消息msg绑定,即将目的套接字地址复制给msg_name。再将要发送的数据iov与msg_iov绑定,如果一次///性要发送多个数据包,则创建一个iovec类型的数组。
- struct msghdr msg;
- msg.msg_name = (void *)&dest_addr;
- msg.msg_namelen = sizeof(dest_addr);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- //.向内核发送消息
- if (sendmsg(sock_fd, &msg, 0) < 0) {
- eprint(errno, "sendmsg", __LINE__);
- return errno;
- }
- //接收内核发来的消息
- memset(&r.nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
- if (recvmsg(sock_fd, &msg, 0) < 0) {
- eprint(errno, "recvmsg", __LINE__);
- return errno;
- }
- printf("Received message payload:%s\n", (char *)NLMSG_DATA(r.nlh));
- close(sock_fd);
- }