ipv4: fix value of ->nlmsg_flags reported in RTM_NEWROUTE events
authorGuillaume Nault <g.nault@alphalink.fr>
Wed, 7 Sep 2016 15:20:46 +0000 (17:20 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Sep 2016 23:50:23 +0000 (16:50 -0700)
fib_table_insert() inconsistently fills the nlmsg_flags field in its
notification messages.

Since commit b8f558313506 ("[RTNETLINK]: Fix sending netlink message
when replace route."), the netlink message has its nlmsg_flags set to
NLM_F_REPLACE if the route replaced a preexisting one.

Then commit a2bb6d7d6f42 ("ipv4: include NLM_F_APPEND flag in append
route notifications") started setting nlmsg_flags to NLM_F_APPEND if
the route matched a preexisting one but was appended.

In other cases (exclusive creation or prepend), nlmsg_flags is 0.

This patch sets ->nlmsg_flags in all situations, preserving the
semantic of the NLM_F_* bits:

  * NLM_F_CREATE: a new fib entry has been created for this route.
  * NLM_F_EXCL: no other fib entry existed for this route.
  * NLM_F_REPLACE: this route has overwritten a preexisting fib entry.
  * NLM_F_APPEND: the new fib entry was added after other entries for
    the same route.

As a result, the possible flag combination can now be reported
(iproute2's terminology into parentheses):

  * NLM_F_CREATE | NLM_F_EXCL: route didn't exist, exclusive creation
    ("add").
  * NLM_F_CREATE | NLM_F_APPEND: route did already exist, new route
    added after preexisting ones ("append").
  * NLM_F_CREATE: route did already exist, new route added before
    preexisting ones ("prepend").
  * NLM_F_REPLACE: route did already exist, new route replaced the
    first preexisting one ("change").

Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/fib_trie.c

index e2ffc2a5c7db6ff3eea20d2083c2013fdf0dcc23..241f27bbd7adcc782d63a2961d3b258ef649325c 100644 (file)
@@ -1081,7 +1081,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
        struct trie *t = (struct trie *)tb->tb_data;
        struct fib_alias *fa, *new_fa;
        struct key_vector *l, *tp;
-       unsigned int nlflags = 0;
+       u16 nlflags = NLM_F_EXCL;
        struct fib_info *fi;
        u8 plen = cfg->fc_dst_len;
        u8 slen = KEYLENGTH - plen;
@@ -1126,6 +1126,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                if (cfg->fc_nlflags & NLM_F_EXCL)
                        goto out;
 
+               nlflags &= ~NLM_F_EXCL;
+
                /* We have 2 goals:
                 * 1. Find exact match for type, scope, fib_info to avoid
                 * duplicate routes
@@ -1151,6 +1153,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        struct fib_info *fi_drop;
                        u8 state;
 
+                       nlflags |= NLM_F_REPLACE;
                        fa = fa_first;
                        if (fa_match) {
                                if (fa == fa_match)
@@ -1191,7 +1194,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        if (state & FA_S_ACCESSED)
                                rt_cache_flush(cfg->fc_nlinfo.nl_net);
                        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
-                               tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);
+                               tb->tb_id, &cfg->fc_nlinfo, nlflags);
 
                        goto succeeded;
                }
@@ -1203,7 +1206,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        goto out;
 
                if (cfg->fc_nlflags & NLM_F_APPEND)
-                       nlflags = NLM_F_APPEND;
+                       nlflags |= NLM_F_APPEND;
                else
                        fa = fa_first;
        }
@@ -1211,6 +1214,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
        if (!(cfg->fc_nlflags & NLM_F_CREATE))
                goto out;
 
+       nlflags |= NLM_F_CREATE;
        err = -ENOBUFS;
        new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
        if (!new_fa)