diff options
author | David Ahern <dsahern@gmail.com> | 2019-04-05 19:30:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-04-08 18:22:41 -0400 |
commit | 448d7248191706cbbd7761e3bc72c2985c4d38a7 (patch) | |
tree | 30cf5625d5b433cce8f8b1bca1369249a2c9954b /net/ipv4/fib_semantics.c | |
parent | a4ea5d43c807be28545625c1e0641905022fa0d1 (diff) |
ipv4: Refactor fib_check_nh
fib_check_nh is currently huge covering multiple uses cases - device only,
device + gateway, and device + gateway with ONLINK. The next patch adds
validation checks for IPv6 which only further complicates it. So, break
fib_check_nh into 2 helpers - one for gateway validation and one for device
only.
Signed-off-by: David Ahern <dsahern@gmail.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fib_semantics.c')
-rw-r--r-- | net/ipv4/fib_semantics.c | 234 |
1 files changed, 125 insertions, 109 deletions
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 680b5a9a911a..32ce6e6202d2 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c | |||
@@ -885,134 +885,150 @@ bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi) | |||
885 | * | | 885 | * | |
886 | * |-> {local prefix} (terminal node) | 886 | * |-> {local prefix} (terminal node) |
887 | */ | 887 | */ |
888 | static int fib_check_nh(struct fib_config *cfg, struct fib_nh *nh, | 888 | static int fib_check_nh_v4_gw(struct net *net, struct fib_nh *nh, u32 table, |
889 | struct netlink_ext_ack *extack) | 889 | u8 scope, struct netlink_ext_ack *extack) |
890 | { | 890 | { |
891 | int err = 0; | ||
892 | struct net *net; | ||
893 | struct net_device *dev; | 891 | struct net_device *dev; |
892 | struct fib_result res; | ||
893 | int err; | ||
894 | 894 | ||
895 | net = cfg->fc_nlinfo.nl_net; | 895 | if (nh->fib_nh_flags & RTNH_F_ONLINK) { |
896 | if (nh->fib_nh_gw4) { | 896 | unsigned int addr_type; |
897 | struct fib_result res; | ||
898 | |||
899 | if (nh->fib_nh_flags & RTNH_F_ONLINK) { | ||
900 | unsigned int addr_type; | ||
901 | 897 | ||
902 | if (cfg->fc_scope >= RT_SCOPE_LINK) { | 898 | if (scope >= RT_SCOPE_LINK) { |
903 | NL_SET_ERR_MSG(extack, | 899 | NL_SET_ERR_MSG(extack, "Nexthop has invalid scope"); |
904 | "Nexthop has invalid scope"); | 900 | return -EINVAL; |
905 | return -EINVAL; | ||
906 | } | ||
907 | dev = __dev_get_by_index(net, nh->fib_nh_oif); | ||
908 | if (!dev) { | ||
909 | NL_SET_ERR_MSG(extack, "Nexthop device required for onlink"); | ||
910 | return -ENODEV; | ||
911 | } | ||
912 | if (!(dev->flags & IFF_UP)) { | ||
913 | NL_SET_ERR_MSG(extack, | ||
914 | "Nexthop device is not up"); | ||
915 | return -ENETDOWN; | ||
916 | } | ||
917 | addr_type = inet_addr_type_dev_table(net, dev, | ||
918 | nh->fib_nh_gw4); | ||
919 | if (addr_type != RTN_UNICAST) { | ||
920 | NL_SET_ERR_MSG(extack, | ||
921 | "Nexthop has invalid gateway"); | ||
922 | return -EINVAL; | ||
923 | } | ||
924 | if (!netif_carrier_ok(dev)) | ||
925 | nh->fib_nh_flags |= RTNH_F_LINKDOWN; | ||
926 | nh->fib_nh_dev = dev; | ||
927 | dev_hold(dev); | ||
928 | nh->fib_nh_scope = RT_SCOPE_LINK; | ||
929 | return 0; | ||
930 | } | 901 | } |
931 | rcu_read_lock(); | 902 | dev = __dev_get_by_index(net, nh->fib_nh_oif); |
932 | { | 903 | if (!dev) { |
933 | struct fib_table *tbl = NULL; | 904 | NL_SET_ERR_MSG(extack, "Nexthop device required for onlink"); |
934 | struct flowi4 fl4 = { | 905 | return -ENODEV; |
935 | .daddr = nh->fib_nh_gw4, | ||
936 | .flowi4_scope = cfg->fc_scope + 1, | ||
937 | .flowi4_oif = nh->fib_nh_oif, | ||
938 | .flowi4_iif = LOOPBACK_IFINDEX, | ||
939 | }; | ||
940 | |||
941 | /* It is not necessary, but requires a bit of thinking */ | ||
942 | if (fl4.flowi4_scope < RT_SCOPE_LINK) | ||
943 | fl4.flowi4_scope = RT_SCOPE_LINK; | ||
944 | |||
945 | if (cfg->fc_table) | ||
946 | tbl = fib_get_table(net, cfg->fc_table); | ||
947 | |||
948 | if (tbl) | ||
949 | err = fib_table_lookup(tbl, &fl4, &res, | ||
950 | FIB_LOOKUP_IGNORE_LINKSTATE | | ||
951 | FIB_LOOKUP_NOREF); | ||
952 | |||
953 | /* on error or if no table given do full lookup. This | ||
954 | * is needed for example when nexthops are in the local | ||
955 | * table rather than the given table | ||
956 | */ | ||
957 | if (!tbl || err) { | ||
958 | err = fib_lookup(net, &fl4, &res, | ||
959 | FIB_LOOKUP_IGNORE_LINKSTATE); | ||
960 | } | ||
961 | |||
962 | if (err) { | ||
963 | NL_SET_ERR_MSG(extack, | ||
964 | "Nexthop has invalid gateway"); | ||
965 | rcu_read_unlock(); | ||
966 | return err; | ||
967 | } | ||
968 | } | 906 | } |
969 | err = -EINVAL; | 907 | if (!(dev->flags & IFF_UP)) { |
970 | if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) { | 908 | NL_SET_ERR_MSG(extack, "Nexthop device is not up"); |
971 | NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); | 909 | return -ENETDOWN; |
972 | goto out; | ||
973 | } | 910 | } |
974 | nh->fib_nh_scope = res.scope; | 911 | addr_type = inet_addr_type_dev_table(net, dev, nh->fib_nh_gw4); |
975 | nh->fib_nh_oif = FIB_RES_OIF(res); | 912 | if (addr_type != RTN_UNICAST) { |
976 | nh->fib_nh_dev = dev = FIB_RES_DEV(res); | 913 | NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); |
977 | if (!dev) { | 914 | return -EINVAL; |
978 | NL_SET_ERR_MSG(extack, | ||
979 | "No egress device for nexthop gateway"); | ||
980 | goto out; | ||
981 | } | 915 | } |
982 | dev_hold(dev); | ||
983 | if (!netif_carrier_ok(dev)) | 916 | if (!netif_carrier_ok(dev)) |
984 | nh->fib_nh_flags |= RTNH_F_LINKDOWN; | 917 | nh->fib_nh_flags |= RTNH_F_LINKDOWN; |
985 | err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; | 918 | nh->fib_nh_dev = dev; |
986 | } else { | 919 | dev_hold(dev); |
987 | struct in_device *in_dev; | 920 | nh->fib_nh_scope = RT_SCOPE_LINK; |
988 | 921 | return 0; | |
989 | if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) { | 922 | } |
990 | NL_SET_ERR_MSG(extack, | 923 | rcu_read_lock(); |
991 | "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set"); | 924 | { |
992 | return -EINVAL; | 925 | struct fib_table *tbl = NULL; |
926 | struct flowi4 fl4 = { | ||
927 | .daddr = nh->fib_nh_gw4, | ||
928 | .flowi4_scope = scope + 1, | ||
929 | .flowi4_oif = nh->fib_nh_oif, | ||
930 | .flowi4_iif = LOOPBACK_IFINDEX, | ||
931 | }; | ||
932 | |||
933 | /* It is not necessary, but requires a bit of thinking */ | ||
934 | if (fl4.flowi4_scope < RT_SCOPE_LINK) | ||
935 | fl4.flowi4_scope = RT_SCOPE_LINK; | ||
936 | |||
937 | if (table) | ||
938 | tbl = fib_get_table(net, table); | ||
939 | |||
940 | if (tbl) | ||
941 | err = fib_table_lookup(tbl, &fl4, &res, | ||
942 | FIB_LOOKUP_IGNORE_LINKSTATE | | ||
943 | FIB_LOOKUP_NOREF); | ||
944 | |||
945 | /* on error or if no table given do full lookup. This | ||
946 | * is needed for example when nexthops are in the local | ||
947 | * table rather than the given table | ||
948 | */ | ||
949 | if (!tbl || err) { | ||
950 | err = fib_lookup(net, &fl4, &res, | ||
951 | FIB_LOOKUP_IGNORE_LINKSTATE); | ||
993 | } | 952 | } |
994 | rcu_read_lock(); | 953 | |
995 | err = -ENODEV; | 954 | if (err) { |
996 | in_dev = inetdev_by_index(net, nh->fib_nh_oif); | 955 | NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); |
997 | if (!in_dev) | ||
998 | goto out; | ||
999 | err = -ENETDOWN; | ||
1000 | if (!(in_dev->dev->flags & IFF_UP)) { | ||
1001 | NL_SET_ERR_MSG(extack, "Device for nexthop is not up"); | ||
1002 | goto out; | 956 | goto out; |
1003 | } | 957 | } |
1004 | nh->fib_nh_dev = in_dev->dev; | ||
1005 | dev_hold(nh->fib_nh_dev); | ||
1006 | nh->fib_nh_scope = RT_SCOPE_HOST; | ||
1007 | if (!netif_carrier_ok(nh->fib_nh_dev)) | ||
1008 | nh->fib_nh_flags |= RTNH_F_LINKDOWN; | ||
1009 | err = 0; | ||
1010 | } | 958 | } |
959 | |||
960 | err = -EINVAL; | ||
961 | if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) { | ||
962 | NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); | ||
963 | goto out; | ||
964 | } | ||
965 | nh->fib_nh_scope = res.scope; | ||
966 | nh->fib_nh_oif = FIB_RES_OIF(res); | ||
967 | nh->fib_nh_dev = dev = FIB_RES_DEV(res); | ||
968 | if (!dev) { | ||
969 | NL_SET_ERR_MSG(extack, | ||
970 | "No egress device for nexthop gateway"); | ||
971 | goto out; | ||
972 | } | ||
973 | dev_hold(dev); | ||
974 | if (!netif_carrier_ok(dev)) | ||
975 | nh->fib_nh_flags |= RTNH_F_LINKDOWN; | ||
976 | err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; | ||
1011 | out: | 977 | out: |
1012 | rcu_read_unlock(); | 978 | rcu_read_unlock(); |
1013 | return err; | 979 | return err; |
1014 | } | 980 | } |
1015 | 981 | ||
982 | static int fib_check_nh_nongw(struct net *net, struct fib_nh *nh, | ||
983 | struct netlink_ext_ack *extack) | ||
984 | { | ||
985 | struct in_device *in_dev; | ||
986 | int err; | ||
987 | |||
988 | if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) { | ||
989 | NL_SET_ERR_MSG(extack, | ||
990 | "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set"); | ||
991 | return -EINVAL; | ||
992 | } | ||
993 | |||
994 | rcu_read_lock(); | ||
995 | |||
996 | err = -ENODEV; | ||
997 | in_dev = inetdev_by_index(net, nh->fib_nh_oif); | ||
998 | if (!in_dev) | ||
999 | goto out; | ||
1000 | err = -ENETDOWN; | ||
1001 | if (!(in_dev->dev->flags & IFF_UP)) { | ||
1002 | NL_SET_ERR_MSG(extack, "Device for nexthop is not up"); | ||
1003 | goto out; | ||
1004 | } | ||
1005 | |||
1006 | nh->fib_nh_dev = in_dev->dev; | ||
1007 | dev_hold(nh->fib_nh_dev); | ||
1008 | nh->fib_nh_scope = RT_SCOPE_HOST; | ||
1009 | if (!netif_carrier_ok(nh->fib_nh_dev)) | ||
1010 | nh->fib_nh_flags |= RTNH_F_LINKDOWN; | ||
1011 | err = 0; | ||
1012 | out: | ||
1013 | rcu_read_unlock(); | ||
1014 | return err; | ||
1015 | } | ||
1016 | |||
1017 | static int fib_check_nh(struct fib_config *cfg, struct fib_nh *nh, | ||
1018 | struct netlink_ext_ack *extack) | ||
1019 | { | ||
1020 | struct net *net = cfg->fc_nlinfo.nl_net; | ||
1021 | u32 table = cfg->fc_table; | ||
1022 | int err; | ||
1023 | |||
1024 | if (nh->fib_nh_gw_family == AF_INET) | ||
1025 | err = fib_check_nh_v4_gw(net, nh, table, cfg->fc_scope, extack); | ||
1026 | else | ||
1027 | err = fib_check_nh_nongw(net, nh, extack); | ||
1028 | |||
1029 | return err; | ||
1030 | } | ||
1031 | |||
1016 | static inline unsigned int fib_laddr_hashfn(__be32 val) | 1032 | static inline unsigned int fib_laddr_hashfn(__be32 val) |
1017 | { | 1033 | { |
1018 | unsigned int mask = (fib_info_hash_size - 1); | 1034 | unsigned int mask = (fib_info_hash_size - 1); |