aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2012-01-31 00:18:33 -0500
committerDavid S. Miller <davem@davemloft.net>2012-01-31 12:14:00 -0500
commita915da9b69273815527ccb3789421cb7027b545b (patch)
tree79b266da33febc50bc54adc033ac9e38a1750bcf /net/ipv4
parenta2d91241a80ec9bbc5ab24b9a2c4d730b3fa5730 (diff)
tcp: md5: rcu conversion
In order to be able to support proper RST messages for TCP MD5 flows, we need to allow access to MD5 keys without locking listener socket. This conversion is a nice cleanup, and shrinks size of timewait sockets by 80 bytes. IPv6 code reuses generic code found in IPv4 instead of duplicating it. Control path uses GFP_KERNEL allocations instead of GFP_ATOMIC. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Cc: Shawn Lu <shawn.lu@ericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/tcp_ipv4.c227
-rw-r--r--net/ipv4/tcp_minisocks.c12
2 files changed, 99 insertions, 140 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 345e24928fa6..1d5fd82c5c08 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -90,16 +90,8 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency);
90 90
91 91
92#ifdef CONFIG_TCP_MD5SIG 92#ifdef CONFIG_TCP_MD5SIG
93static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, 93static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
94 __be32 addr);
95static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
96 __be32 daddr, __be32 saddr, const struct tcphdr *th); 94 __be32 daddr, __be32 saddr, const struct tcphdr *th);
97#else
98static inline
99struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
100{
101 return NULL;
102}
103#endif 95#endif
104 96
105struct inet_hashinfo tcp_hashinfo; 97struct inet_hashinfo tcp_hashinfo;
@@ -631,7 +623,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
631 arg.iov[0].iov_len = sizeof(rep.th); 623 arg.iov[0].iov_len = sizeof(rep.th);
632 624
633#ifdef CONFIG_TCP_MD5SIG 625#ifdef CONFIG_TCP_MD5SIG
634 key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL; 626 key = sk ? tcp_md5_do_lookup(sk,
627 (union tcp_md5_addr *)&ip_hdr(skb)->saddr,
628 AF_INET) : NULL;
635 if (key) { 629 if (key) {
636 rep.opt[0] = htonl((TCPOPT_NOP << 24) | 630 rep.opt[0] = htonl((TCPOPT_NOP << 24) |
637 (TCPOPT_NOP << 16) | 631 (TCPOPT_NOP << 16) |
@@ -759,7 +753,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
759 tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, 753 tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
760 req->ts_recent, 754 req->ts_recent,
761 0, 755 0,
762 tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr), 756 tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr,
757 AF_INET),
763 inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, 758 inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
764 ip_hdr(skb)->tos); 759 ip_hdr(skb)->tos);
765} 760}
@@ -876,146 +871,124 @@ static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk,
876 */ 871 */
877 872
878/* Find the Key structure for an address. */ 873/* Find the Key structure for an address. */
879static struct tcp_md5sig_key * 874struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
880 tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) 875 const union tcp_md5_addr *addr,
876 int family)
881{ 877{
882 struct tcp_sock *tp = tcp_sk(sk); 878 struct tcp_sock *tp = tcp_sk(sk);
883 int i; 879 struct tcp_md5sig_key *key;
880 struct hlist_node *pos;
881 unsigned int size = sizeof(struct in_addr);
884 882
885 if (!tp->md5sig_info || !tp->md5sig_info->entries4) 883 if (!tp->md5sig_info)
886 return NULL; 884 return NULL;
887 for (i = 0; i < tp->md5sig_info->entries4; i++) { 885#if IS_ENABLED(CONFIG_IPV6)
888 if (tp->md5sig_info->keys4[i].addr == addr) 886 if (family == AF_INET6)
889 return &tp->md5sig_info->keys4[i].base; 887 size = sizeof(struct in6_addr);
888#endif
889 hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) {
890 if (key->family != family)
891 continue;
892 if (!memcmp(&key->addr, addr, size))
893 return key;
890 } 894 }
891 return NULL; 895 return NULL;
892} 896}
897EXPORT_SYMBOL(tcp_md5_do_lookup);
893 898
894struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, 899struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
895 struct sock *addr_sk) 900 struct sock *addr_sk)
896{ 901{
897 return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->inet_daddr); 902 union tcp_md5_addr *addr;
903
904 addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr;
905 return tcp_md5_do_lookup(sk, addr, AF_INET);
898} 906}
899EXPORT_SYMBOL(tcp_v4_md5_lookup); 907EXPORT_SYMBOL(tcp_v4_md5_lookup);
900 908
901static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, 909static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
902 struct request_sock *req) 910 struct request_sock *req)
903{ 911{
904 return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr); 912 union tcp_md5_addr *addr;
913
914 addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr;
915 return tcp_md5_do_lookup(sk, addr, AF_INET);
905} 916}
906 917
907/* This can be called on a newly created socket, from other files */ 918/* This can be called on a newly created socket, from other files */
908int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, 919int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
909 u8 *newkey, u8 newkeylen) 920 int family, const u8 *newkey, u8 newkeylen, gfp_t gfp)
910{ 921{
911 /* Add Key to the list */ 922 /* Add Key to the list */
912 struct tcp_md5sig_key *key; 923 struct tcp_md5sig_key *key;
913 struct tcp_sock *tp = tcp_sk(sk); 924 struct tcp_sock *tp = tcp_sk(sk);
914 struct tcp4_md5sig_key *keys; 925 struct tcp_md5sig_info *md5sig;
915 926
916 key = tcp_v4_md5_do_lookup(sk, addr); 927 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
917 if (key) { 928 if (key) {
918 /* Pre-existing entry - just update that one. */ 929 /* Pre-existing entry - just update that one. */
919 kfree(key->key); 930 memcpy(key->key, newkey, newkeylen);
920 key->key = newkey;
921 key->keylen = newkeylen; 931 key->keylen = newkeylen;
922 } else { 932 return 0;
923 struct tcp_md5sig_info *md5sig; 933 }
924
925 if (!tp->md5sig_info) {
926 tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info),
927 GFP_ATOMIC);
928 if (!tp->md5sig_info) {
929 kfree(newkey);
930 return -ENOMEM;
931 }
932 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
933 }
934 934
935 md5sig = tp->md5sig_info; 935 md5sig = tp->md5sig_info;
936 if (md5sig->entries4 == 0 && 936 if (!md5sig) {
937 tcp_alloc_md5sig_pool(sk) == NULL) { 937 md5sig = kmalloc(sizeof(*md5sig), gfp);
938 kfree(newkey); 938 if (!md5sig)
939 return -ENOMEM; 939 return -ENOMEM;
940 }
941
942 if (md5sig->alloced4 == md5sig->entries4) {
943 keys = kmalloc((sizeof(*keys) *
944 (md5sig->entries4 + 1)), GFP_ATOMIC);
945 if (!keys) {
946 kfree(newkey);
947 if (md5sig->entries4 == 0)
948 tcp_free_md5sig_pool();
949 return -ENOMEM;
950 }
951 940
952 if (md5sig->entries4) 941 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
953 memcpy(keys, md5sig->keys4, 942 INIT_HLIST_HEAD(&md5sig->head);
954 sizeof(*keys) * md5sig->entries4); 943 tp->md5sig_info = md5sig;
944 }
955 945
956 /* Free old key list, and reference new one */ 946 key = kmalloc(sizeof(*key), gfp);
957 kfree(md5sig->keys4); 947 if (!key)
958 md5sig->keys4 = keys; 948 return -ENOMEM;
959 md5sig->alloced4++; 949 if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) {
960 } 950 kfree(key);
961 md5sig->entries4++; 951 return -ENOMEM;
962 md5sig->keys4[md5sig->entries4 - 1].addr = addr;
963 md5sig->keys4[md5sig->entries4 - 1].base.key = newkey;
964 md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen;
965 } 952 }
953
954 memcpy(key->key, newkey, newkeylen);
955 key->keylen = newkeylen;
956 key->family = family;
957 memcpy(&key->addr, addr,
958 (family == AF_INET6) ? sizeof(struct in6_addr) :
959 sizeof(struct in_addr));
960 hlist_add_head_rcu(&key->node, &md5sig->head);
966 return 0; 961 return 0;
967} 962}
968EXPORT_SYMBOL(tcp_v4_md5_do_add); 963EXPORT_SYMBOL(tcp_md5_do_add);
969 964
970int tcp_v4_md5_do_del(struct sock *sk, __be32 addr) 965int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
971{ 966{
972 struct tcp_sock *tp = tcp_sk(sk); 967 struct tcp_sock *tp = tcp_sk(sk);
973 int i; 968 struct tcp_md5sig_key *key;
974 969
975 for (i = 0; i < tp->md5sig_info->entries4; i++) { 970 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
976 if (tp->md5sig_info->keys4[i].addr == addr) { 971 if (!key)
977 /* Free the key */ 972 return -ENOENT;
978 kfree(tp->md5sig_info->keys4[i].base.key); 973 hlist_del_rcu(&key->node);
979 tp->md5sig_info->entries4--; 974 kfree_rcu(key, rcu);
980 975 if (hlist_empty(&tp->md5sig_info->head))
981 if (tp->md5sig_info->entries4 == 0) { 976 tcp_free_md5sig_pool();
982 kfree(tp->md5sig_info->keys4); 977 return 0;
983 tp->md5sig_info->keys4 = NULL;
984 tp->md5sig_info->alloced4 = 0;
985 tcp_free_md5sig_pool();
986 } else if (tp->md5sig_info->entries4 != i) {
987 /* Need to do some manipulation */
988 memmove(&tp->md5sig_info->keys4[i],
989 &tp->md5sig_info->keys4[i+1],
990 (tp->md5sig_info->entries4 - i) *
991 sizeof(struct tcp4_md5sig_key));
992 }
993 return 0;
994 }
995 }
996 return -ENOENT;
997} 978}
998EXPORT_SYMBOL(tcp_v4_md5_do_del); 979EXPORT_SYMBOL(tcp_md5_do_del);
999 980
1000static void tcp_v4_clear_md5_list(struct sock *sk) 981void tcp_clear_md5_list(struct sock *sk)
1001{ 982{
1002 struct tcp_sock *tp = tcp_sk(sk); 983 struct tcp_sock *tp = tcp_sk(sk);
984 struct tcp_md5sig_key *key;
985 struct hlist_node *pos, *n;
1003 986
1004 /* Free each key, then the set of key keys, 987 if (!hlist_empty(&tp->md5sig_info->head))
1005 * the crypto element, and then decrement our
1006 * hold on the last resort crypto.
1007 */
1008 if (tp->md5sig_info->entries4) {
1009 int i;
1010 for (i = 0; i < tp->md5sig_info->entries4; i++)
1011 kfree(tp->md5sig_info->keys4[i].base.key);
1012 tp->md5sig_info->entries4 = 0;
1013 tcp_free_md5sig_pool(); 988 tcp_free_md5sig_pool();
1014 } 989 hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) {
1015 if (tp->md5sig_info->keys4) { 990 hlist_del_rcu(&key->node);
1016 kfree(tp->md5sig_info->keys4); 991 kfree_rcu(key, rcu);
1017 tp->md5sig_info->keys4 = NULL;
1018 tp->md5sig_info->alloced4 = 0;
1019 } 992 }
1020} 993}
1021 994
@@ -1024,7 +997,6 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
1024{ 997{
1025 struct tcp_md5sig cmd; 998 struct tcp_md5sig cmd;
1026 struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr; 999 struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr;
1027 u8 *newkey;
1028 1000
1029 if (optlen < sizeof(cmd)) 1001 if (optlen < sizeof(cmd))
1030 return -EINVAL; 1002 return -EINVAL;
@@ -1038,29 +1010,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
1038 if (!cmd.tcpm_key || !cmd.tcpm_keylen) { 1010 if (!cmd.tcpm_key || !cmd.tcpm_keylen) {
1039 if (!tcp_sk(sk)->md5sig_info) 1011 if (!tcp_sk(sk)->md5sig_info)
1040 return -ENOENT; 1012 return -ENOENT;
1041 return tcp_v4_md5_do_del(sk, sin->sin_addr.s_addr); 1013 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
1014 AF_INET);
1042 } 1015 }
1043 1016
1044 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) 1017 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
1045 return -EINVAL; 1018 return -EINVAL;
1046 1019
1047 if (!tcp_sk(sk)->md5sig_info) { 1020 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
1048 struct tcp_sock *tp = tcp_sk(sk); 1021 AF_INET, cmd.tcpm_key, cmd.tcpm_keylen,
1049 struct tcp_md5sig_info *p; 1022 GFP_KERNEL);
1050
1051 p = kzalloc(sizeof(*p), sk->sk_allocation);
1052 if (!p)
1053 return -EINVAL;
1054
1055 tp->md5sig_info = p;
1056 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
1057 }
1058
1059 newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation);
1060 if (!newkey)
1061 return -ENOMEM;
1062 return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr,
1063 newkey, cmd.tcpm_keylen);
1064} 1023}
1065 1024
1066static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, 1025static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
@@ -1086,7 +1045,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
1086 return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp)); 1045 return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
1087} 1046}
1088 1047
1089static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, 1048static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
1090 __be32 daddr, __be32 saddr, const struct tcphdr *th) 1049 __be32 daddr, __be32 saddr, const struct tcphdr *th)
1091{ 1050{
1092 struct tcp_md5sig_pool *hp; 1051 struct tcp_md5sig_pool *hp;
@@ -1186,7 +1145,8 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
1186 int genhash; 1145 int genhash;
1187 unsigned char newhash[16]; 1146 unsigned char newhash[16];
1188 1147
1189 hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); 1148 hash_expected = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr,
1149 AF_INET);
1190 hash_location = tcp_parse_md5sig_option(th); 1150 hash_location = tcp_parse_md5sig_option(th);
1191 1151
1192 /* We've parsed the options - do we have a hash? */ 1152 /* We've parsed the options - do we have a hash? */
@@ -1474,7 +1434,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
1474 1434
1475#ifdef CONFIG_TCP_MD5SIG 1435#ifdef CONFIG_TCP_MD5SIG
1476 /* Copy over the MD5 key from the original socket */ 1436 /* Copy over the MD5 key from the original socket */
1477 key = tcp_v4_md5_do_lookup(sk, newinet->inet_daddr); 1437 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr,
1438 AF_INET);
1478 if (key != NULL) { 1439 if (key != NULL) {
1479 /* 1440 /*
1480 * We're using one, so create a matching key 1441 * We're using one, so create a matching key
@@ -1482,10 +1443,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
1482 * memory, then we end up not copying the key 1443 * memory, then we end up not copying the key
1483 * across. Shucks. 1444 * across. Shucks.
1484 */ 1445 */
1485 char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); 1446 tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr,
1486 if (newkey != NULL) 1447 AF_INET, key->key, key->keylen, GFP_ATOMIC);
1487 tcp_v4_md5_do_add(newsk, newinet->inet_daddr,
1488 newkey, key->keylen);
1489 sk_nocaps_add(newsk, NETIF_F_GSO_MASK); 1448 sk_nocaps_add(newsk, NETIF_F_GSO_MASK);
1490 } 1449 }
1491#endif 1450#endif
@@ -1934,7 +1893,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
1934#ifdef CONFIG_TCP_MD5SIG 1893#ifdef CONFIG_TCP_MD5SIG
1935 /* Clean up the MD5 key list, if any */ 1894 /* Clean up the MD5 key list, if any */
1936 if (tp->md5sig_info) { 1895 if (tp->md5sig_info) {
1937 tcp_v4_clear_md5_list(sk); 1896 tcp_clear_md5_list(sk);
1938 kfree(tp->md5sig_info); 1897 kfree(tp->md5sig_info);
1939 tp->md5sig_info = NULL; 1898 tp->md5sig_info = NULL;
1940 } 1899 }
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 550e755747e0..3cabafb5cdd1 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -359,13 +359,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
359 */ 359 */
360 do { 360 do {
361 struct tcp_md5sig_key *key; 361 struct tcp_md5sig_key *key;
362 memset(tcptw->tw_md5_key, 0, sizeof(tcptw->tw_md5_key)); 362 tcptw->tw_md5_key = NULL;
363 tcptw->tw_md5_keylen = 0;
364 key = tp->af_specific->md5_lookup(sk, sk); 363 key = tp->af_specific->md5_lookup(sk, sk);
365 if (key != NULL) { 364 if (key != NULL) {
366 memcpy(&tcptw->tw_md5_key, key->key, key->keylen); 365 tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
367 tcptw->tw_md5_keylen = key->keylen; 366 if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL)
368 if (tcp_alloc_md5sig_pool(sk) == NULL)
369 BUG(); 367 BUG();
370 } 368 }
371 } while (0); 369 } while (0);
@@ -405,8 +403,10 @@ void tcp_twsk_destructor(struct sock *sk)
405{ 403{
406#ifdef CONFIG_TCP_MD5SIG 404#ifdef CONFIG_TCP_MD5SIG
407 struct tcp_timewait_sock *twsk = tcp_twsk(sk); 405 struct tcp_timewait_sock *twsk = tcp_twsk(sk);
408 if (twsk->tw_md5_keylen) 406 if (twsk->tw_md5_key) {
409 tcp_free_md5sig_pool(); 407 tcp_free_md5sig_pool();
408 kfree_rcu(twsk->tw_md5_key, rcu);
409 }
410#endif 410#endif
411} 411}
412EXPORT_SYMBOL_GPL(tcp_twsk_destructor); 412EXPORT_SYMBOL_GPL(tcp_twsk_destructor);