diff options
author | Ivan Delalande <colona@arista.com> | 2017-06-15 21:07:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-06-19 13:50:55 -0400 |
commit | 6797318e623da68dfbacd0cb5c246f5ecd2baf6e (patch) | |
tree | 64cb66cb77576371cd085f8208f86e2be91cb9db /net/ipv4/tcp_ipv4.c | |
parent | 910603818c6c0558fe9b5e056a3bd5195aaae1a5 (diff) |
tcp: md5: add an address prefix for key lookup
This allows the keys used for TCP MD5 signature to be used for whole
range of addresses, specified with a prefix length, instead of only one
address as it currently is.
Signed-off-by: Bob Gilligan <gilligan@arista.com>
Signed-off-by: Eric Mowat <mowat@arista.com>
Signed-off-by: Ivan Delalande <colona@arista.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 68 |
1 files changed, 59 insertions, 9 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index eec2ff907279..a3c67866b780 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -80,6 +80,7 @@ | |||
80 | #include <linux/stddef.h> | 80 | #include <linux/stddef.h> |
81 | #include <linux/proc_fs.h> | 81 | #include <linux/proc_fs.h> |
82 | #include <linux/seq_file.h> | 82 | #include <linux/seq_file.h> |
83 | #include <linux/inetdevice.h> | ||
83 | 84 | ||
84 | #include <crypto/hash.h> | 85 | #include <crypto/hash.h> |
85 | #include <linux/scatterlist.h> | 86 | #include <linux/scatterlist.h> |
@@ -908,6 +909,9 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk, | |||
908 | struct tcp_md5sig_key *key; | 909 | struct tcp_md5sig_key *key; |
909 | unsigned int size = sizeof(struct in_addr); | 910 | unsigned int size = sizeof(struct in_addr); |
910 | const struct tcp_md5sig_info *md5sig; | 911 | const struct tcp_md5sig_info *md5sig; |
912 | __be32 mask; | ||
913 | struct tcp_md5sig_key *best_match = NULL; | ||
914 | bool match; | ||
911 | 915 | ||
912 | /* caller either holds rcu_read_lock() or socket lock */ | 916 | /* caller either holds rcu_read_lock() or socket lock */ |
913 | md5sig = rcu_dereference_check(tp->md5sig_info, | 917 | md5sig = rcu_dereference_check(tp->md5sig_info, |
@@ -921,12 +925,55 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk, | |||
921 | hlist_for_each_entry_rcu(key, &md5sig->head, node) { | 925 | hlist_for_each_entry_rcu(key, &md5sig->head, node) { |
922 | if (key->family != family) | 926 | if (key->family != family) |
923 | continue; | 927 | continue; |
924 | if (!memcmp(&key->addr, addr, size)) | 928 | |
929 | if (family == AF_INET) { | ||
930 | mask = inet_make_mask(key->prefixlen); | ||
931 | match = (key->addr.a4.s_addr & mask) == | ||
932 | (addr->a4.s_addr & mask); | ||
933 | #if IS_ENABLED(CONFIG_IPV6) | ||
934 | } else if (family == AF_INET6) { | ||
935 | match = ipv6_prefix_equal(&key->addr.a6, &addr->a6, | ||
936 | key->prefixlen); | ||
937 | #endif | ||
938 | } else { | ||
939 | match = false; | ||
940 | } | ||
941 | |||
942 | if (match && (!best_match || | ||
943 | key->prefixlen > best_match->prefixlen)) | ||
944 | best_match = key; | ||
945 | } | ||
946 | return best_match; | ||
947 | } | ||
948 | EXPORT_SYMBOL(tcp_md5_do_lookup); | ||
949 | |||
950 | struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk, | ||
951 | const union tcp_md5_addr *addr, | ||
952 | int family, u8 prefixlen) | ||
953 | { | ||
954 | const struct tcp_sock *tp = tcp_sk(sk); | ||
955 | struct tcp_md5sig_key *key; | ||
956 | unsigned int size = sizeof(struct in_addr); | ||
957 | const struct tcp_md5sig_info *md5sig; | ||
958 | |||
959 | /* caller either holds rcu_read_lock() or socket lock */ | ||
960 | md5sig = rcu_dereference_check(tp->md5sig_info, | ||
961 | lockdep_sock_is_held(sk)); | ||
962 | if (!md5sig) | ||
963 | return NULL; | ||
964 | #if IS_ENABLED(CONFIG_IPV6) | ||
965 | if (family == AF_INET6) | ||
966 | size = sizeof(struct in6_addr); | ||
967 | #endif | ||
968 | hlist_for_each_entry_rcu(key, &md5sig->head, node) { | ||
969 | if (key->family != family) | ||
970 | continue; | ||
971 | if (!memcmp(&key->addr, addr, size) && | ||
972 | key->prefixlen == prefixlen) | ||
925 | return key; | 973 | return key; |
926 | } | 974 | } |
927 | return NULL; | 975 | return NULL; |
928 | } | 976 | } |
929 | EXPORT_SYMBOL(tcp_md5_do_lookup); | ||
930 | 977 | ||
931 | struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, | 978 | struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, |
932 | const struct sock *addr_sk) | 979 | const struct sock *addr_sk) |
@@ -940,14 +987,15 @@ EXPORT_SYMBOL(tcp_v4_md5_lookup); | |||
940 | 987 | ||
941 | /* This can be called on a newly created socket, from other files */ | 988 | /* This can be called on a newly created socket, from other files */ |
942 | int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, | 989 | int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, |
943 | int family, const u8 *newkey, u8 newkeylen, gfp_t gfp) | 990 | int family, u8 prefixlen, const u8 *newkey, u8 newkeylen, |
991 | gfp_t gfp) | ||
944 | { | 992 | { |
945 | /* Add Key to the list */ | 993 | /* Add Key to the list */ |
946 | struct tcp_md5sig_key *key; | 994 | struct tcp_md5sig_key *key; |
947 | struct tcp_sock *tp = tcp_sk(sk); | 995 | struct tcp_sock *tp = tcp_sk(sk); |
948 | struct tcp_md5sig_info *md5sig; | 996 | struct tcp_md5sig_info *md5sig; |
949 | 997 | ||
950 | key = tcp_md5_do_lookup(sk, addr, family); | 998 | key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen); |
951 | if (key) { | 999 | if (key) { |
952 | /* Pre-existing entry - just update that one. */ | 1000 | /* Pre-existing entry - just update that one. */ |
953 | memcpy(key->key, newkey, newkeylen); | 1001 | memcpy(key->key, newkey, newkeylen); |
@@ -978,6 +1026,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, | |||
978 | memcpy(key->key, newkey, newkeylen); | 1026 | memcpy(key->key, newkey, newkeylen); |
979 | key->keylen = newkeylen; | 1027 | key->keylen = newkeylen; |
980 | key->family = family; | 1028 | key->family = family; |
1029 | key->prefixlen = prefixlen; | ||
981 | memcpy(&key->addr, addr, | 1030 | memcpy(&key->addr, addr, |
982 | (family == AF_INET6) ? sizeof(struct in6_addr) : | 1031 | (family == AF_INET6) ? sizeof(struct in6_addr) : |
983 | sizeof(struct in_addr)); | 1032 | sizeof(struct in_addr)); |
@@ -986,11 +1035,12 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, | |||
986 | } | 1035 | } |
987 | EXPORT_SYMBOL(tcp_md5_do_add); | 1036 | EXPORT_SYMBOL(tcp_md5_do_add); |
988 | 1037 | ||
989 | int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) | 1038 | int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, |
1039 | u8 prefixlen) | ||
990 | { | 1040 | { |
991 | struct tcp_md5sig_key *key; | 1041 | struct tcp_md5sig_key *key; |
992 | 1042 | ||
993 | key = tcp_md5_do_lookup(sk, addr, family); | 1043 | key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen); |
994 | if (!key) | 1044 | if (!key) |
995 | return -ENOENT; | 1045 | return -ENOENT; |
996 | hlist_del_rcu(&key->node); | 1046 | hlist_del_rcu(&key->node); |
@@ -1033,13 +1083,13 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, | |||
1033 | 1083 | ||
1034 | if (!cmd.tcpm_keylen) | 1084 | if (!cmd.tcpm_keylen) |
1035 | return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, | 1085 | return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, |
1036 | AF_INET); | 1086 | AF_INET, 32); |
1037 | 1087 | ||
1038 | if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) | 1088 | if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) |
1039 | return -EINVAL; | 1089 | return -EINVAL; |
1040 | 1090 | ||
1041 | return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, | 1091 | return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, |
1042 | AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, | 1092 | AF_INET, 32, cmd.tcpm_key, cmd.tcpm_keylen, |
1043 | GFP_KERNEL); | 1093 | GFP_KERNEL); |
1044 | } | 1094 | } |
1045 | 1095 | ||
@@ -1342,7 +1392,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, | |||
1342 | * across. Shucks. | 1392 | * across. Shucks. |
1343 | */ | 1393 | */ |
1344 | tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr, | 1394 | tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr, |
1345 | AF_INET, key->key, key->keylen, GFP_ATOMIC); | 1395 | AF_INET, 32, key->key, key->keylen, GFP_ATOMIC); |
1346 | sk_nocaps_add(newsk, NETIF_F_GSO_MASK); | 1396 | sk_nocaps_add(newsk, NETIF_F_GSO_MASK); |
1347 | } | 1397 | } |
1348 | #endif | 1398 | #endif |