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 25 struct nsim_fib_entry { 26 u64 max; 27 u64 num; 28 }; 29 30 struct nsim_per_fib_data { 31 struct nsim_fib_entry fib; 32 struct nsim_fib_entry rules; 33 }; 34 35 struct nsim_fib_data { 36 struct nsim_per_fib_data ipv4; 37 struct nsim_per_fib_data ipv6; 38 }; 39 40 static unsigned int nsim_fib_net_id; 41 42 u64 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 67 int 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 101 static 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 120 static 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 138 static 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 157 static 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 175 static 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 */ 197 static 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 215 static struct notifier_block nsim_fib_nb = { 216 .notifier_call = nsim_fib_event_nb, 217 }; 218 219 /* Initialize per network namespace state */ 220 static 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 233 static 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 239 void nsim_fib_exit(void) 240 { 241 unregister_pernet_subsys(&nsim_fib_net_ops); 242 unregister_fib_notifier(&nsim_fib_nb); 243 } 244 245 int 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 261 err_out: 262 return err; 263 } 264