lib/test_overflow.c: avoid tainting the kernel and fix wrap size
[platform/kernel/linux-starfive.git] / net / ipv4 / fib_trie.c
index 868c747..2b2b3d2 100644 (file)
@@ -338,12 +338,18 @@ static struct tnode *tnode_alloc(int bits)
 
 static inline void empty_child_inc(struct key_vector *n)
 {
-       ++tn_info(n)->empty_children ? : ++tn_info(n)->full_children;
+       tn_info(n)->empty_children++;
+
+       if (!tn_info(n)->empty_children)
+               tn_info(n)->full_children++;
 }
 
 static inline void empty_child_dec(struct key_vector *n)
 {
-       tn_info(n)->empty_children-- ? : tn_info(n)->full_children--;
+       if (!tn_info(n)->empty_children)
+               tn_info(n)->full_children--;
+
+       tn_info(n)->empty_children--;
 }
 
 static struct key_vector *leaf_new(t_key key, struct fib_alias *fa)
@@ -1449,6 +1455,7 @@ found:
                fib_alias_accessed(fa);
                err = fib_props[fa->fa_type].error;
                if (unlikely(err < 0)) {
+out_reject:
 #ifdef CONFIG_IP_FIB_TRIE_STATS
                        this_cpu_inc(stats->semantic_match_passed);
 #endif
@@ -1457,7 +1464,13 @@ found:
                }
                if (fi->fib_flags & RTNH_F_DEAD)
                        continue;
-               for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
+
+               if (unlikely(fi->nh && nexthop_is_blackhole(fi->nh))) {
+                       err = fib_props[RTN_BLACKHOLE].error;
+                       goto out_reject;
+               }
+
+               for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) {
                        struct fib_nh_common *nhc = fib_info_nhc(fi, nhsel);
 
                        if (nhc->nhc_flags & RTNH_F_DEAD)
@@ -1931,6 +1944,77 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all)
        return found;
 }
 
+/* derived from fib_trie_free */
+static void __fib_info_notify_update(struct net *net, struct fib_table *tb,
+                                    struct nl_info *info)
+{
+       struct trie *t = (struct trie *)tb->tb_data;
+       struct key_vector *pn = t->kv;
+       unsigned long cindex = 1;
+       struct fib_alias *fa;
+
+       for (;;) {
+               struct key_vector *n;
+
+               if (!(cindex--)) {
+                       t_key pkey = pn->key;
+
+                       if (IS_TRIE(pn))
+                               break;
+
+                       pn = node_parent(pn);
+                       cindex = get_index(pkey, pn);
+                       continue;
+               }
+
+               /* grab the next available node */
+               n = get_child(pn, cindex);
+               if (!n)
+                       continue;
+
+               if (IS_TNODE(n)) {
+                       /* record pn and cindex for leaf walking */
+                       pn = n;
+                       cindex = 1ul << n->bits;
+
+                       continue;
+               }
+
+               hlist_for_each_entry(fa, &n->leaf, fa_list) {
+                       struct fib_info *fi = fa->fa_info;
+
+                       if (!fi || !fi->nh_updated || fa->tb_id != tb->tb_id)
+                               continue;
+
+                       rtmsg_fib(RTM_NEWROUTE, htonl(n->key), fa,
+                                 KEYLENGTH - fa->fa_slen, tb->tb_id,
+                                 info, NLM_F_REPLACE);
+
+                       /* call_fib_entry_notifiers will be removed when
+                        * in-kernel notifier is implemented and supported
+                        * for nexthop objects
+                        */
+                       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
+                                                n->key,
+                                                KEYLENGTH - fa->fa_slen, fa,
+                                                NULL);
+               }
+       }
+}
+
+void fib_info_notify_update(struct net *net, struct nl_info *info)
+{
+       unsigned int h;
+
+       for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
+               struct hlist_head *head = &net->ipv4.fib_table_hash[h];
+               struct fib_table *tb;
+
+               hlist_for_each_entry_rcu(tb, head, tb_hlist)
+                       __fib_info_notify_update(net, tb, info);
+       }
+}
+
 static void fib_leaf_notify(struct net *net, struct key_vector *l,
                            struct fib_table *tb, struct notifier_block *nb)
 {
@@ -2006,22 +2090,26 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
 {
        unsigned int flags = NLM_F_MULTI;
        __be32 xkey = htonl(l->key);
+       int i, s_i, i_fa, s_fa, err;
        struct fib_alias *fa;
-       int i, s_i;
 
-       if (filter->filter_set)
+       if (filter->filter_set ||
+           !filter->dump_exceptions || !filter->dump_routes)
                flags |= NLM_F_DUMP_FILTERED;
 
        s_i = cb->args[4];
+       s_fa = cb->args[5];
        i = 0;
 
        /* rcu_read_lock is hold by caller */
        hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
-               int err;
+               struct fib_info *fi = fa->fa_info;
 
                if (i < s_i)
                        goto next;
 
+               i_fa = 0;
+
                if (tb->tb_id != fa->tb_id)
                        goto next;
 
@@ -2030,29 +2118,49 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
                                goto next;
 
                        if ((filter->protocol &&
-                            fa->fa_info->fib_protocol != filter->protocol))
+                            fi->fib_protocol != filter->protocol))
                                goto next;
 
                        if (filter->dev &&
-                           !fib_info_nh_uses_dev(fa->fa_info, filter->dev))
+                           !fib_info_nh_uses_dev(fi, filter->dev))
                                goto next;
                }
 
-               err = fib_dump_info(skb, NETLINK_CB(cb->skb).portid,
-                                   cb->nlh->nlmsg_seq, RTM_NEWROUTE,
-                                   tb->tb_id, fa->fa_type,
-                                   xkey, KEYLENGTH - fa->fa_slen,
-                                   fa->fa_tos, fa->fa_info, flags);
-               if (err < 0) {
-                       cb->args[4] = i;
-                       return err;
+               if (filter->dump_routes) {
+                       if (!s_fa) {
+                               err = fib_dump_info(skb,
+                                                   NETLINK_CB(cb->skb).portid,
+                                                   cb->nlh->nlmsg_seq,
+                                                   RTM_NEWROUTE,
+                                                   tb->tb_id, fa->fa_type,
+                                                   xkey,
+                                                   KEYLENGTH - fa->fa_slen,
+                                                   fa->fa_tos, fi, flags);
+                               if (err < 0)
+                                       goto stop;
+                       }
+
+                       i_fa++;
                }
+
+               if (filter->dump_exceptions) {
+                       err = fib_dump_info_fnhe(skb, cb, tb->tb_id, fi,
+                                                &i_fa, s_fa);
+                       if (err < 0)
+                               goto stop;
+               }
+
 next:
                i++;
        }
 
        cb->args[4] = i;
        return skb->len;
+
+stop:
+       cb->args[4] = i;
+       cb->args[5] = i_fa;
+       return err;
 }
 
 /* rcu_read_lock needs to be hold by caller from readside */
@@ -2634,14 +2742,18 @@ static void fib_route_seq_stop(struct seq_file *seq, void *v)
        rcu_read_unlock();
 }
 
-static unsigned int fib_flag_trans(int type, __be32 mask, const struct fib_info *fi)
+static unsigned int fib_flag_trans(int type, __be32 mask, struct fib_info *fi)
 {
        unsigned int flags = 0;
 
        if (type == RTN_UNREACHABLE || type == RTN_PROHIBIT)
                flags = RTF_REJECT;
-       if (fi && fi->fib_nh->fib_nh_gw4)
-               flags |= RTF_GATEWAY;
+       if (fi) {
+               const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
+
+               if (nhc->nhc_gw.ipv4)
+                       flags |= RTF_GATEWAY;
+       }
        if (mask == htonl(0xFFFFFFFF))
                flags |= RTF_HOST;
        flags |= RTF_UP;
@@ -2672,7 +2784,7 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
        prefix = htonl(l->key);
 
        hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
-               const struct fib_info *fi = fa->fa_info;
+               struct fib_info *fi = fa->fa_info;
                __be32 mask = inet_make_mask(KEYLENGTH - fa->fa_slen);
                unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi);
 
@@ -2685,26 +2797,31 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
 
                seq_setwidth(seq, 127);
 
-               if (fi)
+               if (fi) {
+                       struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
+                       __be32 gw = 0;
+
+                       if (nhc->nhc_gw_family == AF_INET)
+                               gw = nhc->nhc_gw.ipv4;
+
                        seq_printf(seq,
                                   "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
                                   "%d\t%08X\t%d\t%u\t%u",
-                                  fi->fib_dev ? fi->fib_dev->name : "*",
-                                  prefix,
-                                  fi->fib_nh->fib_nh_gw4, flags, 0, 0,
+                                  nhc->nhc_dev ? nhc->nhc_dev->name : "*",
+                                  prefix, gw, flags, 0, 0,
                                   fi->fib_priority,
                                   mask,
                                   (fi->fib_advmss ?
                                    fi->fib_advmss + 40 : 0),
                                   fi->fib_window,
                                   fi->fib_rtt >> 3);
-               else
+               } else {
                        seq_printf(seq,
                                   "*\t%08X\t%08X\t%04X\t%d\t%u\t"
                                   "%d\t%08X\t%d\t%u\t%u",
                                   prefix, 0, flags, 0, 0, 0,
                                   mask, 0, 0, 0);
-
+               }
                seq_pad(seq, '\n');
        }