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{
  69        struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
  70        struct nsim_fib_entry *entry;
  71        int err = 0;
  72
  73        switch (res_id) {
  74        case NSIM_RESOURCE_IPV4_FIB:
  75                entry = &fib_data->ipv4.fib;
  76                break;
  77        case NSIM_RESOURCE_IPV4_FIB_RULES:
  78                entry = &fib_data->ipv4.rules;
  79                break;
  80        case NSIM_RESOURCE_IPV6_FIB:
  81                entry = &fib_data->ipv6.fib;
  82                break;
  83        case NSIM_RESOURCE_IPV6_FIB_RULES:
  84                entry = &fib_data->ipv6.rules;
  85                break;
  86        default:
  87                return 0;
  88        }
  89
  90        /* not allowing a new max to be less than curren occupancy
  91         * --> no means of evicting entries
  92         */
  93        if (val < entry->num)
  94                err = -EINVAL;
  95        else
  96                entry->max = val;
  97
  98        return err;
  99}
 100
 101static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
 102                                 struct netlink_ext_ack *extack)
 103{
 104        int err = 0;
 105
 106        if (add) {
 107                if (entry->num < entry->max) {
 108                        entry->num++;
 109                } else {
 110                        err = -ENOSPC;
 111                        NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
 112                }
 113        } else {
 114                entry->num--;
 115        }
 116
 117        return err;
 118}
 119
 120static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
 121{
 122        struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
 123        struct netlink_ext_ack *extack = info->extack;
 124        int err = 0;
 125
 126        switch (info->family) {
 127        case AF_INET:
 128                err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
 129                break;
 130        case AF_INET6:
 131                err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
 132                break;
 133        }
 134
 135        return err;
 136}
 137
 138static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
 139                            struct netlink_ext_ack *extack)
 140{
 141        int err = 0;
 142
 143        if (add) {
 144                if (entry->num < entry->max) {
 145                        entry->num++;
 146                } else {
 147                        err = -ENOSPC;
 148                        NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
 149                }
 150        } else {
 151                entry->num--;
 152        }
 153
 154        return err;
 155}
 156
 157static int nsim_fib_event(struct fib_notifier_info *info, bool add)
 158{
 159        struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
 160        struct netlink_ext_ack *extack = info->extack;
 161        int err = 0;
 162
 163        switch (info->family) {
 164        case AF_INET:
 165                err = nsim_fib_account(&data->ipv4.fib, add, extack);
 166                break;
 167        case AF_INET6:
 168                err = nsim_fib_account(&data->ipv6.fib, add, extack);
 169                break;
 170        }
 171
 172        return err;
 173}
 174
 175static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
 176                             void *ptr)
 177{
 178        struct fib_notifier_info *info = ptr;
 179        int err = 0;
 180
 181        switch (event) {
 182        case FIB_EVENT_RULE_ADD: /* fall through */
 183        case FIB_EVENT_RULE_DEL:
 184                err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
 185                break;
 186
 187        case FIB_EVENT_ENTRY_ADD:  /* fall through */
 188        case FIB_EVENT_ENTRY_DEL:
 189                err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
 190                break;
 191        }
 192
 193        return notifier_from_errno(err);
 194}
 195
 196/* inconsistent dump, trying again */
 197static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
 198{
 199        struct nsim_fib_data *data;
 200        struct net *net;
 201
 202        rcu_read_lock();
 203        for_each_net_rcu(net) {
 204                data = net_generic(net, nsim_fib_net_id);
 205
 206                data->ipv4.fib.num = 0ULL;
 207                data->ipv4.rules.num = 0ULL;
 208
 209                data->ipv6.fib.num = 0ULL;
 210                data->ipv6.rules.num = 0ULL;
 211        }
 212        rcu_read_unlock();
 213}
 214
 215static struct notifier_block nsim_fib_nb = {
 216        .notifier_call = nsim_fib_event_nb,
 217};
 218
 219/* Initialize per network namespace state */
 220static int __net_init nsim_fib_netns_init(struct net *net)
 221{
 222        struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
 223
 224        data->ipv4.fib.max = (u64)-1;
 225        data->ipv4.rules.max = (u64)-1;
 226
 227        data->ipv6.fib.max = (u64)-1;
 228        data->ipv6.rules.max = (u64)-1;
 229
 230        return 0;
 231}
 232
 233static struct pernet_operations nsim_fib_net_ops = {
 234        .init = nsim_fib_netns_init,
 235        .id   = &nsim_fib_net_id,
 236        .size = sizeof(struct nsim_fib_data),
 237};
 238
 239void nsim_fib_exit(void)
 240{
 241        unregister_pernet_subsys(&nsim_fib_net_ops);
 242        unregister_fib_notifier(&nsim_fib_nb);
 243}
 244
 245int nsim_fib_init(void)
 246{
 247        int err;
 248
 249        err = register_pernet_subsys(&nsim_fib_net_ops);
 250        if (err < 0) {
 251                pr_err("Failed to register pernet subsystem\n");
 252                goto err_out;
 253        }
 254
 255        err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
 256        if (err < 0) {
 257                pr_err("Failed to register fib notifier\n");
 258                goto err_out;
 259        }
 260
 261err_out:
 262        return err;
 263}
 264