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; } - : 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