精华内容
下载资源
问答
  • AF_NETLINK

    2018-11-30 15:17:00
    读procd时,hotplug相关。 从网上资料来看,大概和硬件输入有关? 资料:http://www.cnblogs.com/sammei/p/4119659.html 转载于:https://www.cnblogs.com/laymond/p/10044357.html...

    读procd时,hotplug相关。

    从网上资料来看,大概和硬件输入有关?

     

    资料:http://www.cnblogs.com/sammei/p/4119659.html

    转载于:https://www.cnblogs.com/laymond/p/10044357.html

    展开全文
  • AF_NetLink结构体及例程

    千次阅读 2017-08-13 22:18:18
    AF_NetLink结构体 目录 一、AF_NETLINK结构体基础... 1 二、 netlink 内核数据结构、常用宏及函数... 4 netlink消息类型:... 4 netlink常用宏:... 4 三、例二:读取内核路由信息... 5 四、例三:自定义通信协议......

     

    一、AF_NETLINK结构体基础

    我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构。在我们的虚拟机上发送icmp回显请求包,ping另一台主机172.16.48.1。我们使用系统调用sendto发送这个icmp包。

        ssize_t sendto(int s, const void *buf, size_t len, int flags,

                           const struct sockaddr *to, socklen_t tolen);

    系统调用sendto最终调用内核函数 asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flags, struct sockaddr __user *addr, int addr_len)

    sys_sendto构建一个结构体struct msghdr,用于接收来自应用层的数据包 ,下面是结构体struct msghdr的定义:

            struct msghdr {

               void           *msg_name;// 存数据包的目的地址,网络包指向sockaddr_in

                                                      //向内核发数据时,指向sockaddr_nl

               int            msg_namelen;// 地址长度

               struct iovec    *msg_iov;

               __kernel_size_t msg_iovlen;

               void           *msg_control;

               __kernel_size_t msg_controllen;

               unsigned        msg_flags;

            };

        这个结构体的内容可以分为四组:

        第一组是msg_namemsg_namelen,记录这个消息的名字,其实就是数据包的目的地址 。msg_name是指向一个结构体struct sockaddr的指针。长度为16:

            structsockaddr{

               sa_family_t sa_family;

               char        sa_addr[14];

            }

    所以,msg_namelen的长度为16。需要注意的是,结构体struct sockaddr只在进行参数传递时使用,无论是在用户态还是在内核态,我们都把其强制转化为结构体struct sockaddr_in:

            strcutsockaddr_in{

               sa_family_t         sin_family;

               unsigned short int sin_port;

               struct in_addr      sin_addr;

               unsigned char       __pad[__SOCK_SIZE__ -sizeof(short int) -

                                   sizeof(unsigned short int) - sizeof(struct in_addr)];

            };

            struct in_addr{

                __u32s_addr;

            }

    __SOCK_SIZE__的值为16,所以,struct sockaddr中真正有用的数据只有8bytes。在我们的ping例子中,传入到内核的msghdr结构中:

    msg.msg_name = { sa_family_t = MY_AF_INET, sin_port = 0, sin_addr.s_addr= 172.16.48.1 }

        msg_msg_namelen = 16。

    请求回显icmp包没有目的端地址的端口号。

    第二组是msg_iov和msg_iovlen,记录这个消息的内容。msg_iov是一个指向结构体struct iovec的指针,实际上,确切地说,应该是一个结构体strcut iovec的数组 。下面是该结构体的定义:

        struct iovec{

            void__user     *iov_base;

            __kernel_size_t iov_len;

        };

    iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff)。在我们的ping程序的实例中:

        msg.msg_iov = { struct iovec = { iov_base= { icmp头+填充字符'E' }, iov_len = 40 } }

        msg.msg_len = 1

    第三组是msg_control和msg_controllen,它们可被用于发送任何的控制信息,在我们的例子中,没有控制信息要发送。暂时略过。

    第四组是msg_flags。其值即为传入的参数flags。raw协议不支持MSG_ OOB 向标志,即带外数据。 向内核发送msg 时使用msghdrnetlink socket使用自己的消息头nlmsghdr和自己的消息地址sockaddr_nl

    struct sockaddr_nl
    {
    sa_family_t    nl_family;
    unsigned short nl_pad;
    __u32          nl_pid;
    __u32          nl_groups;
    };
    struct nlmsghdr
    {
    __u32 nlmsg_len;   /* Length of message */
    __u16 nlmsg_type; /* Message type*/
    __u16 nlmsg_flags; /* Additional flags */
    __u32 nlmsg_seq;   /* Sequence number */
    __u32 nlmsg_pid;   /* Sending process PID */
    };

    其中,nlmsg_flags:消息标记,它们用以表示消息的类型;

    nlmsg_seq:消息序列号,用以将消息排队,有些类似TCP协议中的序号(不完全一样),但是netlink的这个字段是可选的,不强制使用;

    nlmsg_pid:发送端口的ID号,对于内核来说该值就是0对于用户进程来说就是其socket所绑定的ID

     

    过程如下:

    struct msghdr msg; 
    memset(&msg, 0,sizeof(msg));
    msg.msg_name =(void *)&(nladdr);  //绑定目的地址
    msg.msg_namelen = sizeof(nladdr);
    {
    /*初始化一个strcut nlmsghdr结构存,nlmsghdr为netlink socket自己的消息头部,并使iov->iov_base指向在这个结构*/
    char buffer[] = "An example message";
    struct nlmsghdr nlhdr;
    nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
    strcpy(NLMSG_DATA (nlhdr),buffer);//将数据存放在消息头指向的数据地址
    nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));
    nlhdr->nlmsg_pid = getpid(); /* self pid */
    nlhdr->nlmsg_flags = 0;
    iov.iov_base = (void *)nlhdr;
    iov.iov_len = nlh->nlmsg_len;
    }
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    fd=socket(AF_NETLINK, SOCK_RAW, netlink_type);
    sendmsg(fd,&msg,0)

    二、 netlink 内核数据结构、常用宏及函数

    netlink消息类型:

    #define NETLINK_ROUTE       0  /* Routing/device hook             */
    #define NETLINK_UNUSED      1   /* Unused number                */
    #define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */
    #define NETLINK_FIREWALL    3   /* Unused number, formerly ip_queue     */
    #define NETLINK_SOCK_DIAG   4   /* socket monitoring                */
    #define NETLINK_NFLOG       5   /* netfilter/iptables ULOG */
    #define NETLINK_XFRM        6   /* ipsec */
    #define NETLINK_SELINUX     7   /* SELinux event notifications */
    #define NETLINK_ISCSI       8   /* Open-iSCSI */
    #define NETLINK_AUDIT       9   /* auditing */
    #define NETLINK_FIB_LOOKUP  10  
    #define NETLINK_CONNECTOR   11
    #define NETLINK_NETFILTER   12  /* netfilter subsystem */
    #define NETLINK_IP6_FW      13
    #define NETLINK_DNRTMSG     14  /* DECnet routing messages */
    #define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */
    #define NETLINK_GENERIC     16
    /* leave room for NETLINK_DM (DM Events) */
    #define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */
    #define NETLINK_ECRYPTFS    19
    #define NETLINK_RDMA        20
    #define NETLINK_CRYPTO      21  /* Crypto layer */
    #define NETLINK_INET_DIAG   NETLINK_SOCK_DIAG
    #define MAX_LINKS 32 

    netlink常用宏

    #define NLMSG_ALIGNTO   4U
     /* 宏NLMSG_ALIGN(len)用于得到不小于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_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 */
    #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
    /* 宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 */
    #define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
    /* 宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址, 同时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))
    /* NLMSG_PAYLOAD(nlh,len) 用于返回payload的长度*/
    #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
     
    Linux下虽然也有AF_ROUTE族套接字,但是这个定义只是个别名,请看
    /usr/include/linux/socket.h, line 145:
    #define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */
    可见在Linux内核当中真正实现routing socket的是AF_NETLINK族套接字。AF_NETLINK族套接字像一个连接用户空间和内核的双工管道,通过它,用户进程可以修改内核运行参数、读取和设置路由信息、控制特定网卡的up/down状态等等,可以说是一个管理网络资源的绝佳途径。
     

    三、例二:读取内核路由信息

    上面提到的nlmsghdr,它只是一个信息头,后面可以接任意长的数据,上面的例子我们只是填充了一个字符串,实际上这里是针对某一需求所采用的特定数据结构。先来看nlmsghdr:
    struct nlmsghdr {
    _u32 nlmsg_len; /* Length of msg including header */
    _u32 nlmsg_type; /* 操作命令 */
    _u16 nlmsg_flags; /* various flags */
    _u32 nlmsg_seq; /* Sequence number */
    _u32 nlmsg_pid; /* 进程PID */
    };
    /* 紧跟着是实际要发送的数据,长度可以任意 */
    其中nlmsg_type决定这次要执行的操作,如查询当前路由表信息,所使用的就是RTM_GETROUTE。标准nlmsg_type包括:NLMSG_NOOP, NLMSG_DONE, NLMSG_ERROR等。根据采用的nlmsg_type不同,还要选取不同的数据结构来填充到nlmsghdr后面:
    操作                数据结构
    RTM_NEWLINK ifinfomsg
    RTM_DELLINK
    RTM_GETLINK
    RTM_NEWADDR ifaddrmsg
    RTM_DELADDR
    RTM_GETADDR
    RTM_NEWROUTE rtmsg
    RTM_DELROUTE
    RTM_GETROUTE
    RTM_NEWNEIGH ndmsg/nda_chcheinfo
    RTM_DELNEIGH
    RTM_GETNEIGH
    RTM_NEWRULE rtmsg
    RTM_DELRULE
    RTM_GETRULE
    RTM_NEWQDISC tcmsg
    RTM_DELQDISC
    RTM_GETQDISC
    RTM_NEWTCLASS tcmsg
    RTM_DELTCLASS
    RTM_GETTCLASS
    RTM_NEWTFILTER tcmsg
    RTM_DELTFILTER

    由于情形众多,这里以从内核读取IPV4路由表信息为例。从上面表看,nlmsg_type一定使用RTM_xxxROUTE操作,对应的数据结构是rtmsg。既然是读取,那么应该是RTM_GETROUTE了。

    structrtmsg {
    unsigned char rtm_family; /* 路由表地址族 */
    unsigned char rtm_dst_len; /* 目的长度 */
    unsigned char rtm_src_len; /* 源长度 */ (2.4.10头文件的注释标反了?)
    unsigned char rtm_tos; /* TOS */
    
    unsigned char rtm_table; /* 路由表选取 */
    unsigned char rtm_protocol; /* 路由协议 */
    unsigned char rtm_scope;
    unsigned char rtm_type;
    
    unsigned int rtm_flags;
    };

    对于RTM_GETROUTE操作来说,我们只需指定两个成员:rtm_family:AF_INET, rtm_table: RT_TABLE_MAIN。其他成员都初始化为0即可。将这个结构体跟nlmsghdr结合起来,得到我们自己的新结构体:
    struct {
    struct nlmsghdr nl;
    struct rtmsg rt;
    }req;

    填充好rt结构之后,还要调整nl结构相应成员的值。Linux定义了多个宏来处理nlmsghdr成员的值,我们这里用到的是NLMSG_LENGTH(size_tlen);
    req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    这将计算nlmsghdr长度与rtmsg长度的和(其中包括了将rtmsg进行4字节边界对齐的调整),并存储到nlmsghdr的nlmsg_len成员中。

    接下来要做的就是将这个新结构体req放到sendmsg()函数的msghdr.iov处,并调用函数:sendmsg(sockfd, &msg, 0);

    接下来的操作是recv()操作,从该套接字读取内核返回的数据,并进行分析处理。
    recv(sockfd, p, sizeof(buf) - nll, 0);
    其中p是指向一个缓冲区buf的指针,nll是已接收到的nlmsghdr数据的长度。
    由于内核返回信息是一个字节流,需要调用者检查消息结尾。这是通过检查返回的nlmsghdr的nlmsg_type是否等于NLMSG_DONE来完成的。返回的数据格式如下:
    -----------------------------------------------------------
    | nlmsghdr+route entry | nlmsghdr+route entry | ......... 
    -----------------------------------------------------------
    | 解出routeentry
    V
    -----------------------------------------------------------
    | dst_addr | gateway | Output interface| ...............
    -----------------------------------------------------------
    可以看出,返回消息由多个(nlmsghdr+ route entry)组成,当某个nlmsghdr的nlmsg_type == NLMSG_DONE时就表示信息输出已经完毕。而每一个routeentry由多个rtattr结构体组成,每个结构体表示该路由项的某个属性,如目的地址,网关等等。根据这个示意图我们就能够轻松解析需要的数据了。

     

    四、例三:自定义通信协议

    本节通过详解一个简单的实例程序来说明用户进程通过netlink机制如何主动向内核发起会话。在该程序中,用户进程向内核发送一段字符串,内核接收到后再将该字符串后再重新发给用户进程。用户态程序netlink是一种特殊的套接字,在用户态除了一些参数的传递对其使用的方法与一般套接字无较大差异。

    1.宏与数据结构的定义

    在使用netlink进行用户进程和内核的数据交互时,最重要的是定义好通信协议。协议一词直白的说就是用户进程和内核应该以什么样的形式发送数据,以什么样的形式接收数据。而这个“形式”通常对应程序中的一个特定数据结构。

    本文所演示的程序并没有使用netlink已有的通信协议,因此我们自定义一种协议类型NETLINK_TEST

    1 #defineNETLINK_TEST 18
    2 #define MAX_PAYLOAD 1024
    3 
    4 struct req {
    5     struct nlmsghdr nlh;
    6     char buf[MAX_PAYLOAD];
    7 };
    除此之外,我们应该再自定义一个数据报类型req,该结构包含了netlink数据包头结构的变量nlh和一个MAX_PAYLOAD大小的缓冲区。这里我们为了演示简单,并没有像上文中描述的那样将一个特定数据结构与nlmsghdr封装起来。

    2.创建netlink套接字

    要使用netlink,必须先创建一个netlink套接字。创建方法同样采用socket(),只是这里需要注意传递的参数:
    1 int sock_fd;
    2 sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
    3 if (sock_fd < 0) {
    4     eprint(errno, "socket", __LINE__);
    5     return errno;
    6 }

    第一个参数必须指定为PF_NETLINKAF_NETLINK。第二个参数必须指定为SOCK_RAWSOCK_DGRAM,因为netlink提供的是一种无连接的数据报服务。第三个参数则指定具体的协议类型,我们这里使用自定义的协议类型NETLINK_TEST。另外,eprint()是一个自定义的出错处理函数,实现如下:
    1 void eprint(int err_no, char *str, int line)
    2 {
    3     printf("Error %d in line %d:%s() with %s\n",err_no, line, str, strerror(errno));
    4 }

    3.将本地套接字与源地址绑定

    将本地的套接字与源地址进行绑定通过bind()完成。在绑定之前,需要将源地址进行初始化,nl_pid字段指明发送消息一方的pidnl_groups表示多播组的掩码,这里我们并没有涉及多播,因此默认为0

    1 struct sockaddr_nl src_addr;
    2 memset(&src_addr, 0, sizeof(src_addr));
    3 src_addr.nl_family = AF_NETLINK;
    4 src_addr.nl_pid = getpid();
    5 src_addr.nl_groups = 0;
    6 
    7 if (bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0){
    8     eprint(errno, "bind", __LINE__);
    9     return errno;
    10 }

    4.初始化msghdr结构

    用户进程最终发送的是msghdr结构的消息,因此必须对这个结构进行初始化。而此结构又与sockaddr_nliovecnlmsghdr三个结构相关,因此必须依次对这些数据结构进行初始化。首先初始化目的套接字的地址结构,该结构与源套接字地址结构初始化的方法稍有不同,即nl_pid必须为0,表示接收方为内核。

    1 struct sockaddr_nl dest_addr;
    2 memset(&dest_addr, 0, sizeof(dest_addr));
    3 dest_addr.nl_family = AF_NETLINK;
    4 dest_addr.nl_pid = 0;
    5 dest_addr.nl_groups = 0;
    接下来对req类型的数据报进行初始化,即依次对其封装的两个数据结构初始化:
    1 struct req r;
    2 r.nlh.nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    3 r.nlh.nlmsg_pid = getpid();
    4 r.nlh.nlmsg_flags = 0;
    5 memset(r.buf, 0, MAX_PAYLOAD);
    6 strcpy(NLMSG_DATA(&(r.nlh)), "hello, I am edsionte!");

    这里的nlmsg_len为为sizeof(struct nlmsghdr)+MAX_PAYLOAD的总和。宏NLMSG_SPACE会自动将两者的长度相加。接下来对缓冲区向量iov进行初始化,让iov_base字段指向数据报结构,而iov_len为数据报长度。
    1 struct iovec iov;
    2 iov.iov_base = (void *)&r;
    3 iov.iov_len = sizeof(r);

    一切就绪后,将目的套接字地址与当前要发送的消息msg绑定,即将目的套接字地址复制给msg_name。再将要发送的数据iovmsg_iov绑定,如果一次性要发送多个数据包,则创建一个iovec类型的数组。

    1 struct msghdr msg;
    2 msg.msg_name = (void *)&dest_addr;
    3 msg.msg_namelen = sizeof(dest_addr);
    4 msg.msg_iov = &iov;5msg.msg_iovlen = 1;
    5.
    向内核发送消息发送消息则很简单,通过sendmsg函数即可完成,前提是正确的创建netlink套接字和要发送的消息。
    1 if (sendmsg(sock_fd, &msg, 0) < 0) {
    2     eprint(errno, "sendmsg", __LINE__);
    3     return errno;
    4 }
    6.

    接受内核发来的消息如果用户进程需要接收内核发送的消息,则需要通过recvmsg完成,只不过在接收之前需要将数据报r重新初始化,因为发送和接收时传递的数据结构可能是不同的。为了简单演示netlink的用法,本文所述的用户进程发送的是一段字符串,这一点从数据报结构req的定义可以看出。而内核向用户进程发送的也是一段字符串,具体情况下面将会具体说明。
    1 memset(&r.nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    2 if (recvmsg(sock_fd, &msg, 0) < 0) {
    3         eprint(errno,"recvmsg", __LINE__);
    4         return errno;
    5 }
    6
    7 printf("Received message payload:%s\n", (char*)NLMSG_DATA(&r.nlh));
    8 close(sock_fd);
    接收完毕后,通过专门的宏NLMSG_DATA对数据报进行操作。 netlink对数据报的的访问和操作都是通过一系列标准的宏NLMSG_XXX来完成的,具体的说明可以通过man netlink查看。这里的NLMSG_DATA传递进去的是nlh,但它获取的是紧邻nlh的真正数据。本程序中传递的是字符串,所以取数据时候用char *强制类型转换,如果传递的是其他数据结构,则相应转换数据类型即可。

    内核模块netlink既然是一种用户态和内核态之间的双向通信机制,那么除了编写用户程序还要编写内核模块,也就是说用户进程和内核模块之间对数据的处理要彼此对应起来。

    1.内核模块加载和卸载函数内核模块加载函数主要通过netlink_kernel_create函数申请服务器端的套接字nl_sk,内核中对套接字表示为sock结构。另外,在创建套接字时还需要传递和用户进程相同的netlink协议类型NETLINK_TEST。创建套接字函数的第一个参数默认为init_net,第三个参数为多播时使用,我们这里不使用多播因此默认值为0nl_data_handler是一个钩子函数,每当内核接收到一个消息时,这个钩子函数就被回调对用户数据进行处理。

    1 #define NETLINK_TEST 17
    2 struct sock *nl_sk = NULL;
    3 static int __init hello_init(void)
    4 {
    5     printk("hello_init is starting..\n");
    6     nl_sk = netlink_kernel_create(&init_net,NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE);
    7     if (nl_sk == 0)
    8     {
    9         printk("can not createnetlink socket.\n");
    10         return -1;
    11     }
    12     return 0;
    13 }
    内核模块卸载函数所做的工作与加载函数相反,通过sock_release函数释放一开始申请的套接字。
    1 static void __exit hello_exit(void)
    2 {
    3     sock_release(nl_sk->sk_socket);
    4     printk("hello_exit is leaving..\n");
    5 }

    2.钩子函数的实现
    在内核创建netlink套接字时,必须绑定一个钩子函数,该钩子函数原型为:
    1 void (*input)(struct sk_buff *skb);
    钩子函数的实现主要是先接收用户进程发送的消息,接收以后内核再发送一条消息到用户进程。在钩子函数中,先通过skb_get函数对套接字缓冲区增加一次引用值,再通过nlmsg_hdr函数获取netlink消息头指针nlh。接着使用NLMSG_DATA宏获取用户进程发送过来的数据str。除此之外,再打印发送者的pid

    1 void nl_data_handler(struct sk_buff *__skb)
    2 {
    3     struct sk_buff *skb;
    4     struct nlmsghdr *nlh;
    5     u32 pid;
    6     int rc;
    7     char str[100];
    8     int len = NLMSG_SPACE(MAX_PAYLOAD);
    9
    10     printk("read data..\n");
    11     skb = skb_get(__skb);
    12
    13     if (skb->len >= NLMSG_SPACE(0)) {
    14         nlh = nlmsg_hdr(skb);
    15         printk("Recv:%s\n", (char *)NLMSG_DATA(nlh));
    16         memcpy(str, NLMSG_DATA(nlh),sizeof(str));
    17         pid = nlh->nlmsg_pid;
    18         printk("pid is%d\n", pid);
    19         kfree_skb(skb);

    接下来重新申请一个套接字缓冲区,为内核发送消息到用户进程做准备,nlmsg_put函数将填充netlink数据报头。接下来将用户进程发送的字符串复制到nlh紧邻的数据缓冲区中,等待内核发送。netlink_unicast函数将以非阻塞的方式发送数据包到用户进程,pid具体指明了接收消息的进程。
    1         skb = alloc_skb(len,GFP_ATOMIC);
    2         if (!skb){
    3             printk(KERN_ERR"net_link: allocate failed.\n");
    4             return;
    5         }
    6         nlh = nlmsg_put(skb, 0, 0, 0,MAX_PAYLOAD, 0);
    7         NETLINK_CB(skb).pid = 0;
    8
    9         memcpy(NLMSG_DATA(nlh), str,sizeof(str));
    10         printk("net_link: goingto send.\n");
    11         rc = netlink_unicast(nl_sk,skb, pid, MSG_DONTWAIT);
    12         if (rc < 0) {
    13             printk(KERN_ERR"net_link: can not unicast skb (%d)\n", rc);
    14         }
    15         printk("net_link: sendis ok.\n");
    16     }
    17 }

    这样就完成了内核模块的编写,它与用户进程通信共同完成数据交互。

    展开全文
  • <div><p>Currently the <code>AF_NETLINK</code> constant is not available for Android, but ;/android-4.4.y/include/linux/socket.h#177">it has the same value as on Linux</a>. To fix it, this patch moves ...
  • 一个命令行工具(示例),显示来自内核的AF_NETLINK数据 一个非常简单的源代码,显示了如何完成此工作。 我在实现Firefox功能来检测网络何时更改时使用了此功能: 麻省理工学院许可的代码。 请参阅源代码的标题。 ...
  • <p>May 4 18:04:26 DELL-Tower ksnip_ksnip.desktop[34989]: message repeated 3 times: [ Could not create AF_NETLINK socket (Operation not permitted)] May 4 18:04:31 DELL-Tower ksnip_ksnip.desktop[155110]...
  • About AF_NETLINK in Linux Socket

    千次阅读 2017-08-13 22:14:16
    About AF_NETLINK in Linux Socket 由于开发和维护内核的复杂性,只把最为关键同时对性能要求最高的代码放进内核中。其他的诸如GUI,管理和控制代码,通常放在用户空间运行。这种将实现分离在内核和用户空间的思想在...

    About AF_NETLINK in Linux Socket

    由于开发和维护内核的复杂性,只把最为关键同时对性能要求最高的代码放进内核中。其他的诸如GUI,管理和控制代码,通常放在用户空间运行。这种将实现分离在内核和用户空间的思想在Linux中非常常见。现在的问题是内核代码和用户代码如何交互通信。

    答案是内核空间和用户空间存在的各种IPC方法,例如系统调用、ioctl、proc文件系统和netlink socket。这篇文章讨论netlink socket及其作为一种网络特征IPC的优势。

    一、什么是Netlink通信机制

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

    Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在Linux内核中使用netlink 进行应用与内核通信的应用很多;以下是netlink socket当前支持的特征和他们的协议类型的子集:

    1.   NETLINK_ROUTE:用户空间路由damon,如BGP,OSPF,RIP和内核包转发模块的通信信道。用户空间路由damon通过此种netlink协议类型更新内核路由表

    2.   NETLINK_FIREWALL:接收IPv4防火墙代码发送的包

    3.   NETLINK_NFLOG:用户空间iptable管理工具和内核空间Netfilter模块的通信信道

    4.   NETLINK_ARPD:用户空间管理arp表

    另外,netfilter子系统(NETLINK_NETFILTER),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT),通用 netlink(NETLINK_GENERIC)等。它提供了一种全复用的通信链路,和TCP/IP使用的地址族AF_INET相比,Netlink socket使用地址族AF_NETLINK,每个的netlink socket特征定义协议类型在内核头文件中include/linux/netlink.h。

    Netlink的特点

    Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核API 来使用netlink。Netlink相对于系统调用ioctl 以及 proc文件系统而言具有以下优点:
        1、netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_TEST20 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换);
        2、netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息;
        3、使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖;
        4、netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该netlink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性;
        5、内核可以使用 netlink 首先发起会话

    为什么以上的特征使用netlink而不是系统调用,ioctl或者proc文件系统来完成通信?为新特性添加系统调用,ioctl和proc文件系统相对而言是一项比较复杂的工作,我们冒着污染内核和损害系统稳定性的风险。netlink socket相对简单:只有一个常量,协议类型,需要加入到netlink.h中。然后,内核模块和用户程序可以通过socket类型的API进行通信。

    和其他socket API一样,Netlink是异步的,它提供了一个socket队列来平滑突发的信息。发送一个netlink消息的系统调用将消息排列到接受者的netlink队列中然后调用接收者的接收处理函数。接收者,在接收处理函数的上下文中,可以决定是否立即处理该消息还是等待在另一个上下文中处理。而系统调用需要同步处理。因此,如果我们使用系统调用传递一条消息到内核,而且处理该条信息需要很长时间,那么内核调度粒度可能会受影响。

    在内核中实现的系统调用代码在编译时被静态的链接到内核中,因此在一个可以动态加载的模块中包括系统调用代码是不合适的。在netlink socket中,内核中的netlink核心和在一个可加载的模块中没有编译时的相互依赖。

    netlink socket支持多播,这也是其与其他交互手段相比较的优势之一。一个进程可以将一条消息广播到一个netlink组地址。任意多的进程可以监听那个组地址。这提供了一种从内核到用户空间进行事件分发接近完美的机制。

    从会话只能由用户空间应用发起的角度来看,系统调用和ioctl是单一的IPC。但是,如果一个内核模块有一个用户空间应用的紧急消息,没有一种直接的方法来实现这些功能。通常,应用需要阶段性的轮询内核来获取状态变化,尽管密集的轮询会有很大的开销。netlink通过允许内核初始化一个对话来优雅的解决这个问题。我们称之为netlink的复用特性。

    最后,netlink提供了bsd socket风格的API,而这些API是被软件开发社区所熟知。因此,培训费用相较较小。

    BSD路由socket的关系

    在BSD TCP/IP的栈实现中,有一种叫做路由套接字的特殊的socket。它有AF_ROUTE地址族,PF_ROUTE协议族和SOCK_RAW socket类型。在BSD中,路由套接字用于在内核路由表中添加和删除路由。

    在Linux中,路由套接字的实现通过netlink套接字的NETLINK_ROUTE协议类型来支持。netlink套接字提供了BSD路由套接字的功能的超集。

    二、 Netlink套接字API

    标准的套接字API,socket(),sendmsg(),recvmsg()和close(),可以被用户态程序使用。通过查询man手册页来看这些函数的具体定义。本文讨论在netlink上下文中为这些API选择参数。对于写过TCP/IP套接字程序的人对这些API都应该非常熟悉。

    创建一个套接字

           int socket(int domain,int type, int protocol)

    domain指代地址族,即AF_NETLINK;

    套接字类型为SOCK_RAW或SOCK_DGRAM,因为netlink是一个面向数据报的服务;

    protocol选择该套接字使用哪种netlink特征。以下是几种预定义的协议类型:NETLINK_ROUTE,NETLINK_FIREWALL,NETLINK_APRD,NETLINK_ROUTE6_FW。可以非常容易的添加自己的netlink协议。

    为每一个协议类型最多可以定义32个多播组。每一个多播组用一个bitmask来表示,1<<i(0<=i<= 31),这在一组进程和内核进程协同完成一项任务时非常有用。发送多播netlink消息可以减少系统调用的数量,同时减少用来维护多播组成员信息的负担。

    地址绑定bind()

    与TCP/IP套接字一样,netlink  bind()API用来将一个本地socket地址和一个打开的socket关联。一个netlink地址结构如下所示:

    struct sockaddr_nl { 
     sa_family_t nl_family; /* AF_NETLINK */ 
     unsigned short nl_pad; /* zero */ 
     __u32 nl_pid; /* process pid */ 
     __u32 nl_groups; /* mcast groups mask */ 
     } nladdr;

    当使用bind()调用的时候,字段 nl_pad 当前没有使用,因此要总是设置为 0,字段 nl_pid 为接收或发送消息的进程的 ID,把该字段设置为 0,告知内核去选择本地临时端口号【getsockname可以返回由内核赋予的本地端口号】,否则设置为处理消息的进程 ID,一般为本进程的进程 ID,这相当于 netlink socket 的本地地址。字段 nl_groups 用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组。

    nl_pid在这里被当做该netlink套接字的本地地址。程序负责找一个独一无二的32位整数在填充该域。一种常见的做法是:

    nl_pid = getpid();

    公式一使用进程ID号作为nl_pid的值,如果说该进程只需要一个netlink套接字,这是一个自然的选择。当一个进程中的不同线程需要同一个netlink协议多个netlink套接字。公式而可以用来产生nl_pid号:

     pthread_self() << 16 | getpid();

    通过这种方法,同一个进程中的不同线程可以有同一种netlink协议类型的netlink套接字。事实上,即使在同一个线程中,在可能使用同一种协议类型的多个套接字。开发者必须要更有创造力来产生一个唯一的nl_pid。

    如果应用想接受发送给特定多播组的netlink消息,所有感兴趣的多播组bit应该or在一起,并填充到nl_groups域。否则, nl_groups应该被显式至零,说明该应用只接受到该应用的消息,填充完上述域,使用如下方式进行绑定:

    bind(fd, (struct sockaddr*)&nladdr, sizeof(nladdr));

    发送netlink消息

    为了发送一条netlink消息到内核或者其他的用户空间进程,另外一个structsockaddr_nl nladdr需要作为目的地址,这和使用sendmsg()发送一个UDP包是一样的。如果该消息是发送至内核的,那么nl_pid和nl_groups都置为0.

    如果消息是发送给另一个进程的单播消息,nl_pid是另外一个进程的pid值而nl_groups为零

    如果消息是发送给一个或多个多播组的多播消息,所有的目的多播组必须bitmask必须or起来从而形成nl_groups域。当我们填充structmsghdr结构用于sendmsg时,使用如下:

    struct msghdr msg;
     
    msg.msg_name = (void *)&(nladdr);
    msg.msg_namelen = sizeof(nladdr);

    netlink套接字也需要它自己本身的消息头,这是为了给所有协议类型的netlink消息提供一个统一的平台。

    因为Linux内核netlink核心假设每个netlink消息中存在着以下的头,所有应用也必须在其发送的消息中提供这些头信息:

    struct nlmsghdr 
    {
      __u32 nlmsg_len; /* Length of message */ 
      __u16 nlmsg_type; /* Message type*/ 
      __u16 nlmsg_flags; /* Additional flags */
      __u32 nlmsg_seq; /* Sequence number */ 
      __u32 nlmsg_pid; /* Sending process PID */ 
    };

    nlmsg_len指整个netlink消息的长度,包括头信息,这也是netlink核心所必须的。nlmsg_type用于应用但是对于netlink核心而言其是透明的。nlmsg_flags用于给定附加的控制信息,其被netlink核心读取和更新。nlmsg_seq和nlmsg_pid,应用用来跟踪消息,这些对于netlink核心也是透明的。

    所以一个netlink消息由消息头和消息负载组成。一旦一个消息被加入,它就加入到一个通过nlh指针指向的缓冲区。我们也可以将消息发送到struct msghdr msg:

    struct iovec iov; 
    struct msghdr msg; 
    iov.iov_base = (void *)nlh; 
    iov.iov_len = nlh->nlmsg_len; 
    
    msg.msg_iov = &iov; 
    msg.msg_iovlen = 1;

    经过以上步骤,调用sendmsg()函数来发送netlink消息:

    sendmsg(fd, &msg, 0);

     

    接收netlink消息

    一个接收程序必须分配一个足够大的内存用于保存netlink消息头和消息负载。然后其填充structmsghdr msg,再使用标准的recvmsg()函数来接收netlink消息,假设缓存通过nlh指针指向:

    struct sockaddr_nl nladdr; 
    struct msghdr msg; 
    struct iovec iov; 
    
    iov.iov_base = (void *)nlh; 
    iov.iov_len = MAX_NL_MSG_LEN; 
    msg.msg_name = (void *)&(nladdr); 
    msg.msg_namelen = sizeof(nladdr); 
    
    msg.msg_iov = &iov; 
    msg.msg_iovlen = 1; 
    recvmsg(fd, &msg, 0);

    当消息被正确的接收之后,nlh应该指向刚刚接收到的netlink消息的头。nladdr应该包含接收消息的目的地址,其中包括了消息发送者的pid和多播组。同时,宏NLMSG_DATA(nlh),定义在netlink.h中,返回一个指向netlink消息负载的指针。调用close(fd)关闭fd描述符所标识的socket。

    三、 内核空间netlink API

    内核空间的netlinkAPI在内核中被netlink核心支持,即net/core/af_netlink.c。从内核角度看,这些API不同于用户空间的API。这些API可以被内核模块使用从而存取netlink套接字与用户空间程序通信。除非你使用现存的netlink套接字协议类型,否则你必须通过在netlink.h中定义一个常量来添加你自己的协议类型。例如,我们需要添加一个netlink协议类型用于测试,则在netlink.h中加入下面的语句:

     #define NETLINK_TEST  17

    之后,亦可以在linux内核中的任何地方引用添加的协议类型。

    在用户空间,我们使用socket()来创建一个netlink套接字,但是在内核空间,我们使用如下的API:

    struct sock *netlink_kernel_create(int unit,void (*input)(struct sock *sk, int len));

    参数unit,即为netlink协议类型,如NETLINK_TEST,回调函数会在消息到达netlink套接字时调用。当用户态程序发送一个NETLINK_TEST协议类型的消息给内核时,input()函数被调用。下面是一个实现回调函数的例子:

    void input (struct sock *sk, int len) 
    { 
      struct sk_buff *skb;
      struct nlmsghdr *nlh = NULL;
      u8 *payload = NULL;
    
      while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) 
      {
        /* process netlink message pointed by skb->data */
        nlh = (struct nlmsghdr *)skb->data;
        payload = NLMSG_DATA(nlh);
        /* process netlink message with header pointed by nlh and payload pointed by payload */ 
      }
    }

    input()函数在发送进程的sendmsg()系统调用上下文执行。如果在input中处理netlink消息非常快,那是没有问题的。如果处理netlink消息需要很长的时间,我们希望在input()外面处理消息来避免阻塞其他系统调用进入内核。事实上,我们可以使用一个指定的内核线程来来不断执行以下的步骤。使用skb=skb_recv_datagram(nl_sk),其中nl_sk是netlink_kernel_create()返回的netlink套接字。然后,处理由skb->data指向的netlink消息。

    以下的内核线程在没有netlink消息在nl_sk中时睡眠,在回调函数input中,我们只要唤醒睡眠的内核线程,如下所示:

    void input (struct sock *sk, int len) 
    {
      wake_up_interruptible(sk->sleep);
    }

    这是一个更具有扩展性的用户和内核通信的模型。其也提高了上下文交换的粒度。

    在内核中发送netlink消息

    真如在用户空间中一样,在发送一个netlink消息时需要设置源和目的netlink消息地址。假设socket缓存中包含了将要发送的netlink消息,本地地址可以通过以下方式设置:

    NETLINK_CB(skb).groups = local_groups;
    NETLINK_CB(skb).pid = 0;   
    /* from kernel */

    目的地址可以如下设置:

    NETLINK_CB(skb).dst_groups = dst_groups;
    NETLINK_CB(skb).dst_pid = dst_pid;

    这些信息不是存储在skb->data,而是存储在skb中的netlink控制块中。发送一个消息,使用:

    int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock);

    其中ssk是netlink_kernel_create返回的netlink套接字,skb->data指向netlink将要发送的消息而pid是接受该消息的用户程序id。nonblock用于标识在接收缓存不可用时,API是阻塞还是立即返回失败。

    你也可以发送一个多播消息。以下的API用于将消息传送到指定的进程,同时多播至指定的多播组。

    void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, int allocation);

    group是所有接收多播组的bitmask。allocation是内核内存分配的类型。通常,GFP_ATOMIC用于中断上下文而在其他情况下是用GFP_KERNEL.只是因为API可能需要分配一个或者多个套接字缓存来克隆多播消息。

    在内核中关闭一个netlink套接字

    给定了netlink_kernel_create()函数返回的struct sock *nl_sk,我们可以通过调用以下的API关闭netlink套接字。

    sock_release(nl_sk->socket);

    Netlink可靠性机制

    在基于netlink的通信中,有两种可能的情形会导致消息丢失:

    1、内存耗尽,没有足够多的内存分配给消息

    2、缓存复写,接收队列中没有空间存储消息,这在内核空间和用户空间之间通信时可能会发生缓存复写在以下情况很可能会发生:

    3、内核子系统以一个恒定的速度发送netlink消息,但是用户态监听者处理过慢

    4、用户存储消息的空间过小

    如果netlink传送消息失败,那么recvmsg()函数会返回No buffer spaceavailable(ENOBUFS)错误。那么,用户空间进程知道它丢失了信息,如果内核子系统支持dump操作,它可以重新同步来获取最新的消息。在dump操作中,netlink通过在每次调用recvmsg()函数时传输一个包的流控机制来防止接收队列的复写。改包消耗一个内存页,其包含了几个多部分netlink消息。图6中的序列图显示了在一个重新同步的过程中所使用的dump操作。

     

    另一方面,缓存复写不会发生在用户和内核空间的通信中,因为sendmsg()同步的将netlink消息发送到内核子系统。如果使用的是阻塞套接字,那么netlink在从用户空间到内核空间的通信时完全可靠的,因为内存分配可以等待,所以没有内存耗尽的可能。

    netlink也可以提供应答机制。所以如果用户空间进程发送了一个设置了NLM_F_ACK标志的请求,netlink会在netlink错误消息中报告给用户空间刚才请求操作的结果。

    从用户空间的角度来看,Netlink套接字在通用的BSD套接字接口之上实现。因此,netlink套接字编程与通用的TCP/IP编程类似。但是,我们也应该考虑几个与netlink相关的特殊问题:

    1、netlink套接字没有像其他协议一样对用户空间隐藏协议细节。事实上,netlink传递的是整个消息,包括netlink头和其他信息。因此,这就导致了数据处理函数与通用的TCP/IP套接字不同,所以用户态程序必须根据其格式解析和构建netlink信息。然而,没有标准的工具来完成这些工作,所以你必须实现自己的函数或者使用一些现成的库。

    2、来自netlink和内核子系统的错误不是通过recvmsg()函数返回的整数值来表现的。事实上,错误信息时被包装在netlink错误消息中的。唯一的例外是(ENOBUFS)错误,该错误不是包装在netlink消息中,因为报告该错误的原因就是我们没有足够的空间来缓存新的netlink消息。标准的通用套接字错误,如(EAGAIN),通常和其他轮询原语,例如poll()和select(),也是通过recvmsg()返回整数值。

     

     

    展开全文
  • #include <sys/socket.h> int socket(int domain, int type, int protocol); 函数里面domain 的值有如下 Name Purpose Man page AF_UNIX, AF_LOCAL ...

       #include <sys/socket.h>

           int socket(int domain, int type, int protocol); 函数里面domain 的值有如下

          Name                Purpose                          Man page
           AF_UNIX, AF_LOCAL   Local communication              unix(7)
           AF_INET             IPv4 Internet protocols          ip(7)
           AF_INET6            IPv6 Internet protocols          ipv6(7)
           AF_IPX              IPX - Novell protocols
           AF_NETLINK          Kernel user interface device     netlink(7)
           AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
           AF_AX25             Amateur radio AX.25 protocol
           AF_ATMPVC           Access to raw ATM PVCs
           AF_APPLETALK        AppleTalk                        ddp(7)
           AF_PACKET           Low level packet interface       packet(7)
           AF_ALG              Interface to kernel crypto API
     

           搜索一下网上AF_UNIX,PF_UNIX的区别,总是感觉没说的很彻底,于是今天直接找到内核头文件看看,这些到底有什么区别和联系。

    kernel/include/linux/socket.h

    #ifndef _LINUX_SOCKET_H
    #define _LINUX_SOCKET_H
    
    /*
     * Desired design of maximum size and alignment (see RFC2553)
     */
    #define _K_SS_MAXSIZE	128	/* Implementation specific max size */
    #define _K_SS_ALIGNSIZE	(__alignof__ (struct sockaddr *))
    				/* Implementation specific desired alignment */
    
    typedef unsigned short __kernel_sa_family_t;
    
    struct __kernel_sockaddr_storage {
    	__kernel_sa_family_t	ss_family;		/* address family */
    	/* Following field(s) are implementation specific */
    	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
    				/* space to achieve desired size, */
    				/* _SS_MAXSIZE value minus size of ss_family */
    } __attribute__ ((aligned(_K_SS_ALIGNSIZE)));	/* force desired alignment */
    
    #ifdef __KERNEL__
    
    #include <asm/socket.h>			/* arch-dependent defines	*/
    #include <linux/sockios.h>		/* the SIOCxxx I/O controls	*/
    #include <linux/uio.h>			/* iovec support		*/
    #include <linux/types.h>		/* pid_t			*/
    #include <linux/compiler.h>		/* __user			*/
    
    struct pid;
    struct cred;
    
    #define __sockaddr_check_size(size)	\
    	BUILD_BUG_ON(((size) > sizeof(struct __kernel_sockaddr_storage)))
    
    #ifdef CONFIG_PROC_FS
    struct seq_file;
    extern void socket_seq_show(struct seq_file *seq);
    #endif
    
    typedef __kernel_sa_family_t	sa_family_t;
    
    /*
     *	1003.1g requires sa_family_t and that sa_data is char.
     */
     
    struct sockaddr {
    	sa_family_t	sa_family;	/* address family, AF_xxx	*/
    	char		sa_data[14];	/* 14 bytes of protocol address	*/
    };
    
    struct linger {
    	int		l_onoff;	/* Linger active		*/
    	int		l_linger;	/* How long to linger for	*/
    };
    
    #define sockaddr_storage __kernel_sockaddr_storage
    
    /*
     *	As we do 4.4BSD message passing we use a 4.4BSD message passing
     *	system, not 4.3. Thus msg_accrights(len) are now missing. They
     *	belong in an obscure libc emulation or the bin.
     */
     
    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;
    };
    
    /* For recvmmsg/sendmmsg */
    struct mmsghdr {
    	struct msghdr   msg_hdr;
    	unsigned        msg_len;
    };
    
    /*
     *	POSIX 1003.1g - ancillary data object information
     *	Ancillary data consits of a sequence of pairs of
     *	(cmsghdr, cmsg_data[])
     */
    
    struct cmsghdr {
    	__kernel_size_t	cmsg_len;	/* data byte count, including hdr */
            int		cmsg_level;	/* originating protocol */
            int		cmsg_type;	/* protocol-specific type */
    };
    
    /*
     *	Ancillary data object information MACROS
     *	Table 5-14 of POSIX 1003.1g
     */
    
    #define __CMSG_NXTHDR(ctl, len, cmsg) __cmsg_nxthdr((ctl),(len),(cmsg))
    #define CMSG_NXTHDR(mhdr, cmsg) cmsg_nxthdr((mhdr), (cmsg))
    
    #define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) )
    
    #define CMSG_DATA(cmsg)	((void *)((char *)(cmsg) + CMSG_ALIGN(sizeof(struct cmsghdr))))
    #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len))
    #define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
    
    #define __CMSG_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr) ? \
    				  (struct cmsghdr *)(ctl) : \
    				  (struct cmsghdr *)NULL)
    #define CMSG_FIRSTHDR(msg)	__CMSG_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen)
    #define CMSG_OK(mhdr, cmsg) ((cmsg)->cmsg_len >= sizeof(struct cmsghdr) && \
    			     (cmsg)->cmsg_len <= (unsigned long) \
    			     ((mhdr)->msg_controllen - \
    			      ((char *)(cmsg) - (char *)(mhdr)->msg_control)))
    
    /*
     *	Get the next cmsg header
     *
     *	PLEASE, do not touch this function. If you think, that it is
     *	incorrect, grep kernel sources and think about consequences
     *	before trying to improve it.
     *
     *	Now it always returns valid, not truncated ancillary object
     *	HEADER. But caller still MUST check, that cmsg->cmsg_len is
     *	inside range, given by msg->msg_controllen before using
     *	ancillary object DATA.				--ANK (980731)
     */
     
    static inline struct cmsghdr * __cmsg_nxthdr(void *__ctl, __kernel_size_t __size,
    					       struct cmsghdr *__cmsg)
    {
    	struct cmsghdr * __ptr;
    
    	__ptr = (struct cmsghdr*)(((unsigned char *) __cmsg) +  CMSG_ALIGN(__cmsg->cmsg_len));
    	if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
    		return (struct cmsghdr *)0;
    
    	return __ptr;
    }
    
    static inline struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__cmsg)
    {
    	return __cmsg_nxthdr(__msg->msg_control, __msg->msg_controllen, __cmsg);
    }
    
    /* "Socket"-level control message types: */
    
    #define	SCM_RIGHTS	0x01		/* rw: access rights (array of int) */
    #define SCM_CREDENTIALS 0x02		/* rw: struct ucred		*/
    #define SCM_SECURITY	0x03		/* rw: security label		*/
    
    struct ucred {
    	__u32	pid;
    	__u32	uid;
    	__u32	gid;
    };
    
    /* Supported address families. */
    #define AF_UNSPEC	0
    #define AF_UNIX		1	/* Unix domain sockets 		*/
    #define AF_LOCAL	1	/* POSIX name for AF_UNIX	*/
    #define AF_INET		2	/* Internet IP Protocol 	*/
    #define AF_AX25		3	/* Amateur Radio AX.25 		*/
    #define AF_IPX		4	/* Novell IPX 			*/
    #define AF_APPLETALK	5	/* AppleTalk DDP 		*/
    #define AF_NETROM	6	/* Amateur Radio NET/ROM 	*/
    #define AF_BRIDGE	7	/* Multiprotocol bridge 	*/
    #define AF_ATMPVC	8	/* ATM PVCs			*/
    #define AF_X25		9	/* Reserved for X.25 project 	*/
    #define AF_INET6	10	/* IP version 6			*/
    #define AF_ROSE		11	/* Amateur Radio X.25 PLP	*/
    #define AF_DECnet	12	/* Reserved for DECnet project	*/
    #define AF_NETBEUI	13	/* Reserved for 802.2LLC project*/
    #define AF_SECURITY	14	/* Security callback pseudo AF */
    #define AF_KEY		15      /* PF_KEY key management API */
    #define AF_NETLINK	16
    #define AF_ROUTE	AF_NETLINK /* Alias to emulate 4.4BSD */
    #define AF_PACKET	17	/* Packet family		*/
    #define AF_ASH		18	/* Ash				*/
    #define AF_ECONET	19	/* Acorn Econet			*/
    #define AF_ATMSVC	20	/* ATM SVCs			*/
    #define AF_RDS		21	/* RDS sockets 			*/
    #define AF_SNA		22	/* Linux SNA Project (nutters!) */
    #define AF_IRDA		23	/* IRDA sockets			*/
    #define AF_PPPOX	24	/* PPPoX sockets		*/
    #define AF_WANPIPE	25	/* Wanpipe API Sockets */
    #define AF_LLC		26	/* Linux LLC			*/
    #define AF_CAN		29	/* Controller Area Network      */
    #define AF_TIPC		30	/* TIPC sockets			*/
    #define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
    #define AF_IUCV		32	/* IUCV sockets			*/
    #define AF_RXRPC	33	/* RxRPC sockets 		*/
    #define AF_ISDN		34	/* mISDN sockets 		*/
    #define AF_PHONET	35	/* Phonet sockets		*/
    #define AF_IEEE802154	36	/* IEEE802154 sockets		*/
    #define AF_CAIF		37	/* CAIF sockets			*/
    #define AF_ALG		38	/* Algorithm sockets		*/
    #define AF_NFC		39	/* NFC sockets			*/
    #define AF_MAX		40	/* For now.. */
    
    /* Protocol families, same as address families. */
    #define PF_UNSPEC	AF_UNSPEC
    #define PF_UNIX		AF_UNIX
    #define PF_LOCAL	AF_LOCAL
    #define PF_INET		AF_INET
    #define PF_AX25		AF_AX25
    #define PF_IPX		AF_IPX
    #define PF_APPLETALK	AF_APPLETALK
    #define	PF_NETROM	AF_NETROM
    #define PF_BRIDGE	AF_BRIDGE
    #define PF_ATMPVC	AF_ATMPVC
    #define PF_X25		AF_X25
    #define PF_INET6	AF_INET6
    #define PF_ROSE		AF_ROSE
    #define PF_DECnet	AF_DECnet
    #define PF_NETBEUI	AF_NETBEUI
    #define PF_SECURITY	AF_SECURITY
    #define PF_KEY		AF_KEY
    #define PF_NETLINK	AF_NETLINK
    #define PF_ROUTE	AF_ROUTE
    #define PF_PACKET	AF_PACKET
    #define PF_ASH		AF_ASH
    #define PF_ECONET	AF_ECONET
    #define PF_ATMSVC	AF_ATMSVC
    #define PF_RDS		AF_RDS
    #define PF_SNA		AF_SNA
    #define PF_IRDA		AF_IRDA
    #define PF_PPPOX	AF_PPPOX
    #define PF_WANPIPE	AF_WANPIPE
    #define PF_LLC		AF_LLC
    #define PF_CAN		AF_CAN
    #define PF_TIPC		AF_TIPC
    #define PF_BLUETOOTH	AF_BLUETOOTH
    #define PF_IUCV		AF_IUCV
    #define PF_RXRPC	AF_RXRPC
    #define PF_ISDN		AF_ISDN
    #define PF_PHONET	AF_PHONET
    #define PF_IEEE802154	AF_IEEE802154
    #define PF_CAIF		AF_CAIF
    #define PF_ALG		AF_ALG
    #define PF_NFC		AF_NFC
    #define PF_MAX		AF_MAX
    
    /* Maximum queue length specifiable by listen.  */
    #define SOMAXCONN	128
    
    /* Flags we can use with send/ and recv. 
       Added those for 1003.1g not all are supported yet
     */
     
    #define MSG_OOB		1
    #define MSG_PEEK	2
    #define MSG_DONTROUTE	4
    #define MSG_TRYHARD     4       /* Synonym for MSG_DONTROUTE for DECnet */
    #define MSG_CTRUNC	8
    #define MSG_PROBE	0x10	/* Do not send. Only probe path f.e. for MTU */
    #define MSG_TRUNC	0x20
    #define MSG_DONTWAIT	0x40	/* Nonblocking io		 */
    #define MSG_EOR         0x80	/* End of record */
    #define MSG_WAITALL	0x100	/* Wait for a full request */
    #define MSG_FIN         0x200
    #define MSG_SYN		0x400
    #define MSG_CONFIRM	0x800	/* Confirm path validity */
    #define MSG_RST		0x1000
    #define MSG_ERRQUEUE	0x2000	/* Fetch message from error queue */
    #define MSG_NOSIGNAL	0x4000	/* Do not generate SIGPIPE */
    #define MSG_MORE	0x8000	/* Sender will send more */
    #define MSG_WAITFORONE	0x10000	/* recvmmsg(): block until 1+ packets avail */
    #define MSG_SENDPAGE_NOTLAST 0x20000 /* sendpage() internal : not the last page */
    #define MSG_EOF         MSG_FIN
    
    #define MSG_CMSG_CLOEXEC 0x40000000	/* Set close_on_exit for file
    					   descriptor received through
    					   SCM_RIGHTS */
    #if defined(CONFIG_COMPAT)
    #define MSG_CMSG_COMPAT	0x80000000	/* This message needs 32 bit fixups */
    #else
    #define MSG_CMSG_COMPAT	0		/* We never have 32 bit fixups */
    #endif
    
    
    /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
    #define SOL_IP		0
    /* #define SOL_ICMP	1	No-no-no! Due to Linux :-) we cannot use SOL_ICMP=1 */
    #define SOL_TCP		6
    #define SOL_UDP		17
    #define SOL_IPV6	41
    #define SOL_ICMPV6	58
    #define SOL_SCTP	132
    #define SOL_UDPLITE	136     /* UDP-Lite (RFC 3828) */
    #define SOL_RAW		255
    #define SOL_IPX		256
    #define SOL_AX25	257
    #define SOL_ATALK	258
    #define SOL_NETROM	259
    #define SOL_ROSE	260
    #define SOL_DECNET	261
    #define	SOL_X25		262
    #define SOL_PACKET	263
    #define SOL_ATM		264	/* ATM layer (cell level) */
    #define SOL_AAL		265	/* ATM Adaption Layer (packet level) */
    #define SOL_IRDA        266
    #define SOL_NETBEUI	267
    #define SOL_LLC		268
    #define SOL_DCCP	269
    #define SOL_NETLINK	270
    #define SOL_TIPC	271
    #define SOL_RXRPC	272
    #define SOL_PPPOL2TP	273
    #define SOL_BLUETOOTH	274
    #define SOL_PNPIPE	275
    #define SOL_RDS		276
    #define SOL_IUCV	277
    #define SOL_CAIF	278
    #define SOL_ALG		279
    
    /* IPX options */
    #define IPX_TYPE	1
    
    extern void cred_to_ucred(struct pid *pid, const struct cred *cred, struct ucred *ucred);
    
    extern int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
    extern int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
    			       int offset, int len);
    extern int csum_partial_copy_fromiovecend(unsigned char *kdata, 
    					  struct iovec *iov, 
    					  int offset, 
    					  unsigned int len, __wsum *csump);
    
    extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode);
    extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
    extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata,
    			     int offset, int len);
    extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
    extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
    
    struct timespec;
    
    extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
    			  unsigned int flags, struct timespec *timeout);
    extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
    			  unsigned int vlen, unsigned int flags);
    #endif /* not kernel and not glibc */
    #endif /* _LINUX_SOCKET_H */
    

    头文件有:

    #define AF_NETLINK  16
    #define AF_ROUTE    AF_NETLINK /* Alias to emulate 4.4BSD */

    #define AF_UNIX     1   /* Unix domain sockets      */
    #define AF_LOCAL    1   /* POSIX name for AF_UNIX   */
    #define AF_INET     2   /* Internet IP Protocol     */

    /* Protocol families, same as address families. */
    #define PF_UNSPEC    AF_UNSPEC
    #define PF_UNIX        AF_UNIX
    #define PF_LOCAL    AF_LOCAL
    #define PF_INET        AF_INET
    #define PF_AX25        AF_AX25
    #define PF_IPX        AF_IPX
    #define PF_APPLETALK    AF_APPLETALK
    #define    PF_NETROM    AF_NETROM
    #define PF_BRIDGE    AF_BRIDGE
    #define PF_ATMPVC    AF_ATMPVC
    #define PF_X25        AF_X25
    #define PF_INET6    AF_INET6
    #define PF_ROSE        AF_ROSE
    #define PF_DECnet    AF_DECnet
    #define PF_NETBEUI    AF_NETBEUI
    #define PF_SECURITY    AF_SECURITY
    #define PF_KEY        AF_KEY
    #define PF_NETLINK    AF_NETLINK
    #define PF_ROUTE    AF_ROUTE
    #define PF_PACKET    AF_PACKET
    #define PF_ASH        AF_ASH
    #define PF_ECONET    AF_ECONET
    #define PF_ATMSVC    AF_ATMSVC
    #define PF_RDS        AF_RDS
    #define PF_SNA        AF_SNA
    #define PF_IRDA        AF_IRDA
    #define PF_PPPOX    AF_PPPOX
    #define PF_WANPIPE    AF_WANPIPE
    #define PF_LLC        AF_LLC
    #define PF_CAN        AF_CAN
    #define PF_TIPC        AF_TIPC
    #define PF_BLUETOOTH    AF_BLUETOOTH
    #define PF_IUCV        AF_IUCV
    #define PF_RXRPC    AF_RXRPC
    #define PF_ISDN        AF_ISDN
    #define PF_PHONET    AF_PHONET
    #define PF_IEEE802154    AF_IEEE802154
    #define PF_CAIF        AF_CAIF
    #define PF_ALG        AF_ALG
    #define PF_NFC        AF_NFC
    #define PF_MAX        AF_MAX
     

     

    从头文件可以看出, AF_UNIX,PF_UNIX, AF_LOCAL 等的值都是一样的,只是内核设计者赋予了不同的意义,但是很多程序里面这些AF_XXX和PF_XXX混用了,其实我们不用纠结了,就当他们一样就没事了。


     

    展开全文
  • AF_NETLINK 内核与用户空间数据交换

    千次阅读 2011-08-20 11:06:56
    http://blog.csdn.net/rbk_1984/article/details/4408296 http://www.ibm.com/developerworks/cn/linux/l-connector/ http://www.ibm.com/developer
  • keepalived_netlink.c: In function ‘parse_af_spec’: keepalived_netlink.c:1634:35: error: ‘IPV4_DEVCONF_ARP_IGNORE’ undeclared (first use in this function); did you mean ‘HAVE_DECL_IPV4_DEVCONF_ARP...
  • linux_netlink机制

    2013-09-03 11:28:51
    Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由 daemon(NETLINK_...
  • <div><p>Sometimes query gets hung on Redhat 7, the stack is like: ... <p>netstat shows it's related to NETLINK.</p><p>该提问来源于开源项目:greenplum-db/gpdb</p></div>
  • NETLINK_GENERIC

    2014-10-11 17:01:13
    class Connection:  ## Connection(NETLINK_GENERIC)  def __init__(self, nltype, groups=0, unexpected_msg_handler=None):  self.descriptor = socket.socket(socket.AF_NETLINK,  
  • AF_ROUTE

    千次阅读 2011-06-27 19:41:00
    socket编程原理http://www.chinaunix.net/jh/4/198859.htmlAF_NETLINK--linux内核与用户空间通信之netlinkhttp://hi.baidu.com/deep_pro/blog/item/7886b76063dcfd4feaf8f8fa.html连接器(Netlink Connector)及其...
  • NETLINK

    2020-11-11 17:48:50
    netlink-内核和用户空间之间的通信(AF_NETLINK) 概要 #include <asm / types.h> #include <sys / socket.h> #include <linux / netlink.h> netlink_socket = socket(AF_NETLINK, socket_type, ...
  • netlink

    千次阅读 2019-05-09 09:14:04
    一、 用户空间与内核通信的几种方法: * system call * procfs(/proc文件系统) * sysctl(/proc/sys目录) * sysfs(/sys文件系统) * ioctl 系统调用 ...- af_netlink.c - af_netlink.h - genetlink.c ...
  • AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[ { iov_base=[ { {len=92, type=NFNL_SUBSYS_CTNETLINK<<8|IPCTNL_MSG_CT_GET_STATS_CPU, flags&#...
  • Netlink socket用到的结构体如下: Struct sockaddr_nl {  sa_family_t nl_family; //AF_NETLINK 或PF_NETLINK  unsigned short
  • socket函数 int socket(int domain,int ...AF_NETLINK:内核与用户态之间通信 type是指套接字的类型,取值为以下几点 SOCK_STREAM:数据流通信,即使用TCP通信。 SOCK_DGRAM:数据包通信,即使用UDP通信。 protocol
  • Netlink Sockets

    2015-03-05 13:10:48
    The netlink socket interface( AF_NETLINK socket ) was created as a more flexible alternative to the awkward IOCTL communication method between userspace processes and the kernel. The IOCTL handlers ca
  • Netlink 内核实现分析 2

    2019-05-11 19:23:00
    应用层通过socket()系统调用创建Netlink套接字,socket系统调用的第一个参数可以是AF_NETLINK或PF_NETLINK(在Linux系统中它俩实际为同一种宏),第二个参数可以是SOCK_RAW或SOCK_DGRAM(原始套接字或无连接的数据报...
  • python 网络编程

    2019-09-26 16:30:23
    服务器客户端模式,服务器存在唯一目的是:等待客户的请求, 一.套接字 ...unix套接字,AF_LOCAL,地址家族,缩写:AF,AF_LOCAL将代替AF_UNIX,很多时候两者等价 ...python只支持AF_UNIX,AF_NETLINK,AF_INET家...
  • netlink.pdf

    2011-02-18 12:27:26
    netlink socket编程why & how netlink socekt是一种...类似于TCP/IP中使用AF_INET地址族一样,netlink socket使用地址族AF_NETLINK。每一个netlink socket在内核头文件include/linux/netlink.h中定义自己的协议类型。
  • 内核的网络子系统定义了rtnetlink,用做和用户空间的交互,rtnetlink为AF_NETLINK协议的一个类别NETLINK_ROUTE,其它类别包括NETLINK_XFRM、NETLINK_GENERIC等。renetlink主要注册了LINK、ROUTE、...
  • Netlink 介绍说明

    千次阅读 2011-12-11 22:08:59
    介绍:  Netlink socket是一种特别的IPC,用于在内核和用户空间传输数据.它提供了标准socket的全双工的数据连接.... Netlink socket使用address family AF_NETLINK,就如同TCP/IP socket使用AF_INET.每种netlink s
  • netlink知识

    千次阅读 2012-07-10 16:27:02
    (1)netlink使用cb字段传输数据。(sk_buff是Linux在其协议栈里传送的结构体,也就是所谓的“包”) ... sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);  第三个参数指定netlink协议类型,可以是用户自定义
  • af_netlink.c 代码走读 netlink 用户态和内核交互过程 netlink 关键数据结构和函数 sockaddr_nl 协议套接字 nlmsghdr 消息体 msghdr 用户态系发送消息体 netlink 常用宏 netlink 内核常用函数 测试例子代码 ...
  • dbus netlink socket

    千次阅读 2013-01-03 11:00:49
    1、本地unix套接字实现进程间通信。 2、netlink socekt是一种用于在内核态和用户态进程之间进行数据传输的特殊的IPC...类似于TCP/IP中使用AF_INET地址族一样,netlink socket使用地址族AF_NETLINK。每一个netlink sock

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 307
精华内容 122
关键字:

af_netlink