同事最近想用netlink 获取指定网卡的信息,在网上搜了大半天,都是用netlink获取全部网卡的信息的例子,没有获取指定网卡信息的例子,找我帮忙看看,建议他用ioctl(前段时间刚好总结了ioctl 相关的内容,想了解的请参考http://blog.chinaunix.net/uid-30226910-id-5766831.html),不过个人认为netlink 也肯定是能获取指定网卡的信息的。网上估计是搜不出来的,但是像ethtool ,ifconfig,ip 等命令都是能指定网卡的,难道都是向内核全部请求,然后再过滤的,这么低效的做法,感觉不太可能。
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):
-
enum {
-
RTM_BASE = 16,
-
RTM_NEWLINK = 16, /*链路相关的*/
-
RTM_DELLINK,
-
RTM_GETLINK,
-
RTM_SETLINK,
-
-
RTM_NEWADDR = 20, /*ip 地址*/
-
RTM_DELADDR,
-
RTM_GETADDR,
-
-
RTM_NEWROUTE = 24, /*路由信息*/
-
RTM_DELROUTE,
-
RTM_GETROUTE,
-
-
RTM_NEWNEIGH = 28, /*邻居信息*/
-
RTM_DELNEIGH,
-
RTM_GETNEIGH,
-
-
RTM_NEWRULE = 32,/*策略路由*/
-
RTM_DELRULE,
-
RTM_GETRULE,
-
-
RTM_NEWQDISC = 36, /*tc的qdisc*/
-
RTM_DELQDISC,
-
RTM_GETQDISC,
-
-
RTM_NEWTCLASS = 40, /*tc的class*/
-
RTM_DELTCLASS,
-
RTM_GETTCLASS,
-
-
RTM_NEWTFILTER = 44, /*tc的filter*/
-
RTM_DELTFILTER,
-
RTM_GETTFILTER,
-
-
RTM_NEWACTION = 48,/*tc的action*/
-
RTM_DELACTION,
-
RTM_GETACTION,
-
-
RTM_NEWPREFIX = 52,
-
RTM_GETPREFIX = 54,
-
-
RTM_GETMULTICAST = 58,
-
-
RTM_GETANYCAST = 62,
-
-
RTM_NEWNEIGHTBL = 64,
-
RTM_GETNEIGHTBL = 66,
-
RTM_SETNEIGHTBL,
-
-
RTM_NEWADDRLABEL = 72,
-
RTM_DELADDRLABEL,
-
RTM_GETADDRLABEL,
-
-
__RTM_MAX,
-
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
-
}
这个消息类型是放在struct nlmsghdr 的nlmsg_type 成员里的,根据不同的nlmsg_type, 发送/接收的时候可以用不同的xxxmsg数据结构以及对应的enum 来解析不同的nlmsg_type的消息,例如,nlmsg_type = RTM_GETLINK, 使用的msg数据结构和enum 结构如下:
-
struct ifinfomsg
-
{
-
unsigned char ifi_family;
-
unsigned char __ifi_pad;
-
unsigned short ifi_type; /* ARPHRD_* */
-
int ifi_index; /* Link index */
-
unsigned ifi_flags; /* IFF_* flags */
-
unsigned ifi_change; /* IFF_* change mask */
-
};
-
-
enum
-
{
-
IFLA_UNSPEC,
-
IFLA_ADDRESS,
-
IFLA_BROADCAST,
-
IFLA_IFNAME,
-
IFLA_MTU,
-
IFLA_LINK,
-
IFLA_QDISC,
-
IFLA_STATS,
-
IFLA_COST,
-
IFLA_PRIORITY,
-
IFLA_MASTER,
-
IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */
-
IFLA_PROTINFO, /* Protocol specific information for a link */
-
IFLA_TXQLEN,
-
IFLA_MAP,
-
IFLA_WEIGHT,
-
IFLA_OPERSTATE,
-
IFLA_LINKMODE,
-
__IFLA_MAX
-
};
其中enum 是解析xxxmsg数据结构后面的其他参数用的,解析参数用到的结构体如下:
-
struct rtattr
-
{
-
unsigned short rta_len;
-
unsigned short rta_type; /*这个type 就是上面enum 定义的类型*/
-
};
在头文件
linux/rtnetlink.h 中,定义各种消息类型的xxxmsg 数据结构,包括struct rtmsg ,
struct ndmsg,
struct
ndtmsg,
struct prefixmsg,
struct tcmsg,
struct 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的时候就根据family和type 选择对应的处理函数
贴一个例子,参考iproute2源码获取指定接口的链路信息实现的
-
#include<stdio.h>
-
#include<string.h>
-
#include <sys/types.h>
-
#include <sys/socket.h>
-
#include <errno.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
#include <fcntl.h>
-
-
#include<linux/rtnetlink.h>
-
#include<bits/socket.h>
-
#include<net/if.h>
-
-
#define ETH "eth0"
-
-
#define NLMSG_TAIL(nmsg) \
-
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
-
#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-
int seq = 0;
-
-
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,int alen)
-
{
-
int len = RTA_LENGTH(alen);
-
struct rtattr *rta;
-
-
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
-
printf("addattr_l ERROR: message exceeded bound of %d\n",maxlen);
-
return -1;
-
}
-
rta = NLMSG_TAIL(n);
-
rta->rta_type = type;
-
rta->rta_len = len;
-
memcpy(RTA_DATA(rta), data, alen);
-
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
-
return 0;
-
}
-
-
int netlink_socket(unsigned long groups)
-
{
-
socklen_t addr_len;
-
struct sockaddr_nl snl;
-
int fd = -1;
-
int ret;
-
-
-
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-
if (fd < 0) {
-
printf("Netlink: Cannot open netlink socket : (%s)",strerror(errno));
-
return -1;
-
}
-
-
ret = fcntl(fd, F_SETFL, O_NONBLOCK);
-
if (ret < 0) {
-
printf("Netlink: Cannot set netlink socket flags : (%s)",strerror(errno));
-
close(fd);
-
return -1;
-
}
-
-
memset(&snl, 0, sizeof (snl));
-
snl.nl_family = AF_NETLINK;
-
snl.nl_groups = groups; /*设为0 表示不用多播,其他情况按照设置的组来进行多播,就是会多播到这些组*/
-
-
ret = bind(fd, (struct sockaddr *) &snl, sizeof (snl));
-
if (ret < 0) {
-
printf("Netlink: Cannot bind netlink socket : (%s)",
-
strerror(errno));
-
close(fd);
-
return -1;
-
}
-
-
addr_len = sizeof (snl);
-
ret = getsockname(fd, (struct sockaddr *) &snl, &addr_len);
-
if (ret < 0 || addr_len != sizeof (snl)) {
-
printf("Netlink: Cannot getsockname : (%s)",strerror(errno));
-
close(fd);
-
return -1;
-
}
-
-
if (snl.nl_family != AF_NETLINK) {
-
printf("Netlink: Wrong address family %d",snl.nl_family);
-
close(fd);
-
return -1;
-
}
-
-
seq = time(NULL);
-
return fd;
-
}
-
-
int rtnl_talk(int fd , struct nlmsghdr *n,
-
struct nlmsghdr *answer, size_t maxlen)
-
{
-
int status;
-
unsigned int seq;
-
struct nlmsghdr *h;
-
struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
-
struct iovec iov = {
-
.iov_base = n,
-
.iov_len = n->nlmsg_len
-
};
-
struct msghdr msg = {
-
.msg_name = &nladdr,
-
.msg_namelen = sizeof(nladdr),
-
.msg_iov = &iov,
-
.msg_iovlen = 1,
-
};
-
char buf[32768] = {};
-
-
n->nlmsg_seq = ++seq;
-
-
if (answer == NULL)
-
n->nlmsg_flags |= NLM_F_ACK;
-
-
status = sendmsg(fd, &msg, 0);
-
if (status < 0) {
-
perror("Cannot talk to rtnetlink");
-
return -1;
-
}
-
-
iov.iov_base = buf;
-
while (1) {
-
iov.iov_len = sizeof(buf);
-
status = recvmsg(fd, &msg, 0);
-
-
if (status < 0) {
-
if (errno == EINTR || errno == EAGAIN)
-
continue;
-
printf("netlink receive error %s (%d)\n",strerror(errno), errno);
-
return -1;
-
}
-
if (status == 0) {
-
printf("EOF on netlink\n");
-
return -1;
-
}
-
if (msg.msg_namelen != sizeof(nladdr)) {
-
printf("sender address length == %d\n",msg.msg_namelen);
-
exit(1);
-
}
-
for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
-
int len = h->nlmsg_len;
-
int l = len - sizeof(*h);
-
-
if (l < 0 || len > status) {
-
if (msg.msg_flags & MSG_TRUNC) {
-
printf("Truncated message\n");
-
return -1;
-
}
-
printf("!!!malformed message: len=%d\n",len);
-
exit(1);
-
}
-
-
if (nladdr.nl_pid != 0 ||
-
h->nlmsg_seq != seq) {
-
/* Don't forget to skip that message. */
-
status -= NLMSG_ALIGN(len);
-
h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
-
continue;
-
}
-
-
if (h->nlmsg_type == NLMSG_ERROR) {
-
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
-
-
if (l < sizeof(struct nlmsgerr)) {
-
fprintf(stderr, "ERROR truncated\n");
-
} else if (!err->error) {
-
if (answer)
-
memcpy(answer, h,
-
MIN(maxlen, h->nlmsg_len));
-
return 0;
-
}
-
-
errno = -err->error;
-
return -1;
-
}
-
-
if (answer) {
-
memcpy(answer, h,
-
MIN(maxlen, h->nlmsg_len));
-
return 0;
-
}
-
-
printf("Unexpected reply!!!\n");
-
-
status -= NLMSG_ALIGN(len);
-
h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
-
}
-
-
if (msg.msg_flags & MSG_TRUNC) {
-
printf("Message truncated\n");
-
continue;
-
}
-
-
if (status) {
-
fprintf(stderr, "!!!Remnant of size %d\n", status);
-
exit(1);
-
}
-
}
-
}
-
-
void parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len)
-
{
-
while (RTA_OK(rta, len)) {
-
if (rta->rta_type <= max)
-
tb[rta->rta_type] = rta;
-
rta = RTA_NEXT(rta, len);
-
}
-
}
-
-
int main(int argc ,char **argv)
-
{
-
int fd ;
-
struct {
-
struct nlmsghdr n;
-
struct ifinfomsg i;
-
char buf[1024];
-
}req;
-
-
memset(&req,0,sizeof(req));
-
-
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
-
req.n.nlmsg_flags = NLM_F_REQUEST ;
-
req.n.nlmsg_type = RTM_GETLINK; /*消息类型*/
-
req.i.ifi_family = PF_UNSPEC; /*新版本的内核都是用这个来注册rtnl_register, 有旧版本的内核可能是用PF_PACKET*/
-
struct {
-
struct nlmsghdr n;
-
char buf[16384];
-
}answer;
-
-
memset(&answer,0,sizeof(answer));
-
-
fd = netlink_socket(0);
-
if(fd <0){
-
printf("netlink_socket failed\n");
-
return -1;
-
}
-
-
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, ETH, strlen(ETH)+1);
-
if (rtnl_talk(fd, &req.n, &answer.n, sizeof(answer)) < 0)
-
return -2;
-
-
struct ifinfomsg *ifi = NLMSG_DATA(&answer.n);
-
struct rtattr *tb[IFLA_MAX+1];
-
-
printf("struct ifinfomsg: ifi_family=%u,ifi_index=%d\n",ifi->ifi_family,ifi->ifi_index);
-
-
-
int len = answer.n.nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
-
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
-
printf("enum : IFLA_IFNAME=%s,IFLA_MTU=%d,IFLA_TXQLEN=%d\n",(char *)RTA_DATA(tb[IFLA_IFNAME]),
-
*(int *)RTA_DATA(tb[IFLA_MTU]),
-
*(int *)RTA_DATA(tb[IFLA_TXQLEN]));
-
-
printf("if_nametoindex:%d\n",if_nametoindex((char *)RTA_DATA(tb[IFLA_IFNAME])));
-
return 0;
-
}
阅读(11254) | 评论(0) | 转发(0) |