netlink

一、 用户空间与内核通信的几种方法:
* system call
* procfs(/proc文件系统)
* sysctl(/proc/sys目录)
* sysfs(/sys文件系统)
* ioctl 系统调用
* netlink套接字(RFC 3549) net/netlink目录
    - af_netlink.c
    - af_netlink.h
   - genetlink.c
   - diag.c

af_netlink 模块提供了内核态API
genetlink 模块提供了更新的 内核API, 更容易发送数据信息
diag 模块提供了netlink接口监控信息

二、netlink是一种用于内核态和用户态进程之间进行数据传输的特殊的IPC机制。
特点:
    1) 用户态采用socket风格的API
    2) 除了预定义的协议类型之外,支持自定义协议类型
    3) 异步通讯
    4) 支持消息组播
    4) 全双工(特别是支持内核主动发起会话)

三、netlink涉及的数据结构:
  1) netlink地址结构
            struct sockaddr_nl {
                sa_family_t     nl_family;  // AF_NETLINK
                unsigned short  nl_pad;     // 填充0
                unsigned int    nl_pid;     // 进程ID
                unsigned int    nl_groups;  // 多播组mask
            }
            NETLINK_ROUTE的多播组定义位于retnetlink.h,RTMGRP_*格式,这里列出常用的几个:
            RTMGRP_LINK         - 当网卡变动时会触发这个多播组,例如插拔网线、增减网卡设备、启用禁用接口等
            RTMGRP_IPV4_IFADDR  - 当ipv4地址变动时会触发这个多播组,例如修改IP
            RTMGRP_IPV4_ROUTE   - 当ipv4路由变动时会触发这个多播组
            RTMGRP_IPV6_IFADDR  - 当ipv6地址变动时会触发这个多播组,例如修改IP
            RTMGRP_IPV6_ROUTE   - 当ipv6路由变动时会触发这个多播组

   2) netlink消息结构: 
            nlmsghdr + pad + payload + pad    +    nlmsghdr + pad + payload + pad    ...
            可以看出来,netlink消息在顶层呈现数组形式平行排列,也就是说,多条netlink消息可以以数组形式一次性传输

       netlink消息头结构
                struct nlmsghdr {
                    unsigned int    nlmsg_len;  // 消息长(nlmsghdr + pad + payload)
                    unsigned short  nlmsg_type; // 消息类型
                    unsigned short  nlmsg_flags;// 附加的标志位,用来对消息进行额外的控制,NLM_F_*
                    unsigned int    nlmsg_seq;  // 序号(用于追踪)
                    unsigned int    nlmsg_pid;  // 进程ID(用于追踪)
                }

       消息类型:
                    NETLINK_ROUTE的消息类型定义位于rtnetlink.h,RTM_*格式,这里列出常用的几个:
                    RTM_NEWLINK/RTM_DELLINK     - 当网卡变动时内核会发出这个消息
                    RTM_NEWADDR/RTM_DELADDR     - 当地址(IP)变动时内核会发出这个消息
                    RTM_NEWROUTE/RTM_DELROUTE   - 当路由变动时内核会发出这个消息

四、用户态netlink API
用户态的netlink库有两个:
http://www.infradead.org/~tgr/libnl/    功能较全
http://netfilter.org/projects/libmnl/     mini的libnetlink 库
 
以下流程用来对网络环境进行修改:
step 1. 打开套接字
step 2. 在本地 bind 套接字
step 3. 发送消息到目的地
step 4. 在目的地接收消息
step 5. 关闭套接字

/* 添加头文件 */
#include <bits/sockaddr.h>
#include <asm/types.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>

1. 创建socket
netlink套接字调用原型 include/linux/netlink.h 
#include <sys/socket.h>
#include <linux/netlink.h>

int socket(int domain, int type, int protocol);
第一个参数 doamin 表示什么样的套接字类型,使用RTNETLINK, 使用AF_NETLINK
第二个参数 type 表示什么方法 RAW 或 DGRAM ,对于RTNETLINK 都可以使用
第三个参数 protocol , 为了修改路由表,我们使用NETLINK_ROUTE
如果成功,返回一个正数整形
如果失败,返回一个负数
 
从用户态创建socket
/* 示例代码 */
int sock_fd;
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);


2. bind
int bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
第一个参数是 socket创建的点
第二个参数 struct sockaddr* 是本地地址,结构如下:
数据结构 sockaddr_nl 表示一个netlink套接字地址 include/uapi/linux/netlink.h 

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 */
};


bind 函数成功,返回0,失败返回负数

int rtn;
struct sockaddr_nl la;
...
bzero(&la, sizeof(la));
la.nl_family = AF_NETLINK;
la.nl_pad = 0;
la.nl_pid = getpid();
la.nl_groups = 0;

rtn = bind(fd, (struct sockaddr*) &la, sizeof(la));

3. 发送netlink消息
如果消息是发送给内核的,nl_pid 与 nl_groups 的值都为0
如果消息是单播消息发送给另一个进程的, nl_pid是另一个进程的pid值,nl_groups的值为0
如果消息是组播发送的,需要定义nl_groups的值。
ssize_t sendmsg(int fd, const struct msghdr *msg,
                                      int flags);
 
第一个参数 是socket创建点
第二个参数是需要发送消息的首地址,需要如下定义

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

iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;

msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

 

struct msghdr
{
  void *msg_name;        //Address to send to
  socklen_t msg_namelen; //Length of address data
  struct iovec *msg_iov; //Vector of data to send
  size_t msg_iovlen;     //Number of iovec entries
  void *msg_control;     //Ancillary data
  size_t msg_controllen; //Ancillary data buf len
  int msg_flags;         //Flags on received msg
};

 
netlink消息头部 include/uapi/linux/netlink.h

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 */
};

0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          Length                             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |            Type              |           Flags              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Process ID (PID)                       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

32位的nlmsg_len 表示整个netlink消息的数据包长度
16位的nlmsg_type是从应用定义
  NLMSG_NOOP  无任何操作,消息必须丢弃
  NLMSG_ERROR  错误发生了
  NLMSG_DONE   其他消息结束了
  NLMSG_OVERRUN  错误,数据丢失了
 

4. 接收消息
接收的应用必须分配足够的内存空间来获取消息头部与payloads.
ssize_t recv(int fd, void *buf, size_t len,
                                      int flags);

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);


5. 关闭
int close(int fd);

五、在路由表中添加或删除一条路由
ip route add 192.168.1.11 via 192.168.2.20
ip route del 192.168.2.11
ip monitor route 
这条命令从用户态程序中发送netlink 消息(RTM_NEWROUTE)调用rtlinknet套接字来创建路由条目。
消息被内核态rtnetlink套接字的rtnetlink_rcv()方法接收。

netlink_test_rt.c

参考文档:
Netlink 获取网卡接口信息 === http://blog.csdn.net/idwtwt/article/details/51057959 

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值