Hi everybody. As Bernd Eckenfels, the problem in this case is that using the IOCTL API for access to the ifaces info, the kernel always return the first ip v4 info for this device. I think that the info related to scope / ip_address, etc is not available at proc filesystem, so in this case I try to get the info from netlink api (http://en.wikipedia.org/wiki/Netlink)... I attach a initial implementation using netlink, but, first, I want to confirm that a netlink based approach can be used... is there any problem using this API? (I am not sure that it works at free BSD kernel, or if all the debian supported architectures have netlink....)
Now the code (only for testing purposes) print all the ip for a device... select the global scope ip at this point is very easy.... For example if we have eth0 with ips 10.0.2.15/24 and 172.16.0.1/32: efe...@debian:~/net-tools-1.60$ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000 link/ether 08:00:27:73:ed:6c brd ff:ff:ff:ff:ff:ff inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0 inet 172.16.0.1/32 scope global eth0 inet6 fe80::a00:27ff:fe73:ed6c/64 scope link valid_lft forever preferred_lft forever And using the unmodified version of ifconfig the result is: efe...@debian:~/net-tools-1.60$ ./ifconfig eth0 eth0 Link encap:Ethernet HWaddr 08:00:27:73:ed:6c inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe73:ed6c/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:9777 errors:0 dropped:0 overruns:0 frame:0 TX packets:8312 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:10269729 (9.7 MiB) TX bytes:1123870 (1.0 MiB) Interrupt:11 Base address:0xc020 Now with the modified version, the result is: efe...@debian:~/net-tools-1.60$ ./ifconfig eth0 eth0 Link encap:Ethernet HWaddr 08:00:27:73:ed:6c inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 inet addr:172.16.0.1 Mask:255.255.255.255 inet6 addr: fe80::a00:27ff:fe73:ed6c/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:9884 errors:0 dropped:0 overruns:0 frame:0 TX packets:8420 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:10348836 (9.8 MiB) TX bytes:1146259 (1.0 MiB) Interrupt:11 Base address:0xc020 Now it show the two ips... We should decide the functionality to implement: 1) Show all ips 2) Show only one ip (selecting the ip with larger scope) Sorry, but I am begining with the dpatch stuff so I don't get a good diff/dpatch for the code, so I include a diff from the file lib/interface.c (orig version, not debian version) and the version that I change (that include some dpatchs from the debian package). I also add the complete final lib/interface.c code.... but I the solution is accepted I will need some help to generate a dpatch :( Any comment/fixes will be appreciated -- Hasta otra!!! Eduardo Ferro Aldama Alea Soluciones
/* Code to manipulate interface information, shared between ifconfig and netstat. 10/1998 partly rewriten by Andi Kleen to support an interface list. I don't claim that the list operations are efficient @). 8/2000 Andi Kleen make the list operations a bit more efficient. People are crazy enough to use thousands of aliases now. $Id: interface.c,v 1.30 2005/08/23 22:46:51 ecki Exp $ */ #include "config.h" #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <string.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #define NIPQUAD(addr) \ ((unsigned char *)&addr)[0], \ ((unsigned char *)&addr)[1], \ ((unsigned char *)&addr)[2], \ ((unsigned char *)&addr)[3] #define NIPQUAD_FMT "%u.%u.%u.%u" #if HAVE_AFIPX #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) #include <netipx/ipx.h> #else #include "ipx.h" #endif #endif #if HAVE_AFECONET #include <neteconet/ec.h> #endif #ifdef HAVE_HWSLIP #include <linux/if_slip.h> #include <net/if_arp.h> #endif #include "net-support.h" #include "pathnames.h" #include "version.h" #include "proc.h" #include "interface.h" #include "sockets.h" #include "util.h" #include "intl.h" #ifdef IFF_PORTSEL const char *if_port_text[][4] = { /* Keep in step with <linux/netdevice.h> */ {"unknown", NULL, NULL, NULL}, {"10base2", "bnc", "coax", NULL}, {"10baseT", "utp", "tpe", NULL}, {"AUI", "thick", "db15", NULL}, {"100baseT", NULL, NULL, NULL}, {"100baseTX", NULL, NULL, NULL}, {"100baseFX", NULL, NULL, NULL}, {NULL, NULL, NULL, NULL}, }; #endif #define IPV6_ADDR_ANY 0x0000U #define IPV6_ADDR_UNICAST 0x0001U #define IPV6_ADDR_MULTICAST 0x0002U #define IPV6_ADDR_ANYCAST 0x0004U #define IPV6_ADDR_LOOPBACK 0x0010U #define IPV6_ADDR_LINKLOCAL 0x0020U #define IPV6_ADDR_SITELOCAL 0x0040U #define IPV6_ADDR_COMPATv4 0x0080U #define IPV6_ADDR_SCOPE_MASK 0x00f0U #define IPV6_ADDR_MAPPED 0x1000U #define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */ int procnetdev_vsn = 1; int ife_short; int if_list_all = 0; /* do we have requested the complete proc list, yet? */ static struct interface *int_list, *int_last; static int if_readlist_proc(char *); static struct interface *if_cache_add(char *name) { struct interface *ife, **nextp, *new; if (!int_list) int_last = NULL; /* the cache is sorted, so if we hit a smaller if, exit */ for (ife = int_last; ife; ife = ife->prev) { int n = nstrcmp(ife->name, name); if (n == 0) return ife; if (n < 0) break; } new(new); safe_strncpy(new->name, name, IFNAMSIZ); nextp = ife ? &ife->next : &int_list; // keep sorting new->prev = ife; new->next = *nextp; if (new->next) new->next->prev = new; else int_last = new; *nextp = new; return new; } struct interface *lookup_interface(char *name) { /* if we have read all, use it */ if (if_list_all) return if_cache_add(name); /* otherwise we read a limited list */ if (if_readlist_proc(name) < 0) return NULL; return if_cache_add(name); } int for_all_interfaces(int (*doit) (struct interface *, void *), void *cookie) { struct interface *ife; if (!if_list_all && (if_readlist() < 0)) return -1; for (ife = int_list; ife; ife = ife->next) { int err = doit(ife, cookie); if (err) return err; } return 0; } int if_cache_free(void) { struct interface *ife; while ((ife = int_list) != NULL) { int_list = ife->next; free(ife); } int_last = NULL; if_list_all = 0; return 0; } static int if_readconf(void) { int numreqs = 30; struct ifconf ifc; struct ifreq *ifr; int n, err = -1; int skfd; /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets (as of 2.1.128) */ skfd = get_socket_for_af(AF_INET); if (skfd < 0) { fprintf(stderr, _("warning: no inet socket available: %s\n"), strerror(errno)); /* Try to soldier on with whatever socket we can get hold of. */ skfd = sockets_open(0); if (skfd < 0) return -1; } ifc.ifc_buf = NULL; for (;;) { ifc.ifc_len = sizeof(struct ifreq) * numreqs; ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len); if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { perror("SIOCGIFCONF"); goto out; } if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) { /* assume it overflowed and try again */ numreqs *= 2; continue; } break; } ifr = ifc.ifc_req; for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { if_cache_add(ifr->ifr_name); ifr++; } err = 0; out: free(ifc.ifc_buf); return err; } char *get_name(char *name, char *p) { while (isspace(*p)) p++; while (*p) { if (isspace(*p)) break; if (*p == ':') { /* could be an alias */ char *dot = p++; while (*p && isdigit(*p)) p++; if (*p == ':') { /* Yes it is, backup and copy it. */ p = dot; *name++ = *p++; while (*p && isdigit(*p)) { *name++ = *p++; } } else { /* No, it isn't */ p = dot; } p++; break; } *name++ = *p++; } *name++ = '\0'; return p; } int procnetdev_version(char *buf) { if (strstr(buf, "compressed")) return 3; if (strstr(buf, "bytes")) return 2; return 1; } int get_dev_fields(char *bp, struct interface *ife) { switch (procnetdev_vsn) { case 3: sscanf(bp, "%Lu %Lu %lu %lu %lu %lu %lu %lu %Lu %Lu %lu %lu %lu %lu %lu %lu", &ife->stats.rx_bytes, &ife->stats.rx_packets, &ife->stats.rx_errors, &ife->stats.rx_dropped, &ife->stats.rx_fifo_errors, &ife->stats.rx_frame_errors, &ife->stats.rx_compressed, &ife->stats.rx_multicast, &ife->stats.tx_bytes, &ife->stats.tx_packets, &ife->stats.tx_errors, &ife->stats.tx_dropped, &ife->stats.tx_fifo_errors, &ife->stats.collisions, &ife->stats.tx_carrier_errors, &ife->stats.tx_compressed); break; case 2: sscanf(bp, "%Lu %Lu %lu %lu %lu %lu %Lu %Lu %lu %lu %lu %lu %lu", &ife->stats.rx_bytes, &ife->stats.rx_packets, &ife->stats.rx_errors, &ife->stats.rx_dropped, &ife->stats.rx_fifo_errors, &ife->stats.rx_frame_errors, &ife->stats.tx_bytes, &ife->stats.tx_packets, &ife->stats.tx_errors, &ife->stats.tx_dropped, &ife->stats.tx_fifo_errors, &ife->stats.collisions, &ife->stats.tx_carrier_errors); ife->stats.rx_multicast = 0; break; case 1: sscanf(bp, "%Lu %lu %lu %lu %lu %Lu %lu %lu %lu %lu %lu", &ife->stats.rx_packets, &ife->stats.rx_errors, &ife->stats.rx_dropped, &ife->stats.rx_fifo_errors, &ife->stats.rx_frame_errors, &ife->stats.tx_packets, &ife->stats.tx_errors, &ife->stats.tx_dropped, &ife->stats.tx_fifo_errors, &ife->stats.collisions, &ife->stats.tx_carrier_errors); ife->stats.rx_bytes = 0; ife->stats.tx_bytes = 0; ife->stats.rx_multicast = 0; break; } return 0; } static int if_readlist_proc(char *target) { FILE *fh; char buf[512]; struct interface *ife; int err; fh = fopen(_PATH_PROCNET_DEV, "r"); if (!fh) { fprintf(stderr, _("Warning: cannot open %s (%s). Limited output.\n"), _PATH_PROCNET_DEV, strerror(errno)); return -2; } fgets(buf, sizeof buf, fh); /* eat line */ fgets(buf, sizeof buf, fh); #if 0 /* pretty, but can't cope with missing fields */ fmt = proc_gen_fmt(_PATH_PROCNET_DEV, 1, fh, "face", "", /* parsed separately */ "bytes", "%lu", "packets", "%lu", "errs", "%lu", "drop", "%lu", "fifo", "%lu", "frame", "%lu", "compressed", "%lu", "multicast", "%lu", "bytes", "%lu", "packets", "%lu", "errs", "%lu", "drop", "%lu", "fifo", "%lu", "colls", "%lu", "carrier", "%lu", "compressed", "%lu", NULL); if (!fmt) return -1; #else procnetdev_vsn = procnetdev_version(buf); #endif err = 0; while (fgets(buf, sizeof buf, fh)) { char *s, name[IFNAMSIZ]; s = get_name(name, buf); ife = if_cache_add(name); get_dev_fields(s, ife); ife->statistics_valid = 1; if (target && !strcmp(target,name)) break; } if (ferror(fh)) { perror(_PATH_PROCNET_DEV); err = -1; } #if 0 free(fmt); #endif fclose(fh); return err; } int if_readlist(void) { /* caller will/should check not to call this too often * (i.e. only if if_list_all == 0 */ int err = 0; err |= if_readlist_proc(NULL); err |= if_readconf(); if_list_all = 1; return err; } /* Support for fetching an IPX address */ #if HAVE_AFIPX static int ipx_getaddr(int sock, int ft, struct ifreq *ifr) { ((struct sockaddr_ipx *) &ifr->ifr_addr)->sipx_type = ft; return ioctl(sock, SIOCGIFADDR, ifr); } #endif /* Fetch the interface configuration from the kernel. */ int if_fetch(struct interface *ife) { struct ifreq ifr; int fd; char *ifname = ife->name; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) return (-1); ife->flags = ifr.ifr_flags; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) memset(ife->hwaddr, 0, 32); else memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8); ife->type = ifr.ifr_hwaddr.sa_family; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) ife->metric = 0; else ife->metric = ifr.ifr_metric; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) ife->mtu = 0; else ife->mtu = ifr.ifr_mtu; #ifdef HAVE_HWSLIP if (ife->type == ARPHRD_SLIP || ife->type == ARPHRD_CSLIP || ife->type == ARPHRD_SLIP6 || ife->type == ARPHRD_CSLIP6 || ife->type == ARPHRD_ADAPT) { #ifdef SIOCGOUTFILL strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGOUTFILL, &ifr) < 0) ife->outfill = 0; else ife->outfill = (unsigned int) ifr.ifr_data; #endif #ifdef SIOCGKEEPALIVE strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGKEEPALIVE, &ifr) < 0) ife->keepalive = 0; else ife->keepalive = (unsigned int) ifr.ifr_data; #endif } #endif strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) memset(&ife->map, 0, sizeof(struct ifmap)); else memcpy(&ife->map, &ifr.ifr_map, sizeof(struct ifmap)); strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) memset(&ife->map, 0, sizeof(struct ifmap)); else ife->map = ifr.ifr_map; #ifdef HAVE_TXQUEUELEN strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0) ife->tx_queue_len = -1; /* unknown value */ else ife->tx_queue_len = ifr.ifr_qlen; #else ife->tx_queue_len = -1; /* unknown value */ #endif #if HAVE_AFINET /* IPv4 address? */ fd = get_socket_for_af(AF_INET); if (fd >= 0) { strcpy(ifr.ifr_name, ifname); ifr.ifr_addr.sa_family = AF_INET; if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { ife->has_ip = 1; ife->addr = ifr.ifr_addr; strcpy(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0) memset(&ife->dstaddr, 0, sizeof(struct sockaddr)); else ife->dstaddr = ifr.ifr_dstaddr; strcpy(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0) memset(&ife->broadaddr, 0, sizeof(struct sockaddr)); else ife->broadaddr = ifr.ifr_broadaddr; strcpy(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0) memset(&ife->netmask, 0, sizeof(struct sockaddr)); else ife->netmask = ifr.ifr_netmask; } else memset(&ife->addr, 0, sizeof(struct sockaddr)); } #endif #if HAVE_AFATALK /* DDP address maybe ? */ fd = get_socket_for_af(AF_APPLETALK); if (fd >= 0) { strcpy(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { ife->ddpaddr = ifr.ifr_addr; ife->has_ddp = 1; } } #endif #if HAVE_AFIPX /* Look for IPX addresses with all framing types */ fd = get_socket_for_af(AF_IPX); if (fd >= 0) { strcpy(ifr.ifr_name, ifname); if (!ipx_getaddr(fd, IPX_FRAME_ETHERII, &ifr)) { ife->has_ipx_bb = 1; ife->ipxaddr_bb = ifr.ifr_addr; } strcpy(ifr.ifr_name, ifname); if (!ipx_getaddr(fd, IPX_FRAME_SNAP, &ifr)) { ife->has_ipx_sn = 1; ife->ipxaddr_sn = ifr.ifr_addr; } strcpy(ifr.ifr_name, ifname); if (!ipx_getaddr(fd, IPX_FRAME_8023, &ifr)) { ife->has_ipx_e3 = 1; ife->ipxaddr_e3 = ifr.ifr_addr; } strcpy(ifr.ifr_name, ifname); if (!ipx_getaddr(fd, IPX_FRAME_8022, &ifr)) { ife->has_ipx_e2 = 1; ife->ipxaddr_e2 = ifr.ifr_addr; } } #endif #if HAVE_AFECONET /* Econet address maybe? */ fd = get_socket_for_af(AF_ECONET); if (fd >= 0) { strcpy(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { ife->ecaddr = ifr.ifr_addr; ife->has_econet = 1; } } #endif return 0; } int do_if_fetch(struct interface *ife) { if (if_fetch(ife) < 0) { char *errmsg; if (errno == ENODEV) { /* Give better error message for this case. */ errmsg = _("Device not found"); } else { errmsg = strerror(errno); } fprintf(stderr, _("%s: error fetching interface information: %s\n"), ife->name, errmsg); return -1; } return 0; } int do_if_print(struct interface *ife, void *cookie) { int *opt_a = (int *) cookie; int res; res = do_if_fetch(ife); if (res >= 0) { if ((ife->flags & IFF_UP) || *opt_a) ife_print(ife); } return res; } void ife_print_short(struct interface *ptr) { printf("%-9s ", ptr->name); printf("%5d %-2d ", ptr->mtu, ptr->metric); /* If needed, display the interface statistics. */ if (ptr->statistics_valid) { printf("%8llu %6lu %6lu %-6lu ", ptr->stats.rx_packets, ptr->stats.rx_errors, ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors); printf("%8llu %6lu %6lu %6lu ", ptr->stats.tx_packets, ptr->stats.tx_errors, ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors); } else { printf("%-56s", _(" - no statistics available -")); } /* DONT FORGET TO ADD THE FLAGS IN ife_print_long, too */ if (ptr->flags == 0) printf(_("[NO FLAGS]")); if (ptr->flags & IFF_ALLMULTI) printf("A"); if (ptr->flags & IFF_BROADCAST) printf("B"); if (ptr->flags & IFF_DEBUG) printf("D"); if (ptr->flags & IFF_LOOPBACK) printf("L"); if (ptr->flags & IFF_MULTICAST) printf("M"); #ifdef HAVE_DYNAMIC if (ptr->flags & IFF_DYNAMIC) printf("d"); #endif if (ptr->flags & IFF_PROMISC) printf("P"); if (ptr->flags & IFF_NOTRAILERS) printf("N"); if (ptr->flags & IFF_NOARP) printf("O"); if (ptr->flags & IFF_POINTOPOINT) printf("P"); if (ptr->flags & IFF_SLAVE) printf("s"); if (ptr->flags & IFF_MASTER) printf("m"); if (ptr->flags & IFF_RUNNING) printf("R"); if (ptr->flags & IFF_UP) printf("U"); /* DONT FORGET TO ADD THE FLAGS IN ife_print_long, too */ printf("\n"); } void ife_print_long(struct interface *ptr) { struct aftype *ap; struct hwtype *hw; int hf; int can_compress = 0; unsigned long long rx, tx, short_rx, short_tx; const char *Rext = "B"; const char *Text = "B"; #if HAVE_AFIPX static struct aftype *ipxtype = NULL; #endif #if HAVE_AFECONET static struct aftype *ectype = NULL; #endif #if HAVE_AFATALK static struct aftype *ddptype = NULL; #endif #if HAVE_AFINET6 FILE *f; char addr6[40], devname[20]; struct sockaddr_in6 sap; int plen, scope, dad_status, if_idx; extern struct aftype inet6_aftype; char addr6p[8][5]; #endif ap = get_afntype(ptr->addr.sa_family); if (ap == NULL) ap = get_afntype(0); hf = ptr->type; if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6) can_compress = 1; hw = get_hwntype(hf); if (hw == NULL) hw = get_hwntype(-1); printf(_("%-9s Link encap:%s "), ptr->name, hw->title); /* For some hardware types (eg Ash, ATM) we don't print the hardware address if it's null. */ if (hw->print != NULL && (! (hw_null_address(hw, ptr->hwaddr) && hw->suppress_null_addr))) printf(_("HWaddr %s "), hw->print(ptr->hwaddr)); #ifdef IFF_PORTSEL if (ptr->flags & IFF_PORTSEL) { printf(_("Media:%s"), if_port_text[ptr->map.port][0]); if (ptr->flags & IFF_AUTOMEDIA) printf(_("(auto)")); } #endif printf("\n"); #if HAVE_AFINET if (ptr->has_ip) { netlink_ife_inet_print(ptr->name, ap->name); } #endif #if HAVE_AFINET6 /* FIXME: should be integrated into interface.c. */ if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) { while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope, &dad_status, devname) != EOF) { if (!strcmp(devname, ptr->name)) { sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); inet6_aftype.input(1, addr6, (struct sockaddr *) &sap); printf(_(" inet6 addr: %s/%d"), inet6_aftype.sprint((struct sockaddr *) &sap, 1), plen); printf(_(" Scope:")); switch (scope) { case 0: printf(_("Global")); break; case IPV6_ADDR_LINKLOCAL: printf(_("Link")); break; case IPV6_ADDR_SITELOCAL: printf(_("Site")); break; case IPV6_ADDR_COMPATv4: printf(_("Compat")); break; case IPV6_ADDR_LOOPBACK: printf(_("Host")); break; default: printf(_("Unknown")); } printf("\n"); } } fclose(f); } #endif #if HAVE_AFIPX if (ipxtype == NULL) ipxtype = get_afntype(AF_IPX); if (ipxtype != NULL) { if (ptr->has_ipx_bb) printf(_(" IPX/Ethernet II addr:%s\n"), ipxtype->sprint(&ptr->ipxaddr_bb, 1)); if (ptr->has_ipx_sn) printf(_(" IPX/Ethernet SNAP addr:%s\n"), ipxtype->sprint(&ptr->ipxaddr_sn, 1)); if (ptr->has_ipx_e2) printf(_(" IPX/Ethernet 802.2 addr:%s\n"), ipxtype->sprint(&ptr->ipxaddr_e2, 1)); if (ptr->has_ipx_e3) printf(_(" IPX/Ethernet 802.3 addr:%s\n"), ipxtype->sprint(&ptr->ipxaddr_e3, 1)); } #endif #if HAVE_AFATALK if (ddptype == NULL) ddptype = get_afntype(AF_APPLETALK); if (ddptype != NULL) { if (ptr->has_ddp) printf(_(" EtherTalk Phase 2 addr:%s\n"), ddptype->sprint(&ptr->ddpaddr, 1)); } #endif #if HAVE_AFECONET if (ectype == NULL) ectype = get_afntype(AF_ECONET); if (ectype != NULL) { if (ptr->has_econet) printf(_(" econet addr:%s\n"), ectype->sprint(&ptr->ecaddr, 1)); } #endif printf(" "); /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */ if (ptr->flags == 0) printf(_("[NO FLAGS] ")); if (ptr->flags & IFF_UP) printf(_("UP ")); if (ptr->flags & IFF_BROADCAST) printf(_("BROADCAST ")); if (ptr->flags & IFF_DEBUG) printf(_("DEBUG ")); if (ptr->flags & IFF_LOOPBACK) printf(_("LOOPBACK ")); if (ptr->flags & IFF_POINTOPOINT) printf(_("POINTOPOINT ")); if (ptr->flags & IFF_NOTRAILERS) printf(_("NOTRAILERS ")); if (ptr->flags & IFF_RUNNING) printf(_("RUNNING ")); if (ptr->flags & IFF_NOARP) printf(_("NOARP ")); if (ptr->flags & IFF_PROMISC) printf(_("PROMISC ")); if (ptr->flags & IFF_ALLMULTI) printf(_("ALLMULTI ")); if (ptr->flags & IFF_SLAVE) printf(_("SLAVE ")); if (ptr->flags & IFF_MASTER) printf(_("MASTER ")); if (ptr->flags & IFF_MULTICAST) printf(_("MULTICAST ")); #ifdef HAVE_DYNAMIC if (ptr->flags & IFF_DYNAMIC) printf(_("DYNAMIC ")); #endif /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */ printf(_(" MTU:%d Metric:%d"), ptr->mtu, ptr->metric ? ptr->metric : 1); #ifdef SIOCSKEEPALIVE if (ptr->outfill || ptr->keepalive) printf(_(" Outfill:%d Keepalive:%d"), ptr->outfill, ptr->keepalive); #endif printf("\n"); /* If needed, display the interface statistics. */ if (ptr->statistics_valid) { /* XXX: statistics are currently only printed for the primary address, * not for the aliases, although strictly speaking they're shared * by all addresses. */ printf(" "); printf(_("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n"), ptr->stats.rx_packets, ptr->stats.rx_errors, ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors, ptr->stats.rx_frame_errors); if (can_compress) printf(_(" compressed:%lu\n"), ptr->stats.rx_compressed); rx = ptr->stats.rx_bytes; tx = ptr->stats.tx_bytes; short_rx = rx * 10; short_tx = tx * 10; if (rx > 1125899906842624ull) { short_rx /= 1125899906842624ull; Rext = "PiB"; } else if (rx > 1099511627776ull) { short_rx /= 1099511627776ull; Rext = "TiB"; } else if (rx > 1073741824ull) { short_rx /= 1073741824ull; Rext = "GiB"; } else if (rx > 1048576) { short_rx /= 1048576; Rext = "MiB"; } else if (rx > 1024) { short_rx /= 1024; Rext = "KiB"; } if (tx > 1125899906842624ull) { short_tx /= 1125899906842624ull; Text = "PiB"; } else if (tx > 1099511627776ull) { short_tx /= 1099511627776ull; Text = "TiB"; } else if (tx > 1073741824ull) { short_tx /= 1073741824ull; Text = "GiB"; } else if (tx > 1048576) { short_tx /= 1048576; Text = "MiB"; } else if (tx > 1024) { short_tx /= 1024; Text = "KiB"; } printf(" "); printf(_("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n"), ptr->stats.tx_packets, ptr->stats.tx_errors, ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors, ptr->stats.tx_carrier_errors); printf(_(" collisions:%lu "), ptr->stats.collisions); if (can_compress) printf(_("compressed:%lu "), ptr->stats.tx_compressed); if (ptr->tx_queue_len != -1) printf(_("txqueuelen:%d "), ptr->tx_queue_len); printf("\n "); printf(_("RX bytes:%llu (%lu.%lu %s) TX bytes:%llu (%lu.%lu %s)\n"), rx, (unsigned long)(short_rx / 10), (unsigned long)(short_rx % 10), Rext, tx, (unsigned long)(short_tx / 10), (unsigned long)(short_tx % 10), Text); } if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma || ptr->map.base_addr >= 0x100)) { printf(" "); if (ptr->map.irq) printf(_("Interrupt:%d "), ptr->map.irq); if (ptr->map.base_addr >= 0x100) /* Only print devices using it for I/O maps */ printf(_("Base address:0x%x "), ptr->map.base_addr); if (ptr->map.mem_start) { printf(_("Memory:%lx-%lx "), ptr->map.mem_start, ptr->map.mem_end); } if (ptr->map.dma) printf(_("DMA chan:%x "), ptr->map.dma); printf("\n"); } printf("\n"); } void ife_print(struct interface *i) { if (ife_short) ife_print_short(i); else ife_print_long(i); } unsigned int inet_make_mask(int logmask) { if (logmask) return htonl(~((1<<(32-logmask))-1)); return 0; } int netlink_ife_inet_print(const char *const name, const char *const apname) { struct { struct nlmsghdr n; struct ifaddrmsg r; } req; struct rtattr *rta; int status; char buf[16384]; struct nlmsghdr *nlmp; struct ifaddrmsg *rtmp; struct rtattr *rtatp; int rtattrlen; struct in_addr *inp; unsigned int netmask = 0; short broadcast = 0; short device_match = 0; char str_ip[16] = ""; char str_netmask[16] = ""; char str_broadcast[16] = ""; int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; req.n.nlmsg_type = RTM_GETADDR; req.r.ifa_family = AF_INET; rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); rta->rta_len = RTA_LENGTH(4); if ((status = send(fd, &req, req.n.nlmsg_len, 0)) < 0) goto error; if ((status = recv(fd, buf, sizeof(buf), 0)) < 0) goto error; for(nlmp = (struct nlmsghdr *)buf; status > sizeof(*nlmp);){ int len = nlmp->nlmsg_len; int req_len = len - sizeof(*nlmp); if (req_len<0 || len>status) { printf("error\n"); return -1; } if (!NLMSG_OK(nlmp, status)) { printf("NLMSG not OK\n"); return 1; } rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp); rtatp = (struct rtattr *)IFA_RTA(rtmp); broadcast = 0; device_match = 0; netmask = inet_make_mask(rtmp->ifa_prefixlen); rtattrlen = IFA_PAYLOAD(nlmp); for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { if (rtatp->rta_type == IFA_LABEL) { if (!strcmp((char *)RTA_DATA(rtatp), name)) { device_match = 1; } } if(rtatp->rta_type == IFA_ADDRESS){ inp = (struct in_addr *)RTA_DATA(rtatp); sprintf(str_ip, NIPQUAD_FMT, NIPQUAD(*inp)); } if(rtatp->rta_type == IFA_BROADCAST){ inp = (struct in_addr *)RTA_DATA(rtatp); broadcast = 1; sprintf(str_broadcast, NIPQUAD_FMT, NIPQUAD(*inp)); } } sprintf(str_netmask,NIPQUAD_FMT,NIPQUAD(netmask)); if (device_match) { printf(_(" %s addr:%s "), apname, str_ip); if (broadcast) printf(_(" Bcast:%s "), str_broadcast); printf(_(" Mask:%s\n"), str_netmask); } status -= NLMSG_ALIGN(len); nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len)); } close(fd); return 0; error: close(fd); perror("rtnetlink"); return -1; }
--- lib/interface.c.orig 2001-02-10 20:31:15.000000000 +0100 +++ lib/interface.c 2009-04-16 02:24:26.000000000 +0200 @@ -7,7 +7,7 @@ 8/2000 Andi Kleen make the list operations a bit more efficient. People are crazy enough to use thousands of aliases now. - $Id: interface.c,v 1.14 2001/02/10 19:31:15 pb Exp $ + $Id: interface.c,v 1.30 2005/08/23 22:46:51 ecki Exp $ */ #include "config.h" @@ -23,6 +23,20 @@ #include <string.h> #include <unistd.h> #include <ctype.h> +#include <string.h> + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#define NIPQUAD(addr) \ + ((unsigned char *)&addr)[0], \ + ((unsigned char *)&addr)[1], \ + ((unsigned char *)&addr)[2], \ + ((unsigned char *)&addr)[3] + +#define NIPQUAD_FMT "%u.%u.%u.%u" + + #if HAVE_AFIPX #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) @@ -87,14 +101,22 @@ int ife_short; +int if_list_all = 0; /* do we have requested the complete proc list, yet? */ + static struct interface *int_list, *int_last; static int if_readlist_proc(char *); -static struct interface *add_interface(char *name) + + +static struct interface *if_cache_add(char *name) { struct interface *ife, **nextp, *new; + if (!int_list) + int_last = NULL; + + /* the cache is sorted, so if we hit a smaller if, exit */ for (ife = int_last; ife; ife = ife->prev) { int n = nstrcmp(ife->name, name); if (n == 0) @@ -104,7 +126,7 @@ } new(new); safe_strncpy(new->name, name, IFNAMSIZ); - nextp = ife ? &ife->next : &int_list; + nextp = ife ? &ife->next : &int_list; // keep sorting new->prev = ife; new->next = *nextp; if (new->next) @@ -117,19 +139,22 @@ struct interface *lookup_interface(char *name) { - struct interface *ife = NULL; - - if (if_readlist_proc(name) < 0) - return NULL; - ife = add_interface(name); - return ife; + /* if we have read all, use it */ + if (if_list_all) + return if_cache_add(name); + + /* otherwise we read a limited list */ + if (if_readlist_proc(name) < 0) + return NULL; + + return if_cache_add(name); } int for_all_interfaces(int (*doit) (struct interface *, void *), void *cookie) { struct interface *ife; - if (!int_list && (if_readlist() < 0)) + if (!if_list_all && (if_readlist() < 0)) return -1; for (ife = int_list; ife; ife = ife->next) { int err = doit(ife, cookie); @@ -139,13 +164,15 @@ return 0; } -int free_interface_list(void) +int if_cache_free(void) { struct interface *ife; while ((ife = int_list) != NULL) { int_list = ife->next; free(ife); } + int_last = NULL; + if_list_all = 0; return 0; } @@ -180,7 +207,7 @@ } if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) { /* assume it overflowed and try again */ - numreqs += 10; + numreqs *= 2; continue; } break; @@ -188,7 +215,7 @@ ifr = ifc.ifc_req; for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { - add_interface(ifr->ifr_name); + if_cache_add(ifr->ifr_name); ifr++; } err = 0; @@ -198,7 +225,7 @@ return err; } -static char *get_name(char *name, char *p) +char *get_name(char *name, char *p) { while (isspace(*p)) p++; @@ -206,16 +233,19 @@ if (isspace(*p)) break; if (*p == ':') { /* could be an alias */ - char *dot = p, *dotname = name; - *name++ = *p++; - while (isdigit(*p)) - *name++ = *p++; - if (*p != ':') { /* it wasn't, backup */ - p = dot; - name = dotname; + char *dot = p++; + while (*p && isdigit(*p)) p++; + if (*p == ':') { + /* Yes it is, backup and copy it. */ + p = dot; + *name++ = *p++; + while (*p && isdigit(*p)) { + *name++ = *p++; + } + } else { + /* No, it isn't */ + p = dot; } - if (*p == '\0') - return NULL; p++; break; } @@ -225,7 +255,7 @@ return p; } -static int procnetdev_version(char *buf) +int procnetdev_version(char *buf) { if (strstr(buf, "compressed")) return 3; @@ -234,12 +264,12 @@ return 1; } -static int get_dev_fields(char *bp, struct interface *ife) +int get_dev_fields(char *bp, struct interface *ife) { switch (procnetdev_vsn) { case 3: sscanf(bp, - "%llu %llu %lu %lu %lu %lu %lu %lu %llu %llu %lu %lu %lu %lu %lu %lu", + "%Lu %Lu %lu %lu %lu %lu %lu %lu %Lu %Lu %lu %lu %lu %lu %lu %lu", &ife->stats.rx_bytes, &ife->stats.rx_packets, &ife->stats.rx_errors, @@ -259,7 +289,7 @@ &ife->stats.tx_compressed); break; case 2: - sscanf(bp, "%llu %llu %lu %lu %lu %lu %llu %llu %lu %lu %lu %lu %lu", + sscanf(bp, "%Lu %Lu %lu %lu %lu %lu %Lu %Lu %lu %lu %lu %lu %lu", &ife->stats.rx_bytes, &ife->stats.rx_packets, &ife->stats.rx_errors, @@ -277,7 +307,7 @@ ife->stats.rx_multicast = 0; break; case 1: - sscanf(bp, "%llu %lu %lu %lu %lu %llu %lu %lu %lu %lu %lu", + sscanf(bp, "%Lu %lu %lu %lu %lu %Lu %lu %lu %lu %lu %lu", &ife->stats.rx_packets, &ife->stats.rx_errors, &ife->stats.rx_dropped, @@ -300,22 +330,16 @@ static int if_readlist_proc(char *target) { - static int proc_read; FILE *fh; char buf[512]; struct interface *ife; int err; - if (proc_read) - return 0; - if (!target) - proc_read = 1; - fh = fopen(_PATH_PROCNET_DEV, "r"); if (!fh) { fprintf(stderr, _("Warning: cannot open %s (%s). Limited output.\n"), _PATH_PROCNET_DEV, strerror(errno)); - return if_readconf(); + return -2; } fgets(buf, sizeof buf, fh); /* eat line */ fgets(buf, sizeof buf, fh); @@ -350,7 +374,7 @@ while (fgets(buf, sizeof buf, fh)) { char *s, name[IFNAMSIZ]; s = get_name(name, buf); - ife = add_interface(name); + ife = if_cache_add(name); get_dev_fields(s, ife); ife->statistics_valid = 1; if (target && !strcmp(target,name)) @@ -359,7 +383,6 @@ if (ferror(fh)) { perror(_PATH_PROCNET_DEV); err = -1; - proc_read = 0; } #if 0 @@ -371,9 +394,16 @@ int if_readlist(void) { - int err = if_readlist_proc(NULL); - if (!err) - err = if_readconf(); + /* caller will/should check not to call this too often + * (i.e. only if if_list_all == 0 + */ + int err = 0; + + err |= if_readlist_proc(NULL); + err |= if_readconf(); + + if_list_all = 1; + return err; } @@ -579,11 +609,11 @@ void ife_print_short(struct interface *ptr) { - printf("%-5.5s ", ptr->name); - printf("%5d %3d", ptr->mtu, ptr->metric); + printf("%-9s ", ptr->name); + printf("%5d %-2d ", ptr->mtu, ptr->metric); /* If needed, display the interface statistics. */ if (ptr->statistics_valid) { - printf("%8llu %6lu %6lu %6lu", + printf("%8llu %6lu %6lu %-6lu ", ptr->stats.rx_packets, ptr->stats.rx_errors, ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors); printf("%8llu %6lu %6lu %6lu ", @@ -636,8 +666,8 @@ int hf; int can_compress = 0; unsigned long long rx, tx, short_rx, short_tx; - char Rext[5]="b"; - char Text[5]="b"; + const char *Rext = "B"; + const char *Text = "B"; #if HAVE_AFIPX static struct aftype *ipxtype = NULL; @@ -670,7 +700,7 @@ if (hw == NULL) hw = get_hwntype(-1); - printf(_("%-9.9s Link encap:%s "), ptr->name, hw->title); + printf(_("%-9s Link encap:%s "), ptr->name, hw->title); /* For some hardware types (eg Ash, ATM) we don't print the hardware address if it's null. */ if (hw->print != NULL && (! (hw_null_address(hw, ptr->hwaddr) && @@ -687,15 +717,7 @@ #if HAVE_AFINET if (ptr->has_ip) { - printf(_(" %s addr:%s "), ap->name, - ap->sprint(&ptr->addr, 1)); - if (ptr->flags & IFF_POINTOPOINT) { - printf(_(" P-t-P:%s "), ap->sprint(&ptr->dstaddr, 1)); - } - if (ptr->flags & IFF_BROADCAST) { - printf(_(" Bcast:%s "), ap->sprint(&ptr->broadaddr, 1)); - } - printf(_(" Mask:%s\n"), ap->sprint(&ptr->netmask, 1)); + netlink_ife_inet_print(ptr->name, ap->name); } #endif @@ -703,7 +725,7 @@ /* FIXME: should be integrated into interface.c. */ if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) { - while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n", + while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope, &dad_status, devname) != EOF) { @@ -843,10 +865,38 @@ tx = ptr->stats.tx_bytes; short_rx = rx * 10; short_tx = tx * 10; - if (rx > 1048576) { short_rx /= 1048576; strcpy(Rext, "Mb"); } - else if (rx > 1024) { short_rx /= 1024; strcpy(Rext, "Kb"); } - if (tx > 1048576) { short_tx /= 1048576; strcpy(Text, "Mb"); } - else if (tx > 1024) { short_tx /= 1024; strcpy(Text, "Kb"); } + if (rx > 1125899906842624ull) { + short_rx /= 1125899906842624ull; + Rext = "PiB"; + } else if (rx > 1099511627776ull) { + short_rx /= 1099511627776ull; + Rext = "TiB"; + } else if (rx > 1073741824ull) { + short_rx /= 1073741824ull; + Rext = "GiB"; + } else if (rx > 1048576) { + short_rx /= 1048576; + Rext = "MiB"; + } else if (rx > 1024) { + short_rx /= 1024; + Rext = "KiB"; + } + if (tx > 1125899906842624ull) { + short_tx /= 1125899906842624ull; + Text = "PiB"; + } else if (tx > 1099511627776ull) { + short_tx /= 1099511627776ull; + Text = "TiB"; + } else if (tx > 1073741824ull) { + short_tx /= 1073741824ull; + Text = "GiB"; + } else if (tx > 1048576) { + short_tx /= 1048576; + Text = "MiB"; + } else if (tx > 1024) { + short_tx /= 1024; + Text = "KiB"; + } printf(" "); printf(_("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n"), @@ -867,7 +917,7 @@ } if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma || - ptr->map.base_addr)) { + ptr->map.base_addr >= 0x100)) { printf(" "); if (ptr->map.irq) printf(_("Interrupt:%d "), ptr->map.irq); @@ -891,3 +941,113 @@ else ife_print_long(i); } + + +unsigned int inet_make_mask(int logmask) +{ + if (logmask) + return htonl(~((1<<(32-logmask))-1)); + return 0; +} + + +int netlink_ife_inet_print(const char *const name, const char *const apname) +{ + struct { + struct nlmsghdr n; + struct ifaddrmsg r; + } req; + + struct rtattr *rta; + int status; + char buf[16384]; + struct nlmsghdr *nlmp; + struct ifaddrmsg *rtmp; + struct rtattr *rtatp; + int rtattrlen; + struct in_addr *inp; + unsigned int netmask = 0; + short broadcast = 0; + short device_match = 0; + + char str_ip[16] = ""; + char str_netmask[16] = ""; + char str_broadcast[16] = ""; + + int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.n.nlmsg_type = RTM_GETADDR; + + req.r.ifa_family = AF_INET; + rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_len = RTA_LENGTH(4); + + if ((status = send(fd, &req, req.n.nlmsg_len, 0)) < 0) goto error; + if ((status = recv(fd, buf, sizeof(buf), 0)) < 0) goto error; + + + for(nlmp = (struct nlmsghdr *)buf; status > sizeof(*nlmp);){ + int len = nlmp->nlmsg_len; + int req_len = len - sizeof(*nlmp); + + if (req_len<0 || len>status) { + printf("error\n"); + return -1; + } + + if (!NLMSG_OK(nlmp, status)) { + printf("NLMSG not OK\n"); + return 1; + } + + rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp); + rtatp = (struct rtattr *)IFA_RTA(rtmp); + + broadcast = 0; + device_match = 0; + + netmask = inet_make_mask(rtmp->ifa_prefixlen); + rtattrlen = IFA_PAYLOAD(nlmp); + + for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { + if (rtatp->rta_type == IFA_LABEL) { + if (!strcmp((char *)RTA_DATA(rtatp), name)) { + device_match = 1; + } + } + + if(rtatp->rta_type == IFA_ADDRESS){ + inp = (struct in_addr *)RTA_DATA(rtatp); + sprintf(str_ip, NIPQUAD_FMT, NIPQUAD(*inp)); + } + + if(rtatp->rta_type == IFA_BROADCAST){ + inp = (struct in_addr *)RTA_DATA(rtatp); + broadcast = 1; + sprintf(str_broadcast, NIPQUAD_FMT, NIPQUAD(*inp)); + } + } + sprintf(str_netmask,NIPQUAD_FMT,NIPQUAD(netmask)); + + if (device_match) { + printf(_(" %s addr:%s "), apname, str_ip); + if (broadcast) + printf(_(" Bcast:%s "), str_broadcast); + printf(_(" Mask:%s\n"), str_netmask); + } + + status -= NLMSG_ALIGN(len); + nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len)); + } + + close(fd); + return 0; + +error: + close(fd); + perror("rtnetlink"); + return -1; +}