summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/tcp.h2
-rw-r--r--include/net/tcp.h1
-rw-r--r--net/ipv4/tcp_ipv4.c32
-rw-r--r--net/ipv6/tcp_ipv6.c2
4 files changed, 22 insertions, 15 deletions
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index c2025f159641..115389e9b945 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -463,7 +463,7 @@ struct tcp_sock {
463 const struct tcp_sock_af_ops *af_specific; 463 const struct tcp_sock_af_ops *af_specific;
464 464
465/* TCP MD5 Signature Option information */ 465/* TCP MD5 Signature Option information */
466 struct tcp_md5sig_info *md5sig_info; 466 struct tcp_md5sig_info __rcu *md5sig_info;
467#endif 467#endif
468 468
469 /* When the cookie options are generated and exchanged, then this 469 /* When the cookie options are generated and exchanged, then this
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 10ae4c7b6b4f..78880ba0f560 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1150,6 +1150,7 @@ struct tcp_md5sig_key {
1150/* - sock block */ 1150/* - sock block */
1151struct tcp_md5sig_info { 1151struct tcp_md5sig_info {
1152 struct hlist_head head; 1152 struct hlist_head head;
1153 struct rcu_head rcu;
1153}; 1154};
1154 1155
1155/* - pseudo header */ 1156/* - pseudo header */
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index da5d3226771b..567cca9b30df 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -879,14 +879,18 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
879 struct tcp_md5sig_key *key; 879 struct tcp_md5sig_key *key;
880 struct hlist_node *pos; 880 struct hlist_node *pos;
881 unsigned int size = sizeof(struct in_addr); 881 unsigned int size = sizeof(struct in_addr);
882 struct tcp_md5sig_info *md5sig;
882 883
883 if (!tp->md5sig_info) 884 /* caller either holds rcu_read_lock() or socket lock */
885 md5sig = rcu_dereference_check(tp->md5sig_info,
886 sock_owned_by_user(sk));
887 if (!md5sig)
884 return NULL; 888 return NULL;
885#if IS_ENABLED(CONFIG_IPV6) 889#if IS_ENABLED(CONFIG_IPV6)
886 if (family == AF_INET6) 890 if (family == AF_INET6)
887 size = sizeof(struct in6_addr); 891 size = sizeof(struct in6_addr);
888#endif 892#endif
889 hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) { 893 hlist_for_each_entry_rcu(key, pos, &md5sig->head, node) {
890 if (key->family != family) 894 if (key->family != family)
891 continue; 895 continue;
892 if (!memcmp(&key->addr, addr, size)) 896 if (!memcmp(&key->addr, addr, size))
@@ -932,7 +936,8 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
932 return 0; 936 return 0;
933 } 937 }
934 938
935 md5sig = tp->md5sig_info; 939 md5sig = rcu_dereference_protected(tp->md5sig_info,
940 sock_owned_by_user(sk));
936 if (!md5sig) { 941 if (!md5sig) {
937 md5sig = kmalloc(sizeof(*md5sig), gfp); 942 md5sig = kmalloc(sizeof(*md5sig), gfp);
938 if (!md5sig) 943 if (!md5sig)
@@ -940,7 +945,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
940 945
941 sk_nocaps_add(sk, NETIF_F_GSO_MASK); 946 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
942 INIT_HLIST_HEAD(&md5sig->head); 947 INIT_HLIST_HEAD(&md5sig->head);
943 tp->md5sig_info = md5sig; 948 rcu_assign_pointer(tp->md5sig_info, md5sig);
944 } 949 }
945 950
946 key = sock_kmalloc(sk, sizeof(*key), gfp); 951 key = sock_kmalloc(sk, sizeof(*key), gfp);
@@ -966,6 +971,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
966{ 971{
967 struct tcp_sock *tp = tcp_sk(sk); 972 struct tcp_sock *tp = tcp_sk(sk);
968 struct tcp_md5sig_key *key; 973 struct tcp_md5sig_key *key;
974 struct tcp_md5sig_info *md5sig;
969 975
970 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); 976 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
971 if (!key) 977 if (!key)
@@ -973,7 +979,9 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
973 hlist_del_rcu(&key->node); 979 hlist_del_rcu(&key->node);
974 atomic_sub(sizeof(*key), &sk->sk_omem_alloc); 980 atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
975 kfree_rcu(key, rcu); 981 kfree_rcu(key, rcu);
976 if (hlist_empty(&tp->md5sig_info->head)) 982 md5sig = rcu_dereference_protected(tp->md5sig_info,
983 sock_owned_by_user(sk));
984 if (hlist_empty(&md5sig->head))
977 tcp_free_md5sig_pool(); 985 tcp_free_md5sig_pool();
978 return 0; 986 return 0;
979} 987}
@@ -984,10 +992,13 @@ void tcp_clear_md5_list(struct sock *sk)
984 struct tcp_sock *tp = tcp_sk(sk); 992 struct tcp_sock *tp = tcp_sk(sk);
985 struct tcp_md5sig_key *key; 993 struct tcp_md5sig_key *key;
986 struct hlist_node *pos, *n; 994 struct hlist_node *pos, *n;
995 struct tcp_md5sig_info *md5sig;
987 996
988 if (!hlist_empty(&tp->md5sig_info->head)) 997 md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
998
999 if (!hlist_empty(&md5sig->head))
989 tcp_free_md5sig_pool(); 1000 tcp_free_md5sig_pool();
990 hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) { 1001 hlist_for_each_entry_safe(key, pos, n, &md5sig->head, node) {
991 hlist_del_rcu(&key->node); 1002 hlist_del_rcu(&key->node);
992 atomic_sub(sizeof(*key), &sk->sk_omem_alloc); 1003 atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
993 kfree_rcu(key, rcu); 1004 kfree_rcu(key, rcu);
@@ -1009,12 +1020,9 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
1009 if (sin->sin_family != AF_INET) 1020 if (sin->sin_family != AF_INET)
1010 return -EINVAL; 1021 return -EINVAL;
1011 1022
1012 if (!cmd.tcpm_key || !cmd.tcpm_keylen) { 1023 if (!cmd.tcpm_key || !cmd.tcpm_keylen)
1013 if (!tcp_sk(sk)->md5sig_info)
1014 return -ENOENT;
1015 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, 1024 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
1016 AF_INET); 1025 AF_INET);
1017 }
1018 1026
1019 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) 1027 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
1020 return -EINVAL; 1028 return -EINVAL;
@@ -1896,7 +1904,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
1896 /* Clean up the MD5 key list, if any */ 1904 /* Clean up the MD5 key list, if any */
1897 if (tp->md5sig_info) { 1905 if (tp->md5sig_info) {
1898 tcp_clear_md5_list(sk); 1906 tcp_clear_md5_list(sk);
1899 kfree(tp->md5sig_info); 1907 kfree_rcu(tp->md5sig_info, rcu);
1900 tp->md5sig_info = NULL; 1908 tp->md5sig_info = NULL;
1901 } 1909 }
1902#endif 1910#endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index bec41f9a6413..c25018106ef2 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -571,8 +571,6 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
571 return -EINVAL; 571 return -EINVAL;
572 572
573 if (!cmd.tcpm_keylen) { 573 if (!cmd.tcpm_keylen) {
574 if (!tcp_sk(sk)->md5sig_info)
575 return -ENOENT;
576 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) 574 if (ipv6_addr_v4mapped(&sin6->sin6_addr))
577 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], 575 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
578 AF_INET); 576 AF_INET);