diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 231 |
1 files changed, 57 insertions, 174 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3edd05ae4388..12c6ece67f39 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) | |||
| 540 | static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, | 540 | static 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 | ||
| 558 | static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, | 546 | static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, |
| @@ -567,136 +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 | ||
| 570 | static 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 | |||
| 629 | static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk, | ||
| 630 | u8 *newkey, __u8 newkeylen) | ||
| 631 | { | ||
| 632 | return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr, | ||
| 633 | newkey, newkeylen); | ||
| 634 | } | ||
| 635 | |||
| 636 | static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer) | ||
| 637 | { | ||
| 638 | struct tcp_sock *tp = tcp_sk(sk); | ||
| 639 | int i; | ||
| 640 | |||
| 641 | for (i = 0; i < tp->md5sig_info->entries6; i++) { | ||
| 642 | if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, peer)) { | ||
| 643 | /* Free the key */ | ||
| 644 | kfree(tp->md5sig_info->keys6[i].base.key); | ||
| 645 | tp->md5sig_info->entries6--; | ||
| 646 | |||
| 647 | if (tp->md5sig_info->entries6 == 0) { | ||
| 648 | kfree(tp->md5sig_info->keys6); | ||
| 649 | tp->md5sig_info->keys6 = NULL; | ||
| 650 | tp->md5sig_info->alloced6 = 0; | ||
| 651 | tcp_free_md5sig_pool(); | ||
| 652 | } else { | ||
| 653 | /* shrink the database */ | ||
| 654 | if (tp->md5sig_info->entries6 != i) | ||
| 655 | memmove(&tp->md5sig_info->keys6[i], | ||
| 656 | &tp->md5sig_info->keys6[i+1], | ||
| 657 | (tp->md5sig_info->entries6 - i) | ||
| 658 | * sizeof (tp->md5sig_info->keys6[0])); | ||
| 659 | } | ||
| 660 | return 0; | ||
| 661 | } | ||
| 662 | } | ||
| 663 | return -ENOENT; | ||
| 664 | } | ||
| 665 | |||
| 666 | static void tcp_v6_clear_md5_list (struct sock *sk) | ||
| 667 | { | ||
| 668 | struct tcp_sock *tp = tcp_sk(sk); | ||
| 669 | int i; | ||
| 670 | |||
| 671 | if (tp->md5sig_info->entries6) { | ||
| 672 | for (i = 0; i < tp->md5sig_info->entries6; i++) | ||
| 673 | kfree(tp->md5sig_info->keys6[i].base.key); | ||
| 674 | tp->md5sig_info->entries6 = 0; | ||
| 675 | tcp_free_md5sig_pool(); | ||
| 676 | } | ||
| 677 | |||
| 678 | kfree(tp->md5sig_info->keys6); | ||
| 679 | tp->md5sig_info->keys6 = NULL; | ||
| 680 | tp->md5sig_info->alloced6 = 0; | ||
| 681 | |||
| 682 | if (tp->md5sig_info->entries4) { | ||
| 683 | for (i = 0; i < tp->md5sig_info->entries4; i++) | ||
| 684 | kfree(tp->md5sig_info->keys4[i].base.key); | ||
| 685 | tp->md5sig_info->entries4 = 0; | ||
| 686 | tcp_free_md5sig_pool(); | ||
| 687 | } | ||
| 688 | |||
| 689 | kfree(tp->md5sig_info->keys4); | ||
| 690 | tp->md5sig_info->keys4 = NULL; | ||
| 691 | tp->md5sig_info->alloced4 = 0; | ||
| 692 | } | ||
| 693 | |||
| 694 | static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, | 558 | static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, |
| 695 | int optlen) | 559 | int optlen) |
| 696 | { | 560 | { |
| 697 | struct tcp_md5sig cmd; | 561 | struct tcp_md5sig cmd; |
| 698 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; | 562 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; |
| 699 | u8 *newkey; | ||
| 700 | 563 | ||
| 701 | if (optlen < sizeof(cmd)) | 564 | if (optlen < sizeof(cmd)) |
| 702 | return -EINVAL; | 565 | return -EINVAL; |
| @@ -708,36 +571,22 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, | |||
| 708 | return -EINVAL; | 571 | return -EINVAL; |
| 709 | 572 | ||
| 710 | if (!cmd.tcpm_keylen) { | 573 | if (!cmd.tcpm_keylen) { |
| 711 | if (!tcp_sk(sk)->md5sig_info) | ||
| 712 | return -ENOENT; | ||
| 713 | if (ipv6_addr_v4mapped(&sin6->sin6_addr)) | 574 | if (ipv6_addr_v4mapped(&sin6->sin6_addr)) |
| 714 | return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]); | 575 | return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], |
| 715 | return tcp_v6_md5_do_del(sk, &sin6->sin6_addr); | 576 | AF_INET); |
| 577 | return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr, | ||
| 578 | AF_INET6); | ||
| 716 | } | 579 | } |
| 717 | 580 | ||
| 718 | if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) | 581 | if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) |
| 719 | return -EINVAL; | 582 | return -EINVAL; |
| 720 | 583 | ||
| 721 | if (!tcp_sk(sk)->md5sig_info) { | 584 | if (ipv6_addr_v4mapped(&sin6->sin6_addr)) |
| 722 | struct tcp_sock *tp = tcp_sk(sk); | 585 | return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], |
| 723 | struct tcp_md5sig_info *p; | 586 | AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); |
| 724 | |||
| 725 | p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL); | ||
| 726 | if (!p) | ||
| 727 | return -ENOMEM; | ||
| 728 | 587 | ||
| 729 | tp->md5sig_info = p; | 588 | return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, |
| 730 | sk_nocaps_add(sk, NETIF_F_GSO_MASK); | 589 | AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); |
| 731 | } | ||
| 732 | |||
| 733 | newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); | ||
| 734 | if (!newkey) | ||
| 735 | return -ENOMEM; | ||
| 736 | if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { | ||
| 737 | return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3], | ||
| 738 | newkey, cmd.tcpm_keylen); | ||
| 739 | } | ||
| 740 | return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen); | ||
| 741 | } | 590 | } |
| 742 | 591 | ||
| 743 | static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, | 592 | static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, |
| @@ -1074,6 +923,13 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) | |||
| 1074 | const struct tcphdr *th = tcp_hdr(skb); | 923 | const struct tcphdr *th = tcp_hdr(skb); |
| 1075 | u32 seq = 0, ack_seq = 0; | 924 | u32 seq = 0, ack_seq = 0; |
| 1076 | struct tcp_md5sig_key *key = NULL; | 925 | struct tcp_md5sig_key *key = NULL; |
| 926 | #ifdef CONFIG_TCP_MD5SIG | ||
| 927 | const __u8 *hash_location = NULL; | ||
| 928 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||
| 929 | unsigned char newhash[16]; | ||
| 930 | int genhash; | ||
| 931 | struct sock *sk1 = NULL; | ||
| 932 | #endif | ||
| 1077 | 933 | ||
| 1078 | if (th->rst) | 934 | if (th->rst) |
| 1079 | return; | 935 | return; |
| @@ -1082,8 +938,32 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) | |||
| 1082 | return; | 938 | return; |
| 1083 | 939 | ||
| 1084 | #ifdef CONFIG_TCP_MD5SIG | 940 | #ifdef CONFIG_TCP_MD5SIG |
| 1085 | if (sk) | 941 | hash_location = tcp_parse_md5sig_option(th); |
| 1086 | key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr); | 942 | if (!sk && hash_location) { |
| 943 | /* | ||
| 944 | * active side is lost. Try to find listening socket through | ||
| 945 | * source port, and then find md5 key through listening socket. | ||
| 946 | * we are not loose security here: | ||
| 947 | * Incoming packet is checked with md5 hash with finding key, | ||
| 948 | * no RST generated if md5 hash doesn't match. | ||
| 949 | */ | ||
| 950 | sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev), | ||
| 951 | &tcp_hashinfo, &ipv6h->daddr, | ||
| 952 | ntohs(th->source), inet6_iif(skb)); | ||
| 953 | if (!sk1) | ||
| 954 | return; | ||
| 955 | |||
| 956 | rcu_read_lock(); | ||
| 957 | key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr); | ||
| 958 | if (!key) | ||
| 959 | goto release_sk1; | ||
| 960 | |||
| 961 | genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, NULL, skb); | ||
| 962 | if (genhash || memcmp(hash_location, newhash, 16) != 0) | ||
| 963 | goto release_sk1; | ||
| 964 | } else { | ||
| 965 | key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL; | ||
| 966 | } | ||
| 1087 | #endif | 967 | #endif |
| 1088 | 968 | ||
| 1089 | if (th->ack) | 969 | if (th->ack) |
| @@ -1093,6 +973,14 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) | |||
| 1093 | (th->doff << 2); | 973 | (th->doff << 2); |
| 1094 | 974 | ||
| 1095 | tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0); | 975 | tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0); |
| 976 | |||
| 977 | #ifdef CONFIG_TCP_MD5SIG | ||
| 978 | release_sk1: | ||
| 979 | if (sk1) { | ||
| 980 | rcu_read_unlock(); | ||
| 981 | sock_put(sk1); | ||
| 982 | } | ||
| 983 | #endif | ||
| 1096 | } | 984 | } |
| 1097 | 985 | ||
| 1098 | static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, | 986 | static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, |
| @@ -1394,6 +1282,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
| 1394 | newnp->opt = NULL; | 1282 | newnp->opt = NULL; |
| 1395 | newnp->mcast_oif = inet6_iif(skb); | 1283 | newnp->mcast_oif = inet6_iif(skb); |
| 1396 | newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; | 1284 | newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; |
| 1285 | newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); | ||
| 1397 | 1286 | ||
| 1398 | /* | 1287 | /* |
| 1399 | * No need to charge this sock to the relevant IPv6 refcnt debug socks count | 1288 | * No need to charge this sock to the relevant IPv6 refcnt debug socks count |
| @@ -1472,6 +1361,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
| 1472 | newnp->opt = NULL; | 1361 | newnp->opt = NULL; |
| 1473 | newnp->mcast_oif = inet6_iif(skb); | 1362 | newnp->mcast_oif = inet6_iif(skb); |
| 1474 | newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; | 1363 | newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; |
| 1364 | newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); | ||
| 1475 | 1365 | ||
| 1476 | /* Clone native IPv6 options from listening socket (if any) | 1366 | /* Clone native IPv6 options from listening socket (if any) |
| 1477 | 1367 | ||
| @@ -1510,10 +1400,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
| 1510 | * memory, then we end up not copying the key | 1400 | * memory, then we end up not copying the key |
| 1511 | * across. Shucks. | 1401 | * across. Shucks. |
| 1512 | */ | 1402 | */ |
| 1513 | char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); | 1403 | tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr, |
| 1514 | if (newkey != NULL) | 1404 | AF_INET6, key->key, key->keylen, GFP_ATOMIC); |
| 1515 | tcp_v6_md5_do_add(newsk, &newnp->daddr, | ||
| 1516 | newkey, key->keylen); | ||
| 1517 | } | 1405 | } |
| 1518 | #endif | 1406 | #endif |
| 1519 | 1407 | ||
| @@ -1676,6 +1564,8 @@ ipv6_pktoptions: | |||
| 1676 | np->mcast_oif = inet6_iif(opt_skb); | 1564 | np->mcast_oif = inet6_iif(opt_skb); |
| 1677 | if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) | 1565 | if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) |
| 1678 | np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; | 1566 | np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; |
| 1567 | if (np->rxopt.bits.rxtclass) | ||
| 1568 | np->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); | ||
| 1679 | if (ipv6_opt_accepted(sk, opt_skb)) { | 1569 | if (ipv6_opt_accepted(sk, opt_skb)) { |
| 1680 | skb_set_owner_r(opt_skb, sk); | 1570 | skb_set_owner_r(opt_skb, sk); |
| 1681 | opt_skb = xchg(&np->pktoptions, opt_skb); | 1571 | opt_skb = xchg(&np->pktoptions, opt_skb); |
| @@ -1898,7 +1788,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = { | |||
| 1898 | static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { | 1788 | static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { |
| 1899 | .md5_lookup = tcp_v6_md5_lookup, | 1789 | .md5_lookup = tcp_v6_md5_lookup, |
| 1900 | .calc_md5_hash = tcp_v6_md5_hash_skb, | 1790 | .calc_md5_hash = tcp_v6_md5_hash_skb, |
| 1901 | .md5_add = tcp_v6_md5_add_func, | ||
| 1902 | .md5_parse = tcp_v6_parse_md5_keys, | 1791 | .md5_parse = tcp_v6_parse_md5_keys, |
| 1903 | }; | 1792 | }; |
| 1904 | #endif | 1793 | #endif |
| @@ -1930,7 +1819,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = { | |||
| 1930 | static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { | 1819 | static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { |
| 1931 | .md5_lookup = tcp_v4_md5_lookup, | 1820 | .md5_lookup = tcp_v4_md5_lookup, |
| 1932 | .calc_md5_hash = tcp_v4_md5_hash_skb, | 1821 | .calc_md5_hash = tcp_v4_md5_hash_skb, |
| 1933 | .md5_add = tcp_v6_md5_add_func, | ||
| 1934 | .md5_parse = tcp_v6_parse_md5_keys, | 1822 | .md5_parse = tcp_v6_parse_md5_keys, |
| 1935 | }; | 1823 | }; |
| 1936 | #endif | 1824 | #endif |
| @@ -2004,11 +1892,6 @@ static int tcp_v6_init_sock(struct sock *sk) | |||
| 2004 | 1892 | ||
| 2005 | static void tcp_v6_destroy_sock(struct sock *sk) | 1893 | static void tcp_v6_destroy_sock(struct sock *sk) |
| 2006 | { | 1894 | { |
| 2007 | #ifdef CONFIG_TCP_MD5SIG | ||
| 2008 | /* Clean up the MD5 key list */ | ||
| 2009 | if (tcp_sk(sk)->md5sig_info) | ||
| 2010 | tcp_v6_clear_md5_list(sk); | ||
| 2011 | #endif | ||
| 2012 | tcp_v4_destroy_sock(sk); | 1895 | tcp_v4_destroy_sock(sk); |
| 2013 | inet6_destroy_sock(sk); | 1896 | inet6_destroy_sock(sk); |
| 2014 | } | 1897 | } |
