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
  22#include "netdevsim.h"
  23
  24struct nsim_fib_entry {
  25        u64 max;
  26        u64 num;
  27};
  28
  29struct nsim_per_fib_data {
  30        struct nsim_fib_entry fib;
  31        struct nsim_fib_entry rules;
  32};
  33
  34struct nsim_fib_data {
  35        struct notifier_block fib_nb;
  36        struct nsim_per_fib_data ipv4;
  37        struct nsim_per_fib_data ipv6;
  38};
  39
  40u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
  41                     enum nsim_resource_id res_id, bool max)
  42{
  43        struct nsim_fib_entry *entry;
  44
  45        switch (res_id) {
  46        case NSIM_RESOURCE_IPV4_FIB:
  47                entry = &fib_data->ipv4.fib;
  48                break;
  49        case NSIM_RESOURCE_IPV4_FIB_RULES:
  50                entry = &fib_data->ipv4.rules;
  51                break;
  52        case NSIM_RESOURCE_IPV6_FIB:
  53                entry = &fib_data->ipv6.fib;
  54                break;
  55        case NSIM_RESOURCE_IPV6_FIB_RULES:
  56                entry = &fib_data->ipv6.rules;
  57                break;
  58        default:
  59                return 0;
  60        }
  61
  62        return max ? entry->max : entry->num;
  63}
  64
  65int nsim_fib_set_max(struct nsim_fib_data *fib_data,
  66                     enum nsim_resource_id res_id, u64 val,
  67                     struct netlink_ext_ack *extack)
  68{
  69        struct nsim_fib_entry *entry;
  70        int err = 0;
  71
  72        switch (res_id) {
  73        case NSIM_RESOURCE_IPV4_FIB:
  74                entry = &fib_data->ipv4.fib;
  75                break;
  76        case NSIM_RESOURCE_IPV4_FIB_RULES:
  77                entry = &fib_data->ipv4.rules;
  78                break;
  79        case NSIM_RESOURCE_IPV6_FIB:
  80                entry = &fib_data->ipv6.fib;
  81                break;
  82        case NSIM_RESOURCE_IPV6_FIB_RULES:
  83                entry = &fib_data->ipv6.rules;
  84                break;
  85        default:
  86                return 0;
  87        }
  88
  89        /* not allowing a new max to be less than curren occupancy
  90         * --> no means of evicting entries
  91         */
  92        if (val < entry->num) {
  93                NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
  94                err = -EINVAL;
  95        } else {
  96                entry->max = val;
  97        }
  98
  99        return err;
 100}
 101
 102static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
 103                                 struct netlink_ext_ack *extack)
 104{
 105        int err = 0;
 106
 107        if (add) {
 108                if (entry->num < entry->max) {
 109                        entry->num++;
 110                } else {
 111                        err = -ENOSPC;
 112                        NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
 113                }
 114        } else {
 115                entry->num--;
 116        }
 117
 118        return err;
 119}
 120
 121static int nsim_fib_rule_event(struct nsim_fib_data *data,
 122                               struct fib_notifier_info *info, bool add)
 123{
 124        struct netlink_ext_ack *extack = info->extack;
 125        int err = 0;
 126
 127        switch (info->family) {
 128        case AF_INET:
 129                err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
 130                break;
 131        case AF_INET6:
 132                err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
 133                break;
 134        }
 135
 136        return err;
 137}
 138
 139static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
 140                            struct netlink_ext_ack *extack)
 141{
 142        int err = 0;
 143
 144        if (add) {
 145                if (entry->num < entry->max) {
 146                        entry->num++;
 147                } else {
 148                        err = -ENOSPC;
 149                        NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
 150                }
 151        } else {
 152                entry->num--;
 153        }
 154
 155        return err;
 156}
 157
 158static int nsim_fib_event(struct nsim_fib_data *data,
 159                          struct fib_notifier_info *info, bool add)
 160{
 161        struct netlink_ext_ack *extack = info->extack;
 162        int err = 0;
 163
 164        switch (info->family) {
 165        case AF_INET:
 166                err = nsim_fib_account(&data->ipv4.fib, add, extack);
 167                break;
 168        case AF_INET6:
 169                err = nsim_fib_account(&data->ipv6.fib, add, extack);
 170                break;
 171        }
 172
 173        return err;
 174}
 175
 176static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
 177                             void *ptr)
 178{
 179        struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
 180                                                  fib_nb);
 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(data, info,
 188                                          event == FIB_EVENT_RULE_ADD);
 189                break;
 190
 191        case FIB_EVENT_ENTRY_ADD:  /* fall through */
 192        case FIB_EVENT_ENTRY_DEL:
 193                err = nsim_fib_event(data, info,
 194                                     event == FIB_EVENT_ENTRY_ADD);
 195                break;
 196        }
 197
 198        return notifier_from_errno(err);
 199}
 200
 201/* inconsistent dump, trying again */
 202static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
 203{
 204        struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
 205                                                  fib_nb);
 206
 207        data->ipv4.fib.num = 0ULL;
 208        data->ipv4.rules.num = 0ULL;
 209        data->ipv6.fib.num = 0ULL;
 210        data->ipv6.rules.num = 0ULL;
 211}
 212
 213struct nsim_fib_data *nsim_fib_create(void)
 214{
 215        struct nsim_fib_data *data;
 216        int err;
 217
 218        data = kzalloc(sizeof(*data), GFP_KERNEL);
 219        if (!data)
 220                return ERR_PTR(-ENOMEM);
 221
 222        data->ipv4.fib.max = (u64)-1;
 223        data->ipv4.rules.max = (u64)-1;
 224
 225        data->ipv6.fib.max = (u64)-1;
 226        data->ipv6.rules.max = (u64)-1;
 227
 228        data->fib_nb.notifier_call = nsim_fib_event_nb;
 229        err = register_fib_notifier(&data->fib_nb, nsim_fib_dump_inconsistent);
 230        if (err) {
 231                pr_err("Failed to register fib notifier\n");
 232                goto err_out;
 233        }
 234
 235        return data;
 236
 237err_out:
 238        kfree(data);
 239        return ERR_PTR(err);
 240}
 241
 242void nsim_fib_destroy(struct nsim_fib_data *data)
 243{
 244        unregister_fib_notifier(&data->fib_nb);
 245        kfree(data);
 246}
 247