Chinaunix首页 | 论坛 | 博客
  • 博客访问: 228369
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 296
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-22 11:52
文章分类

全部博文(31)

文章存档

2018年(3)

2017年(11)

2016年(12)

2015年(5)

我的朋友

分类: C/C++

2017-07-13 19:25:51


同事最近想用netlink 获取指定网卡的信息,在网上搜了大半天,都是用netlink获取全部网卡的信息的例子,没有获取指定网卡信息的例子,找我帮忙看看,建议他用ioctl(前段时间刚好总结了ioctl 相关的内容,想了解的请参考http://blog.chinaunix.net/uid-30226910-id-5766831.html),不过个人认为netlink 也肯定是能获取指定网卡的信息的。网上估计是搜不出来的,但是像ethtool ifconfigip 等命令都是能指定网卡的,难道都是向内核全部请求,然后再过滤的,这么低效的做法,感觉不太可能。

strace ifconfig eth0 ,strace ethtool eth0, strace ip link eth0 看了一下这几个命令的系统调用,发现ifconfig 和ethtool 都是用ioctl的,ip 是用netlink的,于是找到ip 命令对应的源码包iproute2 查看了ip link eth0 命令的实现,哈哈,果然的,netlink是可以通过rtattr的type 来获取指定网卡的信息的。顺便总结一下相关的内容

1.通过socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) ,NETLINK_ROUTE 协议可以获取的消息类型如下(头文件linux/rtnetlink.h):

点击(此处)折叠或打开

  1. enum {
  2.         RTM_BASE = 16,
  3.         RTM_NEWLINK = 16, /*链路相关的*/
  4.         RTM_DELLINK,
  5.         RTM_GETLINK,
  6.         RTM_SETLINK,

  7.         RTM_NEWADDR = 20, /*ip 地址*/
  8.         RTM_DELADDR,
  9.         RTM_GETADDR,

  10.         RTM_NEWROUTE = 24, /*路由信息*/
  11.         RTM_DELROUTE,
  12.         RTM_GETROUTE,

  13.         RTM_NEWNEIGH = 28, /*邻居信息*/
  14.         RTM_DELNEIGH,
  15.         RTM_GETNEIGH,

  16.         RTM_NEWRULE = 32,/*策略路由*/
  17.         RTM_DELRULE,
  18.         RTM_GETRULE,

  19.         RTM_NEWQDISC = 36, /*tc的qdisc*/
  20.         RTM_DELQDISC,
  21.         RTM_GETQDISC,

  22.         RTM_NEWTCLASS = 40, /*tc的class*/
  23.         RTM_DELTCLASS,
  24.         RTM_GETTCLASS,

  25.         RTM_NEWTFILTER = 44, /*tc的filter*/
  26.         RTM_DELTFILTER,
  27.         RTM_GETTFILTER,

  28.         RTM_NEWACTION = 48,/*tc的action*/
  29.         RTM_DELACTION,
  30.         RTM_GETACTION,

  31.         RTM_NEWPREFIX = 52,
  32.         RTM_GETPREFIX = 54,

  33.         RTM_GETMULTICAST = 58,

  34.         RTM_GETANYCAST = 62,

  35.         RTM_NEWNEIGHTBL = 64,
  36.         RTM_GETNEIGHTBL = 66,
  37.         RTM_SETNEIGHTBL,

  38.         RTM_NEWADDRLABEL = 72,
  39.         RTM_DELADDRLABEL,
  40.         RTM_GETADDRLABEL,

  41.         __RTM_MAX,
  42. #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
  43. }
这个消息类型是放在struct nlmsghdr 的nlmsg_type 成员里的,根据不同的nlmsg_type, 发送/接收的时候可以用不同的xxxmsg数据结构以及对应的enum 来解析不同的nlmsg_type的消息,例如,nlmsg_type = RTM_GETLINK, 使用的msg数据结构和enum 结构如下:

点击(此处)折叠或打开

  1. struct ifinfomsg
  2. {
  3.         unsigned char ifi_family;
  4.         unsigned char __ifi_pad;
  5.         unsigned short ifi_type; /* ARPHRD_* */
  6.         int ifi_index; /* Link index */
  7.         unsigned ifi_flags; /* IFF_* flags */
  8.         unsigned ifi_change; /* IFF_* change mask */
  9. };

  10. enum
  11. {
  12.         IFLA_UNSPEC,
  13.         IFLA_ADDRESS,
  14.         IFLA_BROADCAST,
  15.         IFLA_IFNAME,
  16.         IFLA_MTU,
  17.         IFLA_LINK,
  18.         IFLA_QDISC,
  19.         IFLA_STATS,
  20.         IFLA_COST,
  21.         IFLA_PRIORITY,
  22.         IFLA_MASTER,
  23.         IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */
  24.         IFLA_PROTINFO, /* Protocol specific information for a link */
  25.         IFLA_TXQLEN,
  26.         IFLA_MAP,
  27.         IFLA_WEIGHT,
  28.         IFLA_OPERSTATE,
  29.         IFLA_LINKMODE,
  30.         __IFLA_MAX
  31. };
其中enum 是解析xxxmsg数据结构后面的其他参数用的,解析参数用到的结构体如下:

点击(此处)折叠或打开

  1. struct rtattr
  2. {
  3.         unsigned short rta_len;
  4.         unsigned short rta_type;  /*这个type 就是上面enum 定义的类型*/
  5. };
在头文件linux/rtnetlink.h 中,定义各种消息类型的xxxmsg 数据结构,包括struct rtmsg struct ndmsgstruct ndtmsgstruct prefixmsgstruct tcmsgstruct tcamsg 及其对应的enum ,
ip地址相关的msg 和 enum 定义在linux/if_addr.h

一般协议是NETLINK_ROUTE (估计其他类型的协议也类似),与内核通讯的消息格式如下:

发送的数据格式:nlmsghdr+xxxmsg数据结构+rtattr-->如果没有条件,请求的消息只需要把nlmsghdr->nlmsg_type 和 xxxmsg->family 发送给内核,如果有过滤条件,比如只是获取指定网卡的链路信息,可以把IFLA_IFNAME 这个rtattr_type的rtattr传进去

接收的数据格式:nlmsghdr+xxxmsg数据结构+rtattr -->接收的数据,根据请求的时候nlmsg_type ,用不同的xxxmsg 数据结构接收,并用对应的enum 解析跟在xxxmsg 后面的rtattr

其中各种nlmsg_type 能够获取什么样的信息,在xxxmsg 和enum 中也能看得出来。


内核是通过rtnl_register注册一种nlmsg_type的消息类型及其处理函数来实现不同的处理的。

应用层的请求包过来,数据内容一定要有family,这个family 可以是xxxmsg 数据结构中的第一个成员,或者用通用的struct rtgenmsg也可以,因为每一个rtnl_register 注册的是都是根据family(AF_INET…) 和nlmsg_type(RTM_GETLINK…) 来注册到struct rtnl_link 指针数组里面,然后接收到message的时候就根据familytype 选择对应的处理函数


贴一个例子,参考iproute2源码获取指定接口的链路信息实现的

点击(此处)折叠或打开

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <errno.h>
  6. #include <unistd.h>
  7. #include <stdlib.h>
  8. #include <fcntl.h>

  9. #include<linux/rtnetlink.h>
  10. #include<bits/socket.h>
  11. #include<net/if.h>

  12. #define ETH "eth0"

  13. #define NLMSG_TAIL(nmsg) \
  14.     ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
  15. #define MIN(a, b) ((a) < (b) ? (a) : (b))

  16. int seq = 0;

  17. int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,int alen)
  18. {
  19.     int len = RTA_LENGTH(alen);
  20.     struct rtattr *rta;

  21.     if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
  22.         printf("addattr_l ERROR: message exceeded bound of %d\n",maxlen);
  23.         return -1;
  24.     }
  25.     rta = NLMSG_TAIL(n);
  26.     rta->rta_type = type;
  27.     rta->rta_len = len;
  28.     memcpy(RTA_DATA(rta), data, alen);
  29.     n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
  30.     return 0;
  31. }

  32. int netlink_socket(unsigned long groups)
  33. {
  34.     socklen_t addr_len;
  35.     struct sockaddr_nl snl;
  36.     int fd = -1;
  37.     int ret;


  38.     fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  39.     if (fd < 0) {
  40.         printf("Netlink: Cannot open netlink socket : (%s)",strerror(errno));
  41.         return -1;
  42.     }

  43.     ret = fcntl(fd, F_SETFL, O_NONBLOCK);
  44.     if (ret < 0) {
  45.         printf("Netlink: Cannot set netlink socket flags : (%s)",strerror(errno));
  46.         close(fd);
  47.         return -1;
  48.     }

  49.     memset(&snl, 0, sizeof (snl));
  50.     snl.nl_family = AF_NETLINK;
  51.     snl.nl_groups = groups; /*设为0 表示不用多播,其他情况按照设置的组来进行多播,就是会多播到这些组*/

  52.     ret = bind(fd, (struct sockaddr *) &snl, sizeof (snl));
  53.     if (ret < 0) {
  54.         printf("Netlink: Cannot bind netlink socket : (%s)",
  55.                strerror(errno));
  56.         close(fd);
  57.         return -1;
  58.     }

  59.     addr_len = sizeof (snl);
  60.     ret = getsockname(fd, (struct sockaddr *) &snl, &addr_len);
  61.     if (ret < 0 || addr_len != sizeof (snl)) {
  62.         printf("Netlink: Cannot getsockname : (%s)",strerror(errno));
  63.         close(fd);
  64.         return -1;
  65.     }

  66.     if (snl.nl_family != AF_NETLINK) {
  67.         printf("Netlink: Wrong address family %d",snl.nl_family);
  68.         close(fd);
  69.         return -1;
  70.     }

  71.     seq = time(NULL);
  72.     return fd;
  73. }

  74. int rtnl_talk(int fd , struct nlmsghdr *n,
  75.           struct nlmsghdr *answer, size_t maxlen)
  76. {
  77.     int status;
  78.     unsigned int seq;
  79.     struct nlmsghdr *h;
  80.     struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
  81.     struct iovec iov = {
  82.         .iov_base = n,
  83.         .iov_len = n->nlmsg_len
  84.     };
  85.     struct msghdr msg = {
  86.         .msg_name = &nladdr,
  87.         .msg_namelen = sizeof(nladdr),
  88.         .msg_iov = &iov,
  89.         .msg_iovlen = 1,
  90.     };
  91.     char buf[32768] = {};

  92.     n->nlmsg_seq = ++seq;

  93.     if (answer == NULL)
  94.         n->nlmsg_flags |= NLM_F_ACK;

  95.     status = sendmsg(fd, &msg, 0);
  96.     if (status < 0) {
  97.         perror("Cannot talk to rtnetlink");
  98.         return -1;
  99.     }

  100.     iov.iov_base = buf;
  101.     while (1) {
  102.         iov.iov_len = sizeof(buf);
  103.         status = recvmsg(fd, &msg, 0);

  104.         if (status < 0) {
  105.             if (errno == EINTR || errno == EAGAIN)
  106.                 continue;
  107.             printf("netlink receive error %s (%d)\n",strerror(errno), errno);
  108.             return -1;
  109.         }
  110.         if (status == 0) {
  111.             printf("EOF on netlink\n");
  112.             return -1;
  113.         }
  114.         if (msg.msg_namelen != sizeof(nladdr)) {
  115.             printf("sender address length == %d\n",msg.msg_namelen);
  116.             exit(1);
  117.         }
  118.         for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
  119.             int len = h->nlmsg_len;
  120.             int l = len - sizeof(*h);

  121.             if (l < 0 || len > status) {
  122.                 if (msg.msg_flags & MSG_TRUNC) {
  123.                     printf("Truncated message\n");
  124.                     return -1;
  125.                 }
  126.                 printf("!!!malformed message: len=%d\n",len);
  127.                 exit(1);
  128.             }

  129.             if (nladdr.nl_pid != 0 ||
  130.                 h->nlmsg_seq != seq) {
  131.                 /* Don't forget to skip that message. */
  132.                 status -= NLMSG_ALIGN(len);
  133.                 h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
  134.                 continue;
  135.             }

  136.             if (h->nlmsg_type == NLMSG_ERROR) {
  137.                 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);

  138.                 if (l < sizeof(struct nlmsgerr)) {
  139.                     fprintf(stderr, "ERROR truncated\n");
  140.                 } else if (!err->error) {
  141.                     if (answer)
  142.                         memcpy(answer, h,
  143.                                MIN(maxlen, h->nlmsg_len));
  144.                     return 0;
  145.                 }

  146.                 errno = -err->error;
  147.                 return -1;
  148.             }

  149.             if (answer) {
  150.                 memcpy(answer, h,
  151.                        MIN(maxlen, h->nlmsg_len));
  152.                 return 0;
  153.             }

  154.             printf("Unexpected reply!!!\n");

  155.             status -= NLMSG_ALIGN(len);
  156.             h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
  157.         }

  158.         if (msg.msg_flags & MSG_TRUNC) {
  159.             printf("Message truncated\n");
  160.             continue;
  161.         }

  162.         if (status) {
  163.             fprintf(stderr, "!!!Remnant of size %d\n", status);
  164.             exit(1);
  165.         }
  166.     }
  167. }

  168. void parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len)
  169. {
  170.     while (RTA_OK(rta, len)) {
  171.         if (rta->rta_type <= max)
  172.             tb[rta->rta_type] = rta;
  173.         rta = RTA_NEXT(rta, len);
  174.     }
  175. }

  176. int main(int argc ,char **argv)
  177. {
  178.     int fd ;
  179.     struct {
  180.         struct nlmsghdr n;
  181.         struct ifinfomsg i;
  182.         char buf[1024];
  183.     }req;

  184.     memset(&req,0,sizeof(req));

  185.     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
  186.     req.n.nlmsg_flags = NLM_F_REQUEST ;
  187.     req.n.nlmsg_type = RTM_GETLINK; /*消息类型*/
  188.     req.i.ifi_family = PF_UNSPEC; /*新版本的内核都是用这个来注册rtnl_register, 有旧版本的内核可能是用PF_PACKET*/
  189.     struct {
  190.         struct nlmsghdr n;
  191.         char buf[16384];
  192.     }answer;

  193.     memset(&answer,0,sizeof(answer));

  194.     fd = netlink_socket(0);
  195.     if(fd <0){
  196.         printf("netlink_socket failed\n");
  197.         return -1;
  198.     }

  199.     addattr_l(&req.n, sizeof(req), IFLA_IFNAME, ETH, strlen(ETH)+1);
  200.     if (rtnl_talk(fd, &req.n, &answer.n, sizeof(answer)) < 0)
  201.         return -2;

  202.     struct ifinfomsg *ifi = NLMSG_DATA(&answer.n);
  203.     struct rtattr *tb[IFLA_MAX+1];

  204.     printf("struct ifinfomsg: ifi_family=%u,ifi_index=%d\n",ifi->ifi_family,ifi->ifi_index);


  205.     int len = answer.n.nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
  206.     parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);

  207.     printf("enum : IFLA_IFNAME=%s,IFLA_MTU=%d,IFLA_TXQLEN=%d\n",(char *)RTA_DATA(tb[IFLA_IFNAME]),
  208.                          *(int *)RTA_DATA(tb[IFLA_MTU]),
  209.                          *(int *)RTA_DATA(tb[IFLA_TXQLEN]));

  210.     printf("if_nametoindex:%d\n",if_nametoindex((char *)RTA_DATA(tb[IFLA_IFNAME])));
  211.     return 0;
  212. }




阅读(10830) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~