at v4.17-rc2 5.9 kB view raw
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}