linux/drivers/net/netdevsim/fib.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
   3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
   4 *
   5 * This software is licensed under the GNU General License Version 2,
   6 * June 1991 as shown in the file COPYING in the top-level directory of this
   7 * source tree.
   8 *
   9 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
  10 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  11 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  12 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
  13 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
  14 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  15 */
  16
  17#include <net/fib_notifier.h>
  18#include <net/ip_fib.h>
  19#include <net/ip6_fib.h>
  20#include <net/fib_rules.h>
  21#include <net/netns/generic.h>
  22
  23#include "netdevsim.h"
  24
  25struct nsim_fib_entry {
  26        u64 max;
  27        u64 num;
  28};
  29
  30struct nsim_per_fib_data {
  31        struct nsim_fib_entry fib;
  32        struct nsim_fib_entry rules;
  33};
  34
  35struct nsim_fib_data {
  36        struct nsim_per_fib_data ipv4;
  37        struct nsim_per_fib_data ipv6;
  38};
  39
  40static unsigned int nsim_fib_net_id;
  41
  42u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
  43{
  44        struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
  45        struct nsim_fib_entry *entry;
  46
  47        switch (res_id) {
  48        case NSIM_RESOURCE_IPV4_FIB:
  49                entry = &fib_data->ipv4.fib;
  50                break;
  51        case NSIM_RESOURCE_IPV4_FIB_RULES:
  52                entry = &fib_data->ipv4.rules;
  53                break;
  54        case NSIM_RESOURCE_IPV6_FIB:
  55                entry = &fib_data->ipv6.fib;
  56                break;
  57        case NSIM_RESOURCE_IPV6_FIB_RULES:
  58                entry = &fib_data->ipv6.rules;
  59                break;
  60        default:
  61                return 0;
  62        }
  63
  64        return max ? entry->max : entry->num;
  65}
  66
  67int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
  68                     struct netlink_ext_ack *extack)
  69{
  70        struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
  71        struct nsim_fib_entry *entry;
  72        int err = 0;
  73
  74        switch (res_id) {
  75        case NSIM_RESOURCE_IPV4_FIB:
  76                entry = &fib_data->ipv4.fib;
  77                break;
  78        case NSIM_RESOURCE_IPV4_FIB_RULES:
  79                entry = &fib_data->ipv4.rules;
  80                break;
  81        case NSIM_RESOURCE_IPV6_FIB:
  82                entry = &fib_data->ipv6.fib;
  83                break;
  84        case NSIM_RESOURCE_IPV6_FIB_RULES:
  85                entry = &fib_data->ipv6.rules;
  86                break;
  87        default:
  88                return 0;
  89        }
  90
  91        /* not allowing a new max to be less than curren occupancy
  92         * --> no means of evicting entries
  93         */
  94        if (val < entry->num) {
  95                NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
  96                err = -EINVAL;
  97        } else {
  98                entry->max = val;
  99        }
 100
 101        return err;
 102}
 103
 104static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
 105                                 struct netlink_ext_ack *extack)
 106{
 107        int err = 0;
 108
 109        if (add) {
 110                if (entry->num < entry->max) {
 111                        entry->num++;
 112                } else {
 113                        err = -ENOSPC;
 114                        NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
 115                }
 116        } else {
 117                entry->num--;
 118        }
 119
 120        return err;
 121}
 122
 123static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
 124{
 125        struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
 126        struct netlink_ext_ack *extack = info->extack;
 127        int err = 0;
 128
 129        switch (info->family) {
 130        case AF_INET:
 131                err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
 132                break;
 133        case AF_INET6:
 134                err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
 135                break;
 136        }
 137
 138        return err;
 139}
 140
 141static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
 142                            struct netlink_ext_ack *extack)
 143{
 144        int err = 0;
 145
 146        if (add) {
 147                if (entry->num < entry->max) {
 148                        entry->num++;
 149                } else {
 150                        err = -ENOSPC;
 151                        NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
 152                }
 153        } else {
 154                entry->num--;
 155        }
 156
 157        return err;
 158}
 159
 160static int nsim_fib_event(struct fib_notifier_info *info, bool add)
 161{
 162        struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
 163        struct netlink_ext_ack *extack = info->extack;
 164        int err = 0;
 165
 166        switch (info->family) {
 167        case AF_INET:
 168                err = nsim_fib_account(&data->ipv4.fib, add, extack);
 169                break;
 170        case AF_INET6:
 171                err = nsim_fib_account(&data->ipv6.fib, add, extack);
 172                break;
 173        }
 174
 175        return err;
 176}
 177
 178static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
 179                             void *ptr)
 180{
 181        struct fib_notifier_info *info = ptr;
 182        int err = 0;
 183
 184        switch (event) {
 185        case FIB_EVENT_RULE_ADD: /* fall through */
 186        case FIB_EVENT_RULE_DEL:
 187                err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
 188                break;
 189
 190        case FIB_EVENT_ENTRY_ADD:  /* fall through */
 191        case FIB_EVENT_ENTRY_DEL:
 192                err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
 193                break;
 194        }
 195
 196        return notifier_from_errno(err);
 197}
 198
 199/* inconsistent dump, trying again */
 200static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
 201{
 202        struct nsim_fib_data *data;
 203        struct net *net;
 204
 205        rcu_read_lock();
 206        for_each_net_rcu(net) {
 207                data = net_generic(net, nsim_fib_net_id);
 208
 209                data->ipv4.fib.num = 0ULL;
 210                data->ipv4.rules.num = 0ULL;
 211
 212                data->ipv6.fib.num = 0ULL;
 213                data->ipv6.rules.num = 0ULL;
 214        }
 215        rcu_read_unlock();
 216}
 217
 218static struct notifier_block nsim_fib_nb = {
 219        .notifier_call = nsim_fib_event_nb,
 220};
 221
 222/* Initialize per network namespace state */
 223static int __net_init nsim_fib_netns_init(struct net *net)
 224{
 225        struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
 226
 227        data->ipv4.fib.max = (u64)-1;
 228        data->ipv4.rules.max = (u64)-1;
 229
 230        data->ipv6.fib.max = (u64)-1;
 231        data->ipv6.rules.max = (u64)-1;
 232
 233        return 0;
 234}
 235
 236static struct pernet_operations nsim_fib_net_ops = {
 237        .init = nsim_fib_netns_init,
 238        .id   = &nsim_fib_net_id,
 239        .size = sizeof(struct nsim_fib_data),
 240};
 241
 242void nsim_fib_exit(void)
 243{
 244        unregister_pernet_subsys(&nsim_fib_net_ops);
 245        unregister_fib_notifier(&nsim_fib_nb);
 246}
 247
 248int nsim_fib_init(void)
 249{
 250        int err;
 251
 252        err = register_pernet_subsys(&nsim_fib_net_ops);
 253        if (err < 0) {
 254                pr_err("Failed to register pernet subsystem\n");
 255                goto err_out;
 256        }
 257
 258        err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
 259        if (err < 0) {
 260                pr_err("Failed to register fib notifier\n");
 261                goto err_out;
 262        }
 263
 264err_out:
 265        return err;
 266}
 267