Netlink 获取网卡接口信息

#include <linux/types.h>  
#include <asm/types.h>  
#include <inttypes.h>  
#include <sys/file.h>  
#include <sys/user.h>  
#include <sys/socket.h>  
#include <linux/netlink.h>  
#include <linux/rtnetlink.h>  
#include <linux/if.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <string.h>  
#include <stdio.h>  
#include <stdbool.h>  
#include <errno.h>  


#include <arpa/inet.h>
typedef uint32_t u32;  
typedef uint16_t u16;  
  
struct nlsock {  
    int sock;  
    int seq;  
    struct sockaddr_nl snl;  
    char *name;  
} nl_cmd = { -1, 0, {0}, "netlink-cmd" };  
  
static int index_oif = 0;  
struct nl_if_info {  
    u32 addr;  
    char *name;  
};  
static int nl_socket ( struct nlsock *nl, unsigned long groups )  
{  
    int ret;  
    struct sockaddr_nl snl;  
    int sock;  
    int namelen;  
	
  	/*创建netlink套接字*/
    sock = socket ( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE );  
    if ( sock < 0 ) {  
        fprintf ( stderr,  "Can't open %s socket: %s", nl->name,  
                strerror ( errno ) );  
        return -1;  
    }  
  
    ret = fcntl ( sock, F_SETFL, O_NONBLOCK );  
    if ( ret < 0 ) {  
        fprintf ( stderr,  "Can't set %s socket flags: %s", nl->name,  
                strerror ( errno ) );  
        close ( sock );  
        return -1;  
    }  
	
  	/*设置地址信息*/
    memset ( &snl, 0, sizeof snl );  
    snl.nl_family = AF_NETLINK;  
    snl.nl_groups = groups;  
  
    /* Bind the socket to the netlink structure for anything. */  
    ret = bind ( sock, ( struct sockaddr * ) &snl, sizeof snl );  
    if ( ret < 0 ) {  
        fprintf ( stderr,  "Can't bind %s socket to group 0x%x: %s",  
                nl->name, snl.nl_groups, strerror ( errno ) );  
        close ( sock );  
        return -1;  
    }  
  
    /* multiple netlink sockets will have different nl_pid */  
    namelen = sizeof snl;  
    ret = getsockname ( sock, ( struct sockaddr * ) &snl, &namelen );  
    if ( ret < 0 || namelen != sizeof snl ) {  
        fprintf ( stderr,  "Can't get %s socket name: %s", nl->name,  
                strerror ( errno ) );  
        close ( sock );  
        return -1;  
    }  
  
    nl->snl = snl;  
    nl->sock = sock;  
    return ret;  
}  
/*
*	发送请求
*/
static int nl_request ( int family, int type, struct nlsock *nl )  
{  
    int ret;  
    struct sockaddr_nl snl;  
  
    struct {  
        struct nlmsghdr nlh;  
        struct rtgenmsg g;  
    } req;  
  
  
    /* Check netlink socket. */  
    if ( nl->sock < 0 ) {  
        fprintf ( stderr, "%s socket isn't active.", nl->name );  
        return -1;  
    }  
  
    memset ( &snl, 0, sizeof snl );  
    snl.nl_family = AF_NETLINK;  
  
    req.nlh.nlmsg_len = sizeof req;  									//
    
    req.nlh.nlmsg_type = type;  										//设置自定义消息类型
    
    req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;  	//消息的附加选项
    
    req.nlh.nlmsg_pid = 0;  											//设置发送者的pid
    
    req.nlh.nlmsg_seq = ++nl->seq;  									//
    
    req.g.rtgen_family = family;  										//
  
    ret = sendto ( nl->sock, ( void* ) &req, sizeof req, 0,  
            ( struct sockaddr* ) &snl, sizeof snl );  
    if ( ret < 0 ) {  
        fprintf ( stderr, "%s sendto failed: %s", nl->name, strerror ( errno ) );  
        return -1;  
    }  
    return 0;  
}  
  
/* Receive message from netlink interface and pass those information 
   to the given function. */  
static int  
nl_parse_info ( int ( *filter ) ( struct sockaddr_nl *, struct nlmsghdr *, void * ),  
        struct nlsock *nl, void *arg )  
{  
    int status;  
    int ret = 0;  
    int error;  
	
  	/*接受队列里的所有信息,每次接受4096,并解析*/
    while ( 1 ) {  
        char buf[4096];  
        struct iovec iov = { buf, sizeof buf };  //netlink消息头
        struct sockaddr_nl snl;  
        struct msghdr msg = { ( void* ) &snl, sizeof snl, &iov, 1, NULL, 0, 0};  //设置消息头
        struct nlmsghdr *h;  //netlink消息头
  
        status = recvmsg ( nl->sock, &msg, 0 );  
  
        if ( status < 0 ) {  
            if ( errno == EINTR )  
                continue;  
            if ( errno == EWOULDBLOCK || errno == EAGAIN )  
                break;  
            fprintf ( stderr, "%s recvmsg overrun", nl->name );  
            continue;  
        }  
  		/*如果不是内核的消息,忽略*/
        if ( snl.nl_pid != 0 ) {  
            fprintf ( stderr, "Ignoring non kernel message from pid %u",  
                    snl.nl_pid );  
            continue;  
        }  
  		/*转出错处理*/
        if ( status == 0 ) {  
            fprintf ( stderr, "%s EOF", nl->name );  
            return -1;  
        }  
  		/*如果socket地址长度不对,转出错处理*/
        if ( msg.msg_namelen != sizeof snl ) {  
            fprintf ( stderr, "%s sender address length error: length %d",  
                    nl->name, msg.msg_namelen );  
            return -1;  
        }  
  
        for ( h = ( struct nlmsghdr * ) buf; NLMSG_OK ( h, status );  //枚举缓冲中的netlink消息
                h = NLMSG_NEXT ( h, status ) ) {  
            /* Finish of reading. */  
            if ( h->nlmsg_type == NLMSG_DONE )  //消息已读取完毕
                return ret;  
  
            /* Error handling. */  
            if ( h->nlmsg_type == NLMSG_ERROR ) {  //消息读取出错
                struct nlmsgerr *err = ( struct nlmsgerr * ) NLMSG_DATA ( h );  
  
                /* If the error field is zero, then this is an ACK */  
                if ( err->error == 0 ) {  
                    /* return if not a multipart message, otherwise continue */  
                    if ( ! ( h->nlmsg_flags & NLM_F_MULTI ) ) {  
                        return 0;  
                    }  
                    continue;  
                }  
  
                if ( h->nlmsg_len < NLMSG_LENGTH ( sizeof ( struct nlmsgerr ) ) ) {  
                    fprintf ( stderr, "%s error: message truncated",  
                            nl->name );  
                    return -1;  
                }  
                fprintf ( stderr, "%s error: %s, type=%u, seq=%u, pid=%d",  
                        nl->name, strerror ( -err->error ),  
                        err->msg.nlmsg_type, err->msg.nlmsg_seq,  
                        err->msg.nlmsg_pid );  
                /* 
                ret = -1; 
                continue; 
                */  
                return -1;  
            }  
  
  
            /* skip unsolicited messages originating from command socket */  
            if ( nl != &nl_cmd && h->nlmsg_pid == nl_cmd.snl.nl_pid ) {  
                continue;  
            }  
			
  			/*读取一条netlink消息,就解析得到的消息*/
            error = ( *filter ) ( &snl, h, arg );  
            if ( error < 0 ) {  
                fprintf ( stderr, "%s filter function error/n", nl->name );  
                ret = error;  
            }  
        }  
  
        /* After error care. */  
        if ( msg.msg_flags & MSG_TRUNC ) {  
            fprintf ( stderr, "%s error: message truncated", nl->name );  
            continue;  
        }  
        if ( status ) {  
            fprintf ( stderr, "%s error: data remnant size %d", nl->name,  
                    status );  
            return -1;  
        }  
    }  
    return ret;  
}  
  
static void nl_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 );  
    }  
}  
  
static int nl_get_oif ( struct sockaddr_nl *snl, struct nlmsghdr *h, void *arg )  
{  
    int len;  
    struct rtmsg *rtm;  
    struct rtattr *tb [RTA_MAX + 1];  
    u_char flags = 0;  
  
    char anyaddr[16] = {0};  
  
    int index;  
    int table;  
    void *dest;  
    void *gate;  
  
    rtm = NLMSG_DATA ( h );   //得到消息数据部分
  
    if ( h->nlmsg_type != RTM_NEWROUTE )  
        return 0;  
    if ( rtm->rtm_type != RTN_UNICAST )  
        return 0;  
  
    table = rtm->rtm_table;  
  
    len = h->nlmsg_len - NLMSG_LENGTH ( sizeof ( struct rtmsg ) );  
    if ( len < 0 )  
        return -1;  
  
    memset ( tb, 0, sizeof tb );  
    nl_parse_rtattr ( tb, RTA_MAX, RTM_RTA ( rtm ), len );  
  
    if ( rtm->rtm_flags & RTM_F_CLONED )  
        return 0;  
    if ( rtm->rtm_protocol == RTPROT_REDIRECT )  
        return 0;  
    if ( rtm->rtm_protocol == RTPROT_KERNEL )  
        return 0;  
  
    if ( rtm->rtm_src_len != 0 )  
        return 0;  
  
   
  
    // 这里可以对所有路由进行识别
  
   
  
    // 取得out interface index  
  
    if ( tb[RTA_OIF] ) {  
        index = * ( int * ) RTA_DATA ( tb[RTA_OIF] );  
    }  
  
    if ( tb[RTA_DST] )  
        dest = RTA_DATA ( tb[RTA_DST] );  
    else  
        dest = anyaddr;  
  
    /* Multipath treatment is needed. */  
    if ( tb[RTA_GATEWAY] )  
        gate = RTA_DATA ( tb[RTA_GATEWAY] );  
  
   
  
    // 判断是否为默认路由
  
    if ( dest == anyaddr && gate ) {  
        if ( arg != NULL ) {  
            * ( int * ) arg = index;  
            return 0;  
        }  
    }  
    return 0;  
}  
  
static int nl_get_if_addr ( struct sockaddr_nl *snl, struct nlmsghdr *h, void *arg )  
{  
    int len;  
    struct ifaddrmsg *ifa;  
    struct rtattr *tb [IFA_MAX + 1];  
    void *addr = NULL;  
    void *broad = NULL;  
    u_char flags = 0;  
    char *label = NULL;  
    struct in_addr ifa_addr, ifa_local;  
    char ifa_label[IFNAMSIZ + 1];  
   
    ifa = NLMSG_DATA ( h );  //得到消息数据部分
    if ( ifa->ifa_family != AF_INET )  
     return 0;  
       
    if ( h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR )  
        return 0;  
  
    len = h->nlmsg_len - NLMSG_LENGTH ( sizeof ( struct ifaddrmsg ) );  
    if ( len < 0 )  
        return -1;  
  
    memset ( tb, 0, sizeof tb );  
  
    nl_parse_rtattr ( tb, IFA_MAX, IFA_RTA ( ifa ), len );  
      
    if (tb[IFA_ADDRESS] == NULL)  
    tb[IFA_ADDRESS] = tb[IFA_LOCAL];  

	/*网卡地址*/
    if ( tb[IFA_ADDRESS] )  
     ifa_addr = *(struct in_addr*) RTA_DATA ( tb[IFA_ADDRESS] );  
       
    if ( tb[IFA_LOCAL] )  
     ifa_local = *(struct in_addr *) RTA_DATA ( tb[IFA_LOCAL] );  

	 /*网卡名*/
    if ( tb[IFA_LABEL] )  
     strncpy( ifa_label, RTA_DATA ( tb[IFA_LABEL] ), IFNAMSIZ );   
       
  
    // 打印所有地址信息
    printf( "addr=%s	loal=%s	name=%s\n",   
     inet_ntoa(ifa_addr),  
     inet_ntoa(ifa_local),  
     ifa_label );  
    return 0;  
}  
  
int main()  
{  
    int ret;  
    char if_name[PAGE_SIZE]={0};  
    int err;  
    struct nl_if_info if_info = { -1, "eth0" };  
	
  	/*初始化nl_cmd*/
    ret = nl_socket ( &nl_cmd, 0 );  
    if ( ret < 0 ) {  
        return ret;  
    }  
	
	
    ret = nl_request ( AF_INET, RTM_GETROUTE, &nl_cmd );  //获取路由信息
    if ( ret < 0 ) {  
        return ret;  
    }  

    ret = nl_parse_info ( nl_get_oif, &nl_cmd, &index_oif );  
    if ( ret < 0 )  
        return ret;  
  	
    printf ( "oif=%08x	", index_oif );  
    if ( index_oif > 0 ) {  
     err = if_indextoname( index_oif, if_name );  
     if (err ) {  
         printf ( "interface=%s\n", if_name );  
     }  
   }  
   
   
    ret = nl_request ( AF_INET, RTM_GETADDR, &nl_cmd );  //获取地址信息
    if ( ret < 0 )  
        return ret;  
   
    ret = nl_parse_info ( nl_get_if_addr, &nl_cmd, &if_info );  
    if ( ret < 0 )  
        return ret;  
      
  
    return 0;  
}  


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值