Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Ivan Delalande and committed by
David S. Miller
6797318e 91060381

+70 -16
+4 -2
include/net/tcp.h
··· 1441 1441 u8 keylen; 1442 1442 u8 family; /* AF_INET or AF_INET6 */ 1443 1443 union tcp_md5_addr addr; 1444 + u8 prefixlen; 1444 1445 u8 key[TCP_MD5SIG_MAXKEYLEN]; 1445 1446 struct rcu_head rcu; 1446 1447 }; ··· 1485 1484 int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, 1486 1485 const struct sock *sk, const struct sk_buff *skb); 1487 1486 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, 1488 - int family, const u8 *newkey, u8 newkeylen, gfp_t gfp); 1487 + int family, u8 prefixlen, const u8 *newkey, u8 newkeylen, 1488 + gfp_t gfp); 1489 1489 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, 1490 - int family); 1490 + int family, u8 prefixlen); 1491 1491 struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, 1492 1492 const struct sock *addr_sk); 1493 1493
+59 -9
net/ipv4/tcp_ipv4.c
··· 80 80 #include <linux/stddef.h> 81 81 #include <linux/proc_fs.h> 82 82 #include <linux/seq_file.h> 83 + #include <linux/inetdevice.h> 83 84 84 85 #include <crypto/hash.h> 85 86 #include <linux/scatterlist.h> ··· 909 908 struct tcp_md5sig_key *key; 910 909 unsigned int size = sizeof(struct in_addr); 911 910 const struct tcp_md5sig_info *md5sig; 911 + __be32 mask; 912 + struct tcp_md5sig_key *best_match = NULL; 913 + bool match; 912 914 913 915 /* caller either holds rcu_read_lock() or socket lock */ 914 916 md5sig = rcu_dereference_check(tp->md5sig_info, ··· 925 921 hlist_for_each_entry_rcu(key, &md5sig->head, node) { 926 922 if (key->family != family) 927 923 continue; 928 - if (!memcmp(&key->addr, addr, size)) 924 + 925 + if (family == AF_INET) { 926 + mask = inet_make_mask(key->prefixlen); 927 + match = (key->addr.a4.s_addr & mask) == 928 + (addr->a4.s_addr & mask); 929 + #if IS_ENABLED(CONFIG_IPV6) 930 + } else if (family == AF_INET6) { 931 + match = ipv6_prefix_equal(&key->addr.a6, &addr->a6, 932 + key->prefixlen); 933 + #endif 934 + } else { 935 + match = false; 936 + } 937 + 938 + if (match && (!best_match || 939 + key->prefixlen > best_match->prefixlen)) 940 + best_match = key; 941 + } 942 + return best_match; 943 + } 944 + EXPORT_SYMBOL(tcp_md5_do_lookup); 945 + 946 + struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk, 947 + const union tcp_md5_addr *addr, 948 + int family, u8 prefixlen) 949 + { 950 + const struct tcp_sock *tp = tcp_sk(sk); 951 + struct tcp_md5sig_key *key; 952 + unsigned int size = sizeof(struct in_addr); 953 + const struct tcp_md5sig_info *md5sig; 954 + 955 + /* caller either holds rcu_read_lock() or socket lock */ 956 + md5sig = rcu_dereference_check(tp->md5sig_info, 957 + lockdep_sock_is_held(sk)); 958 + if (!md5sig) 959 + return NULL; 960 + #if IS_ENABLED(CONFIG_IPV6) 961 + if (family == AF_INET6) 962 + size = sizeof(struct in6_addr); 963 + #endif 964 + hlist_for_each_entry_rcu(key, &md5sig->head, node) { 965 + if (key->family != family) 966 + continue; 967 + if (!memcmp(&key->addr, addr, size) && 968 + key->prefixlen == prefixlen) 929 969 return key; 930 970 } 931 971 return NULL; 932 972 } 933 - EXPORT_SYMBOL(tcp_md5_do_lookup); 934 973 935 974 struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, 936 975 const struct sock *addr_sk) ··· 987 940 988 941 /* This can be called on a newly created socket, from other files */ 989 942 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, 990 - int family, const u8 *newkey, u8 newkeylen, gfp_t gfp) 943 + int family, u8 prefixlen, const u8 *newkey, u8 newkeylen, 944 + gfp_t gfp) 991 945 { 992 946 /* Add Key to the list */ 993 947 struct tcp_md5sig_key *key; 994 948 struct tcp_sock *tp = tcp_sk(sk); 995 949 struct tcp_md5sig_info *md5sig; 996 950 997 - key = tcp_md5_do_lookup(sk, addr, family); 951 + key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen); 998 952 if (key) { 999 953 /* Pre-existing entry - just update that one. */ 1000 954 memcpy(key->key, newkey, newkeylen); ··· 1026 978 memcpy(key->key, newkey, newkeylen); 1027 979 key->keylen = newkeylen; 1028 980 key->family = family; 981 + key->prefixlen = prefixlen; 1029 982 memcpy(&key->addr, addr, 1030 983 (family == AF_INET6) ? sizeof(struct in6_addr) : 1031 984 sizeof(struct in_addr)); ··· 1035 986 } 1036 987 EXPORT_SYMBOL(tcp_md5_do_add); 1037 988 1038 - int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) 989 + int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, 990 + u8 prefixlen) 1039 991 { 1040 992 struct tcp_md5sig_key *key; 1041 993 1042 - key = tcp_md5_do_lookup(sk, addr, family); 994 + key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen); 1043 995 if (!key) 1044 996 return -ENOENT; 1045 997 hlist_del_rcu(&key->node); ··· 1083 1033 1084 1034 if (!cmd.tcpm_keylen) 1085 1035 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, 1086 - AF_INET); 1036 + AF_INET, 32); 1087 1037 1088 1038 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) 1089 1039 return -EINVAL; 1090 1040 1091 1041 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, 1092 - AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, 1042 + AF_INET, 32, cmd.tcpm_key, cmd.tcpm_keylen, 1093 1043 GFP_KERNEL); 1094 1044 } 1095 1045 ··· 1392 1342 * across. Shucks. 1393 1343 */ 1394 1344 tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr, 1395 - AF_INET, key->key, key->keylen, GFP_ATOMIC); 1345 + AF_INET, 32, key->key, key->keylen, GFP_ATOMIC); 1396 1346 sk_nocaps_add(newsk, NETIF_F_GSO_MASK); 1397 1347 } 1398 1348 #endif
+7 -5
net/ipv6/tcp_ipv6.c
··· 533 533 if (!cmd.tcpm_keylen) { 534 534 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) 535 535 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], 536 - AF_INET); 536 + AF_INET, 32); 537 537 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr, 538 - AF_INET6); 538 + AF_INET6, 128); 539 539 } 540 540 541 541 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) ··· 543 543 544 544 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) 545 545 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], 546 - AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); 546 + AF_INET, 32, cmd.tcpm_key, 547 + cmd.tcpm_keylen, GFP_KERNEL); 547 548 548 549 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, 549 - AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); 550 + AF_INET6, 128, cmd.tcpm_key, cmd.tcpm_keylen, 551 + GFP_KERNEL); 550 552 } 551 553 552 554 static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, ··· 1188 1186 * across. Shucks. 1189 1187 */ 1190 1188 tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, 1191 - AF_INET6, key->key, key->keylen, 1189 + AF_INET6, 128, key->key, key->keylen, 1192 1190 sk_gfp_mask(sk, GFP_ATOMIC)); 1193 1191 } 1194 1192 #endif