]> granicus.if.org Git - libnl/commitdiff
Big routing code rework (API/ABI BREAK!)
authorThomas Graf <tgr@lsx.localdomain>
Tue, 29 Apr 2008 21:31:30 +0000 (23:31 +0200)
committerThomas Graf <tgr@lsx.localdomain>
Tue, 29 Apr 2008 21:31:30 +0000 (23:31 +0200)
Adds all missing routing attributes and brings the routing
related code to a working state. In the process the API
was broken several times with the justification that nobody
is using this code yet.

The changes include new example code which is also a prototype
for how plain CLI tools could look like to control routes.

28 files changed:
include/netlink-types.h
include/netlink/list.h
include/netlink/route/nexthop.h
include/netlink/route/route.h
include/netlink/route/rtnl.h
include/netlink/route/rule.h
lib/netfilter/ct_obj.c
lib/object.c
lib/route/addr.c
lib/route/link.c
lib/route/neigh.c
lib/route/nexthop.c
lib/route/route.c
lib/route/route_obj.c
lib/route/route_utils.c
lib/route/rule.c
src/.gitignore
src/Makefile
src/f_route.c [deleted file]
src/nl-route-add.c
src/nl-route-del.c [deleted file]
src/nl-route-delete.c [new file with mode: 0644]
src/nl-route-dump.c [deleted file]
src/nl-route-list.c [new file with mode: 0644]
src/route-utils.c [new file with mode: 0644]
src/route-utils.h [new file with mode: 0644]
src/utils.c
src/utils.h

index a690cb29530942483f27dced68fc47d554e94bd5..3678a87a4b85c739b1a91b8b3bfeee024ae38be0 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_LOCAL_TYPES_H_
@@ -104,7 +104,7 @@ struct genl_info
        struct nlattr **        attrs;
 };
 
-#define LOOSE_FLAG_COMPARISON  1
+#define LOOSE_COMPARISON       1
 
 #define NL_OBJ_MARK            1
 
@@ -244,11 +244,6 @@ struct rtnl_addr
        uint32_t a_flag_mask;
 };
 
-#define NEXTHOP_HAS_FLAGS   0x000001
-#define NEXTHOP_HAS_WEIGHT  0x000002
-#define NEXTHOP_HAS_IFINDEX 0x000004
-#define NEXTHOP_HAS_GATEWAY 0x000008
-
 struct rtnl_nexthop
 {
        uint8_t                 rtnh_flags;
@@ -257,9 +252,9 @@ struct rtnl_nexthop
        /* 1 byte spare */
        uint32_t                rtnh_ifindex;
        struct nl_addr *        rtnh_gateway;
-       uint32_t                rtnh_mask;
-
+       uint32_t                ce_mask; /* HACK to support attr macros */
        struct nl_list_head     rtnh_list;
+       uint32_t                rtnh_realms;
 };
 
 struct rtnl_route
@@ -270,24 +265,22 @@ struct rtnl_route
        uint8_t                 rt_dst_len;
        uint8_t                 rt_src_len;
        uint8_t                 rt_tos;
-       uint8_t                 rt_table;
        uint8_t                 rt_protocol;
        uint8_t                 rt_scope;
        uint8_t                 rt_type;
+       uint8_t                 rt_nmetrics;
        uint32_t                rt_flags;
        struct nl_addr *        rt_dst;
        struct nl_addr *        rt_src;
-       char                    rt_iif[IFNAMSIZ];
-       uint32_t                rt_oif;
-       struct nl_addr *        rt_gateway;
+       uint32_t                rt_table;
+       uint32_t                rt_iif;
        uint32_t                rt_prio;
        uint32_t                rt_metrics[RTAX_MAX];
        uint32_t                rt_metrics_mask;
+       uint32_t                rt_nr_nh;
        struct nl_addr *        rt_pref_src;
        struct nl_list_head     rt_nexthops;
-       realm_t                 rt_realms;
        struct rtnl_rtcacheinfo rt_cacheinfo;
-       uint32_t                rt_mp_algo;
        uint32_t                rt_flag_mask;
 };
 
index e7a26464cc5697e4d71eab29660d641a104fe274..c6876a77f76c287cc3fbe37ec7c902ccddd517c0 100644 (file)
@@ -68,6 +68,9 @@ static inline int nl_list_empty(struct nl_list_head *head)
 #define NL_LIST_HEAD(name) \
        struct nl_list_head name = { &(name), &(name) }
 
+#define nl_list_first_entry(head, type, member)                        \
+       nl_list_entry((head)->next, type, member)
+
 #define nl_list_for_each_entry(pos, head, member)                              \
        for (pos = nl_list_entry((head)->next, typeof(*pos), member);   \
             &(pos)->member != (head);  \
index 984f4b55718c3f07a16fc47b9d57bbaa832c8289..2aa44dce6fb0001ef44a2a562827b06c223a6a33 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_ROUTE_NEXTHOP_H_
@@ -21,18 +21,42 @@ extern "C" {
 
 struct rtnl_nexthop;
 
-extern struct rtnl_nexthop *   rtnl_route_nh_alloc(void);
-extern struct rtnl_nexthop *   rtnl_route_nh_clone(struct rtnl_nexthop *);
+enum {
+       NH_DUMP_FROM_ONELINE = -2,
+       NH_DUMP_FROM_DETAILS = -1,
+       NH_DUMP_FROM_ENV = 0,
+       /* > 0 reserved for nexthop index */
+};
+
+extern struct rtnl_nexthop * rtnl_route_nh_alloc(void);
+extern struct rtnl_nexthop * rtnl_route_nh_clone(struct rtnl_nexthop *);
 extern void            rtnl_route_nh_free(struct rtnl_nexthop *);
-extern void            rtnl_route_nh_set_weight(struct rtnl_nexthop *, int);
+
+extern int             rtnl_route_nh_compare(struct rtnl_nexthop *,
+                                             struct rtnl_nexthop *,
+                                             uint32_t, int);
+
+extern void            rtnl_route_nh_dump(struct rtnl_nexthop *,
+                                          struct nl_dump_params *);
+
+extern void            rtnl_route_nh_set_weight(struct rtnl_nexthop *, uint8_t);
+extern uint8_t         rtnl_route_nh_get_weight(struct rtnl_nexthop *);
 extern void            rtnl_route_nh_set_ifindex(struct rtnl_nexthop *, int);
+extern int             rtnl_route_nh_get_ifindex(struct rtnl_nexthop *);
 extern void            rtnl_route_nh_set_gateway(struct rtnl_nexthop *,
                                                  struct nl_addr *);
+extern struct nl_addr *        rtnl_route_nh_get_gateway(struct rtnl_nexthop *);
 extern void            rtnl_route_nh_set_flags(struct rtnl_nexthop *,
                                                unsigned int);
 extern void            rtnl_route_nh_unset_flags(struct rtnl_nexthop *,
                                                  unsigned int);
 extern unsigned int    rtnl_route_nh_get_flags(struct rtnl_nexthop *);
+extern void            rtnl_route_nh_set_realms(struct rtnl_nexthop *,
+                                                uint32_t);
+extern uint32_t                rtnl_route_nh_get_realms(struct rtnl_nexthop *);
+
+extern char *          rtnl_route_nh_flags2str(int, char *, size_t);
+extern int             rtnl_route_nh_str2flags(const char *);
 
 #ifdef __cplusplus
 }
index f59f36b4dc98923cfa892fd926e2ed301bdd103f..53f47bea3200781d451a592bd28d25d10829a2d5 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_ROUTE_H_
@@ -17,6 +17,7 @@
 #include <netlink/addr.h>
 #include <netlink/data.h>
 #include <netlink/route/nexthop.h>
+#include <netlink/route/rtnl.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -46,63 +47,58 @@ extern struct nl_cache *    rtnl_route_alloc_cache(struct nl_handle *);
 extern void            rtnl_route_get(struct rtnl_route *);
 extern void            rtnl_route_put(struct rtnl_route *);
 
+extern struct rtnl_route *rtnl_route_parse(struct nlmsghdr *);
+extern int             rtnl_route_build_msg(struct nl_msg *,
+                                            struct rtnl_route *);
+
 extern struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *, int);
 extern int rtnl_route_add(struct nl_handle *, struct rtnl_route *, int);
 extern struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *, int);
-extern int rtnl_route_del(struct nl_handle *, struct rtnl_route *, int);
-
-extern void            rtnl_route_set_table(struct rtnl_route *, int);
-extern int             rtnl_route_get_table(struct rtnl_route *);
-extern void            rtnl_route_set_scope(struct rtnl_route *, int);
-extern int             rtnl_route_get_scope(struct rtnl_route *);
-extern void            rtnl_route_set_tos(struct rtnl_route *, int);
-extern int             rtnl_route_get_tos(struct rtnl_route *);
-extern void            rtnl_route_set_realms(struct rtnl_route *, realm_t);
-extern realm_t         rtnl_route_get_realms(struct rtnl_route *);
-extern void            rtnl_route_set_protocol(struct rtnl_route *, int);
-extern int             rtnl_route_get_protocol(struct rtnl_route *);
-extern void            rtnl_route_set_prio(struct rtnl_route *, int);
-extern int             rtnl_route_get_prio(struct rtnl_route *);
-extern void            rtnl_route_set_family(struct rtnl_route *, int);
-extern int             rtnl_route_get_family(struct rtnl_route *);
-extern void            rtnl_route_set_type(struct rtnl_route *, int);
-extern int             rtnl_route_get_type(struct rtnl_route *);
-extern void            rtnl_route_set_flags(struct rtnl_route *,
-                                            unsigned int);
-extern void            rtnl_route_unset_flags(struct rtnl_route *,
-                                              unsigned int);
-extern unsigned int    rtnl_route_get_flags(struct rtnl_route *);
+extern int rtnl_route_delete(struct nl_handle *, struct rtnl_route *, int);
+
+extern void            rtnl_route_set_table(struct rtnl_route *, uint32_t);
+extern uint32_t                rtnl_route_get_table(struct rtnl_route *);
+extern void            rtnl_route_set_scope(struct rtnl_route *, uint8_t);
+extern uint8_t         rtnl_route_get_scope(struct rtnl_route *);
+extern void            rtnl_route_set_tos(struct rtnl_route *, uint8_t);
+extern uint8_t         rtnl_route_get_tos(struct rtnl_route *);
+extern void            rtnl_route_set_protocol(struct rtnl_route *, uint8_t);
+extern uint8_t         rtnl_route_get_protocol(struct rtnl_route *);
+extern void            rtnl_route_set_priority(struct rtnl_route *, uint32_t);
+extern uint32_t                rtnl_route_get_priority(struct rtnl_route *);
+extern int             rtnl_route_set_family(struct rtnl_route *, uint8_t);
+extern uint8_t         rtnl_route_get_family(struct rtnl_route *);
+extern int             rtnl_route_set_type(struct rtnl_route *, uint8_t);
+extern uint8_t         rtnl_route_get_type(struct rtnl_route *);
+extern void            rtnl_route_set_flags(struct rtnl_route *, uint32_t);
+extern void            rtnl_route_unset_flags(struct rtnl_route *, uint32_t);
+extern uint32_t                rtnl_route_get_flags(struct rtnl_route *);
 extern int             rtnl_route_set_metric(struct rtnl_route *, int,
                                              unsigned int);
 extern int             rtnl_route_unset_metric(struct rtnl_route *, int);
-extern unsigned int    rtnl_route_get_metric(struct rtnl_route *, int);
+extern int             rtnl_route_get_metric(struct rtnl_route *, int,
+                                             uint32_t *);
 extern int             rtnl_route_set_dst(struct rtnl_route *,
                                           struct nl_addr *);
 extern struct nl_addr *        rtnl_route_get_dst(struct rtnl_route *);
 extern int             rtnl_route_set_src(struct rtnl_route *,
                                           struct nl_addr *);
 extern struct nl_addr *        rtnl_route_get_src(struct rtnl_route *);
-extern int             rtnl_route_set_gateway(struct rtnl_route *,
-                                              struct nl_addr *);
-extern struct nl_addr *        rtnl_route_get_gateway(struct rtnl_route *);
 extern int             rtnl_route_set_pref_src(struct rtnl_route *,
                                                struct nl_addr *);
 extern struct nl_addr *        rtnl_route_get_pref_src(struct rtnl_route *);
-extern void            rtnl_route_set_oif(struct rtnl_route *, int);
-extern int             rtnl_route_get_oif(struct rtnl_route *);
-extern void            rtnl_route_set_iif(struct rtnl_route *, const char *);
-extern char *          rtnl_route_get_iif(struct rtnl_route *);
-extern int             rtnl_route_get_dst_len(struct rtnl_route *);
+extern void            rtnl_route_set_iif(struct rtnl_route *, int);
+extern int             rtnl_route_get_iif(struct rtnl_route *);
 extern int             rtnl_route_get_src_len(struct rtnl_route *);
 
 extern void            rtnl_route_add_nexthop(struct rtnl_route *,
                                               struct rtnl_nexthop *);
-extern void            rtnl_route_remove_nexthop(struct rtnl_nexthop *);
+extern void            rtnl_route_remove_nexthop(struct rtnl_route *,
+                                                 struct rtnl_nexthop *);
 extern struct nl_list_head *   rtnl_route_get_nexthops(struct rtnl_route *);
-extern void            rtnl_route_set_cacheinfo(struct rtnl_route *,
-                                                struct rtnl_rtcacheinfo *);
-extern uint32_t                rtnl_route_get_mp_algo(struct rtnl_route *);
-extern void            rtnl_route_set_mp_algo(struct rtnl_route *, uint32_t);
+extern int             rtnl_route_get_nnexthops(struct rtnl_route *);
+
+extern int             rtnl_route_guess_scope(struct rtnl_route *);
 
 extern char *          rtnl_route_table2str(int, char *, size_t);
 extern int             rtnl_route_str2table(const char *);
@@ -115,9 +111,6 @@ extern int          rtnl_route_read_protocol_names(const char *);
 extern char *          rtnl_route_metric2str(int, char *, size_t);
 extern int             rtnl_route_str2metric(const char *);
 
-extern char *          rtnl_route_nh_flags2str(int, char *, size_t);
-extern int             rtnl_route_nh_str2flags(const char *);
-
 #ifdef __cplusplus
 }
 #endif
index 9d116cd71b517843697ebf486370b0be6ba9e31d..b12479aec3c57f15bdd2a7b5c3939f3458274838 100644 (file)
@@ -23,8 +23,6 @@ extern "C" {
  * @{
  */
 
-typedef uint32_t       realm_t;
-
 /**
  * Mask specying the size of each realm part
  * @ingroup rtnl
index d295b0d739bd0f15b8c363834566b79d0181c68a..e57ef5346c19eddf94a46b8d7fad341db9d979ef 100644 (file)
@@ -69,8 +69,8 @@ extern char *         rtnl_rule_get_iif(struct rtnl_rule *);
 extern void            rtnl_rule_set_classid(struct rtnl_rule *, uint32_t);
 extern uint32_t                rtnl_rule_get_classid(struct rtnl_rule *);
 
-extern void            rtnl_rule_set_realms(struct rtnl_rule *, realm_t);
-extern realm_t         rtnl_rule_get_realms(struct rtnl_rule *);
+extern void            rtnl_rule_set_realms(struct rtnl_rule *, uint32_t);
+extern uint32_t                rtnl_rule_get_realms(struct rtnl_rule *);
 
 #ifdef __cplusplus
 }
index 0f5a79e546fca008b8e81cf2862d9e31eba65bd2..96e5db60b61bbc42e17303372c67a87313de4213 100644 (file)
@@ -197,7 +197,7 @@ static int ct_compare(struct nl_object *_a, struct nl_object *_b,
 #define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR)
 #define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD)
 #define CT_DIFF_ADDR(ATTR, FIELD) \
-       ((flags & LOOSE_FLAG_COMPARISON) \
+       ((flags & LOOSE_COMPARISON) \
                ? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \
                : CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD)))
 
@@ -227,7 +227,7 @@ static int ct_compare(struct nl_object *_a, struct nl_object *_b,
        diff |= CT_DIFF_VAL(REPL_PACKETS,       ct_repl.packets);
        diff |= CT_DIFF_VAL(REPL_BYTES,         ct_repl.bytes);
 
-       if (flags & LOOSE_FLAG_COMPARISON)
+       if (flags & LOOSE_COMPARISON)
                diff |= CT_DIFF(STATUS, (a->ct_status ^ b->ct_status) &
                                        b->ct_status_mask);
        else
index 74f6e2dd00e10669b190287d3f3bcf4ef4514873..72e4ba4a6ba8886ae88d4c1f1d4130f02e5a7d73 100644 (file)
@@ -318,7 +318,7 @@ int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter)
                return 0;
        
        return !(ops->oo_compare(obj, filter, filter->ce_mask,
-                                LOOSE_FLAG_COMPARISON));
+                                LOOSE_COMPARISON));
 }
 
 /**
index b8ec56cc481f363c49fbee718c020c4418956ead..a4a9acf8f488033b928a00406386251945f1993b 100644 (file)
@@ -585,7 +585,7 @@ static int addr_compare(struct nl_object *_a, struct nl_object *_b,
                                                    b->a_multicast));
        diff |= ADDR_DIFF(BROADCAST,    nl_addr_cmp(a->a_bcast, b->a_bcast));
 
-       if (flags & LOOSE_FLAG_COMPARISON)
+       if (flags & LOOSE_COMPARISON)
                diff |= ADDR_DIFF(FLAGS,
                                  (a->a_flags ^ b->a_flags) & b->a_flag_mask);
        else
index 75031d48e2c1600bf8d15a95ccca885b10f0e0ad..13d3e93ec5c05de37db7a3ad8ae159df2547175b 100644 (file)
@@ -795,7 +795,7 @@ static int link_compare(struct nl_object *_a, struct nl_object *_b,
        diff |= LINK_DIFF(ADDR,         nl_addr_cmp(a->l_addr, b->l_addr));
        diff |= LINK_DIFF(BRD,          nl_addr_cmp(a->l_bcast, b->l_bcast));
 
-       if (flags & LOOSE_FLAG_COMPARISON)
+       if (flags & LOOSE_COMPARISON)
                diff |= LINK_DIFF(FLAGS,
                                  (a->l_flags ^ b->l_flags) & b->l_flag_mask);
        else
index 6f2f0d3fd9ce15180bb009d91f40afc42fae3828..fa1dc59f1e9553846b0d85fba80fcc51b96ea889 100644 (file)
@@ -213,7 +213,7 @@ static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
        diff |= NEIGH_DIFF(LLADDR,      nl_addr_cmp(a->n_lladdr, b->n_lladdr));
        diff |= NEIGH_DIFF(DST,         nl_addr_cmp(a->n_dst, b->n_dst));
 
-       if (flags & LOOSE_FLAG_COMPARISON) {
+       if (flags & LOOSE_COMPARISON) {
                diff |= NEIGH_DIFF(STATE,
                                  (a->n_state ^ b->n_state) & b->n_state_mask);
                diff |= NEIGH_DIFF(FLAGS,
index 748676984cb8a6923ec785c8d3f832e2c49e9d35..2db6d9081b4dfc9fad7ee5edc9107fe06c49c6e9 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
 #include <netlink/route/rtnl.h>
 #include <netlink/route/route.h>
 
+/** @cond SKIP */
+#define NH_ATTR_FLAGS   0x000001
+#define NH_ATTR_WEIGHT  0x000002
+#define NH_ATTR_IFINDEX 0x000004
+#define NH_ATTR_GATEWAY 0x000008
+#define NH_ATTR_REALMS  0x000010
+/** @endcond */
+
 /**
  * @name Allocation/Freeing
  * @{
@@ -53,7 +61,7 @@ struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src)
        nh->rtnh_flag_mask = src->rtnh_flag_mask;
        nh->rtnh_weight = src->rtnh_weight;
        nh->rtnh_ifindex = src->rtnh_ifindex;
-       nh->rtnh_mask = src->rtnh_mask;
+       nh->ce_mask = src->ce_mask;
 
        if (src->rtnh_gateway) {
                nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway);
@@ -74,78 +82,251 @@ void rtnl_route_nh_free(struct rtnl_nexthop *nh)
 
 /** @} */
 
+int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b,
+                         uint32_t attrs, int loose)
+{
+       int diff = 0;
+
+#define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR)
+
+       diff |= NH_DIFF(IFINDEX,        a->rtnh_ifindex != b->rtnh_ifindex);
+       diff |= NH_DIFF(WEIGHT,         a->rtnh_weight != b->rtnh_weight);
+       diff |= NH_DIFF(REALMS,         a->rtnh_realms != b->rtnh_realms);
+       diff |= NH_DIFF(GATEWAY,        nl_addr_cmp(a->rtnh_gateway,
+                                                   b->rtnh_gateway));
+
+       if (loose)
+               diff |= NH_DIFF(FLAGS,
+                         (a->rtnh_flags ^ b->rtnh_flags) & b->rtnh_flag_mask);
+       else
+               diff |= NH_DIFF(FLAGS, a->rtnh_flags != b->rtnh_flags);
+       
+#undef NH_DIFF
+
+       return diff;
+}
+
+static void nh_dump_oneline(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+       struct nl_cache *link_cache;
+       char buf[128];
+
+       link_cache = nl_cache_mngt_require("route/link");
+
+       nl_dump(dp, "via");
+
+       if (nh->ce_mask & NH_ATTR_GATEWAY)
+               nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway,
+                                                  buf, sizeof(buf)));
+
+       if(nh->ce_mask & NH_ATTR_IFINDEX) {
+               if (link_cache) {
+                       nl_dump(dp, " dev %s",
+                               rtnl_link_i2name(link_cache,
+                                                nh->rtnh_ifindex,
+                                                buf, sizeof(buf)));
+               } else
+                       nl_dump(dp, " dev %d", nh->rtnh_ifindex);
+       }
+
+       nl_dump(dp, " ");
+}
+
+static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+       struct nl_cache *link_cache;
+       char buf[128];
+
+       link_cache = nl_cache_mngt_require("route/link");
+
+       nl_dump(dp, "nexthop");
+
+       if (nh->ce_mask & NH_ATTR_GATEWAY)
+               nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway,
+                                                  buf, sizeof(buf)));
+
+       if(nh->ce_mask & NH_ATTR_IFINDEX) {
+               if (link_cache) {
+                       nl_dump(dp, " dev %s",
+                               rtnl_link_i2name(link_cache,
+                                                nh->rtnh_ifindex,
+                                                buf, sizeof(buf)));
+               } else
+                       nl_dump(dp, " dev %d", nh->rtnh_ifindex);
+       }
+
+       if (nh->ce_mask & NH_ATTR_WEIGHT)
+               nl_dump(dp, " weight %u", nh->rtnh_weight);
+
+       if (nh->ce_mask & NH_ATTR_REALMS)
+               nl_dump(dp, " realm %04x:%04x",
+                       RTNL_REALM_FROM(nh->rtnh_realms),
+                       RTNL_REALM_TO(nh->rtnh_realms));
+
+       if (nh->ce_mask & NH_ATTR_FLAGS)
+               nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags,
+                                                       buf, sizeof(buf)));
+}
+
+static void nh_dump_env(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+       struct nl_cache *link_cache;
+       char buf[128];
+
+       link_cache = nl_cache_mngt_require("route/link");
+
+       if (nh->ce_mask & NH_ATTR_GATEWAY)
+               nl_dump_line(dp, "ROUTE_NH%d_VIA=%s\n", dp->dp_ivar,
+                       nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf)));
+
+       if(nh->ce_mask & NH_ATTR_IFINDEX) {
+               if (link_cache) {
+                       nl_dump_line(dp, "ROUTE_NH%d_DEV=%s\n", dp->dp_ivar,
+                                       rtnl_link_i2name(link_cache,
+                                                nh->rtnh_ifindex,
+                                                buf, sizeof(buf)));
+               } else
+                       nl_dump_line(dp, "ROUTE_NH%d_DEV=%d\n", dp->dp_ivar,
+                                       nh->rtnh_ifindex);
+       }
+
+       if (nh->ce_mask & NH_ATTR_WEIGHT)
+               nl_dump_line(dp, "ROUTE_NH%d_WEIGHT=%u\n", dp->dp_ivar,
+                               nh->rtnh_weight);
+
+       if (nh->ce_mask & NH_ATTR_REALMS)
+               nl_dump_line(dp, "ROUTE_NH%d_REALM=%04x:%04x\n", dp->dp_ivar,
+                       RTNL_REALM_FROM(nh->rtnh_realms),
+                       RTNL_REALM_TO(nh->rtnh_realms));
+
+       if (nh->ce_mask & NH_ATTR_FLAGS)
+               nl_dump_line(dp, "ROUTE_NH%d_FLAGS=<%s>\n", dp->dp_ivar,
+                       rtnl_route_nh_flags2str(nh->rtnh_flags,
+                                                       buf, sizeof(buf)));
+}
+void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+       switch (dp->dp_type) {
+       case NL_DUMP_ONELINE:
+               nh_dump_oneline(nh, dp);
+               break;
+
+       case NL_DUMP_DETAILS:
+       case NL_DUMP_STATS:
+               if (dp->dp_ivar == NH_DUMP_FROM_DETAILS)
+                       nh_dump_details(nh, dp);
+               break;
+
+       case NL_DUMP_ENV:
+               nh_dump_env(nh, dp);
+               break;
+       
+       default:
+               break;
+       }
+}
+
 /**
  * @name Attributes
+ * @{
  */
 
-void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, int weight)
+void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight)
 {
        nh->rtnh_weight = weight;
-       nh->rtnh_mask |= NEXTHOP_HAS_WEIGHT;
+       nh->ce_mask |= NH_ATTR_WEIGHT;
 }
 
-int rtnl_route_nh_get_weight(struct rtnl_nexthop *nh)
+uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh)
 {
-       if (nh->rtnh_mask & NEXTHOP_HAS_WEIGHT)
-               return nh->rtnh_weight;
-       else
-               return 0;
+       return nh->rtnh_weight;
 }
 
 void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex)
 {
        nh->rtnh_ifindex = ifindex;
-       nh->rtnh_mask |= NEXTHOP_HAS_IFINDEX;
+       nh->ce_mask |= NH_ATTR_IFINDEX;
 }
 
 int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh)
 {
-       if (nh->rtnh_mask & NEXTHOP_HAS_IFINDEX)
-               return nh->rtnh_ifindex;
-       else
-               return -1;
+       return nh->rtnh_ifindex;
 }      
 
 void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr)
 {
        struct nl_addr *old = nh->rtnh_gateway;
 
-       nh->rtnh_gateway = nl_addr_get(addr);
+       if (addr) {
+               nh->rtnh_gateway = nl_addr_get(addr);
+               nh->ce_mask |= NH_ATTR_GATEWAY;
+       } else {
+               nh->ce_mask &= ~NH_ATTR_GATEWAY;
+               nh->rtnh_gateway = NULL;
+       }
+
        if (old)
                nl_addr_put(old);
-
-       nh->rtnh_mask |= NEXTHOP_HAS_GATEWAY;
 }
 
 struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh)
 {
-       if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY)
-               return nh->rtnh_gateway;
-       else
-               return NULL;
+       return nh->rtnh_gateway;
 }
 
 void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags)
 {
        nh->rtnh_flag_mask |= flags;
        nh->rtnh_flags |= flags;
-       nh->rtnh_mask |= NEXTHOP_HAS_FLAGS;
+       nh->ce_mask |= NH_ATTR_FLAGS;
 }
 
 void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags)
 {
        nh->rtnh_flag_mask |= flags;
        nh->rtnh_flags &= ~flags;
-       nh->rtnh_mask |= NEXTHOP_HAS_FLAGS;
+       nh->ce_mask |= NH_ATTR_FLAGS;
 }
 
 unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh)
 {
-       if (nh->rtnh_mask & NEXTHOP_HAS_FLAGS)
-               return nh->rtnh_flags;
-       else
-               return 0;
+       return nh->rtnh_flags;
+}
+
+void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms)
+{
+       nh->rtnh_realms = realms;
+       nh->ce_mask |= NH_ATTR_REALMS;
+}
+
+uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh)
+{
+       return nh->rtnh_realms;
+}
+
+/** @} */
+
+/**
+ * @name Nexthop Flags Translations
+ * @{
+ */
+
+static struct trans_tbl nh_flags[] = {
+       __ADD(RTNH_F_DEAD, dead)
+       __ADD(RTNH_F_PERVASIVE, pervasive)
+       __ADD(RTNH_F_ONLINK, onlink)
+};
+
+char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
+{
+       return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags));
+}
+
+int rtnl_route_nh_str2flags(const char *name)
+{
+       return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags));
 }
 
 /** @} */
+
 /** @} */
index 0644bd7530788853825e3de0375bd5b285a9d5d4..9edaddcb34eae2249b80e7e2bdbb1827d48ce2be 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
 
 static struct nl_cache_ops rtnl_route_ops;
 
-static struct nla_policy route_policy[RTA_MAX+1] = {
-       [RTA_IIF]       = { .type = NLA_STRING,
-                           .maxlen = IFNAMSIZ, },
-       [RTA_OIF]       = { .type = NLA_U32 },
-       [RTA_PRIORITY]  = { .type = NLA_U32 },
-       [RTA_FLOW]      = { .type = NLA_U32 },
-       [RTA_MP_ALGO]   = { .type = NLA_U32 },
-       [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) },
-       [RTA_METRICS]   = { .type = NLA_NESTED },
-       [RTA_MULTIPATH] = { .type = NLA_NESTED },
-};
-
-static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci,
-                                     struct rtnl_route *route)
-{
-       struct rtnl_rtcacheinfo nci = {
-               .rtci_clntref  = ci->rta_clntref,
-               .rtci_last_use = ci->rta_lastuse,
-               .rtci_expires  = ci->rta_expires,
-               .rtci_error    = ci->rta_error,
-               .rtci_used     = ci->rta_used,
-               .rtci_id       = ci->rta_id,
-               .rtci_ts       = ci->rta_ts,
-               .rtci_tsage    = ci->rta_tsage,
-       };
-
-       rtnl_route_set_cacheinfo(route, &nci);
-}
-
 static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
                            struct nlmsghdr *nlh, struct nl_parser_param *pp)
 {
-       struct rtmsg *rtm;
        struct rtnl_route *route;
-       struct nlattr *tb[RTA_MAX + 1];
-       struct nl_addr *src = NULL, *dst = NULL, *addr;
        int err;
 
-       route = rtnl_route_alloc();
-       if (!route) {
-               err = nl_errno(ENOMEM);
-               goto errout;
-       }
-
-       route->ce_msgtype = nlh->nlmsg_type;
+       if (!(route = rtnl_route_parse(nlh)))
+               return -EINVAL;
 
-       err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
-                         route_policy);
-       if (err < 0)
-               goto errout;
-
-       rtm = nlmsg_data(nlh);
-       rtnl_route_set_family(route, rtm->rtm_family);
-       rtnl_route_set_tos(route, rtm->rtm_tos);
-       rtnl_route_set_table(route, rtm->rtm_table);
-       rtnl_route_set_type(route, rtm->rtm_type);
-       rtnl_route_set_scope(route, rtm->rtm_scope);
-       rtnl_route_set_protocol(route, rtm->rtm_protocol);
-       rtnl_route_set_flags(route, rtm->rtm_flags);
-
-       if (tb[RTA_DST]) {
-               dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family);
-               if (dst == NULL)
-                       goto errout_errno;
-       } else {
-               dst = nl_addr_alloc(0);
-               nl_addr_set_family(dst, rtm->rtm_family);
-       }
-
-       nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
-       err = rtnl_route_set_dst(route, dst);
-       if (err < 0)
-               goto errout;
-
-       nl_addr_put(dst);
-
-       if (tb[RTA_SRC]) {
-               src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family);
-               if (src == NULL)
-                       goto errout_errno;
-       } else if (rtm->rtm_src_len)
-               src = nl_addr_alloc(0);
-
-       if (src) {
-               nl_addr_set_prefixlen(src, rtm->rtm_src_len);
-               rtnl_route_set_src(route, src);
-               nl_addr_put(src);
-       }
-
-       if (tb[RTA_IIF])
-               rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF]));
-
-       if (tb[RTA_OIF])
-               rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF]));
-
-       if (tb[RTA_GATEWAY]) {
-               addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family);
-               if (addr == NULL)
-                       goto errout_errno;
-               rtnl_route_set_gateway(route, addr);
-               nl_addr_put(addr);
-       }
-
-       if (tb[RTA_PRIORITY])
-               rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY]));
-
-       if (tb[RTA_PREFSRC]) {
-               addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family);
-               if (addr == NULL)
-                       goto errout_errno;
-               rtnl_route_set_pref_src(route, addr);
-               nl_addr_put(addr);
-       }
-
-       if (tb[RTA_METRICS]) {
-               struct nlattr *mtb[RTAX_MAX + 1];
-               int i;
-
-               err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
-               if (err < 0)
-                       goto errout;
-
-               for (i = 1; i <= RTAX_MAX; i++) {
-                       if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
-                               uint32_t m = nla_get_u32(mtb[i]);
-                               if (rtnl_route_set_metric(route, i, m) < 0)
-                                       goto errout_errno;
-                       }
-               }
-       }
-
-       if (tb[RTA_MULTIPATH]) {
-               struct rtnl_nexthop *nh;
-               struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]);
-               size_t tlen = nla_len(tb[RTA_MULTIPATH]);
-
-               while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
-                       nh = rtnl_route_nh_alloc();
-                       if (!nh)
-                               goto errout;
-
-                       rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
-                       rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
-                       rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);
-
-                       if (rtnh->rtnh_len > sizeof(*rtnh)) {
-                               struct nlattr *ntb[RTA_MAX + 1];
-                               nla_parse(ntb, RTA_MAX, (struct nlattr *)
-                                         RTNH_DATA(rtnh),
-                                         rtnh->rtnh_len - sizeof(*rtnh),
-                                         route_policy);
-
-                               if (ntb[RTA_GATEWAY]) {
-                                       nh->rtnh_gateway = nla_get_addr(
-                                                       ntb[RTA_GATEWAY],
-                                                       route->rt_family);
-                                       nh->rtnh_mask = NEXTHOP_HAS_GATEWAY;
-                               }
-                       }
-
-                       rtnl_route_add_nexthop(route, nh);
-                       tlen -= RTNH_ALIGN(rtnh->rtnh_len);
-                       rtnh = RTNH_NEXT(rtnh);
-               }
-       }
-
-       if (tb[RTA_FLOW])
-               rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW]));
-
-       if (tb[RTA_CACHEINFO])
-               copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route);
-
-       if (tb[RTA_MP_ALGO])
-               rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO]));
-
-       err = pp->pp_cb((struct nl_object *) route, pp);
-       if (err < 0)
+       if ((err = pp->pp_cb((struct nl_object *) route, pp)) < 0)
                goto errout;
 
        err = P_ACCEPT;
@@ -211,10 +44,6 @@ static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 errout:
        rtnl_route_put(route);
        return err;
-
-errout_errno:
-       err = nl_get_errno();
-       goto errout;
 }
 
 static int route_request_update(struct nl_cache *c, struct nl_handle *h)
@@ -265,102 +94,17 @@ static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd,
                                      int flags)
 {
        struct nl_msg *msg;
-       struct nl_addr *addr;
-       int scope, i, oif, nmetrics = 0;
-       struct nlattr *metrics;
-       struct rtmsg rtmsg = {
-               .rtm_family = rtnl_route_get_family(tmpl),
-               .rtm_dst_len = rtnl_route_get_dst_len(tmpl),
-               .rtm_src_len = rtnl_route_get_src_len(tmpl),
-               .rtm_tos = rtnl_route_get_tos(tmpl),
-               .rtm_table = rtnl_route_get_table(tmpl),
-               .rtm_type = rtnl_route_get_type(tmpl),
-               .rtm_protocol = rtnl_route_get_protocol(tmpl),
-               .rtm_flags = rtnl_route_get_flags(tmpl),
-       };
-
-       if (rtmsg.rtm_family == AF_UNSPEC) {
-               nl_error(EINVAL, "Cannot build route message, address " \
-                                "family is unknown.");
-               return NULL;
-       }
-
-       scope = rtnl_route_get_scope(tmpl);
-       if (scope == RT_SCOPE_NOWHERE) {
-               if (rtmsg.rtm_type == RTN_LOCAL)
-                       scope = RT_SCOPE_HOST;
-               else {
-                       /* XXX Change to UNIVERSE if gw || nexthops */
-                       scope = RT_SCOPE_LINK;
-               }
-       }
-
-       rtmsg.rtm_scope = scope;
 
        msg = nlmsg_alloc_simple(cmd, flags);
        if (msg == NULL)
                return NULL;
 
-       if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
-               goto nla_put_failure;
-
-       addr = rtnl_route_get_dst(tmpl);
-       if (addr)
-               NLA_PUT_ADDR(msg, RTA_DST, addr);
-
-       addr = rtnl_route_get_src(tmpl);
-       if (addr)
-               NLA_PUT_ADDR(msg, RTA_SRC, addr);
-
-       addr = rtnl_route_get_gateway(tmpl);
-       if (addr)
-               NLA_PUT_ADDR(msg, RTA_GATEWAY, addr);
-
-       addr = rtnl_route_get_pref_src(tmpl);
-       if (addr)
-               NLA_PUT_ADDR(msg, RTA_PREFSRC, addr);
-
-       NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl));
-
-       oif = rtnl_route_get_oif(tmpl);
-       if (oif != RTNL_LINK_NOT_FOUND)
-               NLA_PUT_U32(msg, RTA_OIF, oif);
-
-       for (i = 1; i <= RTAX_MAX; i++)
-               if (rtnl_route_get_metric(tmpl, i) != UINT_MAX)
-                       nmetrics++;
-
-       if (nmetrics > 0) {
-               unsigned int val;
-
-               metrics = nla_nest_start(msg, RTA_METRICS);
-               if (metrics == NULL)
-                       goto nla_put_failure;
-
-               for (i = 1; i <= RTAX_MAX; i++) {
-                       val = rtnl_route_get_metric(tmpl, i);
-                       if (val != UINT_MAX)
-                               NLA_PUT_U32(msg, i, val);
-               }
-
-               nla_nest_end(msg, metrics);
+       if (rtnl_route_build_msg(msg, tmpl) < 0) {
+               nlmsg_free(msg);
+               return NULL;
        }
 
-#if 0
-       RTA_IIF,
-       RTA_MULTIPATH,
-       RTA_PROTOINFO,
-       RTA_FLOW,
-       RTA_CACHEINFO,
-       RTA_SESSION,
-       RTA_MP_ALGO,
-#endif
-
        return msg;
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return NULL;
 }
 
 struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags)
@@ -391,8 +135,8 @@ struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags)
        return build_route_msg(tmpl, RTM_DELROUTE, flags);
 }
 
-int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route,
-                  int flags)
+int rtnl_route_delete(struct nl_handle *handle, struct rtnl_route *route,
+                     int flags)
 {
        struct nl_msg *msg;
        int err;
index 78e7712ced9f3a130c819a8b99814f18f4a1f3fd..8792c10343294c6854e46a8b572a1297051563b7 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
  * routing table                                  RT_TABLE_MAIN
  * scope                                          RT_SCOPE_NOWHERE
  * tos                                            0
- * realms                                         0
  * protocol                                       RTPROT_STATIC
  * prio                                           0
  * family                                         AF_UNSPEC
  * type                                           RTN_UNICAST
- * oif                                            RTNL_LINK_NOT_FOUND
  * iif                                            NULL
- * mpalgo                                         IP_MP_ALG_NONE
  * @endcode
  *
  * @{
@@ -41,6 +38,7 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/route.h>
 #include <netlink/route/link.h>
+#include <netlink/route/nexthop.h>
 
 /** @cond SKIP */
 #define ROUTE_ATTR_FAMILY    0x000001
 #define ROUTE_ATTR_MULTIPATH 0x008000
 #define ROUTE_ATTR_REALMS    0x010000
 #define ROUTE_ATTR_CACHEINFO 0x020000
-#define ROUTE_ATTR_MP_ALGO   0x040000
 /** @endcond */
 
-static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p);
-
 static void route_constructor(struct nl_object *c)
 {
        struct rtnl_route *r = (struct rtnl_route *) c;
 
+       r->rt_family = AF_UNSPEC;
+       r->rt_scope = RT_SCOPE_NOWHERE;
+       r->rt_table = RT_TABLE_MAIN;
+       r->rt_protocol = RTPROT_STATIC;
+       r->rt_type = RTN_UNICAST;
+
        nl_init_list_head(&r->rt_nexthops);
 }
 
@@ -83,11 +84,10 @@ static void route_free_data(struct nl_object *c)
 
        nl_addr_put(r->rt_dst);
        nl_addr_put(r->rt_src);
-       nl_addr_put(r->rt_gateway);
        nl_addr_put(r->rt_pref_src);
 
        nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) {
-               rtnl_route_remove_nexthop(nh);
+               rtnl_route_remove_nexthop(r, nh);
                rtnl_route_nh_free(nh);
        }
 }
@@ -106,10 +106,6 @@ static int route_clone(struct nl_object *_dst, struct nl_object *_src)
                if (!(dst->rt_src = nl_addr_clone(src->rt_src)))
                        goto errout;
 
-       if (src->rt_gateway)
-               if (!(dst->rt_gateway = nl_addr_clone(src->rt_gateway)))
-                       goto errout;
-       
        if (src->rt_pref_src)
                if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src)))
                        goto errout;
@@ -128,7 +124,7 @@ errout:
        return nl_get_errno();
 }
 
-static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p)
+static int route_dump_oneline(struct nl_object *a, struct nl_dump_params *p)
 {
        struct rtnl_route *r = (struct rtnl_route *) a;
        struct nl_cache *link_cache;
@@ -136,330 +132,203 @@ static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p)
 
        link_cache = nl_cache_mngt_require("route/link");
 
+       nl_dump(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf)));
+
        if (!(r->ce_mask & ROUTE_ATTR_DST) ||
            nl_addr_get_len(r->rt_dst) == 0)
-               dp_dump(p, "default ");
+               nl_dump(p, "default ");
        else
-               dp_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
-
-       if (r->ce_mask & ROUTE_ATTR_OIF) {
-               if (link_cache)
-                       dp_dump(p, "dev %s ",
-                               rtnl_link_i2name(link_cache, r->rt_oif,
-                                                buf, sizeof(buf)));
-               else
-                       dp_dump(p, "dev %d ", r->rt_oif);
-       }
+               nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
 
-       if (r->ce_mask & ROUTE_ATTR_GATEWAY)
-               dp_dump(p, "via %s ", nl_addr2str(r->rt_gateway, buf,
-                                                 sizeof(buf)));
-       else if (r->ce_mask & ROUTE_ATTR_MULTIPATH)
-               dp_dump(p, "via nexthops ");
+       if (r->ce_mask & ROUTE_ATTR_TABLE)
+               nl_dump(p, "table %s ",
+                       rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
 
-       if (r->ce_mask & ROUTE_ATTR_SCOPE)
-               dp_dump(p, "scope %s ",
-                       rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
+       if (r->ce_mask & ROUTE_ATTR_TYPE)
+               nl_dump(p, "type %s ",
+                       nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
+
+       if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0)
+               nl_dump(p, "tos %#x ", r->rt_tos);
+
+       if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
+               struct rtnl_nexthop *nh;
+
+               nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
+                       p->dp_ivar = NH_DUMP_FROM_ONELINE;
+                       rtnl_route_nh_dump(nh, p);
+               }
+       }
 
        if (r->ce_mask & ROUTE_ATTR_FLAGS && r->rt_flags) {
                int flags = r->rt_flags;
 
-               dp_dump(p, "<");
+               nl_dump(p, "<");
                
 #define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \
-               flags &= ~RTNH_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
+               flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
                PRINT_FLAG(DEAD);
                PRINT_FLAG(ONLINK);
                PRINT_FLAG(PERVASIVE);
 #undef PRINT_FLAG
 
 #define PRINT_FLAG(f) if (flags & RTM_F_##f) { \
-               flags &= ~RTM_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
+               flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
                PRINT_FLAG(NOTIFY);
                PRINT_FLAG(CLONED);
                PRINT_FLAG(EQUALIZE);
                PRINT_FLAG(PREFIX);
 #undef PRINT_FLAG
 
-               dp_dump(p, ">");
+               nl_dump(p, ">");
        }
 
-       dp_dump(p, "\n");
+       nl_dump(p, "\n");
 
        return 1;
 }
 
-static int route_dump_full(struct nl_object *a, struct nl_dump_params *p)
+static int route_dump_details(struct nl_object *a, struct nl_dump_params *p)
 {
        struct rtnl_route *r = (struct rtnl_route *) a;
        struct nl_cache *link_cache;
        char buf[128];
-       int i, line;
+       int i;
 
        link_cache = nl_cache_mngt_require("route/link");
-       line = route_dump_brief(a, p);
 
-       if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
-               struct rtnl_nexthop *nh;
-
-               nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
-                       dp_dump_line(p, line++, "  via ");
-
-                       if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY)
-                               dp_dump(p, "%s ",
-                                       nl_addr2str(nh->rtnh_gateway,
-                                                   buf, sizeof(buf)));
-                       if (link_cache) {
-                               dp_dump(p, "dev %s ",
-                                       rtnl_link_i2name(link_cache,
-                                                        nh->rtnh_ifindex,
-                                                        buf, sizeof(buf)));
-                       } else
-                               dp_dump(p, "dev %d ", nh->rtnh_ifindex);
-
-                       dp_dump(p, "weight %u <%s>\n", nh->rtnh_weight,
-                               rtnl_route_nh_flags2str(nh->rtnh_flags,
-                                                       buf, sizeof(buf)));
-               }
-       }
-
-       dp_dump_line(p, line++, "  ");
+       route_dump_oneline(a, p);
+       nl_dump_line(p, "    ");
 
        if (r->ce_mask & ROUTE_ATTR_PREF_SRC)
-               dp_dump(p, "preferred-src %s ",
+               nl_dump(p, "preferred-src %s ",
                        nl_addr2str(r->rt_pref_src, buf, sizeof(buf)));
 
-       if (r->ce_mask & ROUTE_ATTR_TABLE)
-               dp_dump(p, "table %s ",
-                       rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
-
-       if (r->ce_mask & ROUTE_ATTR_TYPE)
-               dp_dump(p, "type %s ",
-                       nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
+       if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE)
+               nl_dump(p, "scope %s ",
+                       rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
 
        if (r->ce_mask & ROUTE_ATTR_PRIO)
-               dp_dump(p, "metric %#x ", r->rt_prio);
-
-       if (r->ce_mask & ROUTE_ATTR_FAMILY)
-               dp_dump(p, "family %s ",
-                       nl_af2str(r->rt_family, buf, sizeof(buf)));
+               nl_dump(p, "priority %#x ", r->rt_prio);
 
        if (r->ce_mask & ROUTE_ATTR_PROTOCOL)
-               dp_dump(p, "protocol %s ",
+               nl_dump(p, "protocol %s ",
                        rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf)));
 
-       dp_dump(p, "\n");
-
-       if ((r->ce_mask & (ROUTE_ATTR_IIF | ROUTE_ATTR_SRC | ROUTE_ATTR_TOS |
-                          ROUTE_ATTR_REALMS)) || 
-           ((r->ce_mask & ROUTE_ATTR_CACHEINFO) &&
-            r->rt_cacheinfo.rtci_error)) {
-               dp_dump_line(p, line++, "  ");
+       if (r->ce_mask & ROUTE_ATTR_IIF)
+               nl_dump(p, "iif %s ", r->rt_iif);
 
-               if (r->ce_mask & ROUTE_ATTR_IIF)
-                       dp_dump(p, "iif %s ", r->rt_iif);
+       if (r->ce_mask & ROUTE_ATTR_SRC)
+               nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf)));
 
-               if (r->ce_mask & ROUTE_ATTR_SRC)
-                       dp_dump(p, "src %s ",
-                               nl_addr2str(r->rt_src, buf, sizeof(buf)));
+       nl_dump(p, "\n");
 
-               if (r->ce_mask & ROUTE_ATTR_TOS)
-                       dp_dump(p, "tos %#x ", r->rt_tos);
-
-               if (r->ce_mask & ROUTE_ATTR_REALMS)
-                       dp_dump(p, "realm %04x:%04x ",
-                               RTNL_REALM_FROM(r->rt_realms),
-                               RTNL_REALM_TO(r->rt_realms));
+       if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
+               struct rtnl_nexthop *nh;
 
-               if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) &&
-                   r->rt_cacheinfo.rtci_error)
-                       dp_dump(p, "error %d (%s) ", r->rt_cacheinfo.rtci_error,
-                               strerror(-r->rt_cacheinfo.rtci_error));
+               nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
+                       nl_dump_line(p, "    ");
+                       p->dp_ivar = NH_DUMP_FROM_DETAILS;
+                       rtnl_route_nh_dump(nh, p);
+                       nl_dump(p, "\n");
+               }
+       }
 
-               dp_dump(p, "\n");
+       if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) {
+               nl_dump_line(p, "    cacheinfo error %d (%s)\n",
+                       r->rt_cacheinfo.rtci_error,
+                       strerror(-r->rt_cacheinfo.rtci_error));
        }
 
        if (r->ce_mask & ROUTE_ATTR_METRICS) {
-               dp_dump_line(p, line++, "  ");
+               nl_dump_line(p, "    metrics [");
                for (i = 0; i < RTAX_MAX; i++)
                        if (r->rt_metrics_mask & (1 << i))
-                               dp_dump(p, "%s %u ",
+                               nl_dump(p, "%s %u ",
                                        rtnl_route_metric2str(i+1,
                                                              buf, sizeof(buf)),
                                        r->rt_metrics[i]);
-               dp_dump(p, "\n");
+               nl_dump(p, "]\n");
        }
 
-       return line;
+       return 0;
 }
 
 static int route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
        struct rtnl_route *route = (struct rtnl_route *) obj;
-       int line;
 
-       line = route_dump_full(obj, p);
+       route_dump_details(obj, p);
 
        if (route->ce_mask & ROUTE_ATTR_CACHEINFO) {
                struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo;
-               dp_dump_line(p, line++, "  used %u refcnt %u ",
-                            ci->rtci_used, ci->rtci_clntref);
-               dp_dump_line(p, line++, "last-use %us expires %us\n",
+
+               nl_dump_line(p, "    used %u refcnt %u last-use %us "
+                               "expires %us\n",
+                            ci->rtci_used, ci->rtci_clntref,
                             ci->rtci_last_use / nl_get_hz(),
                             ci->rtci_expires / nl_get_hz());
        }
 
-       return line;
+       return 0;
 }
 
-static int route_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
+static int route_dump_env(struct nl_object *obj, struct nl_dump_params *p)
 {
        struct rtnl_route *route = (struct rtnl_route *) obj;
        char buf[128];
-       int line = 0;
-       
-       dp_dump_line(p, line++, "<route>\n");
-       dp_dump_line(p, line++, "  <family>%s</family>\n",
+
+       nl_dump(p, "ROUTE_FAMILY=%s\n",
                     nl_af2str(route->rt_family, buf, sizeof(buf)));
 
        if (route->ce_mask & ROUTE_ATTR_DST)
-               dp_dump_line(p, line++, "  <dst>%s</dst>\n",
+               nl_dump_line(p, "ROUTE_DST=%s\n",
                             nl_addr2str(route->rt_dst, buf, sizeof(buf)));
 
        if (route->ce_mask & ROUTE_ATTR_SRC)
-               dp_dump_line(p, line++, "  <src>%s</src>\n",
+               nl_dump_line(p, "ROUTE_SRC=%s\n",
                             nl_addr2str(route->rt_src, buf, sizeof(buf)));
 
-       if (route->ce_mask & ROUTE_ATTR_GATEWAY)
-               dp_dump_line(p, line++, "  <gateway>%s</gateway>\n",
-                            nl_addr2str(route->rt_gateway, buf, sizeof(buf)));
-
        if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
-               dp_dump_line(p, line++, "  <prefsrc>%s</prefsrc>\n",
+               nl_dump_line(p, "ROUTE_PREFSRC=%s\n",
                             nl_addr2str(route->rt_pref_src, buf, sizeof(buf)));
 
        if (route->ce_mask & ROUTE_ATTR_IIF)
-               dp_dump_line(p, line++, "  <iif>%s</iif>\n", route->rt_iif);
-
-       if (route->ce_mask & ROUTE_ATTR_REALMS)
-               dp_dump_line(p, line++, "  <realms>%u</realms>\n",
-                            route->rt_realms);
+               nl_dump_line(p, "ROUTE_IIF=%s\n", route->rt_iif);
 
        if (route->ce_mask & ROUTE_ATTR_TOS)
-               dp_dump_line(p, line++, "  <tos>%u</tos>\n", route->rt_tos);
+               nl_dump_line(p, "ROUTE_TOS=%u\n", route->rt_tos);
 
        if (route->ce_mask & ROUTE_ATTR_TABLE)
-               dp_dump_line(p, line++, "  <table>%u</table>\n",
+               nl_dump_line(p, "ROUTE_TABLE=%u\n",
                             route->rt_table);
 
        if (route->ce_mask & ROUTE_ATTR_SCOPE)
-               dp_dump_line(p, line++, "  <scope>%s</scope>\n",
+               nl_dump_line(p, "ROUTE_SCOPE=%s\n",
                             rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
 
        if (route->ce_mask & ROUTE_ATTR_PRIO)
-               dp_dump_line(p, line++, "  <metric>%u</metric>\n",
+               nl_dump_line(p, "ROUTE_PRIORITY=%u\n",
                             route->rt_prio);
 
-       if (route->ce_mask & ROUTE_ATTR_OIF) {
-               struct nl_cache *link_cache;
-       
-               link_cache = nl_cache_mngt_require("route/link");
-               if (link_cache)
-                       dp_dump_line(p, line++, "  <oif>%s</oif>\n",
-                                    rtnl_link_i2name(link_cache,
-                                                     route->rt_oif,
-                                                     buf, sizeof(buf)));
-               else
-                       dp_dump_line(p, line++, "  <oif>%u</oif>\n",
-                                    route->rt_oif);
-       }
-
        if (route->ce_mask & ROUTE_ATTR_TYPE)
-               dp_dump_line(p, line++, "  <type>%s</type>\n",
+               nl_dump_line(p, "ROUTE_TYPE=%s\n",
                             nl_rtntype2str(route->rt_type, buf, sizeof(buf)));
 
-       dp_dump_line(p, line++, "</route>\n");
-
-#if 0
-       uint8_t                 rt_protocol;
-       uint32_t                rt_flags;
-       uint32_t                rt_metrics[RTAX_MAX];
-       uint32_t                rt_metrics_mask;
-       struct rtnl_nexthop *   rt_nexthops;
-       struct rtnl_rtcacheinfo rt_cacheinfo;
-       uint32_t                rt_mp_algo;
-
-#endif
-
-       return line;
-}
-
-static int route_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
-       struct rtnl_route *route = (struct rtnl_route *) obj;
-       char buf[128];
-       int line = 0;
-
-       dp_dump_line(p, line++, "ROUTE_FAMILY=%s\n",
-                    nl_af2str(route->rt_family, buf, sizeof(buf)));
-
-       if (route->ce_mask & ROUTE_ATTR_DST)
-               dp_dump_line(p, line++, "ROUTE_DST=%s\n",
-                            nl_addr2str(route->rt_dst, buf, sizeof(buf)));
-
-       if (route->ce_mask & ROUTE_ATTR_SRC)
-               dp_dump_line(p, line++, "ROUTE_SRC=%s\n",
-                            nl_addr2str(route->rt_src, buf, sizeof(buf)));
-
-       if (route->ce_mask & ROUTE_ATTR_GATEWAY)
-               dp_dump_line(p, line++, "ROUTE_GATEWAY=%s\n",
-                            nl_addr2str(route->rt_gateway, buf, sizeof(buf)));
-
-       if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
-               dp_dump_line(p, line++, "ROUTE_PREFSRC=%s\n",
-                            nl_addr2str(route->rt_pref_src, buf, sizeof(buf)));
-
-       if (route->ce_mask & ROUTE_ATTR_IIF)
-               dp_dump_line(p, line++, "ROUTE_IIF=%s\n", route->rt_iif);
-
-       if (route->ce_mask & ROUTE_ATTR_REALMS)
-               dp_dump_line(p, line++, "ROUTE_REALM=%u\n",
-                            route->rt_realms);
-
-       if (route->ce_mask & ROUTE_ATTR_TOS)
-               dp_dump_line(p, line++, "ROUTE_TOS=%u\n", route->rt_tos);
-
-       if (route->ce_mask & ROUTE_ATTR_TABLE)
-               dp_dump_line(p, line++, "ROUTE_TABLE=%u\n",
-                            route->rt_table);
-
-       if (route->ce_mask & ROUTE_ATTR_SCOPE)
-               dp_dump_line(p, line++, "ROUTE_SCOPE=%s\n",
-                            rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
-
-       if (route->ce_mask & ROUTE_ATTR_PRIO)
-               dp_dump_line(p, line++, "ROUTE_METRIC=%u\n",
-                            route->rt_prio);
-
-       if (route->ce_mask & ROUTE_ATTR_OIF) {
-               struct nl_cache *link_cache;
+       if (route->ce_mask & ROUTE_ATTR_MULTIPATH) {
+               struct rtnl_nexthop *nh;
+               int index = 1;
 
-               dp_dump_line(p, line++, "ROUTE_OIF_IFINDEX=%u\n",
-                            route->rt_oif);
+               if (route->rt_nr_nh > 0)
+                       nl_dump_line(p, "ROUTE_NR_NH=%u\n", route->rt_nr_nh);
 
-               link_cache = nl_cache_mngt_require("route/link");
-               if (link_cache)
-                       dp_dump_line(p, line++, "ROUTE_OIF_IFNAME=%s\n",
-                                    rtnl_link_i2name(link_cache,
-                                                     route->rt_oif,
-                                                     buf, sizeof(buf)));
+               nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
+                       p->dp_ivar = index++;
+                       rtnl_route_nh_dump(nh, p);
+               }
        }
 
-       if (route->ce_mask & ROUTE_ATTR_TYPE)
-               dp_dump_line(p, line++, "ROUTE_TYPE=%s\n",
-                            nl_rtntype2str(route->rt_type, buf, sizeof(buf)));
-
-       return line;
+       return 0;
 }
 
 static int route_compare(struct nl_object *_a, struct nl_object *_b,
@@ -467,7 +336,8 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b,
 {
        struct rtnl_route *a = (struct rtnl_route *) _a;
        struct rtnl_route *b = (struct rtnl_route *) _b;
-       int diff = 0;
+       struct rtnl_nexthop *nh_a, *nh_b;
+       int i, diff = 0, found;
 
 #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR)
 
@@ -477,29 +347,96 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b,
        diff |= ROUTE_DIFF(PROTOCOL,    a->rt_protocol != b->rt_protocol);
        diff |= ROUTE_DIFF(SCOPE,       a->rt_scope != b->rt_scope);
        diff |= ROUTE_DIFF(TYPE,        a->rt_type != b->rt_type);
-       diff |= ROUTE_DIFF(OIF,         a->rt_oif != b->rt_oif);
        diff |= ROUTE_DIFF(PRIO,        a->rt_prio != b->rt_prio);
-       diff |= ROUTE_DIFF(REALMS,      a->rt_realms != b->rt_realms);
-       diff |= ROUTE_DIFF(MP_ALGO,     a->rt_mp_algo != b->rt_mp_algo);
        diff |= ROUTE_DIFF(DST,         nl_addr_cmp(a->rt_dst, b->rt_dst));
        diff |= ROUTE_DIFF(SRC,         nl_addr_cmp(a->rt_src, b->rt_src));
-       diff |= ROUTE_DIFF(IIF,         strcmp(a->rt_iif, b->rt_iif));
+       diff |= ROUTE_DIFF(IIF,         a->rt_iif != b->rt_iif);
        diff |= ROUTE_DIFF(PREF_SRC,    nl_addr_cmp(a->rt_pref_src,
                                                    b->rt_pref_src));
-       diff |= ROUTE_DIFF(GATEWAY,     nl_addr_cmp(a->rt_gateway,
-                                                   b->rt_gateway));
 
-       /* FIXME: Compare metrics, multipath config */
+       if (flags & LOOSE_COMPARISON) {
+               nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
+                       found = 0;
+                       nl_list_for_each_entry(nh_a, &a->rt_nexthops,
+                                              rtnh_list) {
+                               if (!rtnl_route_nh_compare(nh_a, nh_b,
+                                                       nh_b->ce_mask, 1)) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+
+                       if (!found)
+                               goto nh_mismatch;
+               }
+
+               for (i = 1; i < RTAX_MAX; i++) {
+                       uint32_t val_a, val_b;
+
+                       if (!rtnl_route_get_metric(a, i, &val_a)) {
+                               if (rtnl_route_get_metric(b, i, &val_b) != 0 ||
+                                   val_a != val_b)
+                                       ROUTE_DIFF(METRICS, 1);
+                       }
+               }
 
-       if (flags & LOOSE_FLAG_COMPARISON)
                diff |= ROUTE_DIFF(FLAGS,
                          (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask);
-       else
+       } else {
+               if (a->rt_nr_nh != a->rt_nr_nh)
+                       goto nh_mismatch;
+
+               /* search for a dup in each nh of a */
+               nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) {
+                       found = 0;
+                       nl_list_for_each_entry(nh_b, &b->rt_nexthops,
+                                              rtnh_list) {
+                               if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0))
+                                       found = 1;
+                                       break;
+                       }
+                       if (!found)
+                               goto nh_mismatch;
+               }
+
+               /* search for a dup in each nh of b, covers case where a has
+                * dupes itself */
+               nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
+                       found = 0;
+                       nl_list_for_each_entry(nh_a, &a->rt_nexthops,
+                                              rtnh_list) {
+                               if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0))
+                                       found = 1;
+                                       break;
+                       }
+                       if (!found)
+                               goto nh_mismatch;
+               }
+
+               for (i = 1; i < RTAX_MAX; i++) {
+                       int avail_a, avail_b;
+                       uint32_t val_a, val_b;
+
+                       avail_a = rtnl_route_get_metric(a, i, &val_a);
+                       avail_b = rtnl_route_get_metric(b, i, &val_b);
+
+                       if (avail_a ^ avail_b)
+                               diff |= ROUTE_DIFF(METRICS, 1);
+                       else
+                               diff |= ROUTE_DIFF(METRICS, val_a != val_b);
+               }
+
                diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags);
-       
-#undef ROUTE_DIFF
+       }
 
+out:
        return diff;
+
+nh_mismatch:
+       diff |= ROUTE_DIFF(MULTIPATH, 1);
+       goto out;
+
+#undef ROUTE_DIFF
 }
 
 static struct trans_tbl route_attrs[] = {
@@ -521,7 +458,6 @@ static struct trans_tbl route_attrs[] = {
        __ADD(ROUTE_ATTR_MULTIPATH, multipath)
        __ADD(ROUTE_ATTR_REALMS, realms)
        __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo)
-       __ADD(ROUTE_ATTR_MP_ALGO, mp_algo)
 };
 
 static char *route_attrs2str(int attrs, char *buf, size_t len)
@@ -557,93 +493,76 @@ void rtnl_route_put(struct rtnl_route *route)
  * @{
  */
 
-void rtnl_route_set_table(struct rtnl_route *route, int table)
+void rtnl_route_set_table(struct rtnl_route *route, uint32_t table)
 {
        route->rt_table = table;
        route->ce_mask |= ROUTE_ATTR_TABLE;
 }
 
-int rtnl_route_get_table(struct rtnl_route *route)
+uint32_t rtnl_route_get_table(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_TABLE)
-               return route->rt_table;
-       else
-               return RT_TABLE_MAIN;
+       return route->rt_table;
 }
 
-void rtnl_route_set_scope(struct rtnl_route *route, int scope)
+void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope)
 {
        route->rt_scope = scope;
        route->ce_mask |= ROUTE_ATTR_SCOPE;
 }
 
-int rtnl_route_get_scope(struct rtnl_route *route)
+uint8_t rtnl_route_get_scope(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_SCOPE)
-               return route->rt_scope;
-       else
-               return RT_SCOPE_NOWHERE;
+       return route->rt_scope;
 }
 
-void rtnl_route_set_tos(struct rtnl_route *route, int tos)
+void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos)
 {
        route->rt_tos = tos;
        route->ce_mask |= ROUTE_ATTR_TOS;
 }
 
-int rtnl_route_get_tos(struct rtnl_route *route)
+uint8_t rtnl_route_get_tos(struct rtnl_route *route)
 {
        return route->rt_tos;
 }
 
-void rtnl_route_set_realms(struct rtnl_route *route, realm_t realms)
+void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol)
 {
-       route->rt_realms = realms;
-       route->ce_mask |= ROUTE_ATTR_REALMS;
-}
-
-realm_t rtnl_route_get_realms(struct rtnl_route *route)
-{
-       return route->rt_realms;
-}
-
-void rtnl_route_set_protocol(struct rtnl_route *route, int proto)
-{
-       route->rt_protocol = proto;
+       route->rt_protocol = protocol;
        route->ce_mask |= ROUTE_ATTR_PROTOCOL;
 }
 
-int rtnl_route_get_protocol(struct rtnl_route *route)
+uint8_t rtnl_route_get_protocol(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_PROTOCOL)
-               return route->rt_protocol;
-       else
-               return RTPROT_STATIC;
+       return route->rt_protocol;
 }
 
-void rtnl_route_set_prio(struct rtnl_route *route, int prio)
+void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio)
 {
        route->rt_prio = prio;
        route->ce_mask |= ROUTE_ATTR_PRIO;
 }
 
-int rtnl_route_get_prio(struct rtnl_route *route)
+uint32_t rtnl_route_get_priority(struct rtnl_route *route)
 {
        return route->rt_prio;
 }
 
-void rtnl_route_set_family(struct rtnl_route *route, int family)
+int rtnl_route_set_family(struct rtnl_route *route, uint8_t family)
 {
+       if (family != AF_INET && family != AF_INET6 && family != AF_DECnet)
+               return nl_error(EINVAL, "Unsupported address family, "
+                               "supported: { INET | INET6 | DECnet }");
+
        route->rt_family = family;
        route->ce_mask |= ROUTE_ATTR_FAMILY;
+
+       return 0;
 }
 
-int rtnl_route_get_family(struct rtnl_route *route)
+uint8_t rtnl_route_get_family(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_FAMILY)
-               return route->rt_family;
-       else
-               return AF_UNSPEC;
+       return route->rt_family;
 }
 
 int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr)
@@ -670,16 +589,12 @@ struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route)
        return route->rt_dst;
 }
 
-int rtnl_route_get_dst_len(struct rtnl_route *route)
-{
-       if (route->ce_mask & ROUTE_ATTR_DST)
-               return nl_addr_get_prefixlen(route->rt_dst);
-       else
-               return 0;
-}
-
 int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr)
 {
+       if (addr->a_family == AF_INET)
+               return nl_error(EINVAL, "IPv4 does not support source based "
+                               "routing.");
+
        if (route->ce_mask & ROUTE_ATTR_FAMILY) {
                if (addr->a_family != route->rt_family)
                        return nl_error(EINVAL, "Address family mismatch");
@@ -701,66 +616,37 @@ struct nl_addr *rtnl_route_get_src(struct rtnl_route *route)
        return route->rt_src;
 }
 
-int rtnl_route_get_src_len(struct rtnl_route *route)
-{
-       if (route->ce_mask & ROUTE_ATTR_SRC)
-               return nl_addr_get_prefixlen(route->rt_src);
-       else
-               return 0;
-}
-
-int rtnl_route_set_gateway(struct rtnl_route *route, struct nl_addr *addr)
-{
-       if (route->ce_mask & ROUTE_ATTR_FAMILY) {
-               if (addr->a_family != route->rt_family)
-                       return nl_error(EINVAL, "Address family mismatch");
-       } else
-               route->rt_family = addr->a_family;
-
-       if (route->rt_gateway)
-               nl_addr_put(route->rt_gateway);
-
-       nl_addr_get(addr);
-       route->rt_gateway = addr;
-       route->ce_mask |= (ROUTE_ATTR_GATEWAY | ROUTE_ATTR_FAMILY);
-
-       return 0;
-}
-
-struct nl_addr *rtnl_route_get_gateway(struct rtnl_route *route)
-{
-       return route->rt_gateway;
-}
-
-void rtnl_route_set_type(struct rtnl_route *route, int type)
+int rtnl_route_set_type(struct rtnl_route *route, uint8_t type)
 {
+       if (type > RTN_MAX)
+               return nl_error(ERANGE, "Invalid route type %d, valid range "
+                               "is 0..%d", type, RTN_MAX);
        route->rt_type = type;
        route->ce_mask |= ROUTE_ATTR_TYPE;
+
+       return 0;
 }
 
-int rtnl_route_get_type(struct rtnl_route *route)
+uint8_t rtnl_route_get_type(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_TYPE)
-               return route->rt_type;
-       else
-               return RTN_UNICAST;
+       return route->rt_type;
 }
 
-void rtnl_route_set_flags(struct rtnl_route *route, unsigned int flags)
+void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags)
 {
        route->rt_flag_mask |= flags;
        route->rt_flags |= flags;
        route->ce_mask |= ROUTE_ATTR_FLAGS;
 }
 
-void rtnl_route_unset_flags(struct rtnl_route *route, unsigned int flags)
+void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags)
 {
        route->rt_flag_mask |= flags;
        route->rt_flags &= ~flags;
        route->ce_mask |= ROUTE_ATTR_FLAGS;
 }
 
-unsigned int rtnl_route_get_flags(struct rtnl_route *route)
+uint32_t rtnl_route_get_flags(struct rtnl_route *route)
 {
        return route->rt_flags;
 }
@@ -772,7 +658,13 @@ int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value)
                    RTAX_MAX);
 
        route->rt_metrics[metric - 1] = value;
-       route->rt_metrics_mask |= (1 << (metric - 1));
+
+       if (!(route->rt_metrics_mask & (1 << (metric - 1)))) {
+               route->rt_nmetrics++;
+               route->rt_metrics_mask |= (1 << (metric - 1));
+       }
+
+       route->ce_mask |= ROUTE_ATTR_METRICS;
 
        return 0;
 }
@@ -783,20 +675,27 @@ int rtnl_route_unset_metric(struct rtnl_route *route, int metric)
                return nl_error(EINVAL, "Metric out of range (1..%d)",
                    RTAX_MAX);
 
-       route->rt_metrics_mask &= ~(1 << (metric - 1));
+       if (route->rt_metrics_mask & (1 << (metric - 1))) {
+               route->rt_nmetrics--;
+               route->rt_metrics_mask &= ~(1 << (metric - 1));
+       }
 
        return 0;
 }
 
-unsigned int rtnl_route_get_metric(struct rtnl_route *route, int metric)
+int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value)
 {
        if (metric > RTAX_MAX || metric < 1)
-               return UINT_MAX;
+               return nl_error(EINVAL, "Metric out of range (1..%d)",
+                   RTAX_MAX);
 
        if (!(route->rt_metrics_mask & (1 << (metric - 1))))
-               return UINT_MAX;
+               return nl_error(ENOENT, "Metric not available");
+
+       if (value)
+               *value = route->rt_metrics[metric - 1];
 
-       return route->rt_metrics[metric - 1];
+       return 0;
 }
 
 int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr)
@@ -822,42 +721,27 @@ struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route)
        return route->rt_pref_src;
 }
 
-void rtnl_route_set_oif(struct rtnl_route *route, int ifindex)
-{
-       route->rt_oif = ifindex;
-       route->ce_mask |= ROUTE_ATTR_OIF;
-}
-
-int rtnl_route_get_oif(struct rtnl_route *route)
+void rtnl_route_set_iif(struct rtnl_route *route, int ifindex)
 {
-       if (route->ce_mask & ROUTE_ATTR_OIF)
-               return route->rt_oif;
-       else
-               return RTNL_LINK_NOT_FOUND;
-}
-
-void rtnl_route_set_iif(struct rtnl_route *route, const char *name)
-{
-       strncpy(route->rt_iif, name, sizeof(route->rt_iif) - 1);
+       route->rt_iif = ifindex;
        route->ce_mask |= ROUTE_ATTR_IIF;
 }
 
-char *rtnl_route_get_iif(struct rtnl_route *route)
+int rtnl_route_get_iif(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_IIF)
-               return route->rt_iif;
-       else
-               return NULL;
+       return route->rt_iif;
 }
 
 void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
 {
        nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops);
+       route->rt_nr_nh++;
        route->ce_mask |= ROUTE_ATTR_MULTIPATH;
 }
 
-void rtnl_route_remove_nexthop(struct rtnl_nexthop *nh)
+void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
 {
+       route->rt_nr_nh--;
        nl_list_del(&nh->rtnh_list);
 }
 
@@ -866,44 +750,384 @@ struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route)
        return &route->rt_nexthops;
 }
 
-void rtnl_route_set_cacheinfo(struct rtnl_route *route,
-                             struct rtnl_rtcacheinfo *ci)
+int rtnl_route_get_nnexthops(struct rtnl_route *route)
 {
-       memcpy(&route->rt_cacheinfo, ci, sizeof(*ci));
-       route->ce_mask |= ROUTE_ATTR_CACHEINFO;
+       return route->rt_nr_nh;
 }
 
-uint32_t rtnl_route_get_mp_algo(struct rtnl_route *route)
+/** @} */
+
+/**
+ * @name Utilities
+ * @{
+ */
+
+/**
+ * Guess scope of a route object.
+ * @arg route          Route object.
+ *
+ * Guesses the scope of a route object, based on the following rules:
+ * @code
+ *   1) Local route -> local scope
+ *   2) At least one nexthop not directly connected -> universe scope
+ *   3) All others -> link scope
+ * @endcode
+ *
+ * @return Scope value.
+ */
+int rtnl_route_guess_scope(struct rtnl_route *route)
 {
-       if (route->ce_mask & ROUTE_ATTR_MP_ALGO)
-               return route->rt_mp_algo;
-       else
-               return IP_MP_ALG_NONE;
+       if (route->rt_type == RTN_LOCAL)
+               return RT_SCOPE_HOST;
+
+       if (!nl_list_empty(&route->rt_nexthops)) {
+               struct rtnl_nexthop *nh;
+
+               /*
+                * Use scope uiniverse if there is at least one nexthop which
+                * is not directly connected
+                */
+               nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
+                       if (nh->rtnh_gateway)
+                               return RT_SCOPE_UNIVERSE;
+               }
+       }
+
+       return RT_SCOPE_LINK;
 }
 
-void rtnl_route_set_mp_algo(struct rtnl_route *route, uint32_t algo)
+/** @} */
+
+static struct nla_policy route_policy[RTA_MAX+1] = {
+       [RTA_IIF]       = { .type = NLA_U32 },
+       [RTA_OIF]       = { .type = NLA_U32 },
+       [RTA_PRIORITY]  = { .type = NLA_U32 },
+       [RTA_FLOW]      = { .type = NLA_U32 },
+       [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) },
+       [RTA_METRICS]   = { .type = NLA_NESTED },
+       [RTA_MULTIPATH] = { .type = NLA_NESTED },
+};
+
+struct rtnl_route *rtnl_route_parse(struct nlmsghdr *nlh)
 {
-       route->rt_mp_algo = algo;
-       route->ce_mask |= ROUTE_ATTR_MP_ALGO;
+       struct rtmsg *rtm;
+       struct rtnl_route *route;
+       struct nlattr *tb[RTA_MAX + 1];
+       struct nl_addr *src = NULL, *dst = NULL, *addr;
+       struct rtnl_nexthop *old_nh = NULL;
+       int err;
+
+       route = rtnl_route_alloc();
+       if (!route) {
+               err = nl_errno(ENOMEM);
+               goto errout;
+       }
+
+       route->ce_msgtype = nlh->nlmsg_type;
+
+       err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy);
+       if (err < 0)
+               goto errout;
+
+       rtm = nlmsg_data(nlh);
+       route->rt_family = rtm->rtm_family;
+       route->rt_tos = rtm->rtm_tos;
+       route->rt_table = rtm->rtm_table;
+       route->rt_type = rtm->rtm_type;
+       route->rt_scope = rtm->rtm_scope;
+       route->rt_protocol = rtm->rtm_protocol;
+       route->rt_flags = rtm->rtm_flags;
+
+       route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
+                         ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
+                         ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
+                         ROUTE_ATTR_FLAGS;
+
+       if (tb[RTA_DST]) {
+               dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family);
+               if (dst == NULL)
+                       goto errout;
+       } else {
+               dst = nl_addr_alloc(0);
+               nl_addr_set_family(dst, rtm->rtm_family);
+       }
+
+       nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
+       err = rtnl_route_set_dst(route, dst);
+       if (err < 0)
+               goto errout;
+
+       nl_addr_put(dst);
+
+       if (tb[RTA_SRC]) {
+               src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family);
+               if (src == NULL)
+                       goto errout;
+       } else if (rtm->rtm_src_len)
+               src = nl_addr_alloc(0);
+
+       if (src) {
+               nl_addr_set_prefixlen(src, rtm->rtm_src_len);
+               rtnl_route_set_src(route, src);
+               nl_addr_put(src);
+       }
+
+       if (tb[RTA_IIF])
+               rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF]));
+
+       if (tb[RTA_PRIORITY])
+               rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY]));
+
+       if (tb[RTA_PREFSRC]) {
+               addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family);
+               if (addr == NULL)
+                       goto errout;
+               rtnl_route_set_pref_src(route, addr);
+               nl_addr_put(addr);
+       }
+
+       if (tb[RTA_METRICS]) {
+               struct nlattr *mtb[RTAX_MAX + 1];
+               int i;
+
+               err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
+               if (err < 0)
+                       goto errout;
+
+               for (i = 1; i <= RTAX_MAX; i++) {
+                       if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
+                               uint32_t m = nla_get_u32(mtb[i]);
+                               if (rtnl_route_set_metric(route, i, m) < 0)
+                                       goto errout;
+                       }
+               }
+       }
+
+       if (tb[RTA_MULTIPATH]) {
+               struct rtnl_nexthop *nh;
+               struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]);
+               size_t tlen = nla_len(tb[RTA_MULTIPATH]);
+
+               while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
+                       nh = rtnl_route_nh_alloc();
+                       if (!nh)
+                               goto errout;
+
+                       rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
+                       rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
+                       rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);
+
+                       if (rtnh->rtnh_len > sizeof(*rtnh)) {
+                               struct nlattr *ntb[RTA_MAX + 1];
+                               nla_parse(ntb, RTA_MAX, (struct nlattr *)
+                                         RTNH_DATA(rtnh),
+                                         rtnh->rtnh_len - sizeof(*rtnh),
+                                         route_policy);
+
+                               if (ntb[RTA_GATEWAY]) {
+                                       struct nl_addr *addr;
+
+                                       addr = nla_get_addr(ntb[RTA_GATEWAY],
+                                                       route->rt_family);
+                                       rtnl_route_nh_set_gateway(nh, addr);
+                                       nl_addr_put(addr);
+                               }
+
+                               if (ntb[RTA_FLOW]) {
+                                       uint32_t realms;
+                                       
+                                       realms = nla_get_u32(ntb[RTA_FLOW]);
+                                       rtnl_route_nh_set_realms(nh, realms);
+                               }
+                       }
+
+                       rtnl_route_add_nexthop(route, nh);
+                       tlen -= RTNH_ALIGN(rtnh->rtnh_len);
+                       rtnh = RTNH_NEXT(rtnh);
+               }
+       }
+
+       if (tb[RTA_CACHEINFO]) {
+               nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO],
+                          sizeof(route->rt_cacheinfo));
+               route->ce_mask |= ROUTE_ATTR_CACHEINFO;
+       }
+
+       if (tb[RTA_OIF]) {
+               if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+                       goto errout;
+
+               rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF]));
+       }
+
+       if (tb[RTA_GATEWAY]) {
+               if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+                       goto errout;
+
+               addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family);
+               if (addr == NULL)
+                       goto errout;
+
+               rtnl_route_nh_set_gateway(old_nh, addr);
+               nl_addr_put(addr);
+       }
+
+       if (tb[RTA_FLOW]) {
+               if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+                       goto errout;
+
+               rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW]));
+       }
+
+       if (old_nh) {
+               if (route->rt_nr_nh == 0) {
+                       /* If no nexthops have been provided via RTA_MULTIPATH
+                        * we add it as regular nexthop to maintain backwards
+                        * compatibility */
+                       rtnl_route_add_nexthop(route, old_nh);
+               } else {
+                       /* Kernel supports new style nexthop configuration,
+                        * verify that it is a duplicate and discard nexthop. */
+                       struct rtnl_nexthop *first;
+
+                       first = nl_list_first_entry(&route->rt_nexthops,
+                                                   struct rtnl_nexthop,
+                                                   rtnh_list);
+                       if (!first)
+                               BUG();
+
+                       if (rtnl_route_nh_compare(old_nh, first,
+                                                 old_nh->ce_mask, 0)) {
+                               nl_error(EINVAL, "Mismatch of multipath "
+                                       "configuration.");
+                               goto errout;
+                       }
+
+                       rtnl_route_nh_free(old_nh);
+               }
+       }
+
+       return route;
+
+errout:
+       rtnl_route_put(route);
+       return NULL;
 }
 
-/** @} */
+int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
+{
+       int i;
+       struct nlattr *metrics;
+       struct rtmsg rtmsg = {
+               .rtm_family = route->rt_family,
+               .rtm_tos = route->rt_tos,
+               .rtm_table = route->rt_table,
+               .rtm_protocol = route->rt_protocol,
+               .rtm_scope = route->rt_scope,
+               .rtm_type = route->rt_type,
+               .rtm_flags = route->rt_flags,
+       };
+
+       if (route->rt_dst == NULL)
+               return nl_error(EINVAL, "Cannot build route message, please "
+                               "specify route destination.");
+
+       rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst);
+       if (route->rt_src)
+               rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src);
 
+
+       if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE)
+               rtmsg.rtm_scope = rtnl_route_guess_scope(route);
+
+       if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
+               goto nla_put_failure;
+
+       /* Additional table attribute replacing the 8bit in the header, was
+        * required to allow more than 256 tables. */
+       NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
+
+       NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst);
+       NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
+
+       if (route->ce_mask & ROUTE_ATTR_SRC)
+               NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src);
+
+       if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
+               NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src);
+
+       if (route->ce_mask & ROUTE_ATTR_IIF)
+               NLA_PUT_U32(msg, RTA_IIF, route->rt_iif);
+
+       if (route->rt_nmetrics > 0) {
+               uint32_t val;
+
+               metrics = nla_nest_start(msg, RTA_METRICS);
+               if (metrics == NULL)
+                       goto nla_put_failure;
+
+               for (i = 1; i <= RTAX_MAX; i++) {
+                       if (!rtnl_route_get_metric(route, i, &val))
+                               NLA_PUT_U32(msg, i, val);
+               }
+
+               nla_nest_end(msg, metrics);
+       }
+
+       if (rtnl_route_get_nnexthops(route) > 0) {
+               struct nlattr *multipath;
+               struct rtnl_nexthop *nh;
+
+               if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH)))
+                       goto nla_put_failure;
+
+               nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
+                       struct rtnexthop *rtnh;
+
+                       rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO);
+                       if (!rtnh)
+                               goto nla_put_failure;
+
+                       rtnh->rtnh_flags = nh->rtnh_flags;
+                       rtnh->rtnh_hops = nh->rtnh_weight;
+                       rtnh->rtnh_ifindex = nh->rtnh_ifindex;
+
+                       if (nh->rtnh_gateway)
+                               NLA_PUT_ADDR(msg, RTA_GATEWAY,
+                                            nh->rtnh_gateway);
+
+                       if (nh->rtnh_realms)
+                               NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
+
+                       rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) -
+                                               (void *) rtnh;
+               }
+
+               nla_nest_end(msg, multipath);
+       }
+
+       return 0;
+
+nla_put_failure:
+       return -ENOBUFS;
+}
+
+/** @cond SKIP */
 struct nl_object_ops route_obj_ops = {
        .oo_name                = "route/route",
        .oo_size                = sizeof(struct rtnl_route),
        .oo_constructor         = route_constructor,
        .oo_free_data           = route_free_data,
        .oo_clone               = route_clone,
-       .oo_dump[NL_DUMP_BRIEF] = route_dump_brief,
-       .oo_dump[NL_DUMP_FULL]  = route_dump_full,
-       .oo_dump[NL_DUMP_STATS] = route_dump_stats,
-       .oo_dump[NL_DUMP_XML]   = route_dump_xml,
-       .oo_dump[NL_DUMP_ENV]   = route_dump_env,
+       .oo_dump[NL_DUMP_ONELINE]       = route_dump_oneline,
+       .oo_dump[NL_DUMP_DETAILS]       = route_dump_details,
+       .oo_dump[NL_DUMP_STATS]         = route_dump_stats,
+       .oo_dump[NL_DUMP_ENV]           = route_dump_env,
        .oo_compare             = route_compare,
        .oo_attrs2str           = route_attrs2str,
        .oo_id_attrs            = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
                                   ROUTE_ATTR_TABLE | ROUTE_ATTR_DST),
 };
+/** @endcond */
 
 /** @} */
index 1386cda9681750f85bd7e4feced9689796ded48e..41ae65c48bd9395b81e2b0976191cc587ed4d41d 100644 (file)
@@ -167,27 +167,4 @@ int rtnl_route_str2metric(const char *name)
 
 /** @} */
 
-/**
- * @name Nexthop Flags Translations
- * @{
- */
-
-static struct trans_tbl nh_flags[] = {
-       __ADD(RTNH_F_DEAD, dead)
-       __ADD(RTNH_F_PERVASIVE, pervasive)
-       __ADD(RTNH_F_ONLINK, onlink)
-};
-
-char * rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
-{
-       return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags));
-}
-
-int rtnl_route_nh_str2flags(const char *name)
-{
-       return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags));
-}
-
-/** @} */
-
 /** @} */
index 60defd74701229958da2deee0338b138f6025c4a..38944d390f748b11af3b83d93d7ce6804b53e02e 100644 (file)
@@ -840,13 +840,13 @@ int rtnl_rule_get_action(struct rtnl_rule *rule)
                return nl_errno(ENOENT);
 }
 
-void rtnl_rule_set_realms(struct rtnl_rule *rule, realm_t realms)
+void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms)
 {
        rule->r_realms = realms;
        rule->ce_mask |= RULE_ATTR_REALMS;
 }
 
-realm_t rtnl_rule_get_realms(struct rtnl_rule *rule)
+uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule)
 {
        if (rule->ce_mask & RULE_ATTR_REALMS)
                return rule->r_realms;
index 11fe5ab1bfd917f41023c39a0f3657969aad7235..8d71209521ff58e022d0bef8eb5fea864f88383f 100644 (file)
@@ -23,8 +23,8 @@ nl-qdisc-add
 nl-qdisc-delete
 nl-qdisc-dump
 nl-route-add
-nl-route-del
-nl-route-dump
+nl-route-delete
+nl-route-list
 nl-route-get
 nl-rule-dump
 nl-tctree-dump
index ddbe29a8280decb16918468aa9bd0927d0c60625..0f1e14e3a551fc714ca56fd62b2d899dfd10c4a5 100644 (file)
@@ -6,36 +6,37 @@
 #      License as published by the Free Software Foundation version 2.1
 #      of the License.
 #
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+# Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
 #
 
 ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),)
     include ../Makefile.opts
 endif
 
-LDFLAGS        += -L../lib -lnl utils.o
+LDFLAGS        += -L../lib -lnl
 CIN    := $(wildcard nl-*.c) $(wildcard genl-*.c) $(wildcard nf-*.c)
 TOOLS  := $(CIN:%.c=%)
 
 all: $(TOOLS)
 
 $(TOOLS): utils.o
+nl-route-add nl-route-delete nl-route-list: route-utils.o
 
 nl-%: nl-%.c
        @echo "  LD $@"; \
-       $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+       $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
 
 genl-%: genl-%.c
        @echo "  LD $@"; \
-       $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+       $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
 
 nf-%: nf-%.c
        @echo "  LD $@"; \
-       $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+       $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
 
 clean:
        @echo "  CLEAN src"; \
-       rm -f $(TOOLS) utils.o
+       rm -f $(TOOLS) *.o
 
 distclean: clean
 
diff --git a/src/f_route.c b/src/f_route.c
deleted file mode 100644 (file)
index 581ff65..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * src/f_route.c       Routes Filter
- *
- *     This library is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU Lesser General Public
- *     License as published by the Free Software Foundation version 2.1
- *     of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-static void get_filter(struct rtnl_route *r, int ac, char **av, int idx,
-                      struct nl_cache *cache, struct nl_cache *link_cache)
-{
-       while (ac > idx) {
-               if (!strcasecmp(av[idx], "src")) {
-                       if (ac > ++idx) {
-                               struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-                               if (!a)
-                                       goto err;
-                               rtnl_route_set_pref_src(r, a);
-                               nl_addr_put(a);
-                       }
-               } else if (!strcasecmp(av[idx], "dst")) {
-                       if (ac > ++idx) {
-                               struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-                               if (!a)
-                                       goto err;
-                               rtnl_route_set_dst(r, a);
-                               nl_addr_put(a);
-                       }
-               } else if (!strcasecmp(av[idx], "via")) {
-                       if (ac > ++idx) {
-                               struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-                               if (!a)
-                                       goto err;
-                               rtnl_route_set_gateway(r, a);
-                               nl_addr_put(a);
-                       }
-               } else if (!strcasecmp(av[idx], "from")) {
-                       if (ac > ++idx) {
-                               struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-                               if (!a)
-                                       goto err;
-                               rtnl_route_set_src(r, a);
-                               nl_addr_put(a);
-                       }
-               } else if (!strcasecmp(av[idx], "tos")) {
-                       if (ac > ++idx)
-                               rtnl_route_set_tos(r, strtoul(av[idx++], NULL, 0));
-               } else if (!strcasecmp(av[idx], "prio")) {
-                       if (ac > ++idx)
-                               rtnl_route_set_prio(r, strtoul(av[idx++], NULL, 0));
-               } else if (!strcasecmp(av[idx], "scope")) {
-                       if (ac > ++idx)
-                               rtnl_route_set_scope(r, rtnl_str2scope(av[idx++]));
-               } else if (!strcasecmp(av[idx], "dev")) {
-                       if (ac > ++idx) {
-                               int ifindex = rtnl_link_name2i(link_cache, av[idx++]);
-                               if (ifindex == RTNL_LINK_NOT_FOUND)
-                                       goto err_notfound;
-                               rtnl_route_set_oif(r, ifindex);
-                       }
-               } else if (!strcasecmp(av[idx], "table")) {
-                       if (ac > ++idx)
-                               rtnl_route_set_table(r, strtoul(av[idx++], NULL, 0));
-               } else {
-                       fprintf(stderr, "What is '%s'?\n", av[idx]);
-                       exit(1);
-               }
-       }
-
-       return;
-
-err_notfound:
-       fprintf(stderr, "Unable to find device \"%s\"\n", av[idx-1]);
-       exit(1);
-err:
-       fprintf(stderr, "%s\n", nl_geterror());
-       exit(1);
-}
index 2686397f12469e0bda1424c8653f932f7291b463..eb63a220dfad13d4e54712cbeb50779781c82b98 100644 (file)
 /*
- * src/nl-route-dump.c     Dump route attributes
+ * src/nl-route-add.c     Route addition utility
  *
  *     This library is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU Lesser General Public
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include "route-utils.h"
+
+static struct nl_cache *link_cache, *route_cache;
 
 static void print_usage(void)
 {
        printf(
-       "Usage: nl-route-add [<filter>]\n");
+       "Usage: nl-route-add [OPTION]... --dst=ADDR --nh=NEXTHOP [--nh=...]\n"
+        "       nl-route-add [OPTION]... ADDR NEXTHOP\n"
+       "\n"
+       "Required Options\n"
+       " -d, --dst=ADDR        destination prefix, e.g. 10.10.0.0/16\n"
+       " -n, --nh=NEXTHOP      nexthop configuration:\n"
+       "                         dev=DEV         route via device\n"
+       "                         weight=WEIGHT   weight of nexthop\n"
+       "                         flags=FLAGS\n"
+       "                         via=GATEWAY     route via other node\n"
+       "                         realms=REALMS\n"
+       "\n"
+       "                         e.g. dev=eth0,via=192.168.1.12\n"
+       "\n"
+       "Options\n"
+       " -s, --src=ADDR        source prefix\n"
+       " -i, --iif=DEV         incomming interface\n"
+       " -P, --pref-src=ADDR   preferred source address\n"
+       " -t, --table=TABLE     routing table\n"
+       " -m, --metric=OPTS     metrics\n"
+       " -p, --prio=NUM        priotity\n"
+       " -S, --scope=SCOPE     scope\n"
+       " -x, --proto=PROTO     protocol\n"
+       " -T, --type=TYPE       routing type\n"
+       " -h, --help            show this help\n");
        exit(1);
 }
 
-#include "f_route.c"
-
 int main(int argc, char *argv[])
 {
        struct nl_handle *nlh;
-       struct nl_cache *link_cache, *route_cache;
        struct rtnl_route *route;
        int err = 1;
 
-       if (nltool_init(argc, argv) < 0)
-               return -1;
-
-       if (argc < 2 || !strcmp(argv[1], "-h"))
-               print_usage();
-
        nlh = nltool_alloc_handle();
-       if (!nlh)
-               goto errout;
+       nltool_connect(nlh, NETLINK_ROUTE);
+       link_cache = nltool_alloc_link_cache(nlh);
+       route_cache = nltool_alloc_route_cache(nlh);
 
        route = rtnl_route_alloc();
        if (!route)
                goto errout;
 
-       if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-               goto errout_free;
+       for (;;) {
+               int c, optidx = 0;
+               static struct option long_opts[] = {
+                       { "dst", 1, 0, 'd' },
+                       { "src", 1, 0, 's' },
+                       { "iif", 1, 0, 'i' },
+                       { "nh", 1, 0, 'n' },
+                       { "pref-src", 1, 0, 'P' },
+                       { "table", 1, 0, 't' },
+                       { "metric", 1, 0, 'm' },
+                       { "prio", 1, 0, 'p' },
+                       { "scope", 1, 0, 'S' },
+                       { "proto", 1, 0, 'x' },
+                       { "type", 1, 0, 'T' },
+                       { "help", 0, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
 
-       link_cache = nltool_alloc_link_cache(nlh);
-       if (!link_cache)
-               goto errout_close;
+               c = getopt_long(argc, argv, "d:s:i:n:P:t:m:p:S:x:T:h", long_opts, &optidx);
+               if (c == -1)
+                       break;
 
-       route_cache = nltool_alloc_route_cache(nlh);
-       if (!route_cache)
-               goto errout_link_cache;
+               switch (c) {
+               case 'd': parse_dst(route, optarg); break;
+               case 's': parse_src(route, optarg); break;
+               case 'i': parse_iif(route, optarg, link_cache); break;
+               case 'n': parse_nexthop(route, optarg, link_cache); break;
+               case 'P': parse_pref_src(route, optarg); break;
+               case 't': parse_table(route, optarg); break;
+               case 'm': parse_metric(route, optarg); break;
+               case 'p': parse_prio(route, optarg); break;
+               case 'S': parse_scope(route, optarg); break;
+               case 'x': parse_protocol(route, optarg); break;
+               case 'T': parse_type(route, optarg); break;
+               case 'h': print_usage(); break;
+               }
+       }
 
-       get_filter(route, argc, argv, 1, route_cache, link_cache);
+       while (optind < argc) {
+               if (!rtnl_route_get_dst(route)) {
+                       parse_dst(route, argv[optind++]);
+                       continue;
+               }
+
+               /* This must all be nexthop configuration */
+       }
 
        if (rtnl_route_add(nlh, route, 0) < 0) {
-               fprintf(stderr, "rtnl_route_add failed: %s\n",
-               nl_geterror());
-               goto errout_route_cache;
+               fprintf(stderr, "rtnl_route_add failed: %s\n", nl_geterror());
+               goto errout_free;
        }
 
        err = 0;
-
-errout_route_cache:
-       nl_cache_free(route_cache);
-errout_link_cache:
-       nl_cache_free(link_cache);
-errout_close:
-       nl_close(nlh);
 errout_free:
        rtnl_route_put(route);
 errout:
+       nl_cache_free(route_cache);
+       nl_cache_free(link_cache);
+       nl_close(nlh);
        nl_handle_destroy(nlh);
+
        return err;
 }
diff --git a/src/nl-route-del.c b/src/nl-route-del.c
deleted file mode 100644 (file)
index 9d912e1..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * src/nl-route-del.c     Delete Routes
- *
- *     This library is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU Lesser General Public
- *     License as published by the Free Software Foundation version 2.1
- *     of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-       printf(
-       "Usage: nl-route-del [<filter>]\n");
-       exit(1);
-}
-
-#include "f_route.c"
-
-int main(int argc, char *argv[])
-{
-       struct nl_handle *nlh;
-       struct nl_cache *link_cache, *route_cache;
-       struct rtnl_route *route;
-       int err = 1;
-
-       if (nltool_init(argc, argv) < 0)
-               return -1;
-
-       if (argc < 2 || !strcmp(argv[1], "-h"))
-               print_usage();
-
-       nlh = nltool_alloc_handle();
-       if (!nlh)
-               goto errout;
-
-       route = rtnl_route_alloc();
-       if (!route)
-               goto errout;
-
-       if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-               goto errout_free;
-
-       link_cache = nltool_alloc_link_cache(nlh);
-       if (!link_cache)
-               goto errout_close;
-
-       route_cache = nltool_alloc_route_cache(nlh);
-       if (!route_cache)
-               goto errout_link_cache;
-
-       get_filter(route, argc, argv, 1, route_cache, link_cache);
-
-       if (rtnl_route_del(nlh, route, 0) < 0) {
-               fprintf(stderr, "rtnl_route_del failed: %s\n",
-               nl_geterror());
-               goto errout_route_cache;
-       }
-
-       err = 0;
-
-errout_route_cache:
-       nl_cache_free(route_cache);
-errout_link_cache:
-       nl_cache_free(link_cache);
-errout_close:
-       nl_close(nlh);
-errout_free:
-       rtnl_route_put(route);
-errout:
-       nl_handle_destroy(nlh);
-       return err;
-}
diff --git a/src/nl-route-delete.c b/src/nl-route-delete.c
new file mode 100644 (file)
index 0000000..1c97817
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * src/nl-route-delete.c     Delete Routes
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "route-utils.h"
+
+static void print_usage(void)
+{
+       printf(
+       "Usage: nl-route-delete [OPTION]...\n"
+       "\n"
+       "Options\n"
+       " -f, --family=FAMILY   address family\n"
+       " -d, --dst=ADDR        destination prefix, e.g. 10.10.0.0/16\n"
+       " -n, --nh=NEXTHOP      nexthop configuration:\n"
+       "                         dev=DEV         route via device\n"
+       "                         weight=WEIGHT   weight of nexthop\n"
+       "                         flags=FLAGS\n"
+       "                         via=GATEWAY     route via other node\n"
+       "                         realms=REALMS\n"
+       "                         e.g. dev=eth0,via=192.168.1.12\n"
+       " -s, --src=ADDR        source prefix\n"
+       " -i, --iif=DEV         incomming interface\n"
+       " -P, --pref-src=ADDR   preferred source address\n"
+       " -t, --table=TABLE     routing table\n"
+       " -m, --metric=OPTS     metrics\n"
+       " -p, --prio=NUM        priotity\n"
+       " -S, --scope=SCOPE     scope\n"
+       " -x, --proto=PROTO     protocol\n"
+       " -T, --type=TYPE       routing type\n"
+       " -D, --dumptype=TYPE   { brief | detailed | stats | env }\n"
+       " -h, --help            show this help\n");
+       exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+       struct nl_handle *nlh;
+       struct nl_cache *link_cache, *route_cache;
+       struct rtnl_route *route;
+       int err = 1;
+
+       nlh = nltool_alloc_handle();
+       nltool_connect(nlh, NETLINK_ROUTE);
+       link_cache = nltool_alloc_link_cache(nlh);
+       route_cache = nltool_alloc_route_cache(nlh);
+
+       route = rtnl_route_alloc();
+       if (!route)
+               goto errout;
+
+       for (;;) {
+               int c, optidx = 0;
+               static struct option long_opts[] = {
+                       { "family", 1, 0, 'f' },
+                       { "dst", 1, 0, 'd' },
+                       { "src", 1, 0, 's' },
+                       { "iif", 1, 0, 'i' },
+                       { "nh", 1, 0, 'n' },
+                       { "pref-src", 1, 0, 'P' },
+                       { "table", 1, 0, 't' },
+                       { "metric", 1, 0, 'm' },
+                       { "prio", 1, 0, 'p' },
+                       { "scope", 1, 0, 'S' },
+                       { "proto", 1, 0, 'x' },
+                       { "type", 1, 0, 'T' },
+                       { "help", 0, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               c = getopt_long(argc, argv, "f:d:s:i:n:P:t:m:p:S:x:T:h",
+                               long_opts, &optidx);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'f': parse_family(route, optarg); break;
+               case 'd': parse_dst(route, optarg); break;
+               case 's': parse_src(route, optarg); break;
+               case 'i': parse_iif(route, optarg, link_cache); break;
+               case 'n': parse_nexthop(route, optarg, link_cache); break;
+               case 'P': parse_pref_src(route, optarg); break;
+               case 't': parse_table(route, optarg); break;
+               case 'm': parse_metric(route, optarg); break;
+               case 'p': parse_prio(route, optarg); break;
+               case 'S': parse_scope(route, optarg); break;
+               case 'x': parse_protocol(route, optarg); break;
+               case 'T': parse_type(route, optarg); break;
+               case 'h': print_usage(); break;
+               }
+       }
+
+       if ((err = rtnl_route_delete(nlh, route, 0)) < 0)
+               fatal(err, "rtnl_route_del failed: %s\n", nl_geterror());
+
+       err = 0;
+
+       rtnl_route_put(route);
+errout:
+       nl_cache_free(route_cache);
+       nl_cache_free(link_cache);
+       nl_close(nlh);
+       nl_handle_destroy(nlh);
+
+       return err;
+}
diff --git a/src/nl-route-dump.c b/src/nl-route-dump.c
deleted file mode 100644 (file)
index aed9bd2..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * src/nl-route-dump.c     Dump route attributes
- *
- *     This library is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU Lesser General Public
- *     License as published by the Free Software Foundation version 2.1
- *     of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-       printf(
-       "Usage: nl-route-dump <mode> [<filter>]\n"
-       "  mode := { brief | detailed | stats | xml }\n");
-       exit(1);
-}
-
-#include "f_route.c"
-
-int main(int argc, char *argv[])
-{
-       struct nl_handle *nlh;
-       struct nl_cache *link_cache, *route_cache;
-       struct rtnl_route *route;
-       struct nl_dump_params params = {
-               .dp_fd = stdout,
-               .dp_type = NL_DUMP_BRIEF
-       };
-       int err = 1;
-
-       if (nltool_init(argc, argv) < 0)
-               return -1;
-
-       if (argc < 2 || !strcmp(argv[1], "-h"))
-               print_usage();
-
-       nlh = nltool_alloc_handle();
-       if (!nlh)
-               goto errout;
-
-       route = rtnl_route_alloc();
-       if (!route)
-               goto errout;
-
-       if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-               goto errout_free;
-
-       link_cache = nltool_alloc_link_cache(nlh);
-       if (!link_cache)
-               goto errout_close;
-
-       route_cache = nltool_alloc_route_cache(nlh);
-       if (!route_cache)
-               goto errout_link_cache;
-
-       params.dp_type = nltool_parse_dumptype(argv[1]);
-       if (params.dp_type < 0)
-               goto errout_route_cache;
-
-       get_filter(route, argc, argv, 2, route_cache, link_cache);
-
-       nl_cache_dump_filter(route_cache, &params, (struct nl_object *) route);
-
-       err = 0;
-
-errout_route_cache:
-       nl_cache_free(route_cache);
-errout_link_cache:
-       nl_cache_free(link_cache);
-errout_close:
-       nl_close(nlh);
-errout_free:
-       rtnl_route_put(route);
-errout:
-       nl_handle_destroy(nlh);
-       return err;
-}
diff --git a/src/nl-route-list.c b/src/nl-route-list.c
new file mode 100644 (file)
index 0000000..4f70ff7
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * src/nl-route-list.c     List route attributes
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "route-utils.h"
+
+static void print_usage(void)
+{
+       printf(
+       "Usage: nl-route-list [OPTION]...\n"
+       "\n"
+       "Options\n"
+       " -f, --family=FAMILY   address family\n"
+       " -d, --dst=ADDR        destination prefix, e.g. 10.10.0.0/16\n"
+       " -n, --nh=NEXTHOP      nexthop configuration:\n"
+       "                         dev=DEV         route via device\n"
+       "                         weight=WEIGHT   weight of nexthop\n"
+       "                         flags=FLAGS\n"
+       "                         via=GATEWAY     route via other node\n"
+       "                         realms=REALMS\n"
+       "                         e.g. dev=eth0,via=192.168.1.12\n"
+       " -s, --src=ADDR        source prefix\n"
+       " -i, --iif=DEV         incomming interface\n"
+       " -P, --pref-src=ADDR   preferred source address\n"
+       " -t, --table=TABLE     routing table\n"
+       " -m, --metric=OPTS     metrics\n"
+       " -p, --prio=NUM        priotity\n"
+       " -S, --scope=SCOPE     scope\n"
+       " -x, --proto=PROTO     protocol\n"
+       " -T, --type=TYPE       routing type\n"
+       " -D, --dumptype=TYPE   { brief | detailed | stats | env }\n"
+       " -h, --help            show this help\n");
+       exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+       struct nl_handle *nlh;
+       struct nl_cache *link_cache, *route_cache;
+       struct rtnl_route *route;
+       struct nl_dump_params params = {
+               .dp_fd = stdout,
+               .dp_type = NL_DUMP_BRIEF
+       };
+       int err = 1;
+
+       nlh = nltool_alloc_handle();
+       nltool_connect(nlh, NETLINK_ROUTE);
+       link_cache = nltool_alloc_link_cache(nlh);
+       route_cache = nltool_alloc_route_cache(nlh);
+
+       route = rtnl_route_alloc();
+       if (!route)
+               goto errout;
+
+       for (;;) {
+               int c, optidx = 0;
+               static struct option long_opts[] = {
+                       { "family", 1, 0, 'f' },
+                       { "dst", 1, 0, 'd' },
+                       { "src", 1, 0, 's' },
+                       { "iif", 1, 0, 'i' },
+                       { "nh", 1, 0, 'n' },
+                       { "pref-src", 1, 0, 'P' },
+                       { "table", 1, 0, 't' },
+                       { "metric", 1, 0, 'm' },
+                       { "prio", 1, 0, 'p' },
+                       { "scope", 1, 0, 'S' },
+                       { "proto", 1, 0, 'x' },
+                       { "type", 1, 0, 'T' },
+                       { "dumptype", 1, 0, 'D' },
+                       { "help", 0, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               c = getopt_long(argc, argv, "f:d:s:i:n:P:t:m:p:S:x:T:D:h",
+                               long_opts, &optidx);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'f': parse_family(route, optarg); break;
+               case 'd': parse_dst(route, optarg); break;
+               case 's': parse_src(route, optarg); break;
+               case 'i': parse_iif(route, optarg, link_cache); break;
+               case 'n': parse_nexthop(route, optarg, link_cache); break;
+               case 'P': parse_pref_src(route, optarg); break;
+               case 't': parse_table(route, optarg); break;
+               case 'm': parse_metric(route, optarg); break;
+               case 'p': parse_prio(route, optarg); break;
+               case 'S': parse_scope(route, optarg); break;
+               case 'x': parse_protocol(route, optarg); break;
+               case 'T': parse_type(route, optarg); break;
+               case 'D': params.dp_type = nltool_parse_dumptype(optarg); break;
+               case 'h': print_usage(); break;
+               }
+       }
+
+       nl_cache_dump_filter(route_cache, &params, OBJ_CAST(route));
+
+       err = 0;
+
+       rtnl_route_put(route);
+errout:
+       nl_cache_free(route_cache);
+       nl_cache_free(link_cache);
+       nl_close(nlh);
+       nl_handle_destroy(nlh);
+
+       return err;
+}
diff --git a/src/route-utils.c b/src/route-utils.c
new file mode 100644 (file)
index 0000000..61bc9cd
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * src/route-utils.c     Route Helpers
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "route-utils.h"
+
+void parse_family(struct rtnl_route *route, char *arg)
+{
+       int family;
+
+       if ((family = nl_str2af(arg)) != AF_UNSPEC)
+               rtnl_route_set_family(route, family);
+}
+
+void parse_dst(struct rtnl_route *route, char *arg)
+{
+       struct nl_addr *addr;
+       int err;
+
+       addr = nl_addr_parse(arg, rtnl_route_get_family(route));
+       if (addr == NULL)
+               fatal(nl_get_errno(), nl_geterror());
+
+       if ((err = rtnl_route_set_dst(route, addr)) < 0)
+               fatal(err, nl_geterror());
+
+       nl_addr_put(addr);
+}
+
+void parse_src(struct rtnl_route *route, char *arg)
+{
+       struct nl_addr *addr;
+       int err;
+
+       addr = nl_addr_parse(arg, rtnl_route_get_family(route));
+       if (addr == NULL)
+               fatal(nl_get_errno(), nl_geterror());
+
+       if ((err = rtnl_route_set_src(route, addr)) < 0)
+               fatal(err, nl_geterror());
+
+       nl_addr_put(addr);
+}
+
+void parse_pref_src(struct rtnl_route *route, char *arg)
+{
+       struct nl_addr *addr;
+       int err;
+
+       addr = nl_addr_parse(arg, rtnl_route_get_family(route));
+       if (addr == NULL)
+               fatal(nl_get_errno(), nl_geterror());
+
+       if ((err = rtnl_route_set_pref_src(route, addr)) < 0)
+               fatal(err, nl_geterror());
+
+       nl_addr_put(addr);
+}
+
+void parse_metric(struct rtnl_route *route, char *subopts)
+{
+       /* strict equal order to RTAX_* */
+       static char *const tokens[] = {
+               "unspec",
+               "lock",
+               "mtu",
+               "window",
+               "rtt",
+               "rttvar",
+               "sstresh",
+               "cwnd",
+               "advmss",
+               "reordering",
+               "hoplimit",
+               "initcwnd",
+               "features",
+               NULL,
+       };
+       unsigned long lval;
+       char *arg, *endptr;
+
+       while (*subopts != '\0') {
+               int ret = getsubopt(&subopts, tokens, &arg);
+               if (ret == -1)
+                       fatal(EINVAL, "Unknown metric token \"%s\"", arg);
+
+               if (ret == 0)
+                       fatal(EINVAL, "Invalid metric \"%s\"", tokens[ret]);
+
+               if (arg == NULL)
+                       fatal(EINVAL, "Metric \"%s\", no value given", tokens[ret]);
+
+               lval = strtoul(arg, &endptr, 0);
+               if (endptr == arg)
+                       fatal(EINVAL, "Metric \"%s\", value not numeric", tokens[ret]);
+
+               if ((ret = rtnl_route_set_metric(route, ret, lval)) < 0)
+                       fatal(ret, nl_geterror());
+       }
+}
+
+void parse_nexthop(struct rtnl_route *route, char *subopts,
+                  struct nl_cache *link_cache)
+{
+       enum {
+               NH_DEV,
+               NH_VIA,
+               NH_WEIGHT,
+       };
+       static char *const tokens[] = {
+               "dev",
+               "via",
+               "weight",
+               NULL,
+       };
+       struct rtnl_nexthop *nh;
+       unsigned long lval;
+       struct nl_addr *addr;
+       int ival;
+       char *arg, *endptr;
+
+       if (!(nh = rtnl_route_nh_alloc()))
+               fatal(ENOMEM, "Out of memory");
+
+       while (*subopts != '\0') {
+               int ret = getsubopt(&subopts, tokens, &arg);
+               if (ret == -1)
+                       fatal(EINVAL, "Unknown nexthop token \"%s\"", arg);
+
+               switch (ret) {
+               case NH_DEV:
+                       ival = rtnl_link_name2i(link_cache, arg);
+                       if (ival == RTNL_LINK_NOT_FOUND)
+                               fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+                       rtnl_route_nh_set_ifindex(nh, ival);
+                       break;
+
+               case NH_VIA:
+                       addr = nl_addr_parse(arg, rtnl_route_get_family(route));
+                       if (addr == NULL)
+                               fatal(nl_get_errno(), nl_geterror());
+
+                       rtnl_route_nh_set_gateway(nh, addr);
+                       nl_addr_put(addr);
+                       break;
+
+               case NH_WEIGHT:
+                       lval = strtoul(arg, &endptr, 0);
+                       if (endptr == arg)
+                               fatal(EINVAL, "Invalid weight \"%s\", not numeric", arg);
+                       rtnl_route_nh_set_weight(nh, lval);
+                       break;
+               }
+       }
+
+       rtnl_route_add_nexthop(route, nh);
+}
+
+void parse_table(struct rtnl_route *route, char *arg)
+{
+       unsigned long lval;
+       char *endptr;
+
+       lval = strtoul(arg, &endptr, 0);
+       if (endptr == arg) {
+               if ((lval = rtnl_route_str2table(arg)) < 0)
+                       fatal(EINVAL, "Unknown table name \"%s\"", arg);
+       }
+
+       rtnl_route_set_table(route, lval);
+}
+
+void parse_prio(struct rtnl_route *route, char *arg)
+{
+       unsigned long lval;
+       char *endptr;
+
+       lval = strtoul(arg, &endptr, 0);
+       if (endptr == arg)
+               fatal(EINVAL, "Invalid priority value, not numeric");
+       rtnl_route_set_priority(route, lval);
+}
+
+void parse_scope(struct rtnl_route *route, char *arg)
+{
+       int ival;
+
+       if ((ival = rtnl_str2scope(arg)) < 0)
+               fatal(EINVAL, "Unknown routing scope \"%s\"", arg);
+
+       rtnl_route_set_scope(route, ival);
+}
+
+void parse_protocol(struct rtnl_route *route, char *arg)
+{
+       unsigned long lval;
+       char *endptr;
+
+       lval = strtoul(arg, &endptr, 0);
+       if (endptr == arg) {
+               if ((lval = rtnl_route_str2proto(arg)) < 0)
+                       fatal(EINVAL, "Unknown routing protocol name \"%s\"",
+                               arg);
+       }
+
+       rtnl_route_set_protocol(route, lval);
+}
+
+void parse_type(struct rtnl_route *route, char *arg)
+{
+       int ival;
+
+       if ((ival = nl_str2rtntype(arg)) < 0)
+               fatal(EINVAL, "Unknown routing type \"%s\"", arg);
+
+       if ((ival = rtnl_route_set_type(route, ival)) < 0)
+               fatal(ival, nl_geterror());
+}
+
+void parse_iif(struct rtnl_route *route, char *arg, struct nl_cache *link_cache)
+{
+       int ival;
+
+       ival = rtnl_link_name2i(link_cache, arg);
+       if (ival == RTNL_LINK_NOT_FOUND)
+               fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+       rtnl_route_set_iif(route, ival);
+}
diff --git a/src/route-utils.h b/src/route-utils.h
new file mode 100644 (file)
index 0000000..873835c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * src/route-utils.h     Route Helpers
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __ROUTE_UTILS_H_
+#define __ROUTE_UTILS_H_
+
+#include "utils.h"
+
+extern void    parse_family(struct rtnl_route *, char *);
+extern void    parse_dst(struct rtnl_route *, char *);
+extern void    parse_src(struct rtnl_route *, char *);
+extern void    parse_pref_src(struct rtnl_route *, char *);
+extern void    parse_metric(struct rtnl_route *, char *);
+extern void    parse_nexthop(struct rtnl_route *, char *, struct nl_cache *);
+extern void    parse_table(struct rtnl_route *, char *);
+extern void    parse_prio(struct rtnl_route *, char *);
+extern void    parse_scope(struct rtnl_route *, char *);
+extern void    parse_protocol(struct rtnl_route *, char *);
+extern void    parse_type(struct rtnl_route *, char *);
+extern void    parse_iif(struct rtnl_route *, char *, struct nl_cache *);
+
+#endif
index b43758a0d1b98ea370f34043b2b35c6cdb55cef5..469576065f71d956c58189ef45f7c299d998d3ca 100644 (file)
 
 #include <stdlib.h>
 
+void fatal(int err, const char *fmt, ...)
+{
+       va_list ap;
+
+       fprintf(stderr, "Error: ");
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       fprintf(stderr, "\n");
+
+       exit(err);
+}
+
 int nltool_init(int argc, char *argv[])
 {
        return 0;
@@ -24,7 +39,7 @@ int nltool_connect(struct nl_handle *nlh, int protocol)
 
        err = nl_connect(nlh, protocol);
        if (err < 0)
-               fprintf(stderr, "Unable to connect netlink socket%s\n",
+               fatal(err, "Unable to connect netlink socket: %s",
                        nl_geterror());
 
        return err;
@@ -32,7 +47,12 @@ int nltool_connect(struct nl_handle *nlh, int protocol)
 
 struct nl_handle *nltool_alloc_handle(void)
 {
-       return nl_handle_alloc();
+       struct nl_handle *sock;
+
+       if (!(sock = nl_handle_alloc()))
+               fatal(ENOBUFS, "Unable to allocate netlink socket");
+
+       return sock;
 }
 
 struct nl_addr *nltool_addr_parse(const char *str)
@@ -71,10 +91,10 @@ struct nl_cache *nltool_alloc_link_cache(struct nl_handle *nlh)
 
        cache = rtnl_link_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve link cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve link cache: %s",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
@@ -85,10 +105,10 @@ struct nl_cache *nltool_alloc_addr_cache(struct nl_handle *nlh)
 
        cache = rtnl_addr_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve address cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve address cache: %s\n",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
@@ -99,10 +119,10 @@ struct nl_cache *nltool_alloc_neigh_cache(struct nl_handle *nlh)
 
        cache = rtnl_neigh_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve neighbour cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve neighbour cache: %s\n",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
@@ -113,8 +133,8 @@ struct nl_cache *nltool_alloc_neightbl_cache(struct nl_handle *nlh)
 
        cache = rtnl_neightbl_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve neighbour table "
-                               "cache: %s\n", nl_geterror());
+               fatal(nl_get_errno(), "Unable to retrieve neighbour table "
+                               "cache: %s", nl_geterror());
        else
                nl_cache_mngt_provide(cache);
 
@@ -127,10 +147,10 @@ struct nl_cache *nltool_alloc_route_cache(struct nl_handle *nlh)
 
        cache = rtnl_route_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve route cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve route cache: %s\n",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
@@ -141,10 +161,10 @@ struct nl_cache *nltool_alloc_rule_cache(struct nl_handle *nlh)
 
        cache = rtnl_rule_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve rule cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve rule cache: %s\n",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
@@ -155,10 +175,10 @@ struct nl_cache *nltool_alloc_qdisc_cache(struct nl_handle *nlh)
 
        cache = rtnl_qdisc_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve qdisc cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve qdisc cache: %s\n",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
@@ -169,10 +189,10 @@ struct nl_cache *nltool_alloc_genl_family_cache(struct nl_handle *nlh)
 
        cache = genl_ctrl_alloc_cache(nlh);
        if (!cache)
-               fprintf(stderr, "Unable to retrieve genl family cache: %s\n",
+               fatal(nl_get_errno(), "Unable to retrieve genl family cache: %s\n",
                        nl_geterror());
-       else
-               nl_cache_mngt_provide(cache);
+
+       nl_cache_mngt_provide(cache);
 
        return cache;
 }
index d738ce7a6162f8a73f1f7f0199e8a991319e35f4..7d85e1580f9bc967e1286281efdf0e81158c62a6 100644 (file)
@@ -43,6 +43,8 @@
 #include <netlink/genl/mngt.h>
 #include <netlink/netfilter/ct.h>
 
+extern void fatal(int err, const char *fmt, ...);
+
 extern int nltool_init(int argc, char *argv[]);
 extern int nltool_connect(struct nl_handle *nlh, int protocol);
 extern struct nl_addr *nltool_addr_parse(const char *str);