]> rtime.felk.cvut.cz Git - lisovros/iproute2_canprio.git/blobdiff - ip/ipaddress.c
iproute2: trivial fix of ip link syntax in manpage
[lisovros/iproute2_canprio.git] / ip / ipaddress.c
index 24c9322da519adfef5281585534aeda23ea98181..9ab65ec78e2224290024ec1ef2a6c4cd67890d20 100644 (file)
@@ -8,18 +8,18 @@
  *
  * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
- * Changes:
- *     Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <inttypes.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <sys/errno.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <string.h>
@@ -34,6 +34,7 @@
 #include "ll_map.h"
 #include "ip_common.h"
 
+
 static struct
 {
        int ifindex;
@@ -49,7 +50,7 @@ static struct
        char *flushb;
        int flushp;
        int flushe;
-       struct rtnl_handle *rth;
+       int group;
 } filter;
 
 static int do_link;
@@ -61,7 +62,9 @@ static void usage(void)
        if (do_link) {
                iplink_usage();
        }
-       fprintf(stderr, "Usage: ip addr {add|del} IFADDR dev STRING\n");
+       fprintf(stderr, "Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]\n");
+       fprintf(stderr, "                                                      [ CONFFLAG-LIST ]\n");
+       fprintf(stderr, "       ip addr del IFADDR dev STRING\n");
        fprintf(stderr, "       ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
        fprintf(stderr, "                            [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n");
        fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
@@ -70,13 +73,21 @@ static void usage(void)
        fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
        fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
        fprintf(stderr, "FLAG  := [ permanent | dynamic | secondary | primary |\n");
-       fprintf(stderr, "           tentative | deprecated ]\n");
+       fprintf(stderr, "           tentative | deprecated | dadfailed | temporary |\n");
+       fprintf(stderr, "           CONFFLAG-LIST ]\n");
+       fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n");
+       fprintf(stderr, "CONFFLAG  := [ home | nodad ]\n");
+       fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
+       fprintf(stderr, "LFT := forever | SECONDS\n");
+
        exit(-1);
 }
 
 void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
 {
        fprintf(fp, "<");
+       if (flags & IFF_UP && !(flags & IFF_RUNNING))
+               fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
        flags &= ~IFF_RUNNING;
 #define _PF(f) if (flags&IFF_##f) { \
                   flags &= ~IFF_##f ; \
@@ -96,6 +107,9 @@ void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
        _PF(PORTSEL);
        _PF(NOTRAILERS);
        _PF(UP);
+       _PF(LOWER_UP);
+       _PF(DORMANT);
+       _PF(ECHO);
 #undef _PF
         if (flags)
                fprintf(fp, "%x", flags);
@@ -104,29 +118,247 @@ void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
        fprintf(fp, "> ");
 }
 
-void print_queuelen(char *name)
+static const char *oper_states[] = {
+       "UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN", 
+       "TESTING", "DORMANT",    "UP"
+};
+
+static void print_operstate(FILE *f, __u8 state)
 {
-       struct ifreq ifr;
-       int s;
+       if (state >= sizeof(oper_states)/sizeof(oper_states[0]))
+               fprintf(f, "state %#x ", state);
+       else
+               fprintf(f, "state %s ", oper_states[state]);
+}
 
-       s = socket(AF_INET, SOCK_STREAM, 0);
-       if (s < 0)
-               return;
+int get_operstate(const char *name)
+{
+       int i;
 
-       memset(&ifr, 0, sizeof(ifr));
-       strcpy(ifr.ifr_name, name);
-       if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { 
-               perror("SIOCGIFXQLEN");
+       for (i = 0; i < sizeof(oper_states)/sizeof(oper_states[0]); i++)
+               if (strcasecmp(name, oper_states[i]) == 0)
+                       return i;
+       return -1;
+}
+
+static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1])
+{
+       int qlen;
+
+       if (tb[IFLA_TXQLEN])
+               qlen = *(int *)RTA_DATA(tb[IFLA_TXQLEN]);
+       else {
+               struct ifreq ifr;
+               int s = socket(AF_INET, SOCK_STREAM, 0);
+
+               if (s < 0)
+                       return;
+
+               memset(&ifr, 0, sizeof(ifr));
+               strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME]));
+               if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
+                       fprintf(f, "ioctl(SIOCGIFXQLEN) failed: %s\n", strerror(errno));
+                       close(s);
+                       return;
+               }
                close(s);
+               qlen = ifr.ifr_qlen;
+       }
+       if (qlen)
+               fprintf(f, "qlen %d", qlen);
+}
+
+static const char *link_modes[] = {
+       "DEFAULT", "DORMANT"
+};
+
+static void print_linkmode(FILE *f, struct rtattr *tb)
+{
+       unsigned int mode = rta_getattr_u8(tb);
+
+       if (mode >= sizeof(link_modes) / sizeof(link_modes[0]))
+               fprintf(f, "mode %d ", mode);
+       else
+               fprintf(f, "mode %s ", link_modes[mode]);
+}
+
+static void print_linktype(FILE *fp, struct rtattr *tb)
+{
+       struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+       struct link_util *lu;
+       char *kind;
+
+       parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
+
+       if (!linkinfo[IFLA_INFO_KIND])
+               return;
+       kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]);
+
+       fprintf(fp, "%s", _SL_);
+       fprintf(fp, "    %s ", kind);
+
+       lu = get_link_kind(kind);
+       if (!lu || !lu->print_opt)
+               return;
+
+       if (1) {
+               struct rtattr *attr[lu->maxattr+1], **data = NULL;
+
+               if (linkinfo[IFLA_INFO_DATA]) {
+                       parse_rtattr_nested(attr, lu->maxattr,
+                                           linkinfo[IFLA_INFO_DATA]);
+                       data = attr;
+               }
+               lu->print_opt(lu, fp, data);
+
+               if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
+                   lu->print_xstats)
+                       lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
+       }
+}
+
+static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
+{
+       struct ifla_vf_mac *vf_mac;
+       struct ifla_vf_vlan *vf_vlan;
+       struct ifla_vf_tx_rate *vf_tx_rate;
+       struct ifla_vf_spoofchk *vf_spoofchk;
+       struct rtattr *vf[IFLA_VF_MAX+1];
+       struct rtattr *tmp;
+       SPRINT_BUF(b1);
+
+       if (vfinfo->rta_type != IFLA_VF_INFO) {
+               fprintf(stderr, "BUG: rta type is %d\n", vfinfo->rta_type);
                return;
        }
-       close(s);
 
-       if (ifr.ifr_qlen)
-               printf("qlen %d", ifr.ifr_qlen);
+       parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
+
+       vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
+       vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
+       vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
+
+       /* Check if the spoof checking vf info type is supported by
+        * this kernel.
+        */
+       tmp = (struct rtattr *)((char *)vf[IFLA_VF_TX_RATE] +
+                       vf[IFLA_VF_TX_RATE]->rta_len);
+
+       if (tmp->rta_type != IFLA_VF_SPOOFCHK)
+               vf_spoofchk = NULL;
+       else
+               vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
+
+       fprintf(fp, "\n    vf %d MAC %s", vf_mac->vf,
+               ll_addr_n2a((unsigned char *)&vf_mac->mac,
+               ETH_ALEN, 0, b1, sizeof(b1)));
+       if (vf_vlan->vlan)
+               fprintf(fp, ", vlan %d", vf_vlan->vlan);
+       if (vf_vlan->qos)
+               fprintf(fp, ", qos %d", vf_vlan->qos);
+       if (vf_tx_rate->rate)
+               fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
+       if (vf_spoofchk && vf_spoofchk->setting != -1) {
+               if (vf_spoofchk->setting)
+                       fprintf(fp, ", spoof checking on");
+               else
+                       fprintf(fp, ", spoof checking off");
+       }
+}
+
+static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s) {
+       fprintf(fp, "%s", _SL_);
+       fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+               s->rx_compressed ? "compressed" : "", _SL_);
+       fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+               (uint64_t)s->rx_bytes,
+               (uint64_t)s->rx_packets,
+               (uint64_t)s->rx_errors,
+               (uint64_t)s->rx_dropped,
+               (uint64_t)s->rx_over_errors,
+               (uint64_t)s->multicast);
+       if (s->rx_compressed)
+               fprintf(fp, " %-7"PRIu64"",
+                       (uint64_t)s->rx_compressed);
+       if (show_stats > 1) {
+               fprintf(fp, "%s", _SL_);
+               fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
+               fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+                       (uint64_t)s->rx_length_errors,
+                       (uint64_t)s->rx_crc_errors,
+                       (uint64_t)s->rx_frame_errors,
+                       (uint64_t)s->rx_fifo_errors,
+                       (uint64_t)s->rx_missed_errors);
+       }
+       fprintf(fp, "%s", _SL_);
+       fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+               (uint64_t)s->tx_compressed ? "compressed" : "", _SL_);
+       fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+               (uint64_t)s->tx_bytes,
+               (uint64_t)s->tx_packets,
+               (uint64_t)s->tx_errors,
+               (uint64_t)s->tx_dropped,
+               (uint64_t)s->tx_carrier_errors,
+               (uint64_t)s->collisions);
+       if (s->tx_compressed)
+               fprintf(fp, " %-7"PRIu64"",
+                       (uint64_t)s->tx_compressed);
+       if (show_stats > 1) {
+               fprintf(fp, "%s", _SL_);
+               fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
+               fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+                       (uint64_t)s->tx_aborted_errors,
+                       (uint64_t)s->tx_fifo_errors,
+                       (uint64_t)s->tx_window_errors,
+                       (uint64_t)s->tx_heartbeat_errors);
+       }
+}
+
+static void print_link_stats(FILE *fp, const struct rtnl_link_stats *s)
+{
+       fprintf(fp, "%s", _SL_);
+       fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+               s->rx_compressed ? "compressed" : "", _SL_);
+       fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
+               s->rx_bytes, s->rx_packets, s->rx_errors,
+               s->rx_dropped, s->rx_over_errors,
+               s->multicast
+               );
+       if (s->rx_compressed)
+               fprintf(fp, " %-7u", s->rx_compressed);
+       if (show_stats > 1) {
+               fprintf(fp, "%s", _SL_);
+               fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
+               fprintf(fp, "               %-7u  %-7u %-7u %-7u %-7u",
+                       s->rx_length_errors,
+                       s->rx_crc_errors,
+                       s->rx_frame_errors,
+                       s->rx_fifo_errors,
+                       s->rx_missed_errors
+                       );
+       }
+       fprintf(fp, "%s", _SL_);
+       fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+               s->tx_compressed ? "compressed" : "", _SL_);
+       fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
+               s->tx_bytes, s->tx_packets, s->tx_errors,
+               s->tx_dropped, s->tx_carrier_errors, s->collisions);
+       if (s->tx_compressed)
+               fprintf(fp, " %-7u", s->tx_compressed);
+       if (show_stats > 1) {
+               fprintf(fp, "%s", _SL_);
+               fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
+               fprintf(fp, "               %-7u  %-7u %-7u %-7u",
+                       s->tx_aborted_errors,
+                       s->tx_fifo_errors,
+                       s->tx_window_errors,
+                       s->tx_heartbeat_errors
+                       );
+       }
 }
 
-int print_linkinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n, void *arg)
+int print_linkinfo(const struct sockaddr_nl *who,
+                  struct nlmsghdr *n, void *arg)
 {
        FILE *fp = (FILE*)arg;
        struct ifinfomsg *ifi = NLMSG_DATA(n);
@@ -146,22 +378,26 @@ int print_linkinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n, void
        if (filter.up && !(ifi->ifi_flags&IFF_UP))
                return 0;
 
-       memset(tb, 0, sizeof(tb));
        parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
        if (tb[IFLA_IFNAME] == NULL) {
-               fprintf(stderr, "BUG: nil ifname\n");
-               return -1;
+               fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index);
        }
        if (filter.label &&
            (!filter.family || filter.family == AF_PACKET) &&
            fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
                return 0;
 
+       if (tb[IFLA_GROUP]) {
+               int group = *(int*)RTA_DATA(tb[IFLA_GROUP]);
+               if (group != filter.group)
+                       return -1;
+       }
+
        if (n->nlmsg_type == RTM_DELLINK)
                fprintf(fp, "Deleted ");
 
        fprintf(fp, "%d: %s", ifi->ifi_index,
-               tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+               tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
 
        if (tb[IFLA_LINK]) {
                SPRINT_BUF(b1);
@@ -181,16 +417,21 @@ int print_linkinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n, void
        if (tb[IFLA_MTU])
                fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
        if (tb[IFLA_QDISC])
-               fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
-#ifdef IFLA_MASTER
+               fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC]));
        if (tb[IFLA_MASTER]) {
                SPRINT_BUF(b1);
                fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
        }
-#endif
+
+       if (tb[IFLA_OPERSTATE])
+               print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
+
+       if (do_link && tb[IFLA_LINKMODE])
+               print_linkmode(fp, tb[IFLA_LINKMODE]);
+
        if (filter.showqueue)
-               print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
-       
+               print_queuelen(fp, tb);
+
        if (!filter.family || filter.family == AF_PACKET) {
                SPRINT_BUF(b1);
                fprintf(fp, "%s", _SL_);
@@ -213,53 +454,28 @@ int print_linkinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n, void
                                                      b1, sizeof(b1)));
                }
        }
-       if (do_link && tb[IFLA_STATS] && show_stats) {
-               struct rtnl_link_stats slocal;
-               struct rtnl_link_stats *s = RTA_DATA(tb[IFLA_STATS]);
-               if (((unsigned long)s) & (sizeof(unsigned long)-1)) {
-                       memcpy(&slocal, s, sizeof(slocal));
-                       s = &slocal;
-               }
-               fprintf(fp, "%s", _SL_);
-               fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
-                       s->rx_compressed ? "compressed" : "", _SL_);
-               fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
-                       s->rx_bytes, s->rx_packets, s->rx_errors,
-                       s->rx_dropped, s->rx_over_errors,
-                       s->multicast
-                       );
-               if (s->rx_compressed)
-                       fprintf(fp, " %-7u", s->rx_compressed);
-               if (show_stats > 1) {
-                       fprintf(fp, "%s", _SL_);
-                       fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
-                       fprintf(fp, "               %-7u  %-7u %-7u %-7u %-7u",
-                               s->rx_length_errors,
-                               s->rx_crc_errors,
-                               s->rx_frame_errors,
-                               s->rx_fifo_errors,
-                               s->rx_missed_errors
-                               );
-               }
-               fprintf(fp, "%s", _SL_);
-               fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
-                       s->tx_compressed ? "compressed" : "", _SL_);
-               fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
-                       s->tx_bytes, s->tx_packets, s->tx_errors,
-                       s->tx_dropped, s->tx_carrier_errors, s->collisions);
-               if (s->tx_compressed)
-                       fprintf(fp, " %-7u", s->tx_compressed);
-               if (show_stats > 1) {
-                       fprintf(fp, "%s", _SL_);
-                       fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
-                       fprintf(fp, "               %-7u  %-7u %-7u %-7u",
-                               s->tx_aborted_errors,
-                               s->tx_fifo_errors,
-                               s->tx_window_errors,
-                               s->tx_heartbeat_errors
-                               );
-               }
+
+       if (do_link && tb[IFLA_LINKINFO] && show_details)
+               print_linktype(fp, tb[IFLA_LINKINFO]);
+
+       if (do_link && tb[IFLA_IFALIAS])
+               fprintf(fp,"\n    alias %s", 
+                       rta_getattr_str(tb[IFLA_IFALIAS]));
+
+       if (do_link && show_stats) {
+               if (tb[IFLA_STATS64])
+                       print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]));
+               else if (tb[IFLA_STATS])
+                       print_link_stats(fp, RTA_DATA(tb[IFLA_STATS]));
        }
+
+       if (do_link && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
+               struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
+               int rem = RTA_PAYLOAD(vflist);
+               for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
+                       print_vfinfo(fp, i);
+       }
+
        fprintf(fp, "\n");
        fflush(fp);
        return 0;
@@ -267,20 +483,33 @@ int print_linkinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n, void
 
 static int flush_update(void)
 {
-       if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
-               perror("Failed to send flush request\n");
+       if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
+               perror("Failed to send flush request");
                return -1;
        }
        filter.flushp = 0;
        return 0;
 }
 
-int print_addrinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n, 
+static int set_lifetime(unsigned int *lifetime, char *argv)
+{
+       if (strcmp(argv, "forever") == 0)
+               *lifetime = INFINITY_LIFE_TIME;
+       else if (get_u32(lifetime, argv, 0))
+               return -1;
+
+       return 0;
+}
+
+int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
                   void *arg)
 {
        FILE *fp = (FILE*)arg;
        struct ifaddrmsg *ifa = NLMSG_DATA(n);
        int len = n->nlmsg_len;
+       int deprecated = 0;
+       /* Use local copy of ifa_flags to not interfere with filtering code */
+       unsigned int ifa_flags;
        struct rtattr * rta_tb[IFA_MAX+1];
        char abuf[256];
        SPRINT_BUF(b1);
@@ -296,7 +525,6 @@ int print_addrinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n,
        if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
                return 0;
 
-       memset(rta_tb, 0, sizeof(rta_tb));
        parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
 
        if (!rta_tb[IFA_LOCAL])
@@ -331,6 +559,9 @@ int print_addrinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n,
                }
        }
 
+       if (filter.family && filter.family != ifa->ifa_family)
+               return 0;
+
        if (filter.flushb) {
                struct nlmsghdr *fn;
                if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
@@ -341,7 +572,7 @@ int print_addrinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n,
                memcpy(fn, n, n->nlmsg_len);
                fn->nlmsg_type = RTM_DELADDR;
                fn->nlmsg_flags = NLM_F_REQUEST;
-               fn->nlmsg_seq = ++filter.rth->seq;
+               fn->nlmsg_seq = ++rth.seq;
                filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
                filter.flushed++;
                if (show_stats < 2)
@@ -398,45 +629,87 @@ int print_addrinfo(const struct sockaddr_nl *who, const struct nlmsghdr *n,
                                    abuf, sizeof(abuf)));
        }
        fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+       ifa_flags = ifa->ifa_flags;
        if (ifa->ifa_flags&IFA_F_SECONDARY) {
-               ifa->ifa_flags &= ~IFA_F_SECONDARY;
-               fprintf(fp, "secondary ");
+               ifa_flags &= ~IFA_F_SECONDARY;
+               if (ifa->ifa_family == AF_INET6)
+                       fprintf(fp, "temporary ");
+               else
+                       fprintf(fp, "secondary ");
        }
        if (ifa->ifa_flags&IFA_F_TENTATIVE) {
-               ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+               ifa_flags &= ~IFA_F_TENTATIVE;
                fprintf(fp, "tentative ");
        }
        if (ifa->ifa_flags&IFA_F_DEPRECATED) {
-               ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+               ifa_flags &= ~IFA_F_DEPRECATED;
+               deprecated = 1;
                fprintf(fp, "deprecated ");
        }
+       if (ifa->ifa_flags&IFA_F_HOMEADDRESS) {
+               ifa_flags &= ~IFA_F_HOMEADDRESS;
+               fprintf(fp, "home ");
+       }
+       if (ifa->ifa_flags&IFA_F_NODAD) {
+               ifa_flags &= ~IFA_F_NODAD;
+               fprintf(fp, "nodad ");
+       }
        if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
                fprintf(fp, "dynamic ");
        } else
-               ifa->ifa_flags &= ~IFA_F_PERMANENT;
-       if (ifa->ifa_flags)
-               fprintf(fp, "flags %02x ", ifa->ifa_flags);
+               ifa_flags &= ~IFA_F_PERMANENT;
+       if (ifa->ifa_flags&IFA_F_DADFAILED) {
+               ifa_flags &= ~IFA_F_DADFAILED;
+               fprintf(fp, "dadfailed ");
+       }
+       if (ifa_flags)
+               fprintf(fp, "flags %02x ", ifa_flags);
        if (rta_tb[IFA_LABEL])
-               fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+               fprintf(fp, "%s", rta_getattr_str(rta_tb[IFA_LABEL]));
        if (rta_tb[IFA_CACHEINFO]) {
                struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
-               char buf[128];
                fprintf(fp, "%s", _SL_);
-               if (ci->ifa_valid == 0xFFFFFFFFU)
-                       sprintf(buf, "valid_lft forever");
+               fprintf(fp, "       valid_lft ");
+               if (ci->ifa_valid == INFINITY_LIFE_TIME)
+                       fprintf(fp, "forever");
                else
-                       sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
-               if (ci->ifa_prefered == 0xFFFFFFFFU)
-                       sprintf(buf+strlen(buf), " preferred_lft forever");
-               else
-                       sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
-               fprintf(fp, "       %s", buf);
+                       fprintf(fp, "%usec", ci->ifa_valid);
+               fprintf(fp, " preferred_lft ");
+               if (ci->ifa_prefered == INFINITY_LIFE_TIME)
+                       fprintf(fp, "forever");
+               else {
+                       if (deprecated)
+                               fprintf(fp, "%dsec", ci->ifa_prefered);
+                       else
+                               fprintf(fp, "%usec", ci->ifa_prefered);
+               }
        }
        fprintf(fp, "\n");
        fflush(fp);
        return 0;
 }
 
+int print_addrinfo_primary(const struct sockaddr_nl *who, struct nlmsghdr *n,
+                          void *arg)
+{
+       struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+       if (ifa->ifa_flags & IFA_F_SECONDARY)
+               return 0;
+
+       return print_addrinfo(who, n, arg);
+}
+
+int print_addrinfo_secondary(const struct sockaddr_nl *who, struct nlmsghdr *n,
+                            void *arg)
+{
+       struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+       if (!(ifa->ifa_flags & IFA_F_SECONDARY))
+               return 0;
+
+       return print_addrinfo(who, n, arg);
+}
 
 struct nlmsg_list
 {
@@ -444,7 +717,7 @@ struct nlmsg_list
        struct nlmsghdr   h;
 };
 
-int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
 {
        for ( ;ainfo ;  ainfo = ainfo->next) {
                struct nlmsghdr *n = &ainfo->h;
@@ -456,7 +729,7 @@ int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
                if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
                        return -1;
 
-               if (ifa->ifa_index != ifindex || 
+               if (ifa->ifa_index != ifindex ||
                    (filter.family && filter.family != ifa->ifa_family))
                        continue;
 
@@ -466,7 +739,7 @@ int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
 }
 
 
-static int store_nlmsg(const struct sockaddr_nl *who, const struct nlmsghdr *n, 
+static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
                       void *arg)
 {
        struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
@@ -487,12 +760,11 @@ static int store_nlmsg(const struct sockaddr_nl *who, const struct nlmsghdr *n,
        return 0;
 }
 
-int ipaddr_list_or_flush(int argc, char **argv, int flush)
+static int ipaddr_list_or_flush(int argc, char **argv, int flush)
 {
        struct nlmsg_list *linfo = NULL;
        struct nlmsg_list *ainfo = NULL;
-       struct nlmsg_list *l;
-       struct rtnl_handle rth;
+       struct nlmsg_list *l, *n;
        char *filter_dev = NULL;
        int no_link = 0;
 
@@ -502,9 +774,12 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
        if (filter.family == AF_UNSPEC)
                filter.family = preferred_family;
 
+       filter.group = INIT_NETDEV_GROUP;
+
        if (flush) {
                if (argc <= 0) {
                        fprintf(stderr, "Flush requires arguments.\n");
+
                        return -1;
                }
                if (filter.family == AF_PACKET) {
@@ -520,7 +795,7 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                        if (filter.family == AF_UNSPEC)
                                filter.family = filter.pfx.family;
                } else if (strcmp(*argv, "scope") == 0) {
-                       int scope = 0;
+                       unsigned scope = 0;
                        NEXT_ARG();
                        filter.scopemask = -1;
                        if (rtnl_rtscope_a2n(&scope, *argv)) {
@@ -538,7 +813,8 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                } else if (strcmp(*argv, "permanent") == 0) {
                        filter.flags |= IFA_F_PERMANENT;
                        filter.flagmask |= IFA_F_PERMANENT;
-               } else if (strcmp(*argv, "secondary") == 0) {
+               } else if (strcmp(*argv, "secondary") == 0 ||
+                          strcmp(*argv, "temporary") == 0) {
                        filter.flags |= IFA_F_SECONDARY;
                        filter.flagmask |= IFA_F_SECONDARY;
                } else if (strcmp(*argv, "primary") == 0) {
@@ -550,9 +826,22 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                } else if (strcmp(*argv, "deprecated") == 0) {
                        filter.flags |= IFA_F_DEPRECATED;
                        filter.flagmask |= IFA_F_DEPRECATED;
+               } else if (strcmp(*argv, "home") == 0) {
+                       filter.flags |= IFA_F_HOMEADDRESS;
+                       filter.flagmask |= IFA_F_HOMEADDRESS;
+               } else if (strcmp(*argv, "nodad") == 0) {
+                       filter.flags |= IFA_F_NODAD;
+                       filter.flagmask |= IFA_F_NODAD;
+               } else if (strcmp(*argv, "dadfailed") == 0) {
+                       filter.flags |= IFA_F_DADFAILED;
+                       filter.flagmask |= IFA_F_DADFAILED;
                } else if (strcmp(*argv, "label") == 0) {
                        NEXT_ARG();
                        filter.label = *argv;
+               } else if (strcmp(*argv, "group") == 0) {
+                       NEXT_ARG();
+                       if (rtnl_group_a2n(&filter.group, *argv))
+                               invarg("Invalid \"group\" value\n", *argv);
                } else {
                        if (strcmp(*argv, "dev") == 0) {
                                NEXT_ARG();
@@ -566,15 +855,12 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                argv++; argc--;
        }
 
-       if (rtnl_open(&rth, 0) < 0)
-               exit(1);
-
        if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
                perror("Cannot send dump request");
                exit(1);
        }
 
-       if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
+       if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
                fprintf(stderr, "Dump terminated\n");
                exit(1);
        }
@@ -594,34 +880,62 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                filter.flushb = flushb;
                filter.flushp = 0;
                filter.flushe = sizeof(flushb);
-               filter.rth = &rth;
 
-               for (;;) {
+               while ((max_flush_loops == 0) || (round < max_flush_loops)) {
+                       const struct rtnl_dump_filter_arg a[3] = {
+                               {
+                                       .filter = print_addrinfo_secondary,
+                                       .arg1 = stdout,
+                               },
+                               {
+                                       .filter = print_addrinfo_primary,
+                                       .arg1 = stdout,
+                               },
+                               {
+                                       .filter = NULL,
+                                       .arg1 = NULL,
+                               },
+                       };
                        if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
                                perror("Cannot send dump request");
                                exit(1);
                        }
                        filter.flushed = 0;
-                       if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
+                       if (rtnl_dump_filter_l(&rth, a) < 0) {
                                fprintf(stderr, "Flush terminated\n");
                                exit(1);
                        }
                        if (filter.flushed == 0) {
-                               if (round == 0) {
-                                       fprintf(stderr, "Nothing to flush.\n");
-                               } else if (show_stats)
-                                       printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+flush_done:
+                               if (show_stats) {
+                                       if (round == 0)
+                                               printf("Nothing to flush.\n");
+                                       else 
+                                               printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+                               }
                                fflush(stdout);
                                return 0;
                        }
                        round++;
                        if (flush_update() < 0)
-                               exit(1);
+                               return 1;
+
                        if (show_stats) {
                                printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed);
                                fflush(stdout);
                        }
+
+                       /* If we are flushing, and specifying primary, then we
+                        * want to flush only a single round.  Otherwise, we'll
+                        * start flushing secondaries that were promoted to
+                        * primaries.
+                        */
+                       if (!(filter.flags & IFA_F_SECONDARY) && (filter.flagmask & IFA_F_SECONDARY))
+                               goto flush_done;
                }
+               fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", max_flush_loops);
+               fflush(stderr);
+               return 1;
        }
 
        if (filter.family != AF_PACKET) {
@@ -630,7 +944,7 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                        exit(1);
                }
 
-               if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
+               if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo) < 0) {
                        fprintf(stderr, "Dump terminated\n");
                        exit(1);
                }
@@ -653,7 +967,7 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                                struct nlmsghdr *n = &a->h;
                                struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
-                               if (ifa->ifa_index != ifi->ifi_index || 
+                               if (ifa->ifa_index != ifi->ifi_index ||
                                    (filter.family && filter.family != ifa->ifa_family))
                                        continue;
                                if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
@@ -662,7 +976,6 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                                        continue;
                                if (filter.pfx.family || filter.label) {
                                        struct rtattr *tb[IFA_MAX+1];
-                                       memset(tb, 0, sizeof(tb));
                                        parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
                                        if (!tb[IFA_LOCAL])
                                                tb[IFA_LOCAL] = tb[IFA_ADDRESS];
@@ -697,16 +1010,18 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
                }
        }
 
-       for (l=linfo; l; l = l->next) {
+       for (l=linfo; l; l = n) {
+               n = l->next;
                if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
                        struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
                        if (filter.family != AF_PACKET)
                                print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
                }
                fflush(stdout);
+               free(l);
        }
 
-       exit(0);
+       return 0;
 }
 
 int ipaddr_list_link(int argc, char **argv)
@@ -722,7 +1037,7 @@ void ipaddr_reset_filter(int oneline)
        filter.oneline = oneline;
 }
 
-int default_scope(inet_prefix *lcl)
+static int default_scope(inet_prefix *lcl)
 {
        if (lcl->family == AF_INET) {
                if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
@@ -731,9 +1046,8 @@ int default_scope(inet_prefix *lcl)
        return 0;
 }
 
-int ipaddr_modify(int cmd, int argc, char **argv)
+static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
 {
-       struct rtnl_handle rth;
        struct {
                struct nlmsghdr         n;
                struct ifaddrmsg        ifa;
@@ -741,6 +1055,9 @@ int ipaddr_modify(int cmd, int argc, char **argv)
        } req;
        char  *d = NULL;
        char  *l = NULL;
+       char  *lcl_arg = NULL;
+       char  *valid_lftp = NULL;
+       char  *preferred_lftp = NULL;
        inet_prefix lcl;
        inet_prefix peer;
        int local_len = 0;
@@ -748,11 +1065,14 @@ int ipaddr_modify(int cmd, int argc, char **argv)
        int brd_len = 0;
        int any_len = 0;
        int scoped = 0;
+       __u32 preferred_lft = INFINITY_LIFE_TIME;
+       __u32 valid_lft = INFINITY_LIFE_TIME;
+       struct ifa_cacheinfo cinfo;
 
        memset(&req, 0, sizeof(req));
 
        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
-       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_flags = NLM_F_REQUEST | flags;
        req.n.nlmsg_type = cmd;
        req.ifa.ifa_family = preferred_family;
 
@@ -797,7 +1117,7 @@ int ipaddr_modify(int cmd, int argc, char **argv)
                        addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
                        any_len = addr.bytelen;
                } else if (strcmp(*argv, "scope") == 0) {
-                       int scope = 0;
+                       unsigned scope = 0;
                        NEXT_ARG();
                        if (rtnl_rtscope_a2n(&scope, *argv))
                                invarg(*argv, "invalid scope value.");
@@ -810,6 +1130,24 @@ int ipaddr_modify(int cmd, int argc, char **argv)
                        NEXT_ARG();
                        l = *argv;
                        addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
+               } else if (matches(*argv, "valid_lft") == 0) {
+                       if (valid_lftp)
+                               duparg("valid_lft", *argv);
+                       NEXT_ARG();
+                       valid_lftp = *argv;
+                       if (set_lifetime(&valid_lft, *argv))
+                               invarg("valid_lft value", *argv);
+               } else if (matches(*argv, "preferred_lft") == 0) {
+                       if (preferred_lftp)
+                               duparg("preferred_lft", *argv);
+                       NEXT_ARG();
+                       preferred_lftp = *argv;
+                       if (set_lifetime(&preferred_lft, *argv))
+                               invarg("preferred_lft value", *argv);
+               } else if (strcmp(*argv, "home") == 0) {
+                       req.ifa.ifa_flags |= IFA_F_HOMEADDRESS;
+               } else if (strcmp(*argv, "nodad") == 0) {
+                       req.ifa.ifa_flags |= IFA_F_NODAD;
                } else {
                        if (strcmp(*argv, "local") == 0) {
                                NEXT_ARG();
@@ -818,6 +1156,7 @@ int ipaddr_modify(int cmd, int argc, char **argv)
                                usage();
                        if (local_len)
                                duparg2("local", *argv);
+                       lcl_arg = *argv;
                        get_prefix(&lcl, *argv, req.ifa.ifa_family);
                        if (req.ifa.ifa_family == AF_UNSPEC)
                                req.ifa.ifa_family = lcl.family;
@@ -832,12 +1171,20 @@ int ipaddr_modify(int cmd, int argc, char **argv)
        }
        if (l && matches(d, l) != 0) {
                fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
-               exit(1);
+               return -1;
        }
 
-       if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
-               peer = lcl;
-               addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+       if (peer_len == 0 && local_len) {
+               if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) {
+                       fprintf(stderr,
+                           "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" \
+                           "         Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" \
+                           "         This special behaviour is likely to disappear in further releases,\n" \
+                           "         fix your scripts!\n", lcl_arg, local_len*8);
+               } else {
+                       peer = lcl;
+                       addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+               }
        }
        if (req.ifa.ifa_prefixlen == 0)
                req.ifa.ifa_prefixlen = lcl.bitlen;
@@ -864,9 +1211,6 @@ int ipaddr_modify(int cmd, int argc, char **argv)
        if (!scoped && cmd != RTM_DELADDR)
                req.ifa.ifa_scope = default_scope(&lcl);
 
-       if (rtnl_open(&rth, 0) < 0)
-               exit(1);
-
        ll_init_map(&rth);
 
        if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
@@ -874,10 +1218,27 @@ int ipaddr_modify(int cmd, int argc, char **argv)
                return -1;
        }
 
-       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
-               exit(2);
+       if (valid_lftp || preferred_lftp) {
+               if (!valid_lft) {
+                       fprintf(stderr, "valid_lft is zero\n");
+                       return -1;
+               }
+               if (valid_lft < preferred_lft) {
+                       fprintf(stderr, "preferred_lft is greater than valid_lft\n");
+                       return -1;
+               }
 
-       exit(0);
+               memset(&cinfo, 0, sizeof(cinfo));
+               cinfo.ifa_prefered = preferred_lft;
+               cinfo.ifa_valid = valid_lft;
+               addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
+                         sizeof(cinfo));
+       }
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+               return -2;
+
+       return 0;
 }
 
 int do_ipaddr(int argc, char **argv)
@@ -885,9 +1246,14 @@ int do_ipaddr(int argc, char **argv)
        if (argc < 1)
                return ipaddr_list_or_flush(0, NULL, 0);
        if (matches(*argv, "add") == 0)
-               return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
+               return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
+       if (matches(*argv, "change") == 0 ||
+               strcmp(*argv, "chg") == 0)
+               return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc-1, argv+1);
+       if (matches(*argv, "replace") == 0)
+               return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
        if (matches(*argv, "delete") == 0)
-               return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
+               return ipaddr_modify(RTM_DELADDR, 0, argc-1, argv+1);
        if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
            || matches(*argv, "lst") == 0)
                return ipaddr_list_or_flush(argc-1, argv+1, 0);
@@ -895,7 +1261,7 @@ int do_ipaddr(int argc, char **argv)
                return ipaddr_list_or_flush(argc-1, argv+1, 1);
        if (matches(*argv, "help") == 0)
                usage();
-       fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv);
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip addr help\".\n", *argv);
        exit(-1);
 }