Commit a915da9b authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <eric.dumazet@gmail.com>
Cc: Shawn Lu <shawn.lu@ericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a2d91241
...@@ -486,8 +486,7 @@ struct tcp_timewait_sock { ...@@ -486,8 +486,7 @@ struct tcp_timewait_sock {
u32 tw_ts_recent; u32 tw_ts_recent;
long tw_ts_recent_stamp; long tw_ts_recent_stamp;
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
u16 tw_md5_keylen; struct tcp_md5sig_key *tw_md5_key;
u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN];
#endif #endif
/* Few sockets in timewait have cookies; in that case, then this /* Few sockets in timewait have cookies; in that case, then this
* object holds a reference to them (tw_cookie_values->kref). * object holds a reference to them (tw_cookie_values->kref).
......
...@@ -1130,35 +1130,26 @@ static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp) ...@@ -1130,35 +1130,26 @@ static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp)
/* MD5 Signature */ /* MD5 Signature */
struct crypto_hash; struct crypto_hash;
union tcp_md5_addr {
struct in_addr a4;
#if IS_ENABLED(CONFIG_IPV6)
struct in6_addr a6;
#endif
};
/* - key database */ /* - key database */
struct tcp_md5sig_key { struct tcp_md5sig_key {
u8 *key; struct hlist_node node;
u8 keylen; u8 keylen;
}; u8 family; /* AF_INET or AF_INET6 */
union tcp_md5_addr addr;
struct tcp4_md5sig_key { u8 key[TCP_MD5SIG_MAXKEYLEN];
struct tcp_md5sig_key base; struct rcu_head rcu;
__be32 addr;
};
struct tcp6_md5sig_key {
struct tcp_md5sig_key base;
#if 0
u32 scope_id; /* XXX */
#endif
struct in6_addr addr;
}; };
/* - sock block */ /* - sock block */
struct tcp_md5sig_info { struct tcp_md5sig_info {
struct tcp4_md5sig_key *keys4; struct hlist_head head;
#if IS_ENABLED(CONFIG_IPV6)
struct tcp6_md5sig_key *keys6;
u32 entries6;
u32 alloced6;
#endif
u32 entries4;
u32 alloced4;
}; };
/* - pseudo header */ /* - pseudo header */
...@@ -1195,19 +1186,25 @@ extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, ...@@ -1195,19 +1186,25 @@ extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
const struct sock *sk, const struct sock *sk,
const struct request_sock *req, const struct request_sock *req,
const struct sk_buff *skb); const struct sk_buff *skb);
extern struct tcp_md5sig_key * tcp_v4_md5_lookup(struct sock *sk, extern int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
int family, const u8 *newkey,
u8 newkeylen, gfp_t gfp);
extern int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
int family);
extern struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
struct sock *addr_sk); struct sock *addr_sk);
extern int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, u8 *newkey,
u8 newkeylen);
extern int tcp_v4_md5_do_del(struct sock *sk, __be32 addr);
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
#define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_keylen ? \ extern struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
&(struct tcp_md5sig_key) { \ const union tcp_md5_addr *addr, int family);
.key = (twsk)->tw_md5_key, \ #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key)
.keylen = (twsk)->tw_md5_keylen, \
} : NULL)
#else #else
static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
const union tcp_md5_addr *addr,
int family)
{
return NULL;
}
#define tcp_twsk_md5_key(twsk) NULL #define tcp_twsk_md5_key(twsk) NULL
#endif #endif
......
...@@ -90,16 +90,8 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency); ...@@ -90,16 +90,8 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency);
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
__be32 addr);
static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
__be32 daddr, __be32 saddr, const struct tcphdr *th); __be32 daddr, __be32 saddr, const struct tcphdr *th);
#else
static inline
struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
{
return NULL;
}
#endif #endif
struct inet_hashinfo tcp_hashinfo; struct inet_hashinfo tcp_hashinfo;
...@@ -631,7 +623,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) ...@@ -631,7 +623,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_len = sizeof(rep.th); arg.iov[0].iov_len = sizeof(rep.th);
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL; key = sk ? tcp_md5_do_lookup(sk,
(union tcp_md5_addr *)&ip_hdr(skb)->saddr,
AF_INET) : NULL;
if (key) { if (key) {
rep.opt[0] = htonl((TCPOPT_NOP << 24) | rep.opt[0] = htonl((TCPOPT_NOP << 24) |
(TCPOPT_NOP << 16) | (TCPOPT_NOP << 16) |
...@@ -759,7 +753,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, ...@@ -759,7 +753,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
req->ts_recent, req->ts_recent,
0, 0,
tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr), tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr,
AF_INET),
inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
ip_hdr(skb)->tos); ip_hdr(skb)->tos);
} }
...@@ -876,146 +871,124 @@ static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk, ...@@ -876,146 +871,124 @@ static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk,
*/ */
/* Find the Key structure for an address. */ /* Find the Key structure for an address. */
static struct tcp_md5sig_key * struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) const union tcp_md5_addr *addr,
int family)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
int i; struct tcp_md5sig_key *key;
struct hlist_node *pos;
unsigned int size = sizeof(struct in_addr);
if (!tp->md5sig_info || !tp->md5sig_info->entries4) if (!tp->md5sig_info)
return NULL; return NULL;
for (i = 0; i < tp->md5sig_info->entries4; i++) { #if IS_ENABLED(CONFIG_IPV6)
if (tp->md5sig_info->keys4[i].addr == addr) if (family == AF_INET6)
return &tp->md5sig_info->keys4[i].base; size = sizeof(struct in6_addr);
#endif
hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) {
if (key->family != family)
continue;
if (!memcmp(&key->addr, addr, size))
return key;
} }
return NULL; return NULL;
} }
EXPORT_SYMBOL(tcp_md5_do_lookup);
struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
struct sock *addr_sk) struct sock *addr_sk)
{ {
return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->inet_daddr); union tcp_md5_addr *addr;
addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr;
return tcp_md5_do_lookup(sk, addr, AF_INET);
} }
EXPORT_SYMBOL(tcp_v4_md5_lookup); EXPORT_SYMBOL(tcp_v4_md5_lookup);
static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
struct request_sock *req) struct request_sock *req)
{ {
return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr); union tcp_md5_addr *addr;
addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr;
return tcp_md5_do_lookup(sk, addr, AF_INET);
} }
/* This can be called on a newly created socket, from other files */ /* This can be called on a newly created socket, from other files */
int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
u8 *newkey, u8 newkeylen) int family, const u8 *newkey, u8 newkeylen, gfp_t gfp)
{ {
/* Add Key to the list */ /* Add Key to the list */
struct tcp_md5sig_key *key; struct tcp_md5sig_key *key;
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
struct tcp4_md5sig_key *keys; struct tcp_md5sig_info *md5sig;
key = tcp_v4_md5_do_lookup(sk, addr); key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
if (key) { if (key) {
/* Pre-existing entry - just update that one. */ /* Pre-existing entry - just update that one. */
kfree(key->key); memcpy(key->key, newkey, newkeylen);
key->key = newkey;
key->keylen = newkeylen; key->keylen = newkeylen;
} else { return 0;
struct tcp_md5sig_info *md5sig;
if (!tp->md5sig_info) {
tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info),
GFP_ATOMIC);
if (!tp->md5sig_info) {
kfree(newkey);
return -ENOMEM;
}
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
} }
md5sig = tp->md5sig_info; md5sig = tp->md5sig_info;
if (md5sig->entries4 == 0 && if (!md5sig) {
tcp_alloc_md5sig_pool(sk) == NULL) { md5sig = kmalloc(sizeof(*md5sig), gfp);
kfree(newkey); if (!md5sig)
return -ENOMEM; return -ENOMEM;
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
INIT_HLIST_HEAD(&md5sig->head);
tp->md5sig_info = md5sig;
} }
if (md5sig->alloced4 == md5sig->entries4) { key = kmalloc(sizeof(*key), gfp);
keys = kmalloc((sizeof(*keys) * if (!key)
(md5sig->entries4 + 1)), GFP_ATOMIC); return -ENOMEM;
if (!keys) { if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) {
kfree(newkey); kfree(key);
if (md5sig->entries4 == 0)
tcp_free_md5sig_pool();
return -ENOMEM; return -ENOMEM;
} }
if (md5sig->entries4) memcpy(key->key, newkey, newkeylen);
memcpy(keys, md5sig->keys4, key->keylen = newkeylen;
sizeof(*keys) * md5sig->entries4); key->family = family;
memcpy(&key->addr, addr,
/* Free old key list, and reference new one */ (family == AF_INET6) ? sizeof(struct in6_addr) :
kfree(md5sig->keys4); sizeof(struct in_addr));
md5sig->keys4 = keys; hlist_add_head_rcu(&key->node, &md5sig->head);
md5sig->alloced4++;
}
md5sig->entries4++;
md5sig->keys4[md5sig->entries4 - 1].addr = addr;
md5sig->keys4[md5sig->entries4 - 1].base.key = newkey;
md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen;
}
return 0; return 0;
} }
EXPORT_SYMBOL(tcp_v4_md5_do_add); EXPORT_SYMBOL(tcp_md5_do_add);
int tcp_v4_md5_do_del(struct sock *sk, __be32 addr) int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
int i; struct tcp_md5sig_key *key;
for (i = 0; i < tp->md5sig_info->entries4; i++) { key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
if (tp->md5sig_info->keys4[i].addr == addr) { if (!key)
/* Free the key */ return -ENOENT;
kfree(tp->md5sig_info->keys4[i].base.key); hlist_del_rcu(&key->node);
tp->md5sig_info->entries4--; kfree_rcu(key, rcu);
if (hlist_empty(&tp->md5sig_info->head))
if (tp->md5sig_info->entries4 == 0) {
kfree(tp->md5sig_info->keys4);
tp->md5sig_info->keys4 = NULL;
tp->md5sig_info->alloced4 = 0;
tcp_free_md5sig_pool(); tcp_free_md5sig_pool();
} else if (tp->md5sig_info->entries4 != i) {
/* Need to do some manipulation */
memmove(&tp->md5sig_info->keys4[i],
&tp->md5sig_info->keys4[i+1],
(tp->md5sig_info->entries4 - i) *
sizeof(struct tcp4_md5sig_key));
}
return 0; return 0;
}
}
return -ENOENT;
} }
EXPORT_SYMBOL(tcp_v4_md5_do_del); EXPORT_SYMBOL(tcp_md5_do_del);
static void tcp_v4_clear_md5_list(struct sock *sk) void tcp_clear_md5_list(struct sock *sk)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
struct tcp_md5sig_key *key;
struct hlist_node *pos, *n;
/* Free each key, then the set of key keys, if (!hlist_empty(&tp->md5sig_info->head))
* the crypto element, and then decrement our
* hold on the last resort crypto.
*/
if (tp->md5sig_info->entries4) {
int i;
for (i = 0; i < tp->md5sig_info->entries4; i++)
kfree(tp->md5sig_info->keys4[i].base.key);
tp->md5sig_info->entries4 = 0;
tcp_free_md5sig_pool(); tcp_free_md5sig_pool();
} hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) {
if (tp->md5sig_info->keys4) { hlist_del_rcu(&key->node);
kfree(tp->md5sig_info->keys4); kfree_rcu(key, rcu);
tp->md5sig_info->keys4 = NULL;
tp->md5sig_info->alloced4 = 0;
} }
} }
...@@ -1024,7 +997,6 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, ...@@ -1024,7 +997,6 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
{ {
struct tcp_md5sig cmd; struct tcp_md5sig cmd;
struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr; struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr;
u8 *newkey;
if (optlen < sizeof(cmd)) if (optlen < sizeof(cmd))
return -EINVAL; return -EINVAL;
...@@ -1038,29 +1010,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, ...@@ -1038,29 +1010,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
if (!cmd.tcpm_key || !cmd.tcpm_keylen) { if (!cmd.tcpm_key || !cmd.tcpm_keylen) {
if (!tcp_sk(sk)->md5sig_info) if (!tcp_sk(sk)->md5sig_info)
return -ENOENT; return -ENOENT;
return tcp_v4_md5_do_del(sk, sin->sin_addr.s_addr); return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
AF_INET);
} }
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
return -EINVAL; return -EINVAL;
if (!tcp_sk(sk)->md5sig_info) { return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
struct tcp_sock *tp = tcp_sk(sk); AF_INET, cmd.tcpm_key, cmd.tcpm_keylen,
struct tcp_md5sig_info *p; GFP_KERNEL);
p = kzalloc(sizeof(*p), sk->sk_allocation);
if (!p)
return -EINVAL;
tp->md5sig_info = p;
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
}
newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation);
if (!newkey)
return -ENOMEM;
return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr,
newkey, cmd.tcpm_keylen);
} }
static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, static 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,7 +1045,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp)); return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
} }
static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
__be32 daddr, __be32 saddr, const struct tcphdr *th) __be32 daddr, __be32 saddr, const struct tcphdr *th)
{ {
struct tcp_md5sig_pool *hp; 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,7 +1145,8 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
int genhash; int genhash;
unsigned char newhash[16]; unsigned char newhash[16];
hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); hash_expected = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr,
AF_INET);
hash_location = tcp_parse_md5sig_option(th); hash_location = tcp_parse_md5sig_option(th);
/* We've parsed the options - do we have a hash? */ /* 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,7 +1434,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
/* Copy over the MD5 key from the original socket */ /* Copy over the MD5 key from the original socket */
key = tcp_v4_md5_do_lookup(sk, newinet->inet_daddr); key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr,
AF_INET);
if (key != NULL) { if (key != NULL) {
/* /*
* We're using one, so create a matching key * 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,10 +1443,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* memory, then we end up not copying the key * memory, then we end up not copying the key
* across. Shucks. * across. Shucks.
*/ */
char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr,
if (newkey != NULL) AF_INET, key->key, key->keylen, GFP_ATOMIC);
tcp_v4_md5_do_add(newsk, newinet->inet_daddr,
newkey, key->keylen);
sk_nocaps_add(newsk, NETIF_F_GSO_MASK); sk_nocaps_add(newsk, NETIF_F_GSO_MASK);
} }
#endif #endif
...@@ -1934,7 +1893,7 @@ void tcp_v4_destroy_sock(struct sock *sk) ...@@ -1934,7 +1893,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
/* Clean up the MD5 key list, if any */ /* Clean up the MD5 key list, if any */
if (tp->md5sig_info) { if (tp->md5sig_info) {
tcp_v4_clear_md5_list(sk); tcp_clear_md5_list(sk);
kfree(tp->md5sig_info); kfree(tp->md5sig_info);
tp->md5sig_info = NULL; tp->md5sig_info = NULL;
} }
......
...@@ -359,13 +359,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) ...@@ -359,13 +359,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
*/ */
do { do {
struct tcp_md5sig_key *key; struct tcp_md5sig_key *key;
memset(tcptw->tw_md5_key, 0, sizeof(tcptw->tw_md5_key)); tcptw->tw_md5_key = NULL;
tcptw->tw_md5_keylen = 0;
key = tp->af_specific->md5_lookup(sk, sk); key = tp->af_specific->md5_lookup(sk, sk);
if (key != NULL) { if (key != NULL) {
memcpy(&tcptw->tw_md5_key, key->key, key->keylen); tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
tcptw->tw_md5_keylen = key->keylen; if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL)
if (tcp_alloc_md5sig_pool(sk) == NULL)
BUG(); BUG();
} }
} while (0); } while (0);
...@@ -405,8 +403,10 @@ void tcp_twsk_destructor(struct sock *sk) ...@@ -405,8 +403,10 @@ void tcp_twsk_destructor(struct sock *sk)
{ {
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
struct tcp_timewait_sock *twsk = tcp_twsk(sk); struct tcp_timewait_sock *twsk = tcp_twsk(sk);
if (twsk->tw_md5_keylen) if (twsk->tw_md5_key) {
tcp_free_md5sig_pool(); tcp_free_md5sig_pool();
kfree_rcu(twsk->tw_md5_key, rcu);
}
#endif #endif
} }
EXPORT_SYMBOL_GPL(tcp_twsk_destructor); EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
......
...@@ -540,19 +540,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req) ...@@ -540,19 +540,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req)
static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
const struct in6_addr *addr) const struct in6_addr *addr)
{ {
struct tcp_sock *tp = tcp_sk(sk); return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6);
int i;
BUG_ON(tp == NULL);
if (!tp->md5sig_info || !tp->md5sig_info->entries6)
return NULL;
for (i = 0; i < tp->md5sig_info->entries6; i++) {
if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, addr))
return &tp->md5sig_info->keys6[i].base;
}
return NULL;
} }
static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk,
...@@ -567,129 +555,11 @@ static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk, ...@@ -567,129 +555,11 @@ static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk,
return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr); return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);
} }
static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer,
char *newkey, u8 newkeylen)
{
/* Add key to the list */
struct tcp_md5sig_key *key;
struct tcp_sock *tp = tcp_sk(sk);
struct tcp6_md5sig_key *keys;
key = tcp_v6_md5_do_lookup(sk, peer);
if (key) {
/* modify existing entry - just update that one */
kfree(key->key);
key->key = newkey;
key->keylen = newkeylen;
} else {
/* reallocate new list if current one is full. */
if (!tp->md5sig_info) {
tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC);
if (!tp->md5sig_info) {
kfree(newkey);
return -ENOMEM;
}
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
}
if (tp->md5sig_info->entries6 == 0 &&
tcp_alloc_md5sig_pool(sk) == NULL) {
kfree(newkey);
return -ENOMEM;
}
if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) {
keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) *
(tp->md5sig_info->entries6 + 1)), GFP_ATOMIC);
if (!keys) {
kfree(newkey);
if (tp->md5sig_info->entries6 == 0)
tcp_free_md5sig_pool();
return -ENOMEM;
}
if (tp->md5sig_info->entries6)
memmove(keys, tp->md5sig_info->keys6,
(sizeof (tp->md5sig_info->keys6[0]) *
tp->md5sig_info->entries6));
kfree(tp->md5sig_info->keys6);
tp->md5sig_info->keys6 = keys;
tp->md5sig_info->alloced6++;
}
tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr = *peer;
tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey;
tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen;
tp->md5sig_info->entries6++;
}
return 0;
}
static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer)
{
struct tcp_sock *tp = tcp_sk(sk);
int i;
for (i = 0; i < tp->md5sig_info->entries6; i++) {
if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, peer)) {
/* Free the key */
kfree(tp->md5sig_info->keys6[i].base.key);
tp->md5sig_info->entries6--;
if (tp->md5sig_info->entries6 == 0) {
kfree(tp->md5sig_info->keys6);
tp->md5sig_info->keys6 = NULL;
tp->md5sig_info->alloced6 = 0;
tcp_free_md5sig_pool();
} else {
/* shrink the database */
if (tp->md5sig_info->entries6 != i)
memmove(&tp->md5sig_info->keys6[i],
&tp->md5sig_info->keys6[i+1],
(tp->md5sig_info->entries6 - i)
* sizeof (tp->md5sig_info->keys6[0]));
}
return 0;
}
}
return -ENOENT;
}
static void tcp_v6_clear_md5_list (struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
int i;
if (tp->md5sig_info->entries6) {
for (i = 0; i < tp->md5sig_info->entries6; i++)
kfree(tp->md5sig_info->keys6[i].base.key);
tp->md5sig_info->entries6 = 0;
tcp_free_md5sig_pool();
}
kfree(tp->md5sig_info->keys6);
tp->md5sig_info->keys6 = NULL;
tp->md5sig_info->alloced6 = 0;
if (tp->md5sig_info->entries4) {
for (i = 0; i < tp->md5sig_info->entries4; i++)
kfree(tp->md5sig_info->keys4[i].base.key);
tp->md5sig_info->entries4 = 0;
tcp_free_md5sig_pool();
}
kfree(tp->md5sig_info->keys4);
tp->md5sig_info->keys4 = NULL;
tp->md5sig_info->alloced4 = 0;
}
static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
int optlen) int optlen)
{ {
struct tcp_md5sig cmd; struct tcp_md5sig cmd;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
u8 *newkey;
if (optlen < sizeof(cmd)) if (optlen < sizeof(cmd))
return -EINVAL; return -EINVAL;
...@@ -704,33 +574,21 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, ...@@ -704,33 +574,21 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
if (!tcp_sk(sk)->md5sig_info) if (!tcp_sk(sk)->md5sig_info)
return -ENOENT; return -ENOENT;
if (ipv6_addr_v4mapped(&sin6->sin6_addr)) if (ipv6_addr_v4mapped(&sin6->sin6_addr))
return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]); return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
return tcp_v6_md5_do_del(sk, &sin6->sin6_addr); AF_INET);
return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
AF_INET6);
} }
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
return -EINVAL; return -EINVAL;
if (!tcp_sk(sk)->md5sig_info) { if (ipv6_addr_v4mapped(&sin6->sin6_addr))
struct tcp_sock *tp = tcp_sk(sk); return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
struct tcp_md5sig_info *p; AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL);
if (!p)
return -ENOMEM;
tp->md5sig_info = p;
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
}
newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
if (!newkey) AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
return -ENOMEM;
if (ipv6_addr_v4mapped(&sin6->sin6_addr)) {
return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3],
newkey, cmd.tcpm_keylen);
}
return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen);
} }
static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
...@@ -1503,10 +1361,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ...@@ -1503,10 +1361,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* memory, then we end up not copying the key * memory, then we end up not copying the key
* across. Shucks. * across. Shucks.
*/ */
char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr,
if (newkey != NULL) AF_INET6, key->key, key->keylen, GFP_ATOMIC);
tcp_v6_md5_do_add(newsk, &newnp->daddr,
newkey, key->keylen);
} }
#endif #endif
...@@ -1995,11 +1851,6 @@ static int tcp_v6_init_sock(struct sock *sk) ...@@ -1995,11 +1851,6 @@ static int tcp_v6_init_sock(struct sock *sk)
static void tcp_v6_destroy_sock(struct sock *sk) static void tcp_v6_destroy_sock(struct sock *sk)
{ {
#ifdef CONFIG_TCP_MD5SIG
/* Clean up the MD5 key list */
if (tcp_sk(sk)->md5sig_info)
tcp_v6_clear_md5_list(sk);
#endif
tcp_v4_destroy_sock(sk); tcp_v4_destroy_sock(sk);
inet6_destroy_sock(sk); inet6_destroy_sock(sk);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment