| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * net/core/fib_rules.c Generic Routing Rules |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or |
|---|
| 5 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 6 | | - * published by the Free Software Foundation, version 2. |
|---|
| 7 | 4 | * |
|---|
| 8 | 5 | * Authors: Thomas Graf <tgraf@suug.ch> |
|---|
| 9 | 6 | */ |
|---|
| .. | .. |
|---|
| 17 | 14 | #include <net/sock.h> |
|---|
| 18 | 15 | #include <net/fib_rules.h> |
|---|
| 19 | 16 | #include <net/ip_tunnels.h> |
|---|
| 17 | +#include <linux/indirect_call_wrapper.h> |
|---|
| 18 | + |
|---|
| 19 | +#if defined(CONFIG_IPV6) && defined(CONFIG_IPV6_MULTIPLE_TABLES) |
|---|
| 20 | +#ifdef CONFIG_IP_MULTIPLE_TABLES |
|---|
| 21 | +#define INDIRECT_CALL_MT(f, f2, f1, ...) \ |
|---|
| 22 | + INDIRECT_CALL_INET(f, f2, f1, __VA_ARGS__) |
|---|
| 23 | +#else |
|---|
| 24 | +#define INDIRECT_CALL_MT(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__) |
|---|
| 25 | +#endif |
|---|
| 26 | +#elif defined(CONFIG_IP_MULTIPLE_TABLES) |
|---|
| 27 | +#define INDIRECT_CALL_MT(f, f2, f1, ...) INDIRECT_CALL_1(f, f1, __VA_ARGS__) |
|---|
| 28 | +#else |
|---|
| 29 | +#define INDIRECT_CALL_MT(f, f2, f1, ...) f(__VA_ARGS__) |
|---|
| 30 | +#endif |
|---|
| 20 | 31 | |
|---|
| 21 | 32 | static const struct fib_kuid_range fib_kuid_range_unset = { |
|---|
| 22 | 33 | KUIDT_INIT(0), |
|---|
| .. | .. |
|---|
| 270 | 281 | uid_gt(fl->flowi_uid, rule->uid_range.end)) |
|---|
| 271 | 282 | goto out; |
|---|
| 272 | 283 | |
|---|
| 273 | | - ret = ops->match(rule, fl, flags); |
|---|
| 284 | + ret = INDIRECT_CALL_MT(ops->match, |
|---|
| 285 | + fib6_rule_match, |
|---|
| 286 | + fib4_rule_match, |
|---|
| 287 | + rule, fl, flags); |
|---|
| 274 | 288 | out: |
|---|
| 275 | 289 | return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; |
|---|
| 276 | 290 | } |
|---|
| .. | .. |
|---|
| 301 | 315 | } else if (rule->action == FR_ACT_NOP) |
|---|
| 302 | 316 | continue; |
|---|
| 303 | 317 | else |
|---|
| 304 | | - err = ops->action(rule, fl, flags, arg); |
|---|
| 318 | + err = INDIRECT_CALL_MT(ops->action, |
|---|
| 319 | + fib6_rule_action, |
|---|
| 320 | + fib4_rule_action, |
|---|
| 321 | + rule, fl, flags, arg); |
|---|
| 305 | 322 | |
|---|
| 306 | | - if (!err && ops->suppress && ops->suppress(rule, arg)) |
|---|
| 323 | + if (!err && ops->suppress && INDIRECT_CALL_MT(ops->suppress, |
|---|
| 324 | + fib6_rule_suppress, |
|---|
| 325 | + fib4_rule_suppress, |
|---|
| 326 | + rule, flags, arg)) |
|---|
| 307 | 327 | continue; |
|---|
| 308 | 328 | |
|---|
| 309 | 329 | if (err != -EAGAIN) { |
|---|
| .. | .. |
|---|
| 324 | 344 | } |
|---|
| 325 | 345 | EXPORT_SYMBOL_GPL(fib_rules_lookup); |
|---|
| 326 | 346 | |
|---|
| 327 | | -static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, |
|---|
| 347 | +static int call_fib_rule_notifier(struct notifier_block *nb, |
|---|
| 328 | 348 | enum fib_event_type event_type, |
|---|
| 329 | | - struct fib_rule *rule, int family) |
|---|
| 349 | + struct fib_rule *rule, int family, |
|---|
| 350 | + struct netlink_ext_ack *extack) |
|---|
| 330 | 351 | { |
|---|
| 331 | 352 | struct fib_rule_notifier_info info = { |
|---|
| 332 | 353 | .info.family = family, |
|---|
| 354 | + .info.extack = extack, |
|---|
| 333 | 355 | .rule = rule, |
|---|
| 334 | 356 | }; |
|---|
| 335 | 357 | |
|---|
| 336 | | - return call_fib_notifier(nb, net, event_type, &info.info); |
|---|
| 358 | + return call_fib_notifier(nb, event_type, &info.info); |
|---|
| 337 | 359 | } |
|---|
| 338 | 360 | |
|---|
| 339 | 361 | static int call_fib_rule_notifiers(struct net *net, |
|---|
| .. | .. |
|---|
| 353 | 375 | } |
|---|
| 354 | 376 | |
|---|
| 355 | 377 | /* Called with rcu_read_lock() */ |
|---|
| 356 | | -int fib_rules_dump(struct net *net, struct notifier_block *nb, int family) |
|---|
| 378 | +int fib_rules_dump(struct net *net, struct notifier_block *nb, int family, |
|---|
| 379 | + struct netlink_ext_ack *extack) |
|---|
| 357 | 380 | { |
|---|
| 358 | 381 | struct fib_rules_ops *ops; |
|---|
| 359 | 382 | struct fib_rule *rule; |
|---|
| 383 | + int err = 0; |
|---|
| 360 | 384 | |
|---|
| 361 | 385 | ops = lookup_rules_ops(net, family); |
|---|
| 362 | 386 | if (!ops) |
|---|
| 363 | 387 | return -EAFNOSUPPORT; |
|---|
| 364 | | - list_for_each_entry_rcu(rule, &ops->rules_list, list) |
|---|
| 365 | | - call_fib_rule_notifier(nb, net, FIB_EVENT_RULE_ADD, rule, |
|---|
| 366 | | - family); |
|---|
| 388 | + list_for_each_entry_rcu(rule, &ops->rules_list, list) { |
|---|
| 389 | + err = call_fib_rule_notifier(nb, FIB_EVENT_RULE_ADD, |
|---|
| 390 | + rule, family, extack); |
|---|
| 391 | + if (err) |
|---|
| 392 | + break; |
|---|
| 393 | + } |
|---|
| 367 | 394 | rules_ops_put(ops); |
|---|
| 368 | 395 | |
|---|
| 369 | | - return 0; |
|---|
| 396 | + return err; |
|---|
| 370 | 397 | } |
|---|
| 371 | 398 | EXPORT_SYMBOL_GPL(fib_rules_dump); |
|---|
| 372 | 399 | |
|---|
| .. | .. |
|---|
| 746 | 773 | goto errout; |
|---|
| 747 | 774 | } |
|---|
| 748 | 775 | |
|---|
| 749 | | - err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack); |
|---|
| 776 | + err = nlmsg_parse_deprecated(nlh, sizeof(*frh), tb, FRA_MAX, |
|---|
| 777 | + ops->policy, extack); |
|---|
| 750 | 778 | if (err < 0) { |
|---|
| 751 | 779 | NL_SET_ERR_MSG(extack, "Error parsing msg"); |
|---|
| 752 | 780 | goto errout; |
|---|
| .. | .. |
|---|
| 853 | 881 | goto errout; |
|---|
| 854 | 882 | } |
|---|
| 855 | 883 | |
|---|
| 856 | | - err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack); |
|---|
| 884 | + err = nlmsg_parse_deprecated(nlh, sizeof(*frh), tb, FRA_MAX, |
|---|
| 885 | + ops->policy, extack); |
|---|
| 857 | 886 | if (err < 0) { |
|---|
| 858 | 887 | NL_SET_ERR_MSG(extack, "Error parsing msg"); |
|---|
| 859 | 888 | goto errout; |
|---|
| .. | .. |
|---|
| 1063 | 1092 | return err; |
|---|
| 1064 | 1093 | } |
|---|
| 1065 | 1094 | |
|---|
| 1095 | +static int fib_valid_dumprule_req(const struct nlmsghdr *nlh, |
|---|
| 1096 | + struct netlink_ext_ack *extack) |
|---|
| 1097 | +{ |
|---|
| 1098 | + struct fib_rule_hdr *frh; |
|---|
| 1099 | + |
|---|
| 1100 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) { |
|---|
| 1101 | + NL_SET_ERR_MSG(extack, "Invalid header for fib rule dump request"); |
|---|
| 1102 | + return -EINVAL; |
|---|
| 1103 | + } |
|---|
| 1104 | + |
|---|
| 1105 | + frh = nlmsg_data(nlh); |
|---|
| 1106 | + if (frh->dst_len || frh->src_len || frh->tos || frh->table || |
|---|
| 1107 | + frh->res1 || frh->res2 || frh->action || frh->flags) { |
|---|
| 1108 | + NL_SET_ERR_MSG(extack, |
|---|
| 1109 | + "Invalid values in header for fib rule dump request"); |
|---|
| 1110 | + return -EINVAL; |
|---|
| 1111 | + } |
|---|
| 1112 | + |
|---|
| 1113 | + if (nlmsg_attrlen(nlh, sizeof(*frh))) { |
|---|
| 1114 | + NL_SET_ERR_MSG(extack, "Invalid data after header in fib rule dump request"); |
|---|
| 1115 | + return -EINVAL; |
|---|
| 1116 | + } |
|---|
| 1117 | + |
|---|
| 1118 | + return 0; |
|---|
| 1119 | +} |
|---|
| 1120 | + |
|---|
| 1066 | 1121 | static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) |
|---|
| 1067 | 1122 | { |
|---|
| 1123 | + const struct nlmsghdr *nlh = cb->nlh; |
|---|
| 1068 | 1124 | struct net *net = sock_net(skb->sk); |
|---|
| 1069 | 1125 | struct fib_rules_ops *ops; |
|---|
| 1070 | 1126 | int idx = 0, family; |
|---|
| 1071 | 1127 | |
|---|
| 1072 | | - family = rtnl_msg_family(cb->nlh); |
|---|
| 1128 | + if (cb->strict_check) { |
|---|
| 1129 | + int err = fib_valid_dumprule_req(nlh, cb->extack); |
|---|
| 1130 | + |
|---|
| 1131 | + if (err < 0) |
|---|
| 1132 | + return err; |
|---|
| 1133 | + } |
|---|
| 1134 | + |
|---|
| 1135 | + family = rtnl_msg_family(nlh); |
|---|
| 1073 | 1136 | if (family != AF_UNSPEC) { |
|---|
| 1074 | 1137 | /* Protocol specific dump request */ |
|---|
| 1075 | 1138 | ops = lookup_rules_ops(net, family); |
|---|