diff options
-rw-r--r-- | include/linux/tcp.h | 2 | ||||
-rw-r--r-- | include/net/tcp.h | 1 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 32 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 2 |
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 */ |
1151 | struct tcp_md5sig_info { | 1151 | struct 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); |