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 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 104 static 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 123 static 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 141 static 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 160 static 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 178 static 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 */ 200 static 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 218 static struct notifier_block nsim_fib_nb = { 219 .notifier_call = nsim_fib_event_nb, 220 }; 221 222 /* Initialize per network namespace state */ 223 static 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 236 static 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 242 void nsim_fib_exit(void) 243 { 244 unregister_pernet_subsys(&nsim_fib_net_ops); 245 unregister_fib_notifier(&nsim_fib_nb); 246 } 247 248 int 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 264 err_out: 265 return err; 266 } 267