aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
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/ipv6
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/ipv6')
-rw-r--r--net/ipv6/tcp_ipv6.c173
1 files changed, 12 insertions, 161 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f37769ea6375..bec41f9a6413 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -540,19 +540,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req)
540static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, 540static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
541 const struct in6_addr *addr) 541 const struct in6_addr *addr)
542{ 542{
543 struct tcp_sock *tp = tcp_sk(sk); 543 return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6);
544 int i;
545
546 BUG_ON(tp == NULL);
547
548 if (!tp->md5sig_info || !tp->md5sig_info->entries6)
549 return NULL;
550
551 for (i = 0; i < tp->md5sig_info->entries6; i++) {
552 if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, addr))
553 return &tp->md5sig_info->keys6[i].base;
554 }
555 return NULL;
556} 544}
557 545
558static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, 546static 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 return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr); 555 return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);
568} 556}
569 557
570static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer,
571 char *newkey, u8 newkeylen)
572{
573 /* Add key to the list */
574 struct tcp_md5sig_key *key;
575 struct tcp_sock *tp = tcp_sk(sk);
576 struct tcp6_md5sig_key *keys;
577
578 key = tcp_v6_md5_do_lookup(sk, peer);
579 if (key) {
580 /* modify existing entry - just update that one */
581 kfree(key->key);
582 key->key = newkey;
583 key->keylen = newkeylen;
584 } else {
585 /* reallocate new list if current one is full. */
586 if (!tp->md5sig_info) {
587 tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC);
588 if (!tp->md5sig_info) {
589 kfree(newkey);
590 return -ENOMEM;
591 }
592 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
593 }
594 if (tp->md5sig_info->entries6 == 0 &&
595 tcp_alloc_md5sig_pool(sk) == NULL) {
596 kfree(newkey);
597 return -ENOMEM;
598 }
599 if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) {
600 keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) *
601 (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC);
602
603 if (!keys) {
604 kfree(newkey);
605 if (tp->md5sig_info->entries6 == 0)
606 tcp_free_md5sig_pool();
607 return -ENOMEM;
608 }
609
610 if (tp->md5sig_info->entries6)
611 memmove(keys, tp->md5sig_info->keys6,
612 (sizeof (tp->md5sig_info->keys6[0]) *
613 tp->md5sig_info->entries6));
614
615 kfree(tp->md5sig_info->keys6);
616 tp->md5sig_info->keys6 = keys;
617 tp->md5sig_info->alloced6++;
618 }
619
620 tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr = *peer;
621 tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey;
622 tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen;
623
624 tp->md5sig_info->entries6++;
625 }
626 return 0;
627}
628
629static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer)
630{
631 struct tcp_sock *tp = tcp_sk(sk);
632 int i;
633
634 for (i = 0; i < tp->md5sig_info->entries6; i++) {
635 if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, peer)) {
636 /* Free the key */
637 kfree(tp->md5sig_info->keys6[i].base.key);
638 tp->md5sig_info->entries6--;
639
640 if (tp->md5sig_info->entries6 == 0) {
641 kfree(tp->md5sig_info->keys6);
642 tp->md5sig_info->keys6 = NULL;
643 tp->md5sig_info->alloced6 = 0;
644 tcp_free_md5sig_pool();
645 } else {
646 /* shrink the database */
647 if (tp->md5sig_info->entries6 != i)
648 memmove(&tp->md5sig_info->keys6[i],
649 &tp->md5sig_info->keys6[i+1],
650 (tp->md5sig_info->entries6 - i)
651 * sizeof (tp->md5sig_info->keys6[0]));
652 }
653 return 0;
654 }
655 }
656 return -ENOENT;
657}
658
659static void tcp_v6_clear_md5_list (struct sock *sk)
660{
661 struct tcp_sock *tp = tcp_sk(sk);
662 int i;
663
664 if (tp->md5sig_info->entries6) {
665 for (i = 0; i < tp->md5sig_info->entries6; i++)
666 kfree(tp->md5sig_info->keys6[i].base.key);
667 tp->md5sig_info->entries6 = 0;
668 tcp_free_md5sig_pool();
669 }
670
671 kfree(tp->md5sig_info->keys6);
672 tp->md5sig_info->keys6 = NULL;
673 tp->md5sig_info->alloced6 = 0;
674
675 if (tp->md5sig_info->entries4) {
676 for (i = 0; i < tp->md5sig_info->entries4; i++)
677 kfree(tp->md5sig_info->keys4[i].base.key);
678 tp->md5sig_info->entries4 = 0;
679 tcp_free_md5sig_pool();
680 }
681
682 kfree(tp->md5sig_info->keys4);
683 tp->md5sig_info->keys4 = NULL;
684 tp->md5sig_info->alloced4 = 0;
685}
686
687static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, 558static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
688 int optlen) 559 int optlen)
689{ 560{
690 struct tcp_md5sig cmd; 561 struct tcp_md5sig cmd;
691 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; 562 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
692 u8 *newkey;
693 563
694 if (optlen < sizeof(cmd)) 564 if (optlen < sizeof(cmd))
695 return -EINVAL; 565 return -EINVAL;
@@ -704,33 +574,21 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
704 if (!tcp_sk(sk)->md5sig_info) 574 if (!tcp_sk(sk)->md5sig_info)
705 return -ENOENT; 575 return -ENOENT;
706 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) 576 if (ipv6_addr_v4mapped(&sin6->sin6_addr))
707 return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]); 577 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
708 return tcp_v6_md5_do_del(sk, &sin6->sin6_addr); 578 AF_INET);
579 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
580 AF_INET6);
709 } 581 }
710 582
711 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) 583 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
712 return -EINVAL; 584 return -EINVAL;
713 585
714 if (!tcp_sk(sk)->md5sig_info) { 586 if (ipv6_addr_v4mapped(&sin6->sin6_addr))
715 struct tcp_sock *tp = tcp_sk(sk); 587 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
716 struct tcp_md5sig_info *p; 588 AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
717
718 p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL);
719 if (!p)
720 return -ENOMEM;
721
722 tp->md5sig_info = p;
723 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
724 }
725 589
726 newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); 590 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
727 if (!newkey) 591 AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
728 return -ENOMEM;
729 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) {
730 return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3],
731 newkey, cmd.tcpm_keylen);
732 }
733 return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen);
734} 592}
735 593
736static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, 594static 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 * memory, then we end up not copying the key 1361 * memory, then we end up not copying the key
1504 * across. Shucks. 1362 * across. Shucks.
1505 */ 1363 */
1506 char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); 1364 tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr,
1507 if (newkey != NULL) 1365 AF_INET6, key->key, key->keylen, GFP_ATOMIC);
1508 tcp_v6_md5_do_add(newsk, &newnp->daddr,
1509 newkey, key->keylen);
1510 } 1366 }
1511#endif 1367#endif
1512 1368
@@ -1995,11 +1851,6 @@ static int tcp_v6_init_sock(struct sock *sk)
1995 1851
1996static void tcp_v6_destroy_sock(struct sock *sk) 1852static void tcp_v6_destroy_sock(struct sock *sk)
1997{ 1853{
1998#ifdef CONFIG_TCP_MD5SIG
1999 /* Clean up the MD5 key list */
2000 if (tcp_sk(sk)->md5sig_info)
2001 tcp_v6_clear_md5_list(sk);
2002#endif
2003 tcp_v4_destroy_sock(sk); 1854 tcp_v4_destroy_sock(sk);
2004 inet6_destroy_sock(sk); 1855 inet6_destroy_sock(sk);
2005} 1856}