kernel bug? - routing table entry lost

From: Jeff Haran
Date: Mon Sep 12 2005 - 17:10:40 EST


Hi all,

I've been experimenting with the configuration of IP aliases on a 2.4.19 based kernel and have run across some behavior that I suspect is a bug, but am wondering if perhaps I am just doing something wrong.

In summary, what I find is that if I start with an interface that is configured with a single IP address, then add a secondary IP address to that interface and then delete that secondary IP address so that I am now presumably back where I started, one of the internal routes generated by the kernel disappears.

To get visibility into this, I wrote a program (adopted from some code I found on another site) that sends a RTM_GETROUTE message to a NETLINK_ROUTE socket and then reads and displays the response from the kernel. That program is called route_get in the logs that follow and I've copied the source to route_get to the end of email.

The annotated console log that demonstrates the problem follows. I start with interfaces down and no IP addresses assigned:

FD21:root> ip -s -s link show
1: lo: <LOOPBACK> mtu 16436 qdisc noop
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
RX: bytes packets errors dropped overrun mcast
0 0 0 0 0 0
RX errors: length crc frame fifo missed
0 0 0 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
TX errors: aborted fifo window heartbeat
0 0 0 0
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 100
link/ether 00:60:69:90:02:7a brd ff:ff:ff:ff:ff:ff
RX: bytes packets errors dropped overrun mcast
0 0 0 0 0 0
RX errors: length crc frame fifo missed
0 0 0 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
TX errors: aborted fifo window heartbeat
0 0 0 0
FD21:root> ip -s -s addr show
1: lo: <LOOPBACK> mtu 16436 qdisc noop
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 100
link/ether 00:60:69:90:02:7a brd ff:ff:ff:ff:ff:ff

So now I configure eth0 up, but still with no addresses:

FD21:root> ip link set dev eth0 up
FD21:root> eth0: Link status change: Link Up. 100 Mbps Half duplex Auto (autonegotiation complete).

I add the first IP address and run my route_get program to see the resultant routes:

FD21:root> ip address add 192.168.78.157/24 brd + dev eth0
FD21:root> ./route_get
nlmsg_type = 24 RTM_NEWROUTE
nlmsg_flags = 0x0
readSock() returned 208
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 24
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 254 RT_TABLE_MAIN
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 1 RTN_UNICAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.0
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 3 RTN_BROADCAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.255
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 254 RT_SCOPE_HOST
rtmsg: rtm_type 2 RTN_LOCAL
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.157
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 3 RTN_BROADCAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.0
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
============================================
Destination Gateway Interface Source
192.168.78.0 *.*.*.* eth0 192.168.78.157
192.168.78.255 *.*.*.* eth0 192.168.78.157
192.168.78.157 *.*.*.* eth0 192.168.78.157
192.168.78.0 *.*.*.* eth0 192.168.78.157

route_get displays each route in the response from the kernel in detail, followed by the short netstat style summary of the routes shown in the 4 above lines. The way I interpret the above routes, first to last, is as follows:

1) The unicast route to the directly connected subnet.
2) A broadcast route to the subnet where the broadcast address is the all 1s in the host number form.
3) The unicast route to the host's own IP address.
4) A broadcast route to the subnet where the broadcast address is the all 0s in the host number form.

So now I add the alias, an IP address on the same subnet on the same device:

FD21:root> ip address add 192.168.78.158/24 brd + dev eth0 label eth0:0
FD21:root> ip -s -s addr show
1: lo: <LOOPBACK> mtu 16436 qdisc noop
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:60:69:90:02:7a brd ff:ff:ff:ff:ff:ff
inet 192.168.78.157/24 brd 192.168.78.255 scope global eth0
inet 192.168.78.158/24 brd 192.168.78.255 scope global secondary eth0:0

Running route_get, I see one more route has been created:

FD21:root> ./route_get
nlmsg_type = 24 RTM_NEWROUTE
nlmsg_flags = 0x0
readSock() returned 260
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 24
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 254 RT_TABLE_MAIN
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 1 RTN_UNICAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.0
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 254 RT_SCOPE_HOST
rtmsg: rtm_type 2 RTN_LOCAL
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.158
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 3 RTN_BROADCAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.255
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 254 RT_SCOPE_HOST
rtmsg: rtm_type 2 RTN_LOCAL
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.157
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 3 RTN_BROADCAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.0
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
============================================
Destination Gateway Interface Source
192.168.78.0 *.*.*.* eth0 192.168.78.157
192.168.78.158 *.*.*.* eth0 192.168.78.157
192.168.78.255 *.*.*.* eth0 192.168.78.157
192.168.78.157 *.*.*.* eth0 192.168.78.157
192.168.78.0 *.*.*.* eth0 192.168.78.157

The new route (the second listed) is the unicast route to the additional IP address. All the other routes are the same. Near as I can tell so far, all this is correct.

Now I delete the secondary IP address (the alias). So now I am back to the single IP address and I would think should be in the same state as before I added the alias.

FD21:root> ip address delete 192.168.78.158 dev eth0 label eth0:0
FD21:root> ip -s -s addr show
1: lo: <LOOPBACK> mtu 16436 qdisc noop
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:60:69:90:02:7a brd ff:ff:ff:ff:ff:ff
inet 192.168.78.157/24 brd 192.168.78.255 scope global eth0

So now I run route_get to see my routes:

FD21:root> ./route_get
nlmsg_type = 24 RTM_NEWROUTE
nlmsg_flags = 0x0
readSock() returned 156
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 24
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 254 RT_TABLE_MAIN
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 1 RTN_UNICAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.0
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 253 RT_SCOPE_LINK
rtmsg: rtm_type 3 RTN_BROADCAST
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.255
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
=============================================
rtmsg: rtm_family 2 AF_INET
rtmsg: rtm_dst_len 32
rtmsg: rtm_src_len 0
rtmsg: rtm_tos 0
rtmsg: rtm_table 255 RT_TABLE_LOCAL
rtmsg: rtm_protocol 2 RTPROT_KERNEL
rtmsg: rtm_scope 254 RT_SCOPE_HOST
rtmsg: rtm_type 2 RTN_LOCAL
rtmsg: rtm_flags 0
RTA_DST: 192.168.78.157
RTA_PREFSRC: 192.168.78.157
RTA_OIF: eth0
============================================
Destination Gateway Interface Source
192.168.78.0 *.*.*.* eth0 192.168.78.157
192.168.78.255 *.*.*.* eth0 192.168.78.157
192.168.78.157 *.*.*.* eth0 192.168.78.157
FD21:root>

It seems like I am now missing a route, specifically number (4) from the list above:

4) A broadcast route to the subnet where the broadcast address is the all 0s in the host number form.

Is this a feature or a bug (or is this a figment of route_get)?
If this is a kernel bug, has it been previously reported?

Thanks,

Jeff Haran
Brocade Communications Systems

p.s. The source to route_get is copied below:

/* 8-04-2005, jharan,

stolen from: http://mail.nl.linux.org/kernelnewbies/2003-12/msg00139.html

Hi all,
i am including another piece of code which i have written to print the
routes
in the kernel routing table using rtnetlink. Please do let me know if there
is a better way of doing this.

*/

#include <asm/types.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <string.h>
#include <arpa/inet.h>

#define NL_BUFSIZE 8192

#ifndef IF_NAMESIZE
#define IF_NAMESIZE 16
#endif

struct route_info{
struct route_info *next;
struct in_addr dstAddr;
struct in_addr srcAddr;
struct in_addr gateWay;
char ifName[IF_NAMESIZE + 50];
};

struct route_info *route_info_head = 0;
struct route_info *route_info_tail = 0;

typedef struct {
__u16 nlmsg_type;
char *name;
} nlmsg_type_name;

#define NLMSG_NAME_INIT(name) { name, #name }

nlmsg_type_name nlmsg_type_table[] = {
NLMSG_NAME_INIT(NLMSG_NOOP),
NLMSG_NAME_INIT(NLMSG_ERROR),
NLMSG_NAME_INIT(NLMSG_DONE),
NLMSG_NAME_INIT(NLMSG_OVERRUN),
NLMSG_NAME_INIT(RTM_NEWLINK),
NLMSG_NAME_INIT(RTM_DELLINK),
NLMSG_NAME_INIT(RTM_GETLINK),
NLMSG_NAME_INIT(RTM_NEWADDR),
NLMSG_NAME_INIT(RTM_DELADDR),
NLMSG_NAME_INIT(RTM_GETADDR),
NLMSG_NAME_INIT(RTM_NEWROUTE),
NLMSG_NAME_INIT(RTM_DELROUTE),
NLMSG_NAME_INIT(RTM_GETROUTE),
NLMSG_NAME_INIT(RTM_NEWNEIGH),
NLMSG_NAME_INIT(RTM_DELNEIGH),
NLMSG_NAME_INIT(RTM_GETNEIGH),
NLMSG_NAME_INIT(RTM_NEWRULE),
NLMSG_NAME_INIT(RTM_DELRULE),
NLMSG_NAME_INIT(RTM_GETRULE),
NLMSG_NAME_INIT(RTM_NEWQDISC),
NLMSG_NAME_INIT(RTM_DELQDISC),
NLMSG_NAME_INIT(RTM_GETQDISC),
NLMSG_NAME_INIT(RTM_NEWTCLASS),
NLMSG_NAME_INIT(RTM_DELTCLASS),
NLMSG_NAME_INIT(RTM_GETTCLASS),
NLMSG_NAME_INIT(RTM_NEWTFILTER),
NLMSG_NAME_INIT(RTM_DELTFILTER),
NLMSG_NAME_INIT(RTM_GETTFILTER),
NLMSG_NAME_INIT(RTM_MAX),
{ 0, 0 } /* end of table marker */
};

char *lookup_nlmsg_type(__u16 nlmsg_type)
{
int i;

for (i = 0; nlmsg_type_table[i].name; ++i) {
if (nlmsg_type_table[i].nlmsg_type == nlmsg_type)
return nlmsg_type_table[i].name;
}
return ("unknown nlmsg_type");
}

typedef struct {
int nlmsg_flag;
char *name;
} nlmsg_flags_name;

#define NLMSG_FLAGS_INIT(name) { name, #name }

nlmsg_flags_name nlmsg_flags_table[] = {
NLMSG_FLAGS_INIT(NLM_F_REQUEST),
NLMSG_FLAGS_INIT(NLM_F_MULTI),
NLMSG_FLAGS_INIT(NLM_F_ACK),
NLMSG_FLAGS_INIT(NLM_F_ECHO),
NLMSG_FLAGS_INIT(NLM_F_ROOT),
NLMSG_FLAGS_INIT(NLM_F_MATCH),
NLMSG_FLAGS_INIT(NLM_F_ATOMIC),
NLMSG_FLAGS_INIT(NLM_F_REPLACE),
NLMSG_FLAGS_INIT(NLM_F_EXCL),
NLMSG_FLAGS_INIT(NLM_F_CREATE),
NLMSG_FLAGS_INIT(NLM_F_APPEND),
{ 0, 0 } /* end of table marker */
};

__u16 print_nlmsg_flags(__u16 nlmsg_flags)
{
int i;

/* while some flag bits are still set */
while (nlmsg_flags) {
/* search table for a flag where we know what it is */
for (i = 0; nlmsg_flags_table[i].name; ++i) {
/* known flag? */
if (nlmsg_flags_table[i].nlmsg_flag & nlmsg_flags) {
/* print its name */
fprintf(stdout, "%s ", nlmsg_flags_table[i].name);
/* reset the flag */
nlmsg_flags &= ~nlmsg_flags_table[i].nlmsg_flag;
break;
}
}
/* if we couldn't find any matching flags, bail */
if (!nlmsg_flags_table[i].name)
break;
}
/* if any unknown flags left, display them numerically */
if (nlmsg_flags)
fprintf(stdout, "mystery flags 0x%x", nlmsg_flags);
return nlmsg_flags;
}

typedef struct {
int rtmsg_flag;
char *name;
} rtmsg_flags_name;

#define RTMSG_FLAGS_INIT(name) { name, #name }

rtmsg_flags_name rtmsg_flags_table[] = {
RTMSG_FLAGS_INIT(RTM_F_NOTIFY),
RTMSG_FLAGS_INIT(RTM_F_CLONED),
RTMSG_FLAGS_INIT(RTM_F_EQUALIZE),
{ 0, 0 } /* end of table marker */
};

unsigned int print_rtmsg_flags(unsigned int rtmsg_flags)
{
int i;

/* while some flag bits are still set */
while (rtmsg_flags) {
/* search table for a flag where we know what it is */
for (i = 0; rtmsg_flags_table[i].name; ++i) {
/* known flag? */
if (rtmsg_flags_table[i].rtmsg_flag & rtmsg_flags) {
/* print its name */
fprintf(stdout, "%s ", rtmsg_flags_table[i].name);
/* reset the flag */
rtmsg_flags &= ~rtmsg_flags_table[i].rtmsg_flag;
break;
}
}
/* if we couldn't find any matching flags, bail */
if (!rtmsg_flags_table[i].name)
break;
}
/* if any unknown flags left, display them numerically */
if (rtmsg_flags)
fprintf(stdout, "mystery flags 0x%x", rtmsg_flags);
return rtmsg_flags;
}

int readSock(int sockFd, char *bufPtr, int seqNum, int pId)
{
struct nlmsghdr *nlHdr;
int readLen = 0, flag = 0, msgLen = 0;

do{
/* Recieve response from kernel */
if ((readLen = recv(sockFd, bufPtr, NL_BUFSIZE - msgLen, 0)) < 0){
perror("SOCK READ: ");
return -1;
}
nlHdr = (struct nlmsghdr *)bufPtr;

fprintf(stdout, "nlmsg_type = %d %s\n",
nlHdr->nlmsg_type, lookup_nlmsg_type(nlHdr->nlmsg_type));
fprintf(stdout, "nlmsg_flags = 0x%x ", nlHdr->nlmsg_flags);
print_nlmsg_flags(nlHdr->nlmsg_flags);
fprintf(stdout, "\n");

/* Check if header is valid */
if (NLMSG_OK(nlHdr, readLen) == 0) {
fprintf(stdout, "NLMSG_OK == 0\n");
return -1;
}

/* check for error packet */
if (nlHdr->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *errHdr;

errHdr = (struct nlmsgerr *) NLMSG_DATA(nlHdr);
fprintf(stdout,
"Received NLMSG_ERROR, error = %d %s\n",
errHdr->error, strerror(-(errHdr->error)));
return -1;
}

/* Check if its the last message */
if (nlHdr->nlmsg_type == NLMSG_DONE) {
flag = 1;
break;
} else {
/* Move the buffer pointer appropriately */
bufPtr += readLen;
msgLen += readLen;
}
#if 1
/* jharan, though it seems to work, not clear to me that the following
is "correct". I think we should only be looking for the NLMSG_DONE
to terminate, like is done above */
if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0){
flag = 1;
break;
}
#endif
} while ((nlHdr->nlmsg_seq != seqNum) ||
(nlHdr->nlmsg_pid != pId) || (flag == 0));
return msgLen;
}

/* For printing the routes. */
void printRoute(struct route_info *rtInfo)
{
char tempBuf[512];

/* Print Destination address */
if(rtInfo->dstAddr.s_addr != 0)
strcpy(tempBuf, (char *)inet_ntoa((struct in_addr) (rtInfo->dstAddr)));
else
sprintf(tempBuf,"*.*.*.*\t");
fprintf(stdout,"%s\t", tempBuf);

/* Print Gateway address */
if(rtInfo->gateWay.s_addr != 0)
strcpy(tempBuf, (char *)inet_ntoa((struct in_addr) (rtInfo->gateWay)));
else
sprintf(tempBuf,"*.*.*.*\t");
fprintf(stdout,"%s\t", tempBuf);

/* Print Interface Name*/
fprintf(stdout,"%s\t\t", rtInfo->ifName);

/* Print Source address */
if(rtInfo->srcAddr.s_addr != 0)
strcpy(tempBuf, (char *)inet_ntoa((struct in_addr) (rtInfo->srcAddr)));
else
sprintf(tempBuf,"*.*.*.*\t");
fprintf(stdout,"%s\n", tempBuf);
}

/* For parsing the route info returned */
void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo)
{
struct rtmsg *rtMsg;
struct rtattr *rtAttr;
int rtLen;
char tempBuf[100];

rtMsg = (struct rtmsg *)NLMSG_DATA(nlHdr);

fprintf(stdout, "=============================================\n");
fprintf(stdout, "rtmsg: rtm_family %d\t", rtMsg->rtm_family);
if (rtMsg->rtm_family == AF_INET)
fprintf(stdout, "AF_INET\n");
else
fprintf(stdout, "mystery\n");

/* from the data, rtm_dst_len appears the be the number of bits
set in the subnet mask, e.g. the 8 in 10.0.0.1/8.
in case of rtm_dst_len == 32, looks like a host route */
fprintf(stdout, "rtmsg: rtm_dst_len %d\n", rtMsg->rtm_dst_len);
/* rtm_src_len always seems to be 0, not sure what it means */
fprintf(stdout, "rtmsg: rtm_src_len %d\n", rtMsg->rtm_src_len);
fprintf(stdout, "rtmsg: rtm_tos %d\n", rtMsg->rtm_tos);
fprintf(stdout, "rtmsg: rtm_table %d\t", rtMsg->rtm_table);

if (rtMsg->rtm_table == RT_TABLE_UNSPEC)
fprintf(stdout, "RT_TABLE_UNSPEC\n");
else if (rtMsg->rtm_table == RT_TABLE_DEFAULT)
fprintf(stdout, "RT_TABLE_DEFAULT\n");
else if (rtMsg->rtm_table == RT_TABLE_MAIN)
fprintf(stdout, "RT_TABLE_MAIN\n");
else if (rtMsg->rtm_table == RT_TABLE_LOCAL)
fprintf(stdout, "RT_TABLE_LOCAL\n");
else
fprintf(stdout, "mystery\n");

fprintf(stdout, "rtmsg: rtm_protocol %d\t", rtMsg->rtm_protocol);

if (rtMsg->rtm_protocol == RTPROT_REDIRECT)
fprintf(stdout, "RTPROT_REDIRECT\n");
else if (rtMsg->rtm_protocol == RTPROT_KERNEL)
fprintf(stdout, "RTPROT_KERNEL\n");
else if (rtMsg->rtm_protocol == RTPROT_BOOT)
fprintf(stdout, "RTPROT_BOOT\n");
else if (rtMsg->rtm_protocol == RTPROT_STATIC)
fprintf(stdout, "RTPROT_STATIC\n");
else
/* there's more of these in rtnetlink.h,
just handling the common ones for now */
fprintf(stdout, "mystery\n");

fprintf(stdout, "rtmsg: rtm_scope %d\t", rtMsg->rtm_scope);

if (rtMsg->rtm_scope == RT_SCOPE_UNIVERSE)
fprintf(stdout, "RT_SCOPE_UNIVERSE\n");
else if (rtMsg->rtm_scope == RT_SCOPE_SITE)
fprintf(stdout, "RT_SCOPE_SITE\n");
else if (rtMsg->rtm_scope == RT_SCOPE_LINK)
fprintf(stdout, "RT_SCOPE_LINK\n");
else if (rtMsg->rtm_scope == RT_SCOPE_HOST)
fprintf(stdout, "RT_SCOPE_HOST\n");
else if (rtMsg->rtm_scope == RT_SCOPE_NOWHERE)
fprintf(stdout, "RT_SCOPE_NOWHERE\n");
else
fprintf(stdout, "mystery\n");

fprintf(stdout, "rtmsg: rtm_type %d\t", rtMsg->rtm_type);

if (rtMsg->rtm_type == RTN_UNSPEC)
fprintf(stdout, "RTN_UNSPEC\n");
else if (rtMsg->rtm_type == RTN_UNICAST)
fprintf(stdout, "RTN_UNICAST\n");
else if (rtMsg->rtm_type == RTN_LOCAL)
fprintf(stdout, "RTN_LOCAL\n");
else if (rtMsg->rtm_type == RTN_BROADCAST)
fprintf(stdout, "RTN_BROADCAST\n");
else if (rtMsg->rtm_type == RTN_ANYCAST)
fprintf(stdout, "RTN_ANYCAST\n");
else if (rtMsg->rtm_type == RTN_MULTICAST)
fprintf(stdout, "RTN_MULTICAST\n");
else if (rtMsg->rtm_type == RTN_BLACKHOLE)
fprintf(stdout, "RTN_BLACKHOLE\n");
else if (rtMsg->rtm_type == RTN_UNREACHABLE)
fprintf(stdout, "RTN_UNREACHABLE\n");
else if (rtMsg->rtm_type == RTN_PROHIBIT)
fprintf(stdout, "RTN_PROHIBIT\n");
else if (rtMsg->rtm_type == RTN_THROW)
fprintf(stdout, "RTN_THROW\n");
else if (rtMsg->rtm_type == RTN_NAT)
fprintf(stdout, "RTN_NAT\n");
else if (rtMsg->rtm_type == RTN_XRESOLVE)
fprintf(stdout, "RTN_XRESOLVE\n");
else
fprintf(stdout, "mystery\n");

fprintf(stdout, "rtmsg: rtm_flags %d ", rtMsg->rtm_flags);
print_rtmsg_flags(rtMsg->rtm_flags);
fprintf(stdout, "\n");

#if 0
/* If the route is not for AF_INET or does not belong to main routing table
then return. */
if((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))
return;
#endif

/* get the rtattr field */
rtAttr = (struct rtattr *)RTM_RTA(rtMsg);
rtLen = RTM_PAYLOAD(nlHdr);
for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){
switch(rtAttr->rta_type) {
case RTA_DST:
inet_ntop(AF_INET, RTA_DATA(rtAttr), tempBuf, sizeof(tempBuf));
fprintf(stdout, "RTA_DST: %s\n",tempBuf);
rtInfo->dstAddr.s_addr= *(u_int *)RTA_DATA(rtAttr);
break;
case RTA_SRC:
inet_ntop(AF_INET, RTA_DATA(rtAttr), tempBuf, sizeof(tempBuf));
fprintf(stdout, "RTA_SRC: %s\n",tempBuf);
break;
case RTA_IIF:
if (!if_indextoname(*(int *)RTA_DATA(rtAttr), tempBuf))
sprintf(tempBuf, "errno %d", errno);
fprintf(stdout, "RTA_IIF: %s\n", tempBuf);
break;
case RTA_OIF:
if (!if_indextoname(*(int *)RTA_DATA(rtAttr), tempBuf))
sprintf(tempBuf, "errno %d", errno);
fprintf(stdout, "RTA_OIF: %s\n", tempBuf);
strcpy(rtInfo->ifName, tempBuf);
break;
case RTA_GATEWAY:
inet_ntop(AF_INET, RTA_DATA(rtAttr), tempBuf, sizeof(tempBuf));
fprintf(stdout, "RTA_GATEWAY: %s\n",tempBuf);
rtInfo->gateWay.s_addr = *(u_int *)RTA_DATA(rtAttr);
break;
case RTA_PREFSRC:
inet_ntop(AF_INET, RTA_DATA(rtAttr), tempBuf, sizeof(tempBuf));
fprintf(stdout, "RTA_PREFSRC: %s\n",tempBuf);
rtInfo->srcAddr.s_addr = *(u_int *)RTA_DATA(rtAttr);
break;
default:
/* there's more RTA_s under rtattr_type_t in rtnetlink.h,
just covering some common ones above */
fprintf(stdout, "RTA mystery: %d len %d\n",
rtAttr->rta_type, rtAttr->rta_len);
break;
}
}
return;
}

unsigned char command_rtm_table;
unsigned char command_rtm_protocol;
unsigned char command_rtm_family;
int command_ack;
int command_any_args;

struct option long_options[] = {
{ "table", required_argument, 0, 't'},
{ "protocol", required_argument, 0, 'p'},
{ "family", required_argument, 0, 'f' },
{ "ack", no_argument, 0, 'a'},
{ 0, 0, 0, 0}
};

char short_options[] = "t:p:f:a";

int parse_command_line(int argc, char *argv[])
{
int rc;
int option_index;
unsigned int x;

while (1)
{
rc = getopt_long(argc, argv, short_options,
long_options, &option_index);
switch (rc) {
case 't' :
sscanf(optarg, "%u", &x);
command_rtm_table = (unsigned char) x;
printf("command_rtm_table = %u\n", command_rtm_table);
command_any_args++;
break;
case 'p' :
sscanf(optarg, "%u", &x);
command_rtm_protocol = (unsigned char) x;
printf("command_rtm_protcol = %u\n", command_rtm_protocol);
command_any_args++;
break;
case 'f' :
sscanf(optarg, "%u", &x);
command_rtm_family = (unsigned char) x;
printf("command_rtm_family = %u\n", command_rtm_family);
command_any_args++;
break;
case 'a' :
command_ack = 1;
printf("acknowledgement enabled\n");
break;
case -1 :
break;
default :
printf("unrecognized option %c (%d)\n", rc, rc);
break;
}

if (rc == -1)
break;
}

return 0;
}

int main(int argc, char *argv[])
{
int sock, len, msgSeq = 0;
struct nlmsghdr *nlMsg;
struct rtmsg *rtMsg;
struct route_info *rtInfo;
char msgBuf[NL_BUFSIZE];

if (parse_command_line(argc, argv))
return -1;

/* Create Socket */
if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
perror("Socket Creation: ");

/* Initialize the buffer */
memset(msgBuf, 0, NL_BUFSIZE);

/* point the header and the msg structure pointers into the buffer */
nlMsg = (struct nlmsghdr *)msgBuf;
rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg);

rtMsg->rtm_table = command_rtm_table;
rtMsg->rtm_protocol = command_rtm_protocol;
rtMsg->rtm_family = command_rtm_family;

/* Fill in the nlmsg header*/
// Length of message.
nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
// Get the routes from kernel routing table .
nlMsg->nlmsg_type = RTM_GETROUTE;


nlMsg->nlmsg_flags = NLM_F_REQUEST;
#if 0
/* it appears that only NLM_F_REQUEST | NLM_F_ROOT is supported
by the RTM_GETROUTE service. when I attempt to setup the
fields of ifinfomsg sent to the kernel as above, I either
get all routes back when NLM_F_ROOT is specified or an
error message when its not, jharan, 8-08-2005 */
if (!command_any_args)
#endif
nlMsg->nlmsg_flags |= NLM_F_ROOT;

/* it appears the NLM_F_ACK has no effect on gets. guess this
makes sense given that the get response is sufficent
acknowledgement, jharan, 8-08-2005 */
if (command_ack)
nlMsg->nlmsg_flags |= NLM_F_ACK;

// Sequence of the message packet.
nlMsg->nlmsg_seq = msgSeq++;
// PID of process sending the request.
nlMsg->nlmsg_pid = getpid();

/* Send the request */
if(send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0){
printf("Write To Socket Failed...\n");
return -1;
}

/* Read the response */
if((len = readSock(sock, msgBuf, msgSeq, getpid())) < 0) {
printf("Read From Socket Failed...\n");
return -1;
}

printf("readSock() returned %d\n", len);

/* Parse and print the response */
for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len)){
rtInfo = (struct route_info *) malloc(sizeof(struct route_info));
if (!rtInfo) {
fprintf(stdout, "malloc() failed\n");
return -1;
}
memset(rtInfo, 0, sizeof(struct route_info));
parseRoutes(nlMsg, rtInfo);
if (!route_info_head) {
route_info_head = rtInfo;
route_info_tail = rtInfo;
} else {
route_info_tail->next = rtInfo;
route_info_tail = rtInfo;
}
}
close(sock);
/* netstat style display of accumulated data */
fprintf(stdout, "============================================\n");
fprintf(stdout, "Destination\tGateway\t\tInterface\tSource\n");
for (rtInfo = route_info_head; rtInfo; rtInfo = rtInfo->next)
printRoute(rtInfo);
return 0;
}


-
To unsubscribe from this list: send the line "unsubscribe linux-net" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html