全部博文(298)
分类: LINUX
2011-04-07 18:43:43
(19)linux通过netlink查询路由信息
注:以下两个程序是网上摘抄的
http://blog.csdn.net/force_eagle/archive/2009/07/07/4326662.aspx
实例1:
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct req {
struct nlmsghdr nlmsg;
struct rtmsg rtm;
};
struct rtattr* get_gw_attr(struct nlmsghdr *nlmsghdr);
int print_route(struct rtattr *rtp);
int main(void)
{
int fd;
int n;
char *c;
char buff[BUFSIZ];
char str[22];
struct req req;
struct rtattr *rtp;
/*
struct rtattr
{
unsigned short rta_len;
unsigned short rta_type;
};
*/
struct nlmsghdr nlmsg, *nlp;
/*由于linux内核的netlink部分总是认为在每个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
};
*/
struct rtmsg rtm;
/*
struct rtmsg
{
unsigned char rtm_family;
unsigned char rtm_dst_len;
unsigned char rtm_src_len;
unsigned char rtm_tos;
unsigned char rtm_table; Routing table id
unsigned char rtm_protocol; Routing protocol; see below
unsigned char rtm_scope; See below
unsigned char rtm_type; See below
unsigned rtm_flags;
};
*/
struct sockaddr so;
/*
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;
*/
struct sockaddr_nl nl;
struct in_addr addr;
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
perror("open error");
exit (1);
}
nl.nl_family = AF_NETLINK;
nl.nl_pad = 0;
nl.nl_pid = getpid();
nl.nl_groups = 0;
n = bind(fd, (struct sockaddr*)&nl, sizeof(nl));
if (n < 0) {
perror(" bind error");
exit (1);
}
memset(&nlmsg, 0, sizeof(struct nlmsghdr));
req.nlmsg.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.nlmsg.nlmsg_type = RTM_GETROUTE;
/** In kernel header comments NLM_F_ROOT "specify tree root", but On somebooks
* said this means return the entire table not just one entry **/
req.nlmsg.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
// req.nlmsg.nlmsg_pid = nl.pid; /** this is not required! **/
memset(&rtm, 0, sizeof(struct rtmsg));
req.rtm.rtm_family = AF_NETLINK;
/** we need change here **/
req.rtm.rtm_dst_len = 4;
req.rtm.rtm_src_len = 4;
req.rtm.rtm_table = RT_TABLE_MAIN;
req.rtm.rtm_protocol = RTPROT_BOOT;/** Route installed during boot **/
req.rtm.rtm_scope = RT_SCOPE_LINK; /** located on directly LAN **/
req.rtm.rtm_type = RTN_UNICAST; /** Gateway or direct route **/
n = send(fd, &req, sizeof(req), 0);
if (n < 0)
{
perror("send error!");
exit (-1);
}
else
printf("%d bytes send!\n",n);
n = recv(fd, buff, BUFSIZ, 0);
if (n < 0)
perror("received failed!");
printf("%d bytes received!\n", n);
for (nlp = (struct nlmsghdr*)buff; \
(nlp->nlmsg_type != NLMSG_DONE)&& NLMSG_OK(nlp, n); nlp = NLMSG_NEXT(nlp, n)) {
rtp = get_gw_attr(nlp);
if (rtp) {
print_route(rtp);
return;
}
}
}
struct rtattr* get_gw_attr(struct nlmsghdr *nlmsghdr)
{
struct rtattr *rta;
int len;
int gw;
char str[16];
len = nlmsghdr->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
/** NLMSG_DATA(nlmsghdr) return the rtmsg pointer following, and RTM_RTA return
the rtattr pointer following the rtmsg. **/
rta = RTM_RTA(NLMSG_DATA(nlmsghdr));
while (RTA_OK(rta, len)) {
if (rta->rta_type >= RTA_MAX)
break;
/** We check if the address is INADDR_ANY.I don't know whethher this is needed **/
if(rta->rta_type == RTA_GATEWAY && *(int *)RTA_DATA(rta) != INADDR_ANY)
return rta;
rta = RTA_NEXT(rta, len);
}
return NULL;
};
int print_route(struct rtattr *rtp)
{
char str[16];
char *c;
char buff[16];
c = inet_ntop (AF_INET, RTA_DATA(rtp), buff, INET_ADDRSTRLEN);
if (!c) {
perror("inet_ntop failed !");
exit (1);
}
printf("The gateway IP address is %s\n",c);
}
实例2:
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef uint32_t u32;
typedef uint16_t u16;
struct nlsock {
int sock;//套接字描述符
int seq;//包序号
struct sockaddr_nl snl;//netlink地址结构体
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套接字 ,并且协议域是NETLINK_ROUTE路由器协议
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;
}
/*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;
*/
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;
}
//请求的地址簇,类型
// ret = nl_request ( AF_INET, RTM_GETROUTE, &nl_cmd );
static int nl_request ( int family, int type, struct nlsock *nl )
{
int ret;
struct sockaddr_nl snl;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;//struct rtgenmsg结构
} req;
/*struct rtgenmsg
{
unsigned char rtgen_family;
};*/
/*由于linux内核的netlink部分总是认为在每个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
};
*/
/* 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;
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;
while ( 1 ) {
char buf[4096];
struct iovec iov = { buf, sizeof buf };
struct sockaddr_nl snl;
struct msghdr msg = { ( void* ) &snl, sizeof snl, &iov, 1, NULL, 0, 0};
struct nlmsghdr *h;
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;
}
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 );
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;
}
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;
u32 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 = *(u32 *) RTA_DATA ( tb[IFA_ADDRESS] );
if ( tb[IFA_LOCAL] )
ifa_local = *(u32 *) RTA_DATA ( tb[IFA_LOCAL] );
if ( tb[IFA_LABEL] )
strncpy( ifa_label, RTA_DATA ( tb[IFA_LABEL] ), IFNAMSIZ );
// 打印所有地址信息
printf( "addr=%08x loal=%08x name=%s\n",
ifa_addr,
ifa_local,
ifa_label );
return 0;
}
int main()
{
int ret;
char if_name[PAGE_SIZE];
char *p;
struct nl_if_info if_info = { -1, "eth0" };
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 \n", index_oif );
if ( index_oif > 0 ) {
p = if_indextoname ( index_oif, if_name );
if ( p ) {
printf ( "interface=%s\n", p );
}
}
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;
}